mirror of
https://github.com/libretro/quake2-for-ps2.git
synced 2026-11-07 04:25:58 -10:00
Committing the initial state of the project.
First commit including the original Q2 source plus initial PS2 framework and rendering basics.
This commit is contained in:
1 parent
a8925612eb
commit
95c02097ce
165 files changed
+116944
-61
No files matched your search
@@ -279,62 +279,3 @@ POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
|
||||
#
|
||||
# !!! NOTE: Header files are completely ignored !!!
|
||||
# If you change a header file, either make clean or
|
||||
# change the accompanying source files to force a rebuild!
|
||||
#
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Quake 2 source files:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# All files used by the game and engine:
|
||||
SRC_FILES = \
|
||||
ps2/tests/test_draw2d.c \
|
||||
ps2/builtin/palette.c \
|
||||
ps2/builtin/conchars.c \
|
||||
ps2/builtin/conback.c \
|
||||
ps2/builtin/inventory.c \
|
||||
ps2/builtin/backtile.c \
|
||||
ps2/builtin/help.c \
|
||||
ps2/debug_print.c \
|
||||
ps2/mem_alloc.c \
|
||||
ps2/sys_ps2.c \
|
||||
ps2/vid_ps2.c \
|
||||
ps2/ref_ps2.c \
|
||||
ps2/tex_image.c \
|
||||
ps2/math_funcs.c \
|
||||
ps2/main_ps2.c \
|
||||
null/in_null.c \
|
||||
null/net_null.c \
|
||||
null/cd_null.c \
|
||||
null/snddma_null.c \
|
||||
common/cmd.c \
|
||||
common/cmodel.c \
|
||||
common/common.c \
|
||||
common/crc.c \
|
||||
common/cvar.c \
|
||||
common/filesys.c \
|
||||
common/md4.c \
|
||||
common/net_chan.c \
|
||||
common/pmove.c \
|
||||
game/g_ai.c \
|
||||
game/g_chase.c \
|
||||
game/g_cmds.c \
|
||||
game/g_combat.c \
|
||||
game/g_func.c \
|
||||
game/g_items.c \
|
||||
game/g_main.c \
|
||||
game/g_misc.c \
|
||||
game/g_monster.c \
|
||||
game/g_phys.c \
|
||||
game/g_save.c \
|
||||
game/g_spawn.c \
|
||||
game/g_svcmds.c \
|
||||
game/g_target.c \
|
||||
game/g_trigger.c \
|
||||
game/g_turret.c \
|
||||
game/g_utils.c \
|
||||
game/g_weapon.c \
|
||||
game/m_actor.c \
|
||||
game/m_berserk.c \
|
||||
game/m_boss2.c \
|
||||
game/m_boss3.c \
|
||||
game/m_boss31.c \
|
||||
game/m_boss32.c \
|
||||
game/m_brain.c \
|
||||
game/m_chick.c \
|
||||
game/m_flash.c \
|
||||
game/m_flipper.c \
|
||||
game/m_float.c \
|
||||
game/m_flyer.c \
|
||||
game/m_gladiator.c \
|
||||
game/m_gunner.c \
|
||||
game/m_hover.c \
|
||||
game/m_infantry.c \
|
||||
game/m_insane.c \
|
||||
game/m_medic.c \
|
||||
game/m_move.c \
|
||||
game/m_mutant.c \
|
||||
game/m_parasite.c \
|
||||
game/m_soldier.c \
|
||||
game/m_supertank.c \
|
||||
game/m_tank.c \
|
||||
game/p_client.c \
|
||||
game/p_hud.c \
|
||||
game/p_trail.c \
|
||||
game/p_view.c \
|
||||
game/p_weapon.c \
|
||||
game/q_shared.c \
|
||||
client/cl_cin.c \
|
||||
client/cl_ents.c \
|
||||
client/cl_fx.c \
|
||||
client/cl_input.c \
|
||||
client/cl_inv.c \
|
||||
client/cl_main.c \
|
||||
client/cl_newfx.c \
|
||||
client/cl_parse.c \
|
||||
client/cl_pred.c \
|
||||
client/cl_scrn.c \
|
||||
client/cl_tent.c \
|
||||
client/cl_view.c \
|
||||
client/console.c \
|
||||
client/keys.c \
|
||||
client/menu.c \
|
||||
client/qmenu.c \
|
||||
client/snd_dma.c \
|
||||
client/snd_mem.c \
|
||||
client/snd_mix.c \
|
||||
server/sv_ccmds.c \
|
||||
server/sv_ents.c \
|
||||
server/sv_game.c \
|
||||
server/sv_init.c \
|
||||
server/sv_main.c \
|
||||
server/sv_send.c \
|
||||
server/sv_user.c \
|
||||
server/sv_world.c
|
||||
|
||||
# IOP modules pulled from the PS2DEV SDK:
|
||||
IRX_FILES = \
|
||||
usbd.irx
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Libs from the PS2DEV SDK:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
PS2_LIBS = \
|
||||
-ldma \
|
||||
-lgraph \
|
||||
-ldraw \
|
||||
-lpatches \
|
||||
-lpacket \
|
||||
-lmf \
|
||||
-lc \
|
||||
-lkernel
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Make flags/macros:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
#
|
||||
# Where to find the source files and where to dump the
|
||||
# intermediate object files and the final ELF executable.
|
||||
#
|
||||
OUTPUT_DIR = build
|
||||
SRC_DIR = src
|
||||
IOP_OUTPUT_DIR = irx
|
||||
|
||||
#
|
||||
# Name of the binary executable (QPS2) and
|
||||
# object (.o) filenames derived from source filenames.
|
||||
#
|
||||
BIN_TARGET = $(OUTPUT_DIR)/QPS2.ELF
|
||||
OBJ_FILES = $(addprefix $(OUTPUT_DIR)/$(SRC_DIR)/, $(patsubst %.c, %.o, $(SRC_FILES)))
|
||||
|
||||
#
|
||||
# The IRX IOProcessor modules we embed:
|
||||
#
|
||||
IOP_MODULES = $(addprefix $(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/, $(patsubst %.irx, %.o, $(IRX_FILES)))
|
||||
|
||||
#
|
||||
# Global #defines, C-flags and include paths:
|
||||
#
|
||||
PS2_GLOBAL_DEFS = -D_EE -DGAME_HARD_LINKED -DPS2_QUAKE
|
||||
PS2_CFLAGS = $(PS2_GLOBAL_DEFS) -O3 -G0
|
||||
PS2_INCS = -I$(PS2SDK)/ee/include -I$(PS2SDK)/common/include -I$(SRC_DIR)
|
||||
|
||||
#
|
||||
# Custom GCC for the PS2 EE CPU (based on GCC v3):
|
||||
#
|
||||
PS2_CC = ee-gcc
|
||||
|
||||
#
|
||||
# Linker stuff:
|
||||
#
|
||||
PS2_LDFLAGS = -L$(PS2SDK)/ee/lib
|
||||
PS2_LINKCMD = -mno-crt0 -T$(PS2SDK)/ee/startup/linkfile
|
||||
PS2_STARTUP_FILE = $(PS2SDK)/ee/startup/crt0.o
|
||||
|
||||
#
|
||||
# The same structure of src/ is recreated in build/, so we need mkdir.
|
||||
#
|
||||
MKDIR_CMD = mkdir -p
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Define VERBOSE somewhere before this to get the
|
||||
# full make command printed to stdout.
|
||||
# If VERBOSE is NOT defined you can also:
|
||||
# - Define QUIET_W_CFLAGS to print the CFLAGS
|
||||
# ---------------------------------------------------------
|
||||
ifndef VERBOSE
|
||||
QUIET = @
|
||||
# Colored text if the terminal supports ANSI color codes:
|
||||
COLOR_ALT1 = "\033[36;1m"
|
||||
COLOR_ALT2 = "\033[35;1m"
|
||||
COLOR_DEFAULT = "\033[0;1m"
|
||||
ifdef QUIET_W_CFLAGS
|
||||
ECHO_COMPILING = @echo $(COLOR_ALT1)"-> Compiling "$(PS2_CFLAGS) $(COLOR_ALT2)$<$(COLOR_ALT1)" ..."$(COLOR_DEFAULT)
|
||||
else # !QUIET_W_CFLAGS
|
||||
ECHO_COMPILING = @echo $(COLOR_ALT1)"-> Compiling "$(COLOR_ALT2)$<$(COLOR_ALT1)" ..."$(COLOR_DEFAULT)
|
||||
endif # QUIET_W_CFLAGS
|
||||
ECHO_LINKING = @echo $(COLOR_ALT1)"-> Linking ..."$(COLOR_DEFAULT)
|
||||
ECHO_CLEANING = @echo $(COLOR_ALT1)"-> Cleaning ..."$(COLOR_DEFAULT)
|
||||
ECHO_BUILDING_IOP_MODS = @echo $(COLOR_ALT1)"-> Assembling IOP module '$@' ..."$(COLOR_DEFAULT)
|
||||
endif # VERBOSE
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Make rules:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
all: $(BIN_TARGET)
|
||||
$(QUIET) ee-strip --strip-all $(BIN_TARGET)
|
||||
|
||||
#
|
||||
# C source files => OBJ files:
|
||||
#
|
||||
$(BIN_TARGET): $(OBJ_FILES) $(IOP_MODULES)
|
||||
$(ECHO_LINKING)
|
||||
$(QUIET) $(PS2_CC) $(PS2_LINKCMD) $(PS2_CFLAGS) -o \
|
||||
$(BIN_TARGET) $(PS2_STARTUP_FILE) $(OBJ_FILES) \
|
||||
$(IOP_MODULES) $(PS2_LDFLAGS) $(PS2_LIBS)
|
||||
|
||||
$(OBJ_FILES): $(OUTPUT_DIR)/%.o: %.c
|
||||
$(ECHO_COMPILING)
|
||||
$(QUIET) $(MKDIR_CMD) $(dir $@)
|
||||
$(QUIET) $(PS2_CC) $(PS2_CFLAGS) $(PS2_INCS) -c $< -o $@
|
||||
|
||||
#
|
||||
# IOP/IRX modules, compiled into the program:
|
||||
#
|
||||
$(IOP_MODULES): $(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/%.o: $(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/%.s
|
||||
$(ECHO_BUILDING_IOP_MODS)
|
||||
$(QUIET) $(PS2_CC) $(PS2_CFLAGS) -c $< -o $@
|
||||
|
||||
$(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/%.s:
|
||||
$(QUIET) $(MKDIR_CMD) $(dir $@)
|
||||
$(QUIET) bin2s $(PS2SDK)/iop/irx/$*.irx $@ $*_irx
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Custom 'run' rule:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
ifndef INSTALL_PATH
|
||||
INSTALL_PATH = /Applications/pcsx2.app/Contents/Resources/pcsx2
|
||||
endif
|
||||
|
||||
ifndef RUN_CMD
|
||||
RUN_CMD = open -a pcsx2
|
||||
endif
|
||||
|
||||
# Copies the executable file to the "install dir" and calls the user-defined
|
||||
# run command, which normally is the shell command to start the emulator app.
|
||||
run: $(BIN_TARGET)
|
||||
cp $(BIN_TARGET) $(INSTALL_PATH)/$(notdir $(BIN_TARGET))
|
||||
$(RUN_CMD)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Custom 'clean' rule:
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# Using 'find' because we need to recursively clear all subdirs.
|
||||
clean:
|
||||
$(ECHO_CLEANING)
|
||||
$(QUIET) rm -f $(BIN_TARGET)
|
||||
$(QUIET) rm -f $(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/*.s
|
||||
$(QUIET) rm -f $(OUTPUT_DIR)/$(IOP_OUTPUT_DIR)/*.o
|
||||
$(QUIET) rm -f $(INSTALL_PATH)/$(notdir $(BIN_TARGET))
|
||||
$(QUIET) find $(OUTPUT_DIR) -name "*.o" -type f -delete
|
||||
|
||||
@@ -1,2 +1,27 @@
|
||||
# quake2-for-ps2
|
||||
A fan-made port of id's Quake-2 for the PlayStation 2 Console.
|
||||
|
||||
# Quake II port for the PlayStation 2 Console
|
||||
|
||||
## Overview
|
||||
|
||||
This is an unofficial fan made port, targeting the PS2 Console, of the original
|
||||
[Quake II source code released by id Software][link_id_repo].
|
||||
|
||||
This port relies on the free [PS2DEV SDK][link_ps2_dev] to provide rendering,
|
||||
input, audio and system services for the Quake Engine.
|
||||
|
||||
The project is in early development stage, but 2D rendering of menus and cinematics
|
||||
is already implemented and working on both the emulator and the PS2. The long term
|
||||
goal is to have a fully functional and playable (single-player) Quake II on the
|
||||
PlayStation 2, relying only on the freely available tools and libraries.
|
||||
|
||||
## License
|
||||
|
||||
Quake II was originally release as GPL, and it remains as such. New code written
|
||||
for the PS2 port or any changes made to the original source are also release under the
|
||||
GNU General Public License version 2. See the accompanying LICENSE file for the details.
|
||||
|
||||
You can also find a copy of the GPL version 2 [in here][link_gpl_v2].
|
||||
|
||||
[link_id_repo]: https://github.com/id-Software/Quake-2
|
||||
[link_ps2_dev]: https://github.com/ps2dev
|
||||
[link_gpl_v2]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
# BasedOnStyle: Mozilla
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: true
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BinPackParameters: true
|
||||
BinPackArguments: true
|
||||
ColumnLimit: 0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
DerivePointerAlignment: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: false
|
||||
IndentWrappedFunctionNames: false
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Middle
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: false
|
||||
Standard: Cpp03
|
||||
IndentWidth: 4
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Allman
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 0
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
SpaceBeforeParens: ControlStatements
|
||||
DisableFormat: false
|
||||
...
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
This file is here so that Git creates the directory entry for it in the repo.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 513 KiB |
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.525731, 0.000000, 0.850651 },
|
||||
{ -0.442863, 0.238856, 0.864188 },
|
||||
{ -0.295242, 0.000000, 0.955423 },
|
||||
{ -0.309017, 0.500000, 0.809017 },
|
||||
{ -0.162460, 0.262866, 0.951056 },
|
||||
{ 0.000000, 0.000000, 1.000000 },
|
||||
{ 0.000000, 0.850651, 0.525731 },
|
||||
{ -0.147621, 0.716567, 0.681718 },
|
||||
{ 0.147621, 0.716567, 0.681718 },
|
||||
{ 0.000000, 0.525731, 0.850651 },
|
||||
{ 0.309017, 0.500000, 0.809017 },
|
||||
{ 0.525731, 0.000000, 0.850651 },
|
||||
{ 0.295242, 0.000000, 0.955423 },
|
||||
{ 0.442863, 0.238856, 0.864188 },
|
||||
{ 0.162460, 0.262866, 0.951056 },
|
||||
{ -0.681718, 0.147621, 0.716567 },
|
||||
{ -0.809017, 0.309017, 0.500000 },
|
||||
{ -0.587785, 0.425325, 0.688191 },
|
||||
{ -0.850651, 0.525731, 0.000000 },
|
||||
{ -0.864188, 0.442863, 0.238856 },
|
||||
{ -0.716567, 0.681718, 0.147621 },
|
||||
{ -0.688191, 0.587785, 0.425325 },
|
||||
{ -0.500000, 0.809017, 0.309017 },
|
||||
{ -0.238856, 0.864188, 0.442863 },
|
||||
{ -0.425325, 0.688191, 0.587785 },
|
||||
{ -0.716567, 0.681718, -0.147621 },
|
||||
{ -0.500000, 0.809017, -0.309017 },
|
||||
{ -0.525731, 0.850651, 0.000000 },
|
||||
{ 0.000000, 0.850651, -0.525731 },
|
||||
{ -0.238856, 0.864188, -0.442863 },
|
||||
{ 0.000000, 0.955423, -0.295242 },
|
||||
{ -0.262866, 0.951056, -0.162460 },
|
||||
{ 0.000000, 1.000000, 0.000000 },
|
||||
{ 0.000000, 0.955423, 0.295242 },
|
||||
{ -0.262866, 0.951056, 0.162460 },
|
||||
{ 0.238856, 0.864188, 0.442863 },
|
||||
{ 0.262866, 0.951056, 0.162460 },
|
||||
{ 0.500000, 0.809017, 0.309017 },
|
||||
{ 0.238856, 0.864188, -0.442863 },
|
||||
{ 0.262866, 0.951056, -0.162460 },
|
||||
{ 0.500000, 0.809017, -0.309017 },
|
||||
{ 0.850651, 0.525731, 0.000000 },
|
||||
{ 0.716567, 0.681718, 0.147621 },
|
||||
{ 0.716567, 0.681718, -0.147621 },
|
||||
{ 0.525731, 0.850651, 0.000000 },
|
||||
{ 0.425325, 0.688191, 0.587785 },
|
||||
{ 0.864188, 0.442863, 0.238856 },
|
||||
{ 0.688191, 0.587785, 0.425325 },
|
||||
{ 0.809017, 0.309017, 0.500000 },
|
||||
{ 0.681718, 0.147621, 0.716567 },
|
||||
{ 0.587785, 0.425325, 0.688191 },
|
||||
{ 0.955423, 0.295242, 0.000000 },
|
||||
{ 1.000000, 0.000000, 0.000000 },
|
||||
{ 0.951056, 0.162460, 0.262866 },
|
||||
{ 0.850651, -0.525731, 0.000000 },
|
||||
{ 0.955423, -0.295242, 0.000000 },
|
||||
{ 0.864188, -0.442863, 0.238856 },
|
||||
{ 0.951056, -0.162460, 0.262866 },
|
||||
{ 0.809017, -0.309017, 0.500000 },
|
||||
{ 0.681718, -0.147621, 0.716567 },
|
||||
{ 0.850651, 0.000000, 0.525731 },
|
||||
{ 0.864188, 0.442863, -0.238856 },
|
||||
{ 0.809017, 0.309017, -0.500000 },
|
||||
{ 0.951056, 0.162460, -0.262866 },
|
||||
{ 0.525731, 0.000000, -0.850651 },
|
||||
{ 0.681718, 0.147621, -0.716567 },
|
||||
{ 0.681718, -0.147621, -0.716567 },
|
||||
{ 0.850651, 0.000000, -0.525731 },
|
||||
{ 0.809017, -0.309017, -0.500000 },
|
||||
{ 0.864188, -0.442863, -0.238856 },
|
||||
{ 0.951056, -0.162460, -0.262866 },
|
||||
{ 0.147621, 0.716567, -0.681718 },
|
||||
{ 0.309017, 0.500000, -0.809017 },
|
||||
{ 0.425325, 0.688191, -0.587785 },
|
||||
{ 0.442863, 0.238856, -0.864188 },
|
||||
{ 0.587785, 0.425325, -0.688191 },
|
||||
{ 0.688191, 0.587785, -0.425325 },
|
||||
{ -0.147621, 0.716567, -0.681718 },
|
||||
{ -0.309017, 0.500000, -0.809017 },
|
||||
{ 0.000000, 0.525731, -0.850651 },
|
||||
{ -0.525731, 0.000000, -0.850651 },
|
||||
{ -0.442863, 0.238856, -0.864188 },
|
||||
{ -0.295242, 0.000000, -0.955423 },
|
||||
{ -0.162460, 0.262866, -0.951056 },
|
||||
{ 0.000000, 0.000000, -1.000000 },
|
||||
{ 0.295242, 0.000000, -0.955423 },
|
||||
{ 0.162460, 0.262866, -0.951056 },
|
||||
{ -0.442863, -0.238856, -0.864188 },
|
||||
{ -0.309017, -0.500000, -0.809017 },
|
||||
{ -0.162460, -0.262866, -0.951056 },
|
||||
{ 0.000000, -0.850651, -0.525731 },
|
||||
{ -0.147621, -0.716567, -0.681718 },
|
||||
{ 0.147621, -0.716567, -0.681718 },
|
||||
{ 0.000000, -0.525731, -0.850651 },
|
||||
{ 0.309017, -0.500000, -0.809017 },
|
||||
{ 0.442863, -0.238856, -0.864188 },
|
||||
{ 0.162460, -0.262866, -0.951056 },
|
||||
{ 0.238856, -0.864188, -0.442863 },
|
||||
{ 0.500000, -0.809017, -0.309017 },
|
||||
{ 0.425325, -0.688191, -0.587785 },
|
||||
{ 0.716567, -0.681718, -0.147621 },
|
||||
{ 0.688191, -0.587785, -0.425325 },
|
||||
{ 0.587785, -0.425325, -0.688191 },
|
||||
{ 0.000000, -0.955423, -0.295242 },
|
||||
{ 0.000000, -1.000000, 0.000000 },
|
||||
{ 0.262866, -0.951056, -0.162460 },
|
||||
{ 0.000000, -0.850651, 0.525731 },
|
||||
{ 0.000000, -0.955423, 0.295242 },
|
||||
{ 0.238856, -0.864188, 0.442863 },
|
||||
{ 0.262866, -0.951056, 0.162460 },
|
||||
{ 0.500000, -0.809017, 0.309017 },
|
||||
{ 0.716567, -0.681718, 0.147621 },
|
||||
{ 0.525731, -0.850651, 0.000000 },
|
||||
{ -0.238856, -0.864188, -0.442863 },
|
||||
{ -0.500000, -0.809017, -0.309017 },
|
||||
{ -0.262866, -0.951056, -0.162460 },
|
||||
{ -0.850651, -0.525731, 0.000000 },
|
||||
{ -0.716567, -0.681718, -0.147621 },
|
||||
{ -0.716567, -0.681718, 0.147621 },
|
||||
{ -0.525731, -0.850651, 0.000000 },
|
||||
{ -0.500000, -0.809017, 0.309017 },
|
||||
{ -0.238856, -0.864188, 0.442863 },
|
||||
{ -0.262866, -0.951056, 0.162460 },
|
||||
{ -0.864188, -0.442863, 0.238856 },
|
||||
{ -0.809017, -0.309017, 0.500000 },
|
||||
{ -0.688191, -0.587785, 0.425325 },
|
||||
{ -0.681718, -0.147621, 0.716567 },
|
||||
{ -0.442863, -0.238856, 0.864188 },
|
||||
{ -0.587785, -0.425325, 0.688191 },
|
||||
{ -0.309017, -0.500000, 0.809017 },
|
||||
{ -0.147621, -0.716567, 0.681718 },
|
||||
{ -0.425325, -0.688191, 0.587785 },
|
||||
{ -0.162460, -0.262866, 0.951056 },
|
||||
{ 0.442863, -0.238856, 0.864188 },
|
||||
{ 0.162460, -0.262866, 0.951056 },
|
||||
{ 0.309017, -0.500000, 0.809017 },
|
||||
{ 0.147621, -0.716567, 0.681718 },
|
||||
{ 0.000000, -0.525731, 0.850651 },
|
||||
{ 0.425325, -0.688191, 0.587785 },
|
||||
{ 0.587785, -0.425325, 0.688191 },
|
||||
{ 0.688191, -0.587785, 0.425325 },
|
||||
{ -0.955423, 0.295242, 0.000000 },
|
||||
{ -0.951056, 0.162460, 0.262866 },
|
||||
{ -1.000000, 0.000000, 0.000000 },
|
||||
{ -0.850651, 0.000000, 0.525731 },
|
||||
{ -0.955423, -0.295242, 0.000000 },
|
||||
{ -0.951056, -0.162460, 0.262866 },
|
||||
{ -0.864188, 0.442863, -0.238856 },
|
||||
{ -0.951056, 0.162460, -0.262866 },
|
||||
{ -0.809017, 0.309017, -0.500000 },
|
||||
{ -0.864188, -0.442863, -0.238856 },
|
||||
{ -0.951056, -0.162460, -0.262866 },
|
||||
{ -0.809017, -0.309017, -0.500000 },
|
||||
{ -0.681718, 0.147621, -0.716567 },
|
||||
{ -0.681718, -0.147621, -0.716567 },
|
||||
{ -0.850651, 0.000000, -0.525731 },
|
||||
{ -0.688191, 0.587785, -0.425325 },
|
||||
{ -0.587785, 0.425325, -0.688191 },
|
||||
{ -0.425325, 0.688191, -0.587785 },
|
||||
{ -0.425325, -0.688191, -0.587785 },
|
||||
{ -0.587785, -0.425325, -0.688191 },
|
||||
{ -0.688191, -0.587785, -0.425325 },
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_CDAUDIO_H
|
||||
#define CL_CDAUDIO_H
|
||||
|
||||
int CDAudio_Init(void);
|
||||
void CDAudio_Shutdown(void);
|
||||
void CDAudio_Play(int track, qboolean looping);
|
||||
void CDAudio_Stop(void);
|
||||
void CDAudio_Update(void);
|
||||
void CDAudio_Activate(qboolean active);
|
||||
|
||||
#endif // CL_CDAUDIO_H
|
||||
@@ -0,0 +1,801 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
byte * data;
|
||||
int count;
|
||||
} cblock_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean restart_sound;
|
||||
int s_rate;
|
||||
int s_width;
|
||||
int s_channels;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
byte * pic;
|
||||
byte * pic_pending;
|
||||
|
||||
// order 1 huffman stuff
|
||||
int * hnodes1; // [256][256][2];
|
||||
int numhnodes1[256];
|
||||
|
||||
int h_used[512];
|
||||
int h_count[512];
|
||||
} cinematics_t;
|
||||
|
||||
static cinematics_t cin;
|
||||
|
||||
/*
|
||||
==============
|
||||
SCR_LoadPCX
|
||||
==============
|
||||
*/
|
||||
void SCR_LoadPCX(char * filename, byte ** pic, byte ** palette, int * width, int * height)
|
||||
{
|
||||
byte * raw;
|
||||
pcx_t * pcx;
|
||||
int x, y;
|
||||
int len;
|
||||
int dataByte, runLength;
|
||||
byte *out, *pix;
|
||||
|
||||
*pic = NULL;
|
||||
|
||||
//
|
||||
// load the file
|
||||
//
|
||||
len = FS_LoadFile(filename, (void **)&raw);
|
||||
if (!raw)
|
||||
{
|
||||
Com_DPrintf("Bad pcx file %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// parse the PCX file
|
||||
//
|
||||
pcx = (pcx_t *)raw;
|
||||
raw = &pcx->data;
|
||||
|
||||
if (pcx->manufacturer != 0x0A || pcx->version != 5 || pcx->encoding != 1 ||
|
||||
pcx->bits_per_pixel != 8 || pcx->xmax >= 640 || pcx->ymax >= 480)
|
||||
{
|
||||
Com_DPrintf("Bad pcx file %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
out = Z_Malloc((pcx->ymax + 1) * (pcx->xmax + 1));
|
||||
*pic = out;
|
||||
pix = out;
|
||||
|
||||
if (palette)
|
||||
{
|
||||
*palette = Z_Malloc(768);
|
||||
memcpy(*palette, (byte *)pcx + len - 768, 768);
|
||||
}
|
||||
|
||||
if (width)
|
||||
{
|
||||
*width = pcx->xmax + 1;
|
||||
}
|
||||
if (height)
|
||||
{
|
||||
*height = pcx->ymax + 1;
|
||||
}
|
||||
|
||||
for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1)
|
||||
{
|
||||
for (x = 0; x <= pcx->xmax;)
|
||||
{
|
||||
dataByte = *raw++;
|
||||
|
||||
if ((dataByte & 0xC0) == 0xC0)
|
||||
{
|
||||
runLength = dataByte & 0x3F;
|
||||
dataByte = *raw++;
|
||||
}
|
||||
else
|
||||
{
|
||||
runLength = 1;
|
||||
}
|
||||
|
||||
while (runLength-- > 0)
|
||||
{
|
||||
pix[x++] = dataByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (raw - (byte *)pcx > len)
|
||||
{
|
||||
Com_DPrintf("PCX file %s was malformed\n", filename);
|
||||
Z_Free(*pic);
|
||||
*pic = NULL;
|
||||
}
|
||||
|
||||
FS_FreeFile(pcx);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_StopCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_StopCinematic(void)
|
||||
{
|
||||
cl.cinematictime = 0; // done
|
||||
if (cin.pic)
|
||||
{
|
||||
Z_Free(cin.pic);
|
||||
cin.pic = NULL;
|
||||
}
|
||||
if (cin.pic_pending)
|
||||
{
|
||||
Z_Free(cin.pic_pending);
|
||||
cin.pic_pending = NULL;
|
||||
}
|
||||
if (cl.cinematicpalette_active)
|
||||
{
|
||||
re.CinematicSetPalette(NULL);
|
||||
cl.cinematicpalette_active = false;
|
||||
}
|
||||
if (cl.cinematic_file)
|
||||
{
|
||||
fclose(cl.cinematic_file);
|
||||
cl.cinematic_file = NULL;
|
||||
}
|
||||
if (cin.hnodes1)
|
||||
{
|
||||
Z_Free(cin.hnodes1);
|
||||
cin.hnodes1 = NULL;
|
||||
}
|
||||
|
||||
// switch back down to 11 khz sound if necessary
|
||||
if (cin.restart_sound)
|
||||
{
|
||||
cin.restart_sound = false;
|
||||
CL_Snd_Restart_f();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
SCR_FinishCinematic
|
||||
|
||||
Called when either the cinematic completes, or it is aborted
|
||||
====================
|
||||
*/
|
||||
void SCR_FinishCinematic(void)
|
||||
{
|
||||
// tell the server to advance to the next map / cinematic
|
||||
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
|
||||
SZ_Print(&cls.netchan.message, va("nextserver %i\n", cl.servercount));
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SmallestNode1
|
||||
==================
|
||||
*/
|
||||
int SmallestNode1(int numhnodes)
|
||||
{
|
||||
int i;
|
||||
int best, bestnode;
|
||||
|
||||
best = 99999999;
|
||||
bestnode = -1;
|
||||
for (i = 0; i < numhnodes; i++)
|
||||
{
|
||||
if (cin.h_used[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cin.h_count[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cin.h_count[i] < best)
|
||||
{
|
||||
best = cin.h_count[i];
|
||||
bestnode = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestnode == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
cin.h_used[bestnode] = true;
|
||||
return bestnode;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Huff1TableInit
|
||||
|
||||
Reads the 64k counts table and initializes the node trees
|
||||
==================
|
||||
*/
|
||||
void Huff1TableInit(void)
|
||||
{
|
||||
int prev;
|
||||
int j;
|
||||
int *node, *nodebase;
|
||||
byte counts[256];
|
||||
int numhnodes;
|
||||
|
||||
cin.hnodes1 = Z_Malloc(256 * 256 * 2 * 4);
|
||||
memset(cin.hnodes1, 0, 256 * 256 * 2 * 4);
|
||||
|
||||
for (prev = 0; prev < 256; prev++)
|
||||
{
|
||||
memset(cin.h_count, 0, sizeof(cin.h_count));
|
||||
memset(cin.h_used, 0, sizeof(cin.h_used));
|
||||
|
||||
// read a row of counts
|
||||
FS_Read(counts, sizeof(counts), cl.cinematic_file);
|
||||
for (j = 0; j < 256; j++)
|
||||
{
|
||||
cin.h_count[j] = counts[j];
|
||||
}
|
||||
|
||||
// build the nodes
|
||||
numhnodes = 256;
|
||||
nodebase = cin.hnodes1 + prev * 256 * 2;
|
||||
|
||||
while (numhnodes != 511)
|
||||
{
|
||||
node = nodebase + (numhnodes - 256) * 2;
|
||||
|
||||
// pick two lowest counts
|
||||
node[0] = SmallestNode1(numhnodes);
|
||||
if (node[0] == -1)
|
||||
{
|
||||
break; // no more
|
||||
}
|
||||
|
||||
node[1] = SmallestNode1(numhnodes);
|
||||
if (node[1] == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cin.h_count[numhnodes] = cin.h_count[node[0]] + cin.h_count[node[1]];
|
||||
numhnodes++;
|
||||
}
|
||||
|
||||
cin.numhnodes1[prev] = numhnodes - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Huff1Decompress
|
||||
==================
|
||||
*/
|
||||
cblock_t Huff1Decompress(cblock_t in)
|
||||
{
|
||||
byte * input;
|
||||
byte * out_p;
|
||||
int nodenum;
|
||||
int count;
|
||||
cblock_t out;
|
||||
int inbyte;
|
||||
int *hnodes, *hnodesbase;
|
||||
|
||||
// get decompressed count
|
||||
count = in.data[0] + (in.data[1] << 8) + (in.data[2] << 16) + (in.data[3] << 24);
|
||||
input = in.data + 4;
|
||||
out_p = out.data = Z_Malloc(count);
|
||||
|
||||
// read bits
|
||||
|
||||
hnodesbase = cin.hnodes1 - 256 * 2; // nodes 0-255 aren't stored
|
||||
|
||||
hnodes = hnodesbase;
|
||||
nodenum = cin.numhnodes1[0];
|
||||
while (count)
|
||||
{
|
||||
inbyte = *input++;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
//-----------
|
||||
if (nodenum < 256)
|
||||
{
|
||||
hnodes = hnodesbase + (nodenum << 9);
|
||||
*out_p++ = nodenum;
|
||||
if (!--count)
|
||||
break;
|
||||
nodenum = cin.numhnodes1[nodenum];
|
||||
}
|
||||
nodenum = hnodes[nodenum * 2 + (inbyte & 1)];
|
||||
inbyte >>= 1;
|
||||
}
|
||||
|
||||
if (input - in.data != in.count && input - in.data != in.count + 1)
|
||||
{
|
||||
Com_DPrintf("Decompression overread by %i\n", (input - in.data) - in.count);
|
||||
}
|
||||
out.count = out_p - out.data;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_ReadNextFrame
|
||||
==================
|
||||
*/
|
||||
byte * SCR_ReadNextFrame(void)
|
||||
{
|
||||
// LAMPERT:
|
||||
// These were originally stack variables, but I fear they
|
||||
// are dangerously big and would stress the PS2 stack. Since
|
||||
// this is not threaded code, no worries with race conditions.
|
||||
static byte samples[22050 / 14 * 4] __attribute__((aligned(16)));
|
||||
static byte compressed[0x20000] __attribute__((aligned(16)));
|
||||
|
||||
int r;
|
||||
int command;
|
||||
int size;
|
||||
byte * pic;
|
||||
cblock_t in, huf1;
|
||||
int start, end, count;
|
||||
|
||||
if (feof(cl.cinematic_file))
|
||||
{
|
||||
Com_DPrintf("Cinematic at the end of file!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// read the next frame
|
||||
r = fread(&command, 4, 1, cl.cinematic_file);
|
||||
if (r == 0) // we'll give it one more chance
|
||||
{
|
||||
r = fread(&command, 4, 1, cl.cinematic_file);
|
||||
}
|
||||
|
||||
if (r != 1)
|
||||
{
|
||||
Com_DPrintf("Failed to fread() cinematic frame!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
command = LittleLong(command);
|
||||
if (command == 2)
|
||||
{
|
||||
Com_DPrintf("Cinematic hit last frame!\n");
|
||||
return NULL; // last frame marker
|
||||
}
|
||||
|
||||
if (command == 1)
|
||||
{
|
||||
// read palette
|
||||
FS_Read(cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file);
|
||||
cl.cinematicpalette_active = 0; // dubious.... exposes an edge case
|
||||
}
|
||||
|
||||
// decompress the next frame
|
||||
size = 0;
|
||||
FS_Read(&size, 4, cl.cinematic_file);
|
||||
size = LittleLong(size);
|
||||
|
||||
if (size > sizeof(compressed) || size < 1)
|
||||
{
|
||||
Com_DPrintf("Cinematic error: Bad compressed frame size! %d of %u\n", size, sizeof(compressed));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FS_Read(compressed, size, cl.cinematic_file);
|
||||
|
||||
// read sound
|
||||
start = cl.cinematicframe * cin.s_rate / 14;
|
||||
end = (cl.cinematicframe + 1) * cin.s_rate / 14;
|
||||
count = end - start;
|
||||
|
||||
FS_Read(samples, count * cin.s_width * cin.s_channels, cl.cinematic_file);
|
||||
|
||||
S_RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, samples);
|
||||
|
||||
in.data = compressed;
|
||||
in.count = size;
|
||||
|
||||
huf1 = Huff1Decompress(in);
|
||||
|
||||
pic = huf1.data;
|
||||
|
||||
cl.cinematicframe++;
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_RunCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_RunCinematic(void)
|
||||
{
|
||||
int frame;
|
||||
|
||||
if (cl.cinematictime <= 0)
|
||||
{
|
||||
SCR_StopCinematic();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl.cinematicframe == -1)
|
||||
{
|
||||
return; // static image
|
||||
}
|
||||
|
||||
if (cls.key_dest != key_game)
|
||||
{
|
||||
// pause if menu or console is up
|
||||
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
|
||||
return;
|
||||
}
|
||||
|
||||
frame = (cls.realtime - cl.cinematictime) * 14.0 / 1000;
|
||||
if (frame <= cl.cinematicframe)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame > cl.cinematicframe + 1)
|
||||
{
|
||||
Com_DPrintf("Dropped frame: %i > %i\n", frame, cl.cinematicframe + 1);
|
||||
cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
|
||||
}
|
||||
|
||||
if (cin.pic)
|
||||
{
|
||||
Z_Free(cin.pic);
|
||||
}
|
||||
|
||||
cin.pic = cin.pic_pending;
|
||||
cin.pic_pending = SCR_ReadNextFrame();
|
||||
|
||||
if (!cin.pic_pending)
|
||||
{
|
||||
SCR_StopCinematic();
|
||||
SCR_FinishCinematic();
|
||||
cl.cinematictime = 1; // hack to get the black screen behind loading
|
||||
SCR_BeginLoadingPlaque();
|
||||
cl.cinematictime = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_DrawCinematic
|
||||
|
||||
Returns true if a cinematic is active, meaning
|
||||
the view rendering should be skipped.
|
||||
==================
|
||||
*/
|
||||
qboolean SCR_DrawCinematic(void)
|
||||
{
|
||||
if (cl.cinematictime <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cls.key_dest == key_menu)
|
||||
{
|
||||
// blank screen and pause if menu is up
|
||||
re.CinematicSetPalette(NULL);
|
||||
cl.cinematicpalette_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cl.cinematicpalette_active)
|
||||
{
|
||||
re.CinematicSetPalette(cl.cinematicpalette);
|
||||
cl.cinematicpalette_active = true;
|
||||
}
|
||||
|
||||
if (!cin.pic)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
re.DrawStretchRaw(0, 0, viddef.width, viddef.height, cin.width, cin.height, cin.pic);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SCR_PlayCinematic
|
||||
==================
|
||||
*/
|
||||
void SCR_PlayCinematic(const char * arg)
|
||||
{
|
||||
int width, height;
|
||||
byte * palette;
|
||||
char name[MAX_OSPATH];
|
||||
const char * dot;
|
||||
int old_khz;
|
||||
|
||||
Com_DPrintf("Trying to play cinematic '%s' ...\n", arg);
|
||||
|
||||
// make sure CD isn't playing music
|
||||
CDAudio_Stop();
|
||||
|
||||
cl.cinematicframe = 0;
|
||||
dot = strstr(arg, ".");
|
||||
if (dot && !strcmp(dot, ".pcx"))
|
||||
{
|
||||
// static pcx image
|
||||
Com_sprintf(name, sizeof(name), "pics/%s", arg);
|
||||
SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height);
|
||||
cl.cinematicframe = -1;
|
||||
cl.cinematictime = 1;
|
||||
SCR_EndLoadingPlaque();
|
||||
cls.state = ca_active;
|
||||
if (!cin.pic)
|
||||
{
|
||||
Com_DPrintf("%s not found.\n", name);
|
||||
cl.cinematictime = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(cl.cinematicpalette, palette, sizeof(cl.cinematicpalette));
|
||||
Z_Free(palette);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Com_sprintf(name, sizeof(name), "video/%s", arg);
|
||||
FS_FOpenFile(name, &cl.cinematic_file);
|
||||
if (!cl.cinematic_file)
|
||||
{
|
||||
Com_DPrintf("Cinematic %s not found!\n", name);
|
||||
SCR_FinishCinematic();
|
||||
cl.cinematictime = 0; // done
|
||||
return;
|
||||
}
|
||||
|
||||
SCR_EndLoadingPlaque();
|
||||
cls.state = ca_active;
|
||||
|
||||
FS_Read(&width, 4, cl.cinematic_file);
|
||||
FS_Read(&height, 4, cl.cinematic_file);
|
||||
cin.width = LittleLong(width);
|
||||
cin.height = LittleLong(height);
|
||||
|
||||
FS_Read(&cin.s_rate, 4, cl.cinematic_file);
|
||||
cin.s_rate = LittleLong(cin.s_rate);
|
||||
FS_Read(&cin.s_width, 4, cl.cinematic_file);
|
||||
cin.s_width = LittleLong(cin.s_width);
|
||||
FS_Read(&cin.s_channels, 4, cl.cinematic_file);
|
||||
cin.s_channels = LittleLong(cin.s_channels);
|
||||
|
||||
Huff1TableInit();
|
||||
|
||||
// switch up to 22 khz sound if necessary
|
||||
old_khz = Cvar_VariableValue("s_khz");
|
||||
if (old_khz != cin.s_rate / 1000)
|
||||
{
|
||||
cin.restart_sound = true;
|
||||
Cvar_SetValue("s_khz", cin.s_rate / 1000);
|
||||
CL_Snd_Restart_f();
|
||||
Cvar_SetValue("s_khz", old_khz);
|
||||
}
|
||||
|
||||
cl.cinematicframe = 0;
|
||||
cin.pic = SCR_ReadNextFrame();
|
||||
cl.cinematictime = Sys_Milliseconds();
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CinematicTest_PlayDirect
|
||||
|
||||
LAMPERT 2015-10-23
|
||||
Plays a cinematic directly from a ".cin" file in the file system (not from a pak).
|
||||
This is used for testing in the PS2 port.
|
||||
==================
|
||||
*/
|
||||
static char last_test_cinematic[MAX_QPATH];
|
||||
qboolean CinematicTest_PlayDirect(const char * filename)
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int old_khz = 0;
|
||||
|
||||
Com_DPrintf("Trying to play cinematic '%s' ...\n", filename);
|
||||
|
||||
cl.cinematicframe = 0;
|
||||
cl.cinematic_file = fopen(filename, "rb");
|
||||
|
||||
if (cl.cinematic_file == NULL)
|
||||
{
|
||||
Com_DPrintf("Cinematic '%s' can't be opened!\n", filename);
|
||||
cl.cinematictime = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
FS_Read(&width, 4, cl.cinematic_file);
|
||||
FS_Read(&height, 4, cl.cinematic_file);
|
||||
cin.width = LittleLong(width);
|
||||
cin.height = LittleLong(height);
|
||||
|
||||
FS_Read(&cin.s_rate, 4, cl.cinematic_file);
|
||||
cin.s_rate = LittleLong(cin.s_rate);
|
||||
|
||||
FS_Read(&cin.s_width, 4, cl.cinematic_file);
|
||||
cin.s_width = LittleLong(cin.s_width);
|
||||
|
||||
FS_Read(&cin.s_channels, 4, cl.cinematic_file);
|
||||
cin.s_channels = LittleLong(cin.s_channels);
|
||||
|
||||
Huff1TableInit();
|
||||
|
||||
// switch up to 22 khz sound if necessary
|
||||
old_khz = Cvar_VariableValue("s_khz");
|
||||
if (old_khz != cin.s_rate / 1000)
|
||||
{
|
||||
cin.restart_sound = true;
|
||||
Cvar_SetValue("s_khz", cin.s_rate / 1000);
|
||||
CL_Snd_Restart_f();
|
||||
Cvar_SetValue("s_khz", old_khz);
|
||||
}
|
||||
|
||||
cl.cinematicframe = 0;
|
||||
cin.pic = SCR_ReadNextFrame();
|
||||
cl.cinematictime = Sys_Milliseconds();
|
||||
|
||||
strncpy(last_test_cinematic, filename, sizeof(last_test_cinematic));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CinematicTest_RunFrame
|
||||
|
||||
LAMPERT 2015-10-23
|
||||
Used in combination with CinematicTest_PlayDirect for testing cinematics in the PS2 port.
|
||||
It will unconditionally run the next cinematic frame, then draw it to screen. Must be called
|
||||
inside a BeginFrame/EndFrame loop.
|
||||
==================
|
||||
*/
|
||||
qboolean CinematicTest_RunFrame(void)
|
||||
{
|
||||
//
|
||||
// SCR_DrawCinematic:
|
||||
//
|
||||
|
||||
if (cin.pic != NULL)
|
||||
{
|
||||
if (!cl.cinematicpalette_active)
|
||||
{
|
||||
re.CinematicSetPalette(cl.cinematicpalette);
|
||||
cl.cinematicpalette_active = true;
|
||||
}
|
||||
re.DrawStretchRaw(0, 0, viddef.width, viddef.height, cin.width, cin.height, cin.pic);
|
||||
}
|
||||
|
||||
//
|
||||
// SCR_RunCinematic:
|
||||
//
|
||||
|
||||
if (cin.pic != NULL)
|
||||
{
|
||||
Z_Free(cin.pic);
|
||||
}
|
||||
|
||||
cin.pic = cin.pic_pending;
|
||||
cin.pic_pending = SCR_ReadNextFrame();
|
||||
|
||||
if (cin.pic_pending == NULL)
|
||||
{
|
||||
Com_DPrintf("Stopping cinematic '%s'\n", last_test_cinematic);
|
||||
SCR_StopCinematic(); // Frees memory and closes the file handle.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+2376
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
// cl.input.c -- builds an intended movement command to send to the server
|
||||
|
||||
#include "client.h"
|
||||
|
||||
cvar_t * cl_nodelta;
|
||||
|
||||
unsigned frame_msec;
|
||||
unsigned old_sys_frame_time;
|
||||
extern unsigned sys_frame_time;
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
KEY BUTTONS
|
||||
|
||||
Continuous button event tracking is complicated by the fact that two different
|
||||
input sources (say, mouse button 1 and the control key) can both press the
|
||||
same button, but the button should only be released when both of the
|
||||
pressing key have been released.
|
||||
|
||||
When a key event issues a button command (+forward, +attack, etc), it appends
|
||||
its key number as a parameter to the command so it can be matched up with
|
||||
the release.
|
||||
|
||||
state bit 0 is the current state of the key
|
||||
state bit 1 is edge triggered on the up to down transition
|
||||
state bit 2 is edge triggered on the down to up transition
|
||||
|
||||
|
||||
Key_Event (int key, qboolean down, unsigned time);
|
||||
|
||||
+mlook src time
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
kbutton_t in_klook;
|
||||
kbutton_t in_left, in_right, in_forward, in_back;
|
||||
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
|
||||
kbutton_t in_strafe, in_speed, in_use, in_attack;
|
||||
kbutton_t in_up, in_down;
|
||||
|
||||
int in_impulse;
|
||||
|
||||
void KeyDown(kbutton_t * b)
|
||||
{
|
||||
int k;
|
||||
char * c;
|
||||
|
||||
c = Cmd_Argv(1);
|
||||
if (c[0])
|
||||
k = atoi(c);
|
||||
else
|
||||
k = -1; // typed manually at the console for continuous down
|
||||
|
||||
if (k == b->down[0] || k == b->down[1])
|
||||
return; // repeating key
|
||||
|
||||
if (!b->down[0])
|
||||
b->down[0] = k;
|
||||
else if (!b->down[1])
|
||||
b->down[1] = k;
|
||||
else
|
||||
{
|
||||
Com_Printf("Three keys down for a button!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (b->state & 1)
|
||||
return; // still down
|
||||
|
||||
// save timestamp
|
||||
c = Cmd_Argv(2);
|
||||
b->downtime = atoi(c);
|
||||
if (!b->downtime)
|
||||
b->downtime = sys_frame_time - 100;
|
||||
|
||||
b->state |= 1 + 2; // down + impulse down
|
||||
}
|
||||
|
||||
void KeyUp(kbutton_t * b)
|
||||
{
|
||||
int k;
|
||||
char * c;
|
||||
unsigned uptime;
|
||||
|
||||
c = Cmd_Argv(1);
|
||||
if (c[0])
|
||||
k = atoi(c);
|
||||
else
|
||||
{ // typed manually at the console, assume for unsticking, so clear all
|
||||
b->down[0] = b->down[1] = 0;
|
||||
b->state = 4; // impulse up
|
||||
return;
|
||||
}
|
||||
|
||||
if (b->down[0] == k)
|
||||
b->down[0] = 0;
|
||||
else if (b->down[1] == k)
|
||||
b->down[1] = 0;
|
||||
else
|
||||
return; // key up without coresponding down (menu pass through)
|
||||
if (b->down[0] || b->down[1])
|
||||
return; // some other key is still holding it down
|
||||
|
||||
if (!(b->state & 1))
|
||||
return; // still up (this should not happen)
|
||||
|
||||
// save timestamp
|
||||
c = Cmd_Argv(2);
|
||||
uptime = atoi(c);
|
||||
if (uptime)
|
||||
b->msec += uptime - b->downtime;
|
||||
else
|
||||
b->msec += 10;
|
||||
|
||||
b->state &= ~1; // now up
|
||||
b->state |= 4; // impulse up
|
||||
}
|
||||
|
||||
void IN_KLookDown(void) { KeyDown(&in_klook); }
|
||||
void IN_KLookUp(void) { KeyUp(&in_klook); }
|
||||
void IN_UpDown(void) { KeyDown(&in_up); }
|
||||
void IN_UpUp(void) { KeyUp(&in_up); }
|
||||
void IN_DownDown(void) { KeyDown(&in_down); }
|
||||
void IN_DownUp(void) { KeyUp(&in_down); }
|
||||
void IN_LeftDown(void) { KeyDown(&in_left); }
|
||||
void IN_LeftUp(void) { KeyUp(&in_left); }
|
||||
void IN_RightDown(void) { KeyDown(&in_right); }
|
||||
void IN_RightUp(void) { KeyUp(&in_right); }
|
||||
void IN_ForwardDown(void) { KeyDown(&in_forward); }
|
||||
void IN_ForwardUp(void) { KeyUp(&in_forward); }
|
||||
void IN_BackDown(void) { KeyDown(&in_back); }
|
||||
void IN_BackUp(void) { KeyUp(&in_back); }
|
||||
void IN_LookupDown(void) { KeyDown(&in_lookup); }
|
||||
void IN_LookupUp(void) { KeyUp(&in_lookup); }
|
||||
void IN_LookdownDown(void) { KeyDown(&in_lookdown); }
|
||||
void IN_LookdownUp(void) { KeyUp(&in_lookdown); }
|
||||
void IN_MoveleftDown(void) { KeyDown(&in_moveleft); }
|
||||
void IN_MoveleftUp(void) { KeyUp(&in_moveleft); }
|
||||
void IN_MoverightDown(void) { KeyDown(&in_moveright); }
|
||||
void IN_MoverightUp(void) { KeyUp(&in_moveright); }
|
||||
|
||||
void IN_SpeedDown(void) { KeyDown(&in_speed); }
|
||||
void IN_SpeedUp(void) { KeyUp(&in_speed); }
|
||||
void IN_StrafeDown(void) { KeyDown(&in_strafe); }
|
||||
void IN_StrafeUp(void) { KeyUp(&in_strafe); }
|
||||
|
||||
void IN_AttackDown(void) { KeyDown(&in_attack); }
|
||||
void IN_AttackUp(void) { KeyUp(&in_attack); }
|
||||
|
||||
void IN_UseDown(void) { KeyDown(&in_use); }
|
||||
void IN_UseUp(void) { KeyUp(&in_use); }
|
||||
|
||||
void IN_Impulse(void) { in_impulse = atoi(Cmd_Argv(1)); }
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_KeyState
|
||||
|
||||
Returns the fraction of the frame that the key was down
|
||||
===============
|
||||
*/
|
||||
float CL_KeyState(kbutton_t * key)
|
||||
{
|
||||
float val;
|
||||
int msec;
|
||||
|
||||
key->state &= 1; // clear impulses
|
||||
|
||||
msec = key->msec;
|
||||
key->msec = 0;
|
||||
|
||||
if (key->state)
|
||||
{ // still down
|
||||
msec += sys_frame_time - key->downtime;
|
||||
key->downtime = sys_frame_time;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (msec)
|
||||
{
|
||||
Com_Printf ("%i ", msec);
|
||||
}
|
||||
#endif
|
||||
|
||||
val = (float)msec / frame_msec;
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
if (val > 1)
|
||||
val = 1;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
||||
cvar_t * cl_upspeed;
|
||||
cvar_t * cl_forwardspeed;
|
||||
cvar_t * cl_sidespeed;
|
||||
cvar_t * cl_yawspeed;
|
||||
cvar_t * cl_pitchspeed;
|
||||
cvar_t * cl_run;
|
||||
cvar_t * cl_anglespeedkey;
|
||||
|
||||
/*
|
||||
================
|
||||
CL_AdjustAngles
|
||||
|
||||
Moves the local angle positions
|
||||
================
|
||||
*/
|
||||
void CL_AdjustAngles(void)
|
||||
{
|
||||
float speed;
|
||||
float up, down;
|
||||
|
||||
if (in_speed.state & 1)
|
||||
speed = cls.frametime * cl_anglespeedkey->value;
|
||||
else
|
||||
speed = cls.frametime;
|
||||
|
||||
if (!(in_strafe.state & 1))
|
||||
{
|
||||
cl.viewangles[YAW] -= speed * cl_yawspeed->value * CL_KeyState(&in_right);
|
||||
cl.viewangles[YAW] += speed * cl_yawspeed->value * CL_KeyState(&in_left);
|
||||
}
|
||||
if (in_klook.state & 1)
|
||||
{
|
||||
cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * CL_KeyState(&in_forward);
|
||||
cl.viewangles[PITCH] += speed * cl_pitchspeed->value * CL_KeyState(&in_back);
|
||||
}
|
||||
|
||||
up = CL_KeyState(&in_lookup);
|
||||
down = CL_KeyState(&in_lookdown);
|
||||
|
||||
cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * up;
|
||||
cl.viewangles[PITCH] += speed * cl_pitchspeed->value * down;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_BaseMove
|
||||
|
||||
Send the intended movement message to the server
|
||||
================
|
||||
*/
|
||||
void CL_BaseMove(usercmd_t * cmd)
|
||||
{
|
||||
CL_AdjustAngles();
|
||||
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
|
||||
VectorCopy(cl.viewangles, cmd->angles);
|
||||
if (in_strafe.state & 1)
|
||||
{
|
||||
cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_right);
|
||||
cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_left);
|
||||
}
|
||||
|
||||
cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_moveright);
|
||||
cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_moveleft);
|
||||
|
||||
cmd->upmove += cl_upspeed->value * CL_KeyState(&in_up);
|
||||
cmd->upmove -= cl_upspeed->value * CL_KeyState(&in_down);
|
||||
|
||||
if (!(in_klook.state & 1))
|
||||
{
|
||||
cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward);
|
||||
cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState(&in_back);
|
||||
}
|
||||
|
||||
//
|
||||
// adjust for speed key / running
|
||||
//
|
||||
if ((in_speed.state & 1) ^ (int)(cl_run->value))
|
||||
{
|
||||
cmd->forwardmove *= 2;
|
||||
cmd->sidemove *= 2;
|
||||
cmd->upmove *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void CL_ClampPitch(void)
|
||||
{
|
||||
float pitch;
|
||||
|
||||
pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
||||
if (pitch > 180)
|
||||
pitch -= 360;
|
||||
if (cl.viewangles[PITCH] + pitch > 89)
|
||||
cl.viewangles[PITCH] = 89 - pitch;
|
||||
if (cl.viewangles[PITCH] + pitch < -89)
|
||||
cl.viewangles[PITCH] = -89 - pitch;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CL_FinishMove
|
||||
==============
|
||||
*/
|
||||
void CL_FinishMove(usercmd_t * cmd)
|
||||
{
|
||||
int ms;
|
||||
int i;
|
||||
|
||||
//
|
||||
// figure button bits
|
||||
//
|
||||
if (in_attack.state & 3)
|
||||
cmd->buttons |= BUTTON_ATTACK;
|
||||
in_attack.state &= ~2;
|
||||
|
||||
if (in_use.state & 3)
|
||||
cmd->buttons |= BUTTON_USE;
|
||||
in_use.state &= ~2;
|
||||
|
||||
if (anykeydown && cls.key_dest == key_game)
|
||||
cmd->buttons |= BUTTON_ANY;
|
||||
|
||||
// send milliseconds of time to apply the move
|
||||
ms = cls.frametime * 1000;
|
||||
if (ms > 250)
|
||||
ms = 100; // time was unreasonable
|
||||
cmd->msec = ms;
|
||||
|
||||
CL_ClampPitch();
|
||||
for (i = 0; i < 3; i++)
|
||||
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
|
||||
|
||||
cmd->impulse = in_impulse;
|
||||
in_impulse = 0;
|
||||
|
||||
// send the ambient light level at the player's current position
|
||||
cmd->lightlevel = (byte)cl_lightlevel->value;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_CreateCmd
|
||||
=================
|
||||
*/
|
||||
usercmd_t CL_CreateCmd(void)
|
||||
{
|
||||
usercmd_t cmd;
|
||||
|
||||
frame_msec = sys_frame_time - old_sys_frame_time;
|
||||
if (frame_msec < 1)
|
||||
frame_msec = 1;
|
||||
if (frame_msec > 200)
|
||||
frame_msec = 200;
|
||||
|
||||
// get basic movement from keyboard
|
||||
CL_BaseMove(&cmd);
|
||||
|
||||
// allow mice or other external controllers to add to the move
|
||||
IN_Move(&cmd);
|
||||
|
||||
CL_FinishMove(&cmd);
|
||||
|
||||
old_sys_frame_time = sys_frame_time;
|
||||
|
||||
//cmd.impulse = cls.framecount;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void IN_CenterView(void)
|
||||
{
|
||||
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
CL_InitInput
|
||||
============
|
||||
*/
|
||||
void CL_InitInput(void)
|
||||
{
|
||||
Cmd_AddCommand("centerview", IN_CenterView);
|
||||
Cmd_AddCommand("+moveup", IN_UpDown);
|
||||
Cmd_AddCommand("-moveup", IN_UpUp);
|
||||
Cmd_AddCommand("+movedown", IN_DownDown);
|
||||
Cmd_AddCommand("-movedown", IN_DownUp);
|
||||
Cmd_AddCommand("+left", IN_LeftDown);
|
||||
Cmd_AddCommand("-left", IN_LeftUp);
|
||||
Cmd_AddCommand("+right", IN_RightDown);
|
||||
Cmd_AddCommand("-right", IN_RightUp);
|
||||
Cmd_AddCommand("+forward", IN_ForwardDown);
|
||||
Cmd_AddCommand("-forward", IN_ForwardUp);
|
||||
Cmd_AddCommand("+back", IN_BackDown);
|
||||
Cmd_AddCommand("-back", IN_BackUp);
|
||||
Cmd_AddCommand("+lookup", IN_LookupDown);
|
||||
Cmd_AddCommand("-lookup", IN_LookupUp);
|
||||
Cmd_AddCommand("+lookdown", IN_LookdownDown);
|
||||
Cmd_AddCommand("-lookdown", IN_LookdownUp);
|
||||
Cmd_AddCommand("+strafe", IN_StrafeDown);
|
||||
Cmd_AddCommand("-strafe", IN_StrafeUp);
|
||||
Cmd_AddCommand("+moveleft", IN_MoveleftDown);
|
||||
Cmd_AddCommand("-moveleft", IN_MoveleftUp);
|
||||
Cmd_AddCommand("+moveright", IN_MoverightDown);
|
||||
Cmd_AddCommand("-moveright", IN_MoverightUp);
|
||||
Cmd_AddCommand("+speed", IN_SpeedDown);
|
||||
Cmd_AddCommand("-speed", IN_SpeedUp);
|
||||
Cmd_AddCommand("+attack", IN_AttackDown);
|
||||
Cmd_AddCommand("-attack", IN_AttackUp);
|
||||
Cmd_AddCommand("+use", IN_UseDown);
|
||||
Cmd_AddCommand("-use", IN_UseUp);
|
||||
Cmd_AddCommand("impulse", IN_Impulse);
|
||||
Cmd_AddCommand("+klook", IN_KLookDown);
|
||||
Cmd_AddCommand("-klook", IN_KLookUp);
|
||||
|
||||
cl_nodelta = Cvar_Get("cl_nodelta", "0", 0);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_SendCmd
|
||||
=================
|
||||
*/
|
||||
void CL_SendCmd(void)
|
||||
{
|
||||
sizebuf_t buf;
|
||||
byte data[128];
|
||||
int i;
|
||||
usercmd_t *cmd, *oldcmd;
|
||||
usercmd_t nullcmd;
|
||||
int checksumIndex;
|
||||
|
||||
// build a command even if not connected
|
||||
|
||||
// save this command off for prediction
|
||||
i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1);
|
||||
cmd = &cl.cmds[i];
|
||||
cl.cmd_time[i] = cls.realtime; // for netgraph ping calculation
|
||||
|
||||
*cmd = CL_CreateCmd();
|
||||
|
||||
cl.cmd = *cmd;
|
||||
|
||||
if (cls.state == ca_disconnected || cls.state == ca_connecting)
|
||||
return;
|
||||
|
||||
if (cls.state == ca_connected)
|
||||
{
|
||||
if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000)
|
||||
Netchan_Transmit(&cls.netchan, 0, buf.data);
|
||||
return;
|
||||
}
|
||||
|
||||
// send a userinfo update if needed
|
||||
if (userinfo_modified)
|
||||
{
|
||||
CL_FixUpGender();
|
||||
userinfo_modified = false;
|
||||
MSG_WriteByte(&cls.netchan.message, clc_userinfo);
|
||||
MSG_WriteString(&cls.netchan.message, Cvar_Userinfo());
|
||||
}
|
||||
|
||||
SZ_Init(&buf, data, sizeof(data));
|
||||
|
||||
if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop && cls.realtime - cl.cinematictime > 1000)
|
||||
{ // skip the rest of the cinematic
|
||||
SCR_FinishCinematic();
|
||||
}
|
||||
|
||||
// begin a client move command
|
||||
MSG_WriteByte(&buf, clc_move);
|
||||
|
||||
// save the position for a checksum byte
|
||||
checksumIndex = buf.cursize;
|
||||
MSG_WriteByte(&buf, 0);
|
||||
|
||||
// let the server know what the last frame we
|
||||
// got was, so the next message can be delta compressed
|
||||
if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
|
||||
MSG_WriteLong(&buf, -1); // no compression
|
||||
else
|
||||
MSG_WriteLong(&buf, cl.frame.serverframe);
|
||||
|
||||
// send this and the previous cmds in the message, so
|
||||
// if the last packet was dropped, it can be recovered
|
||||
i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1);
|
||||
cmd = &cl.cmds[i];
|
||||
memset(&nullcmd, 0, sizeof(nullcmd));
|
||||
MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
|
||||
oldcmd = cmd;
|
||||
|
||||
i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1);
|
||||
cmd = &cl.cmds[i];
|
||||
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
||||
oldcmd = cmd;
|
||||
|
||||
i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1);
|
||||
cmd = &cl.cmds[i];
|
||||
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
||||
|
||||
// calculate a checksum over the move commands
|
||||
buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
|
||||
buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
|
||||
cls.netchan.outgoing_sequence);
|
||||
|
||||
//
|
||||
// deliver the message
|
||||
//
|
||||
Netchan_Transmit(&cls.netchan, buf.cursize, buf.data);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
// cl_inv.c -- client inventory screen
|
||||
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
================
|
||||
CL_ParseInventory
|
||||
================
|
||||
*/
|
||||
void CL_ParseInventory(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ITEMS; i++)
|
||||
cl.inventory[i] = MSG_ReadShort(&net_message);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Inv_DrawString
|
||||
================
|
||||
*/
|
||||
void Inv_DrawString(int x, int y, char * string)
|
||||
{
|
||||
while (*string)
|
||||
{
|
||||
re.DrawChar(x, y, *string);
|
||||
x += 8;
|
||||
string++;
|
||||
}
|
||||
}
|
||||
|
||||
void SetStringHighBit(char * s)
|
||||
{
|
||||
while (*s)
|
||||
*s++ |= 128;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_DrawInventory
|
||||
================
|
||||
*/
|
||||
void CL_DrawInventory(void)
|
||||
{
|
||||
int i, j;
|
||||
int num, selected_num, item;
|
||||
int index[MAX_ITEMS];
|
||||
char string[1024];
|
||||
int x, y;
|
||||
char binding[1024];
|
||||
const char * bind;
|
||||
int selected;
|
||||
int top;
|
||||
|
||||
enum
|
||||
{
|
||||
DISPLAY_ITEMS = 17
|
||||
};
|
||||
|
||||
selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM];
|
||||
|
||||
num = 0;
|
||||
selected_num = 0;
|
||||
for (i = 0; i < MAX_ITEMS; i++)
|
||||
{
|
||||
if (i == selected)
|
||||
{
|
||||
selected_num = num;
|
||||
}
|
||||
if (cl.inventory[i])
|
||||
{
|
||||
index[num] = i;
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
||||
// determine scroll point
|
||||
top = selected_num - DISPLAY_ITEMS / 2;
|
||||
if (num - top < DISPLAY_ITEMS)
|
||||
{
|
||||
top = num - DISPLAY_ITEMS;
|
||||
}
|
||||
if (top < 0)
|
||||
{
|
||||
top = 0;
|
||||
}
|
||||
|
||||
x = (viddef.width - 256) / 2;
|
||||
y = (viddef.height - 240) / 2;
|
||||
|
||||
// repaint everything next frame
|
||||
SCR_DirtyScreen();
|
||||
|
||||
re.DrawPic(x, y + 8, "inventory");
|
||||
|
||||
y += 24;
|
||||
x += 24;
|
||||
Inv_DrawString(x, y, "hotkey ### item");
|
||||
Inv_DrawString(x, y + 8, "------ --- ----");
|
||||
y += 16;
|
||||
for (i = top; i < num && i < top + DISPLAY_ITEMS; i++)
|
||||
{
|
||||
item = index[i];
|
||||
// search for a binding
|
||||
Com_sprintf(binding, sizeof(binding), "use %s", cl.configstrings[CS_ITEMS + item]);
|
||||
bind = "";
|
||||
|
||||
for (j = 0; j < 256; j++)
|
||||
{
|
||||
if (keybindings[j] && !Q_stricmp(keybindings[j], binding))
|
||||
{
|
||||
bind = Key_KeynumToString(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Com_sprintf(string, sizeof(string), "%6s %3i %s", bind, cl.inventory[item],
|
||||
cl.configstrings[CS_ITEMS + item]);
|
||||
|
||||
if (item != selected)
|
||||
{
|
||||
SetStringHighBit(string);
|
||||
}
|
||||
else // draw a blinky cursor by the selected item
|
||||
{
|
||||
if ((int)(cls.realtime * 10) & 1)
|
||||
re.DrawChar(x - 8, y, 15);
|
||||
}
|
||||
|
||||
Inv_DrawString(x, y, string);
|
||||
y += 8;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
// cl_parse.c -- parse a message received from the server
|
||||
|
||||
#include "client.h"
|
||||
|
||||
char * svc_strings[256] =
|
||||
{
|
||||
"svc_bad",
|
||||
"svc_muzzleflash",
|
||||
"svc_muzzlflash2",
|
||||
"svc_temp_entity",
|
||||
"svc_layout",
|
||||
"svc_inventory",
|
||||
"svc_nop",
|
||||
"svc_disconnect",
|
||||
"svc_reconnect",
|
||||
"svc_sound",
|
||||
"svc_print",
|
||||
"svc_stufftext",
|
||||
"svc_serverdata",
|
||||
"svc_configstring",
|
||||
"svc_spawnbaseline",
|
||||
"svc_centerprint",
|
||||
"svc_download",
|
||||
"svc_playerinfo",
|
||||
"svc_packetentities",
|
||||
"svc_deltapacketentities",
|
||||
"svc_frame"
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void CL_DownloadFileName(char * dest, int destlen, char * fn)
|
||||
{
|
||||
if (strncmp(fn, "players", 7) == 0)
|
||||
Com_sprintf(dest, destlen, "%s/%s", BASEDIRNAME, fn);
|
||||
else
|
||||
Com_sprintf(dest, destlen, "%s/%s", FS_Gamedir(), fn);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_CheckOrDownloadFile
|
||||
|
||||
Returns true if the file exists, otherwise it attempts
|
||||
to start a download from the server.
|
||||
===============
|
||||
*/
|
||||
qboolean CL_CheckOrDownloadFile(char * filename)
|
||||
{
|
||||
FILE * fp;
|
||||
char name[MAX_OSPATH];
|
||||
|
||||
if (strstr(filename, ".."))
|
||||
{
|
||||
Com_Printf("Refusing to download a path with ..\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FS_LoadFile(filename, NULL) != -1)
|
||||
{ // it exists, no need to download
|
||||
return true;
|
||||
}
|
||||
|
||||
strcpy(cls.downloadname, filename);
|
||||
|
||||
// download to a temp name, and only rename
|
||||
// to the real name when done, so if interrupted
|
||||
// a runt file wont be left
|
||||
COM_StripExtension(cls.downloadname, cls.downloadtempname);
|
||||
strcat(cls.downloadtempname, ".tmp");
|
||||
|
||||
//ZOID
|
||||
// check to see if we already have a tmp for this file, if so, try to resume
|
||||
// open the file if not opened yet
|
||||
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
|
||||
|
||||
// FS_CreatePath (name);
|
||||
|
||||
fp = fopen(name, "r+b");
|
||||
if (fp)
|
||||
{ // it exists
|
||||
int len;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
len = ftell(fp);
|
||||
|
||||
cls.download = fp;
|
||||
|
||||
// give the server an offset to start the download
|
||||
Com_Printf("Resuming %s\n", cls.downloadname);
|
||||
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
|
||||
MSG_WriteString(&cls.netchan.message,
|
||||
va("download %s %i", cls.downloadname, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf("Downloading %s\n", cls.downloadname);
|
||||
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
|
||||
MSG_WriteString(&cls.netchan.message,
|
||||
va("download %s", cls.downloadname));
|
||||
}
|
||||
|
||||
cls.downloadnumber++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_Download_f
|
||||
|
||||
Request a download from the server
|
||||
===============
|
||||
*/
|
||||
void CL_Download_f(void)
|
||||
{
|
||||
char filename[MAX_OSPATH];
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
Com_Printf("Usage: download <filename>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
|
||||
|
||||
if (strstr(filename, ".."))
|
||||
{
|
||||
Com_Printf("Refusing to download a path with ..\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FS_LoadFile(filename, NULL) != -1)
|
||||
{ // it exists, no need to download
|
||||
Com_Printf("File already exists.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(cls.downloadname, filename);
|
||||
Com_Printf("Downloading %s\n", cls.downloadname);
|
||||
|
||||
// download to a temp name, and only rename
|
||||
// to the real name when done, so if interrupted
|
||||
// a runt file wont be left
|
||||
COM_StripExtension(cls.downloadname, cls.downloadtempname);
|
||||
strcat(cls.downloadtempname, ".tmp");
|
||||
|
||||
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
|
||||
MSG_WriteString(&cls.netchan.message,
|
||||
va("download %s", cls.downloadname));
|
||||
|
||||
cls.downloadnumber++;
|
||||
}
|
||||
|
||||
/*
|
||||
======================
|
||||
CL_RegisterSounds
|
||||
======================
|
||||
*/
|
||||
void CL_RegisterSounds(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
S_BeginRegistration();
|
||||
CL_RegisterTEntSounds();
|
||||
for (i = 1; i < MAX_SOUNDS; i++)
|
||||
{
|
||||
if (!cl.configstrings[CS_SOUNDS + i][0])
|
||||
break;
|
||||
cl.sound_precache[i] = S_RegisterSound(cl.configstrings[CS_SOUNDS + i]);
|
||||
Sys_SendKeyEvents(); // pump message loop
|
||||
}
|
||||
S_EndRegistration();
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseDownload
|
||||
|
||||
A download message has been received from the server
|
||||
=====================
|
||||
*/
|
||||
void CL_ParseDownload(void)
|
||||
{
|
||||
int size, percent;
|
||||
char name[MAX_OSPATH];
|
||||
int r;
|
||||
|
||||
// read the data
|
||||
size = MSG_ReadShort(&net_message);
|
||||
percent = MSG_ReadByte(&net_message);
|
||||
if (size == -1)
|
||||
{
|
||||
Com_Printf("Server does not have this file.\n");
|
||||
if (cls.download)
|
||||
{
|
||||
// if here, we tried to resume a file but the server said no
|
||||
fclose(cls.download);
|
||||
cls.download = NULL;
|
||||
}
|
||||
CL_RequestNextDownload();
|
||||
return;
|
||||
}
|
||||
|
||||
// open the file if not opened yet
|
||||
if (!cls.download)
|
||||
{
|
||||
CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
|
||||
|
||||
FS_CreatePath(name);
|
||||
|
||||
cls.download = fopen(name, "wb");
|
||||
if (!cls.download)
|
||||
{
|
||||
net_message.readcount += size;
|
||||
Com_Printf("Failed to open %s\n", cls.downloadtempname);
|
||||
CL_RequestNextDownload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fwrite(net_message.data + net_message.readcount, 1, size, cls.download);
|
||||
net_message.readcount += size;
|
||||
|
||||
if (percent != 100)
|
||||
{
|
||||
// request next block
|
||||
// change display routines by zoid
|
||||
#if 0
|
||||
Com_Printf (".");
|
||||
if (10*(percent/10) != cls.downloadpercent)
|
||||
{
|
||||
cls.downloadpercent = 10*(percent/10);
|
||||
Com_Printf ("%i%%", cls.downloadpercent);
|
||||
}
|
||||
#endif
|
||||
cls.downloadpercent = percent;
|
||||
|
||||
MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
|
||||
SZ_Print(&cls.netchan.message, "nextdl");
|
||||
}
|
||||
else
|
||||
{
|
||||
char oldn[MAX_OSPATH];
|
||||
char newn[MAX_OSPATH];
|
||||
|
||||
// Com_Printf ("100%%\n");
|
||||
|
||||
fclose(cls.download);
|
||||
|
||||
// rename the temp file to it's final name
|
||||
CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
|
||||
CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
|
||||
r = rename(oldn, newn);
|
||||
if (r)
|
||||
Com_Printf("failed to rename.\n");
|
||||
|
||||
cls.download = NULL;
|
||||
cls.downloadpercent = 0;
|
||||
|
||||
// get another file if needed
|
||||
|
||||
CL_RequestNextDownload();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================================================
|
||||
|
||||
SERVER CONNECTING MESSAGES
|
||||
|
||||
=====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_ParseServerData
|
||||
==================
|
||||
*/
|
||||
void CL_ParseServerData(void)
|
||||
{
|
||||
extern cvar_t * fs_gamedirvar;
|
||||
char * str;
|
||||
int i;
|
||||
|
||||
Com_DPrintf("Serverdata packet received.\n");
|
||||
//
|
||||
// wipe the client_state_t struct
|
||||
//
|
||||
CL_ClearState();
|
||||
cls.state = ca_connected;
|
||||
|
||||
// parse protocol version number
|
||||
i = MSG_ReadLong(&net_message);
|
||||
cls.serverProtocol = i;
|
||||
|
||||
// BIG HACK to let demos from release work with the 3.0x patch!!!
|
||||
if (Com_ServerState() && PROTOCOL_VERSION == 34)
|
||||
{
|
||||
}
|
||||
else if (i != PROTOCOL_VERSION)
|
||||
{
|
||||
Com_Error(ERR_DROP, "Server returned version %i, not %i", i, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
cl.servercount = MSG_ReadLong(&net_message);
|
||||
cl.attractloop = MSG_ReadByte(&net_message);
|
||||
|
||||
// game directory
|
||||
str = MSG_ReadString(&net_message);
|
||||
strncpy(cl.gamedir, str, sizeof(cl.gamedir) - 1);
|
||||
|
||||
// set gamedir
|
||||
if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
|
||||
Cvar_Set("game", str);
|
||||
|
||||
// parse player entity number
|
||||
cl.playernum = MSG_ReadShort(&net_message);
|
||||
|
||||
// get the full level name
|
||||
str = MSG_ReadString(&net_message);
|
||||
|
||||
if (cl.playernum == -1)
|
||||
{
|
||||
// playing a cinematic or showing a pic, not a level
|
||||
SCR_PlayCinematic(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
// separate the printfs so the server message can have a color
|
||||
Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
|
||||
Com_Printf("%c%s\n", 2, str);
|
||||
|
||||
// need to prep refresh at next opportunity
|
||||
cl.refresh_prepped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_ParseBaseline
|
||||
==================
|
||||
*/
|
||||
void CL_ParseBaseline(void)
|
||||
{
|
||||
entity_state_t * es;
|
||||
int bits;
|
||||
int newnum;
|
||||
entity_state_t nullstate;
|
||||
|
||||
memset(&nullstate, 0, sizeof(nullstate));
|
||||
|
||||
newnum = CL_ParseEntityBits(&bits);
|
||||
es = &cl_entities[newnum].baseline;
|
||||
CL_ParseDelta(&nullstate, es, newnum, bits);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_LoadClientinfo
|
||||
|
||||
================
|
||||
*/
|
||||
void CL_LoadClientinfo(clientinfo_t * ci, char * s)
|
||||
{
|
||||
int i;
|
||||
char * t;
|
||||
char model_name[MAX_QPATH];
|
||||
char skin_name[MAX_QPATH];
|
||||
char model_filename[MAX_QPATH];
|
||||
char skin_filename[MAX_QPATH];
|
||||
char weapon_filename[MAX_QPATH];
|
||||
|
||||
strncpy(ci->cinfo, s, sizeof(ci->cinfo));
|
||||
ci->cinfo[sizeof(ci->cinfo) - 1] = 0;
|
||||
|
||||
// isolate the player's name
|
||||
strncpy(ci->name, s, sizeof(ci->name));
|
||||
ci->name[sizeof(ci->name) - 1] = 0;
|
||||
t = strstr(s, "\\");
|
||||
if (t)
|
||||
{
|
||||
ci->name[t - s] = 0;
|
||||
s = t + 1;
|
||||
}
|
||||
|
||||
if (cl_noskins->value || *s == 0)
|
||||
{
|
||||
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
|
||||
Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
|
||||
Com_sprintf(skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
|
||||
Com_sprintf(ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
|
||||
ci->model = re.RegisterModel(model_filename);
|
||||
memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
|
||||
ci->weaponmodel[0] = re.RegisterModel(weapon_filename);
|
||||
ci->skin = re.RegisterSkin(skin_filename);
|
||||
ci->icon = re.RegisterPic(ci->iconname);
|
||||
}
|
||||
else
|
||||
{
|
||||
// isolate the model name
|
||||
strcpy(model_name, s);
|
||||
t = strstr(model_name, "/");
|
||||
if (!t)
|
||||
t = strstr(model_name, "\\");
|
||||
if (!t)
|
||||
t = model_name;
|
||||
*t = 0;
|
||||
|
||||
// isolate the skin name
|
||||
strcpy(skin_name, s + strlen(model_name) + 1);
|
||||
|
||||
// model file
|
||||
Com_sprintf(model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
|
||||
ci->model = re.RegisterModel(model_filename);
|
||||
if (!ci->model)
|
||||
{
|
||||
strcpy(model_name, "male");
|
||||
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
|
||||
ci->model = re.RegisterModel(model_filename);
|
||||
}
|
||||
|
||||
// skin file
|
||||
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
|
||||
ci->skin = re.RegisterSkin(skin_filename);
|
||||
|
||||
// if we don't have the skin and the model wasn't male,
|
||||
// see if the male has it (this is for CTF's skins)
|
||||
if (!ci->skin && Q_stricmp(model_name, "male"))
|
||||
{
|
||||
// change model to male
|
||||
strcpy(model_name, "male");
|
||||
Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2");
|
||||
ci->model = re.RegisterModel(model_filename);
|
||||
|
||||
// see if the skin exists for the male model
|
||||
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
|
||||
ci->skin = re.RegisterSkin(skin_filename);
|
||||
}
|
||||
|
||||
// if we still don't have a skin, it means that the male model didn't have
|
||||
// it, so default to grunt
|
||||
if (!ci->skin)
|
||||
{
|
||||
// see if the skin exists for the male model
|
||||
Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
|
||||
ci->skin = re.RegisterSkin(skin_filename);
|
||||
}
|
||||
|
||||
// weapon file
|
||||
for (i = 0; i < num_cl_weaponmodels; i++)
|
||||
{
|
||||
Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
|
||||
ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
|
||||
if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0)
|
||||
{
|
||||
// try male
|
||||
Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
|
||||
ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
|
||||
}
|
||||
if (!cl_vwep->value)
|
||||
break; // only one when vwep is off
|
||||
}
|
||||
|
||||
// icon file
|
||||
Com_sprintf(ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
|
||||
ci->icon = re.RegisterPic(ci->iconname);
|
||||
}
|
||||
|
||||
// must have loaded all data types to be valud
|
||||
if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
|
||||
{
|
||||
ci->skin = NULL;
|
||||
ci->icon = NULL;
|
||||
ci->model = NULL;
|
||||
ci->weaponmodel[0] = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_ParseClientinfo
|
||||
|
||||
Load the skin, icon, and model for a client
|
||||
================
|
||||
*/
|
||||
void CL_ParseClientinfo(int player)
|
||||
{
|
||||
char * s;
|
||||
clientinfo_t * ci;
|
||||
|
||||
s = cl.configstrings[player + CS_PLAYERSKINS];
|
||||
|
||||
ci = &cl.clientinfo[player];
|
||||
|
||||
CL_LoadClientinfo(ci, s);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_ParseConfigString
|
||||
================
|
||||
*/
|
||||
void CL_ParseConfigString(void)
|
||||
{
|
||||
int i;
|
||||
char * s;
|
||||
|
||||
i = MSG_ReadShort(&net_message);
|
||||
if (i < 0 || i >= MAX_CONFIGSTRINGS)
|
||||
{
|
||||
Com_Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
|
||||
}
|
||||
|
||||
s = MSG_ReadString(&net_message);
|
||||
strcpy(cl.configstrings[i], s);
|
||||
|
||||
// do something apropriate
|
||||
|
||||
if (i >= CS_LIGHTS && i < CS_LIGHTS + MAX_LIGHTSTYLES)
|
||||
{
|
||||
CL_SetLightstyle(i - CS_LIGHTS);
|
||||
}
|
||||
else if (i == CS_CDTRACK)
|
||||
{
|
||||
if (cl.refresh_prepped)
|
||||
{
|
||||
CDAudio_Play(atoi(cl.configstrings[CS_CDTRACK]), true);
|
||||
}
|
||||
}
|
||||
else if (i >= CS_MODELS && i < CS_MODELS + MAX_MODELS)
|
||||
{
|
||||
if (cl.refresh_prepped)
|
||||
{
|
||||
cl.model_draw[i - CS_MODELS] = re.RegisterModel(cl.configstrings[i]);
|
||||
if (cl.configstrings[i][0] == '*')
|
||||
cl.model_clip[i - CS_MODELS] = CM_InlineModel(cl.configstrings[i]);
|
||||
else
|
||||
cl.model_clip[i - CS_MODELS] = NULL;
|
||||
}
|
||||
}
|
||||
else if (i >= CS_SOUNDS && i < CS_SOUNDS + MAX_MODELS)
|
||||
{
|
||||
if (cl.refresh_prepped)
|
||||
cl.sound_precache[i - CS_SOUNDS] = S_RegisterSound(cl.configstrings[i]);
|
||||
}
|
||||
else if (i >= CS_IMAGES && i < CS_IMAGES + MAX_MODELS)
|
||||
{
|
||||
if (cl.refresh_prepped)
|
||||
cl.image_precache[i - CS_IMAGES] = re.RegisterPic(cl.configstrings[i]);
|
||||
}
|
||||
else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS + MAX_CLIENTS)
|
||||
{
|
||||
if (cl.refresh_prepped)
|
||||
CL_ParseClientinfo(i - CS_PLAYERSKINS);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=====================================================================
|
||||
|
||||
ACTION MESSAGES
|
||||
|
||||
=====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_ParseStartSoundPacket
|
||||
==================
|
||||
*/
|
||||
void CL_ParseStartSoundPacket(void)
|
||||
{
|
||||
vec3_t pos_v;
|
||||
float * pos;
|
||||
int channel, ent;
|
||||
int sound_num;
|
||||
float volume;
|
||||
float attenuation;
|
||||
int flags;
|
||||
float ofs;
|
||||
|
||||
flags = MSG_ReadByte(&net_message);
|
||||
sound_num = MSG_ReadByte(&net_message);
|
||||
|
||||
if (flags & SND_VOLUME)
|
||||
volume = MSG_ReadByte(&net_message) / 255.0;
|
||||
else
|
||||
volume = DEFAULT_SOUND_PACKET_VOLUME;
|
||||
|
||||
if (flags & SND_ATTENUATION)
|
||||
attenuation = MSG_ReadByte(&net_message) / 64.0;
|
||||
else
|
||||
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
|
||||
|
||||
if (flags & SND_OFFSET)
|
||||
ofs = MSG_ReadByte(&net_message) / 1000.0;
|
||||
else
|
||||
ofs = 0;
|
||||
|
||||
if (flags & SND_ENT)
|
||||
{ // entity reletive
|
||||
channel = MSG_ReadShort(&net_message);
|
||||
ent = channel >> 3;
|
||||
if (ent > MAX_EDICTS)
|
||||
Com_Error(ERR_DROP, "CL_ParseStartSoundPacket: ent = %i", ent);
|
||||
|
||||
channel &= 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent = 0;
|
||||
channel = 0;
|
||||
}
|
||||
|
||||
if (flags & SND_POS)
|
||||
{ // positioned in space
|
||||
MSG_ReadPos(&net_message, pos_v);
|
||||
|
||||
pos = pos_v;
|
||||
}
|
||||
else // use entity number
|
||||
pos = NULL;
|
||||
|
||||
if (!cl.sound_precache[sound_num])
|
||||
return;
|
||||
|
||||
S_StartSound(pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
|
||||
}
|
||||
|
||||
void SHOWNET(char * s)
|
||||
{
|
||||
if (cl_shownet->value >= 2)
|
||||
Com_Printf("%3i:%s\n", net_message.readcount - 1, s);
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
CL_ParseServerMessage
|
||||
=====================
|
||||
*/
|
||||
void CL_ParseServerMessage(void)
|
||||
{
|
||||
int cmd;
|
||||
char * s;
|
||||
int i;
|
||||
|
||||
//
|
||||
// if recording demos, copy the message out
|
||||
//
|
||||
if (cl_shownet->value == 1)
|
||||
Com_Printf("%i ", net_message.cursize);
|
||||
else if (cl_shownet->value >= 2)
|
||||
Com_Printf("------------------\n");
|
||||
|
||||
//
|
||||
// parse the message
|
||||
//
|
||||
while (1)
|
||||
{
|
||||
if (net_message.readcount > net_message.cursize)
|
||||
{
|
||||
Com_Error(ERR_DROP, "CL_ParseServerMessage: Bad server message");
|
||||
break;
|
||||
}
|
||||
|
||||
cmd = MSG_ReadByte(&net_message);
|
||||
|
||||
if (cmd == -1)
|
||||
{
|
||||
SHOWNET("END OF MESSAGE");
|
||||
break;
|
||||
}
|
||||
|
||||
if (cl_shownet->value >= 2)
|
||||
{
|
||||
if (!svc_strings[cmd])
|
||||
Com_Printf("%3i:BAD CMD %i\n", net_message.readcount - 1, cmd);
|
||||
else
|
||||
SHOWNET(svc_strings[cmd]);
|
||||
}
|
||||
|
||||
// other commands
|
||||
switch (cmd)
|
||||
{
|
||||
default:
|
||||
Com_Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n");
|
||||
break;
|
||||
|
||||
case svc_nop:
|
||||
// Com_Printf ("svc_nop\n");
|
||||
break;
|
||||
|
||||
case svc_disconnect:
|
||||
Com_Error(ERR_DISCONNECT, "Server disconnected\n");
|
||||
break;
|
||||
|
||||
case svc_reconnect:
|
||||
Com_Printf("Server disconnected, reconnecting\n");
|
||||
if (cls.download)
|
||||
{
|
||||
//ZOID, close download
|
||||
fclose(cls.download);
|
||||
cls.download = NULL;
|
||||
}
|
||||
cls.state = ca_connecting;
|
||||
cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
|
||||
break;
|
||||
|
||||
case svc_print:
|
||||
i = MSG_ReadByte(&net_message);
|
||||
if (i == PRINT_CHAT)
|
||||
{
|
||||
S_StartLocalSound("misc/talk.wav");
|
||||
con.ormask = 128;
|
||||
}
|
||||
Com_Printf("%s", MSG_ReadString(&net_message));
|
||||
con.ormask = 0;
|
||||
break;
|
||||
|
||||
case svc_centerprint:
|
||||
SCR_CenterPrint(MSG_ReadString(&net_message));
|
||||
break;
|
||||
|
||||
case svc_stufftext:
|
||||
s = MSG_ReadString(&net_message);
|
||||
Com_DPrintf("stufftext: %s\n", s);
|
||||
Cbuf_AddText(s);
|
||||
break;
|
||||
|
||||
case svc_serverdata:
|
||||
Cbuf_Execute(); // make sure any stuffed commands are done
|
||||
CL_ParseServerData();
|
||||
break;
|
||||
|
||||
case svc_configstring:
|
||||
CL_ParseConfigString();
|
||||
break;
|
||||
|
||||
case svc_sound:
|
||||
CL_ParseStartSoundPacket();
|
||||
break;
|
||||
|
||||
case svc_spawnbaseline:
|
||||
CL_ParseBaseline();
|
||||
break;
|
||||
|
||||
case svc_temp_entity:
|
||||
CL_ParseTEnt();
|
||||
break;
|
||||
|
||||
case svc_muzzleflash:
|
||||
CL_ParseMuzzleFlash();
|
||||
break;
|
||||
|
||||
case svc_muzzleflash2:
|
||||
CL_ParseMuzzleFlash2();
|
||||
break;
|
||||
|
||||
case svc_download:
|
||||
CL_ParseDownload();
|
||||
break;
|
||||
|
||||
case svc_frame:
|
||||
CL_ParseFrame();
|
||||
break;
|
||||
|
||||
case svc_inventory:
|
||||
CL_ParseInventory();
|
||||
break;
|
||||
|
||||
case svc_layout:
|
||||
s = MSG_ReadString(&net_message);
|
||||
strncpy(cl.layout, s, sizeof(cl.layout) - 1);
|
||||
break;
|
||||
|
||||
case svc_playerinfo:
|
||||
case svc_packetentities:
|
||||
case svc_deltapacketentities:
|
||||
Com_Error(ERR_DROP, "Out of place frame data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CL_AddNetgraph();
|
||||
|
||||
//
|
||||
// we don't know if it is ok to save a demo message until
|
||||
// after we have parsed the frame
|
||||
//
|
||||
if (cls.demorecording && !cls.demowaiting)
|
||||
CL_WriteDemoMessage();
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
===================
|
||||
CL_CheckPredictionError
|
||||
===================
|
||||
*/
|
||||
void CL_CheckPredictionError(void)
|
||||
{
|
||||
int frame;
|
||||
int delta[3];
|
||||
int i;
|
||||
int len;
|
||||
|
||||
if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
|
||||
return;
|
||||
|
||||
// calculate the last usercmd_t we sent that the server has processed
|
||||
frame = cls.netchan.incoming_acknowledged;
|
||||
frame &= (CMD_BACKUP - 1);
|
||||
|
||||
// compare what the server returned with what we had predicted it to be
|
||||
VectorSubtract(cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame], delta);
|
||||
|
||||
// save the prediction error for interpolation
|
||||
len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
|
||||
if (len > 640) // 80 world units
|
||||
{ // a teleport or something
|
||||
VectorClear(cl.prediction_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]))
|
||||
Com_Printf("prediction miss on %i: %i\n", cl.frame.serverframe,
|
||||
delta[0] + delta[1] + delta[2]);
|
||||
|
||||
VectorCopy(cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame]);
|
||||
|
||||
// save for error itnerpolation
|
||||
for (i = 0; i < 3; i++)
|
||||
cl.prediction_error[i] = delta[i] * 0.125;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CL_ClipMoveToEntities
|
||||
|
||||
====================
|
||||
*/
|
||||
void CL_ClipMoveToEntities(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t * tr)
|
||||
{
|
||||
int i, x, zd, zu;
|
||||
trace_t trace;
|
||||
int headnode;
|
||||
float * angles;
|
||||
entity_state_t * ent;
|
||||
int num;
|
||||
cmodel_t * cmodel;
|
||||
vec3_t bmins, bmaxs;
|
||||
|
||||
for (i = 0; i < cl.frame.num_entities; i++)
|
||||
{
|
||||
num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
|
||||
ent = &cl_parse_entities[num];
|
||||
|
||||
if (!ent->solid)
|
||||
continue;
|
||||
|
||||
if (ent->number == cl.playernum + 1)
|
||||
continue;
|
||||
|
||||
if (ent->solid == 31)
|
||||
{ // special value for bmodel
|
||||
cmodel = cl.model_clip[ent->modelindex];
|
||||
if (!cmodel)
|
||||
continue;
|
||||
headnode = cmodel->headnode;
|
||||
angles = ent->angles;
|
||||
}
|
||||
else
|
||||
{ // encoded bbox
|
||||
x = 8 * (ent->solid & 31);
|
||||
zd = 8 * ((ent->solid >> 5) & 31);
|
||||
zu = 8 * ((ent->solid >> 10) & 63) - 32;
|
||||
|
||||
bmins[0] = bmins[1] = -x;
|
||||
bmaxs[0] = bmaxs[1] = x;
|
||||
bmins[2] = -zd;
|
||||
bmaxs[2] = zu;
|
||||
|
||||
headnode = CM_HeadnodeForBox(bmins, bmaxs);
|
||||
angles = vec3_origin; // boxes don't rotate
|
||||
}
|
||||
|
||||
if (tr->allsolid)
|
||||
return;
|
||||
|
||||
trace = CM_TransformedBoxTrace(start, end,
|
||||
mins, maxs, headnode, MASK_PLAYERSOLID,
|
||||
ent->origin, angles);
|
||||
|
||||
if (trace.allsolid || trace.startsolid ||
|
||||
trace.fraction < tr->fraction)
|
||||
{
|
||||
trace.ent = (struct edict_s *)ent;
|
||||
if (tr->startsolid)
|
||||
{
|
||||
*tr = trace;
|
||||
tr->startsolid = true;
|
||||
}
|
||||
else
|
||||
*tr = trace;
|
||||
}
|
||||
else if (trace.startsolid)
|
||||
tr->startsolid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CL_PMTrace
|
||||
================
|
||||
*/
|
||||
trace_t CL_PMTrace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
|
||||
{
|
||||
trace_t t;
|
||||
|
||||
// check against world
|
||||
t = CM_BoxTrace(start, end, mins, maxs, 0, MASK_PLAYERSOLID);
|
||||
if (t.fraction < 1.0)
|
||||
t.ent = (struct edict_s *)1;
|
||||
|
||||
// check all other solid models
|
||||
CL_ClipMoveToEntities(start, mins, maxs, end, &t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int CL_PMpointcontents(vec3_t point)
|
||||
{
|
||||
int i;
|
||||
entity_state_t * ent;
|
||||
int num;
|
||||
cmodel_t * cmodel;
|
||||
int contents;
|
||||
|
||||
contents = CM_PointContents(point, 0);
|
||||
|
||||
for (i = 0; i < cl.frame.num_entities; i++)
|
||||
{
|
||||
num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1);
|
||||
ent = &cl_parse_entities[num];
|
||||
|
||||
if (ent->solid != 31) // special value for bmodel
|
||||
continue;
|
||||
|
||||
cmodel = cl.model_clip[ent->modelindex];
|
||||
if (!cmodel)
|
||||
continue;
|
||||
|
||||
contents |= CM_TransformedPointContents(point, cmodel->headnode, ent->origin, ent->angles);
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_PredictMovement
|
||||
|
||||
Sets cl.predicted_origin and cl.predicted_angles
|
||||
=================
|
||||
*/
|
||||
void CL_PredictMovement(void)
|
||||
{
|
||||
int ack, current;
|
||||
int frame;
|
||||
int oldframe;
|
||||
usercmd_t * cmd;
|
||||
pmove_t pm;
|
||||
int i;
|
||||
int step;
|
||||
int oldz;
|
||||
|
||||
if (cls.state != ca_active)
|
||||
return;
|
||||
|
||||
if (cl_paused->value)
|
||||
return;
|
||||
|
||||
if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
|
||||
{ // just set angles
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ack = cls.netchan.incoming_acknowledged;
|
||||
current = cls.netchan.outgoing_sequence;
|
||||
|
||||
// if we are too far out of date, just freeze
|
||||
if (current - ack >= CMD_BACKUP)
|
||||
{
|
||||
if (cl_showmiss->value)
|
||||
Com_Printf("exceeded CMD_BACKUP\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// copy current state to pmove
|
||||
memset(&pm, 0, sizeof(pm));
|
||||
pm.trace = CL_PMTrace;
|
||||
pm.pointcontents = CL_PMpointcontents;
|
||||
|
||||
pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]);
|
||||
|
||||
pm.s = cl.frame.playerstate.pmove;
|
||||
|
||||
// SCR_DebugGraph (current - ack - 1, 0);
|
||||
|
||||
frame = 0;
|
||||
|
||||
// run frames
|
||||
while (++ack < current)
|
||||
{
|
||||
frame = ack & (CMD_BACKUP - 1);
|
||||
cmd = &cl.cmds[frame];
|
||||
|
||||
pm.cmd = *cmd;
|
||||
Pmove(&pm);
|
||||
|
||||
// save for debug checking
|
||||
VectorCopy(pm.s.origin, cl.predicted_origins[frame]);
|
||||
}
|
||||
|
||||
oldframe = (ack - 2) & (CMD_BACKUP - 1);
|
||||
oldz = cl.predicted_origins[oldframe][2];
|
||||
step = pm.s.origin[2] - oldz;
|
||||
if (step > 63 && step < 160 && (pm.s.pm_flags & PMF_ON_GROUND))
|
||||
{
|
||||
cl.predicted_step = step * 0.125;
|
||||
cl.predicted_step_time = cls.realtime - cls.frametime * 500;
|
||||
}
|
||||
|
||||
// copy results out for rendering
|
||||
cl.predicted_origin[0] = pm.s.origin[0] * 0.125;
|
||||
cl.predicted_origin[1] = pm.s.origin[1] * 0.125;
|
||||
cl.predicted_origin[2] = pm.s.origin[2] * 0.125;
|
||||
|
||||
VectorCopy(pm.viewangles, cl.predicted_angles);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
// cl_view.c -- player rendering positioning
|
||||
|
||||
#include "client.h"
|
||||
|
||||
//=============
|
||||
//
|
||||
// development tools for weapons
|
||||
//
|
||||
int gun_frame;
|
||||
struct model_s * gun_model;
|
||||
|
||||
//=============
|
||||
|
||||
cvar_t * crosshair;
|
||||
cvar_t * cl_testparticles;
|
||||
cvar_t * cl_testentities;
|
||||
cvar_t * cl_testlights;
|
||||
cvar_t * cl_testblend;
|
||||
cvar_t * cl_stats;
|
||||
|
||||
int r_numdlights;
|
||||
dlight_t r_dlights[MAX_DLIGHTS];
|
||||
|
||||
int r_numentities;
|
||||
entity_t r_entities[MAX_ENTITIES];
|
||||
|
||||
int r_numparticles;
|
||||
particle_t r_particles[MAX_PARTICLES];
|
||||
|
||||
lightstyle_t r_lightstyles[MAX_LIGHTSTYLES];
|
||||
|
||||
char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
|
||||
int num_cl_weaponmodels;
|
||||
|
||||
/*
|
||||
====================
|
||||
V_ClearScene
|
||||
|
||||
Specifies the model that will be used as the world
|
||||
====================
|
||||
*/
|
||||
void V_ClearScene(void)
|
||||
{
|
||||
r_numdlights = 0;
|
||||
r_numentities = 0;
|
||||
r_numparticles = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
V_AddEntity
|
||||
=====================
|
||||
*/
|
||||
void V_AddEntity(entity_t * ent)
|
||||
{
|
||||
if (r_numentities >= MAX_ENTITIES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
r_entities[r_numentities++] = *ent;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
V_AddParticle
|
||||
=====================
|
||||
*/
|
||||
void V_AddParticle(vec3_t org, int color, float alpha)
|
||||
{
|
||||
particle_t * p;
|
||||
|
||||
if (r_numparticles >= MAX_PARTICLES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p = &r_particles[r_numparticles++];
|
||||
VectorCopy(org, p->origin);
|
||||
p->color = color;
|
||||
p->alpha = alpha;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
V_AddLight
|
||||
=====================
|
||||
*/
|
||||
void V_AddLight(vec3_t org, float intensity, float r, float g, float b)
|
||||
{
|
||||
dlight_t * dl;
|
||||
|
||||
if (r_numdlights >= MAX_DLIGHTS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dl = &r_dlights[r_numdlights++];
|
||||
VectorCopy(org, dl->origin);
|
||||
dl->intensity = intensity;
|
||||
dl->color[0] = r;
|
||||
dl->color[1] = g;
|
||||
dl->color[2] = b;
|
||||
}
|
||||
|
||||
/*
|
||||
=====================
|
||||
V_AddLightStyle
|
||||
=====================
|
||||
*/
|
||||
void V_AddLightStyle(int style, float r, float g, float b)
|
||||
{
|
||||
lightstyle_t * ls;
|
||||
|
||||
if (style < 0 || style > MAX_LIGHTSTYLES)
|
||||
{
|
||||
Com_Error(ERR_DROP, "Bad light style %i", style);
|
||||
}
|
||||
|
||||
ls = &r_lightstyles[style];
|
||||
|
||||
ls->white = r + g + b;
|
||||
ls->rgb[0] = r;
|
||||
ls->rgb[1] = g;
|
||||
ls->rgb[2] = b;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
V_TestParticles
|
||||
|
||||
If cl_testparticles is set, create 4096 particles in the view
|
||||
================
|
||||
*/
|
||||
void V_TestParticles(void)
|
||||
{
|
||||
particle_t * p;
|
||||
int i, j;
|
||||
float d, r, u;
|
||||
|
||||
r_numparticles = MAX_PARTICLES;
|
||||
for (i = 0; i < r_numparticles; i++)
|
||||
{
|
||||
d = i * 0.25;
|
||||
r = 4 * ((i & 7) - 3.5);
|
||||
u = 4 * (((i >> 3) & 7) - 3.5);
|
||||
p = &r_particles[i];
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * d + cl.v_right[j] * r + cl.v_up[j] * u;
|
||||
}
|
||||
|
||||
p->color = 8;
|
||||
p->alpha = cl_testparticles->value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
V_TestEntities
|
||||
|
||||
If cl_testentities is set, create 32 player models
|
||||
================
|
||||
*/
|
||||
void V_TestEntities(void)
|
||||
{
|
||||
int i, j;
|
||||
float f, r;
|
||||
entity_t * ent;
|
||||
|
||||
r_numentities = 32;
|
||||
memset(r_entities, 0, sizeof(r_entities));
|
||||
|
||||
for (i = 0; i < r_numentities; i++)
|
||||
{
|
||||
ent = &r_entities[i];
|
||||
|
||||
r = 64 * ((i % 4) - 1.5);
|
||||
f = 64 * (i / 4) + 128;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f + cl.v_right[j] * r;
|
||||
}
|
||||
|
||||
ent->model = cl.baseclientinfo.model;
|
||||
ent->skin = cl.baseclientinfo.skin;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
V_TestLights
|
||||
|
||||
If cl_testlights is set, create 32 lights models
|
||||
================
|
||||
*/
|
||||
void V_TestLights(void)
|
||||
{
|
||||
int i, j;
|
||||
float f, r;
|
||||
dlight_t * dl;
|
||||
|
||||
r_numdlights = 32;
|
||||
memset(r_dlights, 0, sizeof(r_dlights));
|
||||
|
||||
for (i = 0; i < r_numdlights; i++)
|
||||
{
|
||||
dl = &r_dlights[i];
|
||||
|
||||
r = 64 * ((i % 4) - 1.5);
|
||||
f = 64 * (i / 4) + 128;
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f + cl.v_right[j] * r;
|
||||
}
|
||||
|
||||
dl->color[0] = ((i % 6) + 1) & 1;
|
||||
dl->color[1] = (((i % 6) + 1) & 2) >> 1;
|
||||
dl->color[2] = (((i % 6) + 1) & 4) >> 2;
|
||||
dl->intensity = 200;
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_PrepRefresh
|
||||
|
||||
Call before entering a new level, or after changing dlls
|
||||
=================
|
||||
*/
|
||||
void CL_PrepRefresh(void)
|
||||
{
|
||||
char mapname[32];
|
||||
int i;
|
||||
char name[MAX_QPATH];
|
||||
float rotate;
|
||||
vec3_t axis;
|
||||
|
||||
if (!cl.configstrings[CS_MODELS + 1][0])
|
||||
return; // no map loaded
|
||||
|
||||
SCR_AddDirtyPoint(0, 0);
|
||||
SCR_AddDirtyPoint(viddef.width - 1, viddef.height - 1);
|
||||
|
||||
// let the render dll load the map
|
||||
strcpy(mapname, cl.configstrings[CS_MODELS + 1] + 5); // skip "maps/"
|
||||
mapname[strlen(mapname) - 4] = 0; // cut off ".bsp"
|
||||
|
||||
// register models, pics, and skins
|
||||
Com_Printf("Map: %s\r", mapname);
|
||||
SCR_UpdateScreen();
|
||||
re.BeginRegistration(mapname);
|
||||
Com_Printf(" \r");
|
||||
|
||||
// precache status bar pics
|
||||
Com_Printf("pics\r");
|
||||
SCR_UpdateScreen();
|
||||
SCR_TouchPics();
|
||||
Com_Printf(" \r");
|
||||
|
||||
CL_RegisterTEntModels();
|
||||
|
||||
num_cl_weaponmodels = 1;
|
||||
strcpy(cl_weaponmodels[0], "weapon.md2");
|
||||
|
||||
for (i = 1; i < MAX_MODELS && cl.configstrings[CS_MODELS + i][0]; i++)
|
||||
{
|
||||
strcpy(name, cl.configstrings[CS_MODELS + i]);
|
||||
name[37] = 0; // never go beyond one line
|
||||
if (name[0] != '*')
|
||||
Com_Printf("%s\r", name);
|
||||
|
||||
SCR_UpdateScreen();
|
||||
Sys_SendKeyEvents(); // pump message loop
|
||||
|
||||
if (name[0] == '#')
|
||||
{
|
||||
// special player weapon model
|
||||
if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
|
||||
{
|
||||
strncpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS + i] + 1,
|
||||
sizeof(cl_weaponmodels[num_cl_weaponmodels]) - 1);
|
||||
num_cl_weaponmodels++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cl.model_draw[i] = re.RegisterModel(cl.configstrings[CS_MODELS + i]);
|
||||
if (name[0] == '*')
|
||||
cl.model_clip[i] = CM_InlineModel(cl.configstrings[CS_MODELS + i]);
|
||||
else
|
||||
cl.model_clip[i] = NULL;
|
||||
}
|
||||
if (name[0] != '*')
|
||||
Com_Printf(" \r");
|
||||
}
|
||||
|
||||
Com_Printf("images\r", i);
|
||||
SCR_UpdateScreen();
|
||||
for (i = 1; i < MAX_IMAGES && cl.configstrings[CS_IMAGES + i][0]; i++)
|
||||
{
|
||||
cl.image_precache[i] = re.RegisterPic(cl.configstrings[CS_IMAGES + i]);
|
||||
Sys_SendKeyEvents(); // pump message loop
|
||||
}
|
||||
|
||||
Com_Printf(" \r");
|
||||
for (i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if (!cl.configstrings[CS_PLAYERSKINS + i][0])
|
||||
continue;
|
||||
Com_Printf("client %i\r", i);
|
||||
SCR_UpdateScreen();
|
||||
Sys_SendKeyEvents(); // pump message loop
|
||||
CL_ParseClientinfo(i);
|
||||
Com_Printf(" \r");
|
||||
}
|
||||
|
||||
CL_LoadClientinfo(&cl.baseclientinfo, "unnamed\\male/grunt");
|
||||
|
||||
// set sky textures and speed
|
||||
Com_Printf("sky\r", i);
|
||||
SCR_UpdateScreen();
|
||||
rotate = atof(cl.configstrings[CS_SKYROTATE]);
|
||||
sscanf(cl.configstrings[CS_SKYAXIS], "%f %f %f",
|
||||
&axis[0], &axis[1], &axis[2]);
|
||||
re.SetSky(cl.configstrings[CS_SKY], rotate, axis);
|
||||
Com_Printf(" \r");
|
||||
|
||||
// the renderer can now free unneeded stuff
|
||||
re.EndRegistration();
|
||||
|
||||
// clear any lines of console text
|
||||
Con_ClearNotify();
|
||||
|
||||
SCR_UpdateScreen();
|
||||
cl.refresh_prepped = true;
|
||||
cl.force_refdef = true; // make sure we have a valid refdef
|
||||
|
||||
// start the cd track
|
||||
CDAudio_Play(atoi(cl.configstrings[CS_CDTRACK]), true);
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CalcFov
|
||||
====================
|
||||
*/
|
||||
float CalcFov(float fov_x, float width, float height)
|
||||
{
|
||||
float a;
|
||||
float x;
|
||||
|
||||
if (fov_x < 1 || fov_x > 179)
|
||||
{
|
||||
Com_Error(ERR_DROP, "Bad fov: %f", fov_x);
|
||||
}
|
||||
|
||||
x = width / tan(fov_x / 360 * M_PI);
|
||||
a = atan(height / x);
|
||||
a = a * 360 / M_PI;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
// gun frame debugging functions
|
||||
void V_Gun_Next_f(void)
|
||||
{
|
||||
gun_frame++;
|
||||
Com_Printf("frame %i\n", gun_frame);
|
||||
}
|
||||
|
||||
void V_Gun_Prev_f(void)
|
||||
{
|
||||
gun_frame--;
|
||||
if (gun_frame < 0)
|
||||
gun_frame = 0;
|
||||
Com_Printf("frame %i\n", gun_frame);
|
||||
}
|
||||
|
||||
void V_Gun_Model_f(void)
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
gun_model = NULL;
|
||||
return;
|
||||
}
|
||||
Com_sprintf(name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
|
||||
gun_model = re.RegisterModel(name);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
SCR_DrawCrosshair
|
||||
=================
|
||||
*/
|
||||
void SCR_DrawCrosshair(void)
|
||||
{
|
||||
if (!crosshair->value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (crosshair->modified)
|
||||
{
|
||||
crosshair->modified = false;
|
||||
SCR_TouchPics();
|
||||
}
|
||||
|
||||
if (!crosshair_pic[0])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
re.DrawPic(scr_vrect.x + ((scr_vrect.width - crosshair_width) >> 1), scr_vrect.y + ((scr_vrect.height - crosshair_height) >> 1), crosshair_pic);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
V_RenderView
|
||||
|
||||
==================
|
||||
*/
|
||||
void V_RenderView(float stereo_separation)
|
||||
{
|
||||
extern int entitycmpfnc(const entity_t *, const entity_t *);
|
||||
|
||||
if (cls.state != ca_active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cl.refresh_prepped)
|
||||
{
|
||||
return; // still loading
|
||||
}
|
||||
|
||||
if (cl_timedemo->value)
|
||||
{
|
||||
if (!cl.timedemo_start)
|
||||
{
|
||||
cl.timedemo_start = Sys_Milliseconds();
|
||||
}
|
||||
cl.timedemo_frames++;
|
||||
}
|
||||
|
||||
// an invalid frame will just use the exact previous refdef
|
||||
// we can't use the old frame if the video mode has changed, though...
|
||||
if (cl.frame.valid && (cl.force_refdef || !cl_paused->value))
|
||||
{
|
||||
cl.force_refdef = false;
|
||||
|
||||
V_ClearScene();
|
||||
|
||||
// build a refresh entity list and calc cl.sim*
|
||||
// this also calls CL_CalcViewValues which loads
|
||||
// v_forward, etc.
|
||||
CL_AddEntities();
|
||||
|
||||
if (cl_testparticles->value)
|
||||
{
|
||||
V_TestParticles();
|
||||
}
|
||||
if (cl_testentities->value)
|
||||
{
|
||||
V_TestEntities();
|
||||
}
|
||||
if (cl_testlights->value)
|
||||
{
|
||||
V_TestLights();
|
||||
}
|
||||
|
||||
if (cl_testblend->value)
|
||||
{
|
||||
cl.refdef.blend[0] = 1;
|
||||
cl.refdef.blend[1] = 0.5;
|
||||
cl.refdef.blend[2] = 0.25;
|
||||
cl.refdef.blend[3] = 0.5;
|
||||
}
|
||||
|
||||
// offset vieworg appropriately if we're doing stereo separation
|
||||
if (stereo_separation != 0)
|
||||
{
|
||||
vec3_t tmp;
|
||||
VectorScale(cl.v_right, stereo_separation, tmp);
|
||||
VectorAdd(cl.refdef.vieworg, tmp, cl.refdef.vieworg);
|
||||
}
|
||||
|
||||
// never let it sit exactly on a node line, because a water plane can
|
||||
// disappear when viewed with the eye exactly on it.
|
||||
// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
|
||||
cl.refdef.vieworg[0] += 1.0 / 16;
|
||||
cl.refdef.vieworg[1] += 1.0 / 16;
|
||||
cl.refdef.vieworg[2] += 1.0 / 16;
|
||||
|
||||
cl.refdef.x = scr_vrect.x;
|
||||
cl.refdef.y = scr_vrect.y;
|
||||
cl.refdef.width = scr_vrect.width;
|
||||
cl.refdef.height = scr_vrect.height;
|
||||
cl.refdef.fov_y = CalcFov(cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
|
||||
cl.refdef.time = cl.time * 0.001;
|
||||
|
||||
cl.refdef.areabits = cl.frame.areabits;
|
||||
|
||||
if (!cl_add_entities->value)
|
||||
{
|
||||
r_numentities = 0;
|
||||
}
|
||||
if (!cl_add_particles->value)
|
||||
{
|
||||
r_numparticles = 0;
|
||||
}
|
||||
if (!cl_add_lights->value)
|
||||
{
|
||||
r_numdlights = 0;
|
||||
}
|
||||
if (!cl_add_blend->value)
|
||||
{
|
||||
VectorClear(cl.refdef.blend);
|
||||
}
|
||||
|
||||
cl.refdef.num_entities = r_numentities;
|
||||
cl.refdef.entities = r_entities;
|
||||
cl.refdef.num_particles = r_numparticles;
|
||||
cl.refdef.particles = r_particles;
|
||||
cl.refdef.num_dlights = r_numdlights;
|
||||
cl.refdef.dlights = r_dlights;
|
||||
cl.refdef.lightstyles = r_lightstyles;
|
||||
|
||||
cl.refdef.rdflags = cl.frame.playerstate.rdflags;
|
||||
|
||||
// sort entities for better cache locality
|
||||
qsort(cl.refdef.entities, cl.refdef.num_entities, sizeof(cl.refdef.entities[0]),
|
||||
(int (*)(const void *, const void *))entitycmpfnc);
|
||||
}
|
||||
|
||||
re.RenderFrame(&cl.refdef);
|
||||
|
||||
if (cl_stats->value)
|
||||
{
|
||||
Com_Printf("ent:%i lt:%i part:%i\n", r_numentities, r_numdlights, r_numparticles);
|
||||
}
|
||||
|
||||
if (log_stats->value && (log_stats_file != 0))
|
||||
{
|
||||
fprintf(log_stats_file, "%i,%i,%i,", r_numentities, r_numdlights, r_numparticles);
|
||||
}
|
||||
|
||||
SCR_AddDirtyPoint(scr_vrect.x, scr_vrect.y);
|
||||
SCR_AddDirtyPoint(scr_vrect.x + scr_vrect.width - 1,
|
||||
scr_vrect.y + scr_vrect.height - 1);
|
||||
|
||||
SCR_DrawCrosshair();
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
V_Viewpos_f
|
||||
=============
|
||||
*/
|
||||
void V_Viewpos_f(void)
|
||||
{
|
||||
Com_Printf("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
|
||||
(int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
|
||||
(int)cl.refdef.viewangles[YAW]);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
V_Init
|
||||
=============
|
||||
*/
|
||||
void V_Init(void)
|
||||
{
|
||||
Cmd_AddCommand("gun_next", V_Gun_Next_f);
|
||||
Cmd_AddCommand("gun_prev", V_Gun_Prev_f);
|
||||
Cmd_AddCommand("gun_model", V_Gun_Model_f);
|
||||
Cmd_AddCommand("viewpos", V_Viewpos_f);
|
||||
|
||||
crosshair = Cvar_Get("crosshair", "0", CVAR_ARCHIVE);
|
||||
|
||||
cl_testblend = Cvar_Get("cl_testblend", "0", 0);
|
||||
cl_testparticles = Cvar_Get("cl_testparticles", "0", 0);
|
||||
cl_testentities = Cvar_Get("cl_testentities", "0", 0);
|
||||
cl_testlights = Cvar_Get("cl_testlights", "0", 0);
|
||||
cl_stats = Cvar_Get("cl_stats", "0", 0);
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_CLIENT_H
|
||||
#define CL_CLIENT_H
|
||||
|
||||
// client.h -- primary header for client
|
||||
|
||||
#include "ps2/math_funcs.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ref.h"
|
||||
#include "vid.h"
|
||||
#include "screen.h"
|
||||
#include "sound.h"
|
||||
#include "input.h"
|
||||
#include "keys.h"
|
||||
#include "console.h"
|
||||
#include "cdaudio.h"
|
||||
|
||||
#if !defined(RESTRICT_DEFINED) && !defined(restrict)
|
||||
#define restrict __restrict
|
||||
#define RESTRICT_DEFINED 1
|
||||
#endif // RESTRICT_DEFINED
|
||||
|
||||
//#define PARANOID // speed sapping error checking
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean valid; // cleared if delta parsing was invalid
|
||||
int serverframe;
|
||||
int servertime; // server time the message is valid for (in msec)
|
||||
int deltaframe;
|
||||
byte areabits[MAX_MAP_AREAS / 8]; // portalarea visibility bits
|
||||
player_state_t playerstate;
|
||||
int num_entities;
|
||||
int parse_entities; // non-masked index into cl_parse_entities array
|
||||
} frame_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
entity_state_t baseline; // delta from this if not from a previous frame
|
||||
entity_state_t current;
|
||||
entity_state_t prev; // will always be valid, but might just be a copy of current
|
||||
int serverframe; // if not current, this ent isn't in the frame
|
||||
int trailcount; // for diminishing grenade trails
|
||||
vec3_t lerp_origin; // for trails (variable hz)
|
||||
int fly_stoptime;
|
||||
} centity_t;
|
||||
|
||||
#define MAX_CLIENTWEAPONMODELS 20 // PGM -- upped from 16 to fit the chainfist vwep
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
char cinfo[MAX_QPATH];
|
||||
struct image_s * skin;
|
||||
struct image_s * icon;
|
||||
char iconname[MAX_QPATH];
|
||||
struct model_s * model;
|
||||
struct model_s * weaponmodel[MAX_CLIENTWEAPONMODELS];
|
||||
} clientinfo_t;
|
||||
|
||||
extern char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
|
||||
extern int num_cl_weaponmodels;
|
||||
|
||||
#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems
|
||||
|
||||
//
|
||||
// the client_state_t structure is wiped completely at every
|
||||
// server map change
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
int timeoutcount;
|
||||
int timedemo_frames;
|
||||
int timedemo_start;
|
||||
|
||||
qboolean refresh_prepped; // false if on new level or new ref dll
|
||||
qboolean sound_prepped; // ambient sounds can start
|
||||
qboolean force_refdef; // vid has changed, so we can't use a paused refdef
|
||||
|
||||
int parse_entities; // index (not anded off) into cl_parse_entities[]
|
||||
|
||||
usercmd_t cmd;
|
||||
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
|
||||
int cmd_time[CMD_BACKUP]; // time sent, for calculating pings
|
||||
short predicted_origins[CMD_BACKUP][3]; // for debug comparing against server
|
||||
|
||||
float predicted_step; // for stair up smoothing
|
||||
unsigned predicted_step_time;
|
||||
|
||||
vec3_t predicted_origin; // generated by CL_PredictMovement
|
||||
vec3_t predicted_angles;
|
||||
vec3_t prediction_error;
|
||||
|
||||
frame_t frame; // received from server
|
||||
int surpressCount; // number of messages rate supressed
|
||||
frame_t frames[UPDATE_BACKUP];
|
||||
|
||||
// the client maintains its own idea of view angles, which are
|
||||
// sent to the server each frame. It is cleared to 0 upon entering each level.
|
||||
// the server sends a delta each frame which is added to the locally
|
||||
// tracked view angles to account for standing on rotating objects,
|
||||
// and teleport direction changes
|
||||
vec3_t viewangles;
|
||||
|
||||
int time; // this is the time value that the client
|
||||
// is rendering at. always <= cls.realtime
|
||||
float lerpfrac; // between oldframe and frame
|
||||
|
||||
refdef_t refdef;
|
||||
vec3_t v_forward, v_right, v_up; // set when refdef.angles is set
|
||||
|
||||
//
|
||||
// transient data from server
|
||||
//
|
||||
char layout[1024]; // general 2D overlay
|
||||
int inventory[MAX_ITEMS];
|
||||
|
||||
//
|
||||
// non-gameserver infornamtion
|
||||
// FIXME: move this cinematic stuff into the cin_t structure
|
||||
FILE * cinematic_file;
|
||||
int cinematictime; // cls.realtime for first cinematic frame
|
||||
int cinematicframe;
|
||||
char cinematicpalette[768];
|
||||
qboolean cinematicpalette_active;
|
||||
|
||||
//
|
||||
// server state information
|
||||
//
|
||||
qboolean attractloop; // running the attract loop, any key will menu
|
||||
int servercount; // server identification for prespawns
|
||||
char gamedir[MAX_QPATH];
|
||||
int playernum;
|
||||
|
||||
char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];
|
||||
|
||||
//
|
||||
// locally derived information from server state
|
||||
//
|
||||
struct model_s * model_draw[MAX_MODELS];
|
||||
struct cmodel_s * model_clip[MAX_MODELS];
|
||||
|
||||
struct sfx_s * sound_precache[MAX_SOUNDS];
|
||||
struct image_s * image_precache[MAX_IMAGES];
|
||||
|
||||
clientinfo_t clientinfo[MAX_CLIENTS];
|
||||
clientinfo_t baseclientinfo;
|
||||
} client_state_t;
|
||||
|
||||
extern client_state_t cl;
|
||||
|
||||
/*
|
||||
==================================================================
|
||||
|
||||
the client_static_t structure is persistant through an arbitrary number
|
||||
of server connections
|
||||
|
||||
==================================================================
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ca_uninitialized,
|
||||
ca_disconnected, // not talking to a server
|
||||
ca_connecting, // sending request packets to the server
|
||||
ca_connected, // netchan_t established, waiting for svc_serverdata
|
||||
ca_active // game views should be displayed
|
||||
} connstate_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
dl_none,
|
||||
dl_model,
|
||||
dl_sound,
|
||||
dl_skin,
|
||||
dl_single
|
||||
} dltype_t; // download type
|
||||
|
||||
typedef enum
|
||||
{
|
||||
key_game,
|
||||
key_console,
|
||||
key_message,
|
||||
key_menu
|
||||
} keydest_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
connstate_t state;
|
||||
keydest_t key_dest;
|
||||
|
||||
int framecount;
|
||||
int realtime; // always increasing, no clamping, etc
|
||||
float frametime; // seconds since last frame
|
||||
|
||||
// screen rendering information
|
||||
float disable_screen; // showing loading plaque between levels
|
||||
// or changing rendering dlls
|
||||
// if time gets > 30 seconds ahead, break it
|
||||
int disable_servercount; // when we receive a frame and cl.servercount
|
||||
// > cls.disable_servercount, clear disable_screen
|
||||
|
||||
// connection information
|
||||
char servername[MAX_OSPATH]; // name of server from original connect
|
||||
float connect_time; // for connection retransmits
|
||||
|
||||
int quakePort; // a 16 bit value that allows quake servers
|
||||
// to work around address translating routers
|
||||
netchan_t netchan;
|
||||
int serverProtocol; // in case we are doing some kind of version hack
|
||||
|
||||
int challenge; // from the server to use for connecting
|
||||
|
||||
FILE * download; // file transfer from server
|
||||
char downloadtempname[MAX_OSPATH];
|
||||
char downloadname[MAX_OSPATH];
|
||||
int downloadnumber;
|
||||
dltype_t downloadtype;
|
||||
int downloadpercent;
|
||||
|
||||
// demo recording info must be here, so it isn't cleared on level change
|
||||
qboolean demorecording;
|
||||
qboolean demowaiting; // don't record until a non-delta message is received
|
||||
FILE * demofile;
|
||||
} client_static_t;
|
||||
|
||||
extern client_static_t cls;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
//
|
||||
// cvars
|
||||
//
|
||||
extern cvar_t * cl_stereo_separation;
|
||||
extern cvar_t * cl_stereo;
|
||||
extern cvar_t * cl_gun;
|
||||
extern cvar_t * cl_add_blend;
|
||||
extern cvar_t * cl_add_lights;
|
||||
extern cvar_t * cl_add_particles;
|
||||
extern cvar_t * cl_add_entities;
|
||||
extern cvar_t * cl_predict;
|
||||
extern cvar_t * cl_footsteps;
|
||||
extern cvar_t * cl_noskins;
|
||||
extern cvar_t * cl_autoskins;
|
||||
extern cvar_t * cl_upspeed;
|
||||
extern cvar_t * cl_forwardspeed;
|
||||
extern cvar_t * cl_sidespeed;
|
||||
extern cvar_t * cl_yawspeed;
|
||||
extern cvar_t * cl_pitchspeed;
|
||||
extern cvar_t * cl_run;
|
||||
extern cvar_t * cl_anglespeedkey;
|
||||
extern cvar_t * cl_shownet;
|
||||
extern cvar_t * cl_showmiss;
|
||||
extern cvar_t * cl_showclamp;
|
||||
extern cvar_t * lookspring;
|
||||
extern cvar_t * lookstrafe;
|
||||
extern cvar_t * sensitivity;
|
||||
extern cvar_t * m_pitch;
|
||||
extern cvar_t * m_yaw;
|
||||
extern cvar_t * m_forward;
|
||||
extern cvar_t * m_side;
|
||||
extern cvar_t * freelook;
|
||||
extern cvar_t * cl_lightlevel; // FIXME HACK
|
||||
extern cvar_t * cl_paused;
|
||||
extern cvar_t * cl_timedemo;
|
||||
extern cvar_t * cl_vwep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int key; // so entities can reuse same entry
|
||||
vec3_t color;
|
||||
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
|
||||
} cdlight_t;
|
||||
|
||||
extern centity_t cl_entities[MAX_EDICTS];
|
||||
extern cdlight_t cl_dlights[MAX_DLIGHTS];
|
||||
|
||||
// the cl_parse_entities must be large enough to hold UPDATE_BACKUP frames of
|
||||
// entities, so that when a delta compressed message arives from the server
|
||||
// it can be un-deltad from the original
|
||||
#define MAX_PARSE_ENTITIES 1024
|
||||
extern entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
|
||||
|
||||
//=============================================================================
|
||||
|
||||
extern netadr_t net_from;
|
||||
extern sizebuf_t net_message;
|
||||
|
||||
void DrawString(int x, int y, const char * s);
|
||||
void DrawAltString(int x, int y, const char * s); // toggle high bit
|
||||
qboolean CL_CheckOrDownloadFile(char * filename);
|
||||
|
||||
void CL_AddNetgraph(void);
|
||||
|
||||
//ROGUE
|
||||
typedef struct cl_sustain
|
||||
{
|
||||
int id;
|
||||
int type;
|
||||
int endtime;
|
||||
int nextthink;
|
||||
int thinkinterval;
|
||||
vec3_t org;
|
||||
vec3_t dir;
|
||||
int color;
|
||||
int count;
|
||||
int magnitude;
|
||||
void (*think)(struct cl_sustain * self);
|
||||
} cl_sustain_t;
|
||||
|
||||
#define MAX_SUSTAINS 32
|
||||
void CL_ParticleSteamEffect2(cl_sustain_t * self);
|
||||
void CL_TeleporterParticles(entity_state_t * ent);
|
||||
void CL_ParticleEffect(vec3_t org, vec3_t dir, int color, int count);
|
||||
void CL_ParticleEffect2(vec3_t org, vec3_t dir, int color, int count);
|
||||
void CL_ParticleEffect3(vec3_t org, vec3_t dir, int color, int count); // RAFAEL
|
||||
|
||||
//=================================================
|
||||
|
||||
// PGM
|
||||
typedef struct particle_s
|
||||
{
|
||||
struct particle_s * next;
|
||||
float time;
|
||||
vec3_t org;
|
||||
vec3_t vel;
|
||||
vec3_t accel;
|
||||
float color;
|
||||
float colorvel;
|
||||
float alpha;
|
||||
float alphavel;
|
||||
} cparticle_t;
|
||||
|
||||
#define PARTICLE_GRAVITY 40
|
||||
#define BLASTER_PARTICLE_COLOR 0xE0
|
||||
#define INSTANT_PARTICLE -10000.0
|
||||
|
||||
void CL_ClearEffects(void);
|
||||
void CL_ClearTEnts(void);
|
||||
void CL_BlasterTrail(vec3_t start, vec3_t end);
|
||||
void CL_QuadTrail(vec3_t start, vec3_t end);
|
||||
void CL_RailTrail(vec3_t start, vec3_t end);
|
||||
void CL_BubbleTrail(vec3_t start, vec3_t end);
|
||||
void CL_FlagTrail(vec3_t start, vec3_t end, float color);
|
||||
void CL_IonripperTrail(vec3_t start, vec3_t end); // RAFAEL
|
||||
|
||||
// ========
|
||||
// PGM
|
||||
void CL_BlasterParticles2(vec3_t org, vec3_t dir, unsigned int color);
|
||||
void CL_BlasterTrail2(vec3_t start, vec3_t end);
|
||||
void CL_DebugTrail(vec3_t start, vec3_t end);
|
||||
void CL_SmokeTrail(vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing);
|
||||
void CL_Flashlight(int ent, vec3_t pos);
|
||||
void CL_ForceWall(vec3_t start, vec3_t end, int color);
|
||||
void CL_FlameEffects(centity_t * ent, vec3_t origin);
|
||||
void CL_GenericParticleEffect(vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel);
|
||||
void CL_BubbleTrail2(vec3_t start, vec3_t end, int dist);
|
||||
void CL_Heatbeam(vec3_t start, vec3_t end);
|
||||
void CL_ParticleSteamEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude);
|
||||
void CL_TrackerTrail(vec3_t start, vec3_t end, int particleColor);
|
||||
void CL_Tracker_Explode(vec3_t origin);
|
||||
void CL_TagTrail(vec3_t start, vec3_t end, float color);
|
||||
void CL_ColorFlash(vec3_t pos, int ent, int intensity, float r, float g, float b);
|
||||
void CL_Tracker_Shell(vec3_t origin);
|
||||
void CL_MonsterPlasma_Shell(vec3_t origin);
|
||||
void CL_ColorExplosionParticles(vec3_t org, int color, int run);
|
||||
void CL_ParticleSmokeEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude);
|
||||
void CL_Widowbeamout(cl_sustain_t * self);
|
||||
void CL_Nukeblast(cl_sustain_t * self);
|
||||
void CL_WidowSplash(vec3_t org);
|
||||
// PGM
|
||||
// ========
|
||||
|
||||
int CL_ParseEntityBits(unsigned * bits);
|
||||
void CL_ParseDelta(entity_state_t * from, entity_state_t * to, int number, int bits);
|
||||
void CL_ParseFrame(void);
|
||||
|
||||
void CL_ParseTEnt(void);
|
||||
void CL_ParseConfigString(void);
|
||||
void CL_ParseMuzzleFlash(void);
|
||||
void CL_ParseMuzzleFlash2(void);
|
||||
void SmokeAndFlash(vec3_t origin);
|
||||
|
||||
void CL_SetLightstyle(int i);
|
||||
|
||||
void CL_RunParticles(void);
|
||||
void CL_RunDLights(void);
|
||||
void CL_RunLightStyles(void);
|
||||
|
||||
void CL_AddEntities(void);
|
||||
void CL_AddDLights(void);
|
||||
void CL_AddTEnts(void);
|
||||
void CL_AddLightStyles(void);
|
||||
|
||||
//=================================================
|
||||
|
||||
void CL_PrepRefresh(void);
|
||||
void CL_RegisterSounds(void);
|
||||
void CL_Quit_f(void);
|
||||
void CL_ParseLayout(void);
|
||||
void IN_Accumulate(void);
|
||||
|
||||
//
|
||||
// cl_main
|
||||
//
|
||||
extern refexport_t re; // interface to refresh .dll
|
||||
|
||||
void CL_Init(void);
|
||||
void CL_FixUpGender(void);
|
||||
void CL_Disconnect(void);
|
||||
void CL_Disconnect_f(void);
|
||||
void CL_GetChallengePacket(void);
|
||||
void CL_PingServers_f(void);
|
||||
void CL_Snd_Restart_f(void);
|
||||
void CL_RequestNextDownload(void);
|
||||
|
||||
//
|
||||
// cl_input
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
int down[2]; // key nums holding it down
|
||||
unsigned downtime; // msec timestamp
|
||||
unsigned msec; // msec down this frame
|
||||
int state;
|
||||
} kbutton_t;
|
||||
|
||||
extern kbutton_t in_mlook, in_klook;
|
||||
extern kbutton_t in_strafe;
|
||||
extern kbutton_t in_speed;
|
||||
|
||||
void CL_InitInput(void);
|
||||
void CL_SendCmd(void);
|
||||
void CL_SendMove(usercmd_t * cmd);
|
||||
void CL_ClearState(void);
|
||||
void CL_ReadPackets(void);
|
||||
int CL_ReadFromServer(void);
|
||||
void CL_WriteToServer(usercmd_t * cmd);
|
||||
void CL_BaseMove(usercmd_t * cmd);
|
||||
void IN_CenterView(void);
|
||||
float CL_KeyState(kbutton_t * key);
|
||||
const char * Key_KeynumToString(int keynum);
|
||||
|
||||
//
|
||||
// cl_demo.c
|
||||
//
|
||||
void CL_WriteDemoMessage(void);
|
||||
void CL_Stop_f(void);
|
||||
void CL_Record_f(void);
|
||||
|
||||
//
|
||||
// cl_parse.c
|
||||
//
|
||||
extern char * svc_strings[256];
|
||||
|
||||
void CL_ParseServerMessage(void);
|
||||
void CL_LoadClientinfo(clientinfo_t * ci, char * s);
|
||||
void SHOWNET(char * s);
|
||||
void CL_ParseClientinfo(int player);
|
||||
void CL_Download_f(void);
|
||||
|
||||
//
|
||||
// cl_view.c
|
||||
//
|
||||
extern int gun_frame;
|
||||
extern struct model_s * gun_model;
|
||||
|
||||
void V_Init(void);
|
||||
void V_RenderView(float stereo_separation);
|
||||
void V_AddEntity(entity_t * ent);
|
||||
void V_AddParticle(vec3_t org, int color, float alpha);
|
||||
void V_AddLight(vec3_t org, float intensity, float r, float g, float b);
|
||||
void V_AddLightStyle(int style, float r, float g, float b);
|
||||
|
||||
//
|
||||
// cl_tent.c
|
||||
//
|
||||
void CL_RegisterTEntSounds(void);
|
||||
void CL_RegisterTEntModels(void);
|
||||
void CL_SmokeAndFlash(vec3_t origin);
|
||||
|
||||
//
|
||||
// cl_pred.c
|
||||
//
|
||||
void CL_InitPrediction(void);
|
||||
void CL_PredictMove(void);
|
||||
void CL_CheckPredictionError(void);
|
||||
|
||||
//
|
||||
// cl_fx.c
|
||||
//
|
||||
cdlight_t * CL_AllocDlight(int key);
|
||||
void CL_BigTeleportParticles(vec3_t org);
|
||||
void CL_RocketTrail(vec3_t start, vec3_t end, centity_t * old);
|
||||
void CL_DiminishingTrail(vec3_t start, vec3_t end, centity_t * old, int flags);
|
||||
void CL_FlyEffect(centity_t * ent, vec3_t origin);
|
||||
void CL_BfgParticles(entity_t * ent);
|
||||
void CL_AddParticles(void);
|
||||
void CL_EntityEvent(entity_state_t * ent);
|
||||
void CL_TrapParticles(entity_t * ent); // RAFAEL
|
||||
|
||||
//
|
||||
// menus
|
||||
//
|
||||
void M_Init(void);
|
||||
void M_Keydown(int key);
|
||||
void M_Draw(void);
|
||||
void M_Menu_Main_f(void);
|
||||
void M_ForceMenuOff(void);
|
||||
void M_AddToServerList(netadr_t adr, char * info);
|
||||
|
||||
//
|
||||
// cl_inv.c
|
||||
//
|
||||
void CL_ParseInventory(void);
|
||||
void CL_KeyInventory(int key);
|
||||
void CL_DrawInventory(void);
|
||||
|
||||
//
|
||||
// cl_pred.c
|
||||
//
|
||||
void CL_PredictMovement(void);
|
||||
|
||||
#endif // CL_CLIENT_H
|
||||
@@ -0,0 +1,744 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// console.c
|
||||
|
||||
#include "client.h"
|
||||
|
||||
console_t con = {0};
|
||||
cvar_t * con_notifytime = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
MAXCMDLINE = 256
|
||||
};
|
||||
extern char key_lines[32][MAXCMDLINE];
|
||||
extern int edit_line;
|
||||
extern int key_linepos;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void DrawString(int x, int y, const char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
re.DrawChar(x, y, *s);
|
||||
x += 8;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawAltString(int x, int y, const char * s)
|
||||
{
|
||||
while (*s)
|
||||
{
|
||||
re.DrawChar(x, y, *s ^ 0x80);
|
||||
x += 8;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
void Key_ClearTyping(void)
|
||||
{
|
||||
key_lines[edit_line][1] = 0; // clear any typing
|
||||
key_linepos = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_ToggleConsole_f
|
||||
================
|
||||
*/
|
||||
void Con_ToggleConsole_f(void)
|
||||
{
|
||||
SCR_EndLoadingPlaque(); // get rid of loading plaque
|
||||
|
||||
if (cl.attractloop)
|
||||
{
|
||||
Cbuf_AddText("killserver\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.state == ca_disconnected)
|
||||
{ // start the demo loop again
|
||||
Cbuf_AddText("d1\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Key_ClearTyping();
|
||||
Con_ClearNotify();
|
||||
|
||||
if (cls.key_dest == key_console)
|
||||
{
|
||||
M_ForceMenuOff();
|
||||
Cvar_Set("paused", "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
M_ForceMenuOff();
|
||||
cls.key_dest = key_console;
|
||||
|
||||
if (Cvar_VariableValue("maxclients") == 1 && Com_ServerState())
|
||||
Cvar_Set("paused", "1");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_ToggleChat_f
|
||||
================
|
||||
*/
|
||||
void Con_ToggleChat_f(void)
|
||||
{
|
||||
Key_ClearTyping();
|
||||
|
||||
if (cls.key_dest == key_console)
|
||||
{
|
||||
if (cls.state == ca_active)
|
||||
{
|
||||
M_ForceMenuOff();
|
||||
cls.key_dest = key_game;
|
||||
}
|
||||
}
|
||||
else
|
||||
cls.key_dest = key_console;
|
||||
|
||||
Con_ClearNotify();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Clear_f
|
||||
================
|
||||
*/
|
||||
void Con_Clear_f(void)
|
||||
{
|
||||
memset(con.text, ' ', CON_TEXTSIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Dump_f
|
||||
|
||||
Save the console contents out to a file
|
||||
================
|
||||
*/
|
||||
void Con_Dump_f(void)
|
||||
{
|
||||
int l, x;
|
||||
char * line;
|
||||
FILE * f;
|
||||
char buffer[1024];
|
||||
char name[MAX_OSPATH];
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
Com_Printf("usage: condump <filename>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Com_sprintf(name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
|
||||
|
||||
Com_Printf("Dumped console text to %s.\n", name);
|
||||
FS_CreatePath(name);
|
||||
f = fopen(name, "w");
|
||||
if (!f)
|
||||
{
|
||||
Com_Printf("ERROR: couldn't open.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// skip empty lines
|
||||
for (l = con.current - con.totallines + 1; l <= con.current; l++)
|
||||
{
|
||||
line = con.text + (l % con.totallines) * con.linewidth;
|
||||
for (x = 0; x < con.linewidth; x++)
|
||||
if (line[x] != ' ')
|
||||
break;
|
||||
if (x != con.linewidth)
|
||||
break;
|
||||
}
|
||||
|
||||
// write the remaining lines
|
||||
buffer[con.linewidth] = 0;
|
||||
for (; l <= con.current; l++)
|
||||
{
|
||||
line = con.text + (l % con.totallines) * con.linewidth;
|
||||
strncpy(buffer, line, con.linewidth);
|
||||
for (x = con.linewidth - 1; x >= 0; x--)
|
||||
{
|
||||
if (buffer[x] == ' ')
|
||||
buffer[x] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
for (x = 0; buffer[x]; x++)
|
||||
buffer[x] &= 0x7f;
|
||||
|
||||
fprintf(f, "%s\n", buffer);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_ClearNotify
|
||||
================
|
||||
*/
|
||||
void Con_ClearNotify(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CON_TIMES; i++)
|
||||
con.times[i] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode_f(void)
|
||||
{
|
||||
chat_team = false;
|
||||
cls.key_dest = key_message;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_MessageMode2_f
|
||||
================
|
||||
*/
|
||||
void Con_MessageMode2_f(void)
|
||||
{
|
||||
chat_team = true;
|
||||
cls.key_dest = key_message;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_CheckResize
|
||||
|
||||
If the line width has changed, reformat the buffer.
|
||||
================
|
||||
*/
|
||||
void Con_CheckResize(void)
|
||||
{
|
||||
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
|
||||
char tbuf[CON_TEXTSIZE];
|
||||
|
||||
width = (viddef.width >> 3) - 2;
|
||||
|
||||
if (width == con.linewidth)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (width < 1) // video hasn't been initialized yet
|
||||
{
|
||||
width = 75;
|
||||
con.linewidth = width;
|
||||
con.totallines = CON_TEXTSIZE / con.linewidth;
|
||||
memset(con.text, ' ', CON_TEXTSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldwidth = con.linewidth;
|
||||
con.linewidth = width;
|
||||
oldtotallines = con.totallines;
|
||||
con.totallines = CON_TEXTSIZE / con.linewidth;
|
||||
numlines = oldtotallines;
|
||||
|
||||
if (con.totallines < numlines)
|
||||
{
|
||||
numlines = con.totallines;
|
||||
}
|
||||
|
||||
numchars = oldwidth;
|
||||
|
||||
if (con.linewidth < numchars)
|
||||
{
|
||||
numchars = con.linewidth;
|
||||
}
|
||||
|
||||
memcpy(tbuf, con.text, CON_TEXTSIZE);
|
||||
memset(con.text, ' ', CON_TEXTSIZE);
|
||||
|
||||
for (i = 0; i < numlines; i++)
|
||||
{
|
||||
for (j = 0; j < numchars; j++)
|
||||
{
|
||||
con.text[(con.totallines - 1 - i) * con.linewidth + j] =
|
||||
tbuf[((con.current - i + oldtotallines) %
|
||||
oldtotallines) *
|
||||
oldwidth +
|
||||
j];
|
||||
}
|
||||
}
|
||||
|
||||
Con_ClearNotify();
|
||||
}
|
||||
|
||||
con.current = con.totallines - 1;
|
||||
con.display = con.current;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Init
|
||||
================
|
||||
*/
|
||||
void Con_Init(void)
|
||||
{
|
||||
if (con.initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
con.linewidth = -1;
|
||||
Con_CheckResize();
|
||||
Com_Printf("Console initialized.\n");
|
||||
|
||||
con_notifytime = Cvar_Get("con_notifytime", "3", 0);
|
||||
|
||||
// register our commands:
|
||||
Cmd_AddCommand("toggleconsole", Con_ToggleConsole_f);
|
||||
Cmd_AddCommand("togglechat", Con_ToggleChat_f);
|
||||
Cmd_AddCommand("messagemode", Con_MessageMode_f);
|
||||
Cmd_AddCommand("messagemode2", Con_MessageMode2_f);
|
||||
Cmd_AddCommand("clear", Con_Clear_f);
|
||||
Cmd_AddCommand("condump", Con_Dump_f);
|
||||
|
||||
con.initialized = true;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Con_Linefeed
|
||||
===============
|
||||
*/
|
||||
void Con_Linefeed(void)
|
||||
{
|
||||
con.x = 0;
|
||||
if (con.display == con.current)
|
||||
{
|
||||
con.display++;
|
||||
}
|
||||
con.current++;
|
||||
memset(&con.text[(con.current % con.totallines) * con.linewidth], ' ', con.linewidth);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Print
|
||||
|
||||
Handles cursor positioning, line wrapping, etc
|
||||
All console printing must go through this in order to be logged to disk
|
||||
If no console is visible, the text will appear at the top of the game window
|
||||
================
|
||||
*/
|
||||
void Con_Print(char * txt)
|
||||
{
|
||||
int y;
|
||||
int c, l;
|
||||
int mask;
|
||||
static int cr;
|
||||
|
||||
if (!con.initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (txt[0] == 1 || txt[0] == 2)
|
||||
{
|
||||
mask = 128; // go to colored text
|
||||
txt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 0;
|
||||
}
|
||||
|
||||
while ((c = *txt))
|
||||
{
|
||||
// count word length
|
||||
for (l = 0; l < con.linewidth; l++)
|
||||
{
|
||||
if (txt[l] <= ' ')
|
||||
break;
|
||||
}
|
||||
|
||||
// word wrap
|
||||
if (l != con.linewidth && (con.x + l > con.linewidth))
|
||||
{
|
||||
con.x = 0;
|
||||
}
|
||||
|
||||
txt++;
|
||||
|
||||
if (cr)
|
||||
{
|
||||
con.current--;
|
||||
cr = false;
|
||||
}
|
||||
|
||||
if (!con.x)
|
||||
{
|
||||
Con_Linefeed();
|
||||
// mark time for transparent overlay
|
||||
if (con.current >= 0)
|
||||
{
|
||||
con.times[con.current % NUM_CON_TIMES] = cls.realtime;
|
||||
}
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
con.x = 0;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
con.x = 0;
|
||||
cr = 1;
|
||||
break;
|
||||
|
||||
default: // display character and advance
|
||||
y = con.current % con.totallines;
|
||||
con.text[y * con.linewidth + con.x] = c | mask | con.ormask;
|
||||
con.x++;
|
||||
if (con.x >= con.linewidth)
|
||||
{
|
||||
con.x = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
Con_CenteredPrint
|
||||
==============
|
||||
*/
|
||||
void Con_CenteredPrint(char * text)
|
||||
{
|
||||
int l;
|
||||
char buffer[1024];
|
||||
|
||||
l = strlen(text);
|
||||
l = (con.linewidth - l) / 2;
|
||||
if (l < 0)
|
||||
{
|
||||
l = 0;
|
||||
}
|
||||
memset(buffer, ' ', l);
|
||||
strcpy(buffer + l, text);
|
||||
strcat(buffer, "\n");
|
||||
Con_Print(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
DRAWING
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawInput
|
||||
|
||||
The input line scrolls horizontally if typing goes beyond the right edge
|
||||
================
|
||||
*/
|
||||
void Con_DrawInput(void)
|
||||
{
|
||||
int y;
|
||||
int i;
|
||||
char * text;
|
||||
|
||||
if (cls.key_dest == key_menu)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.key_dest != key_console && cls.state == ca_active)
|
||||
{
|
||||
return; // don't draw anything (always draw if not active)
|
||||
}
|
||||
|
||||
text = key_lines[edit_line];
|
||||
|
||||
// add the cursor frame
|
||||
text[key_linepos] = 10 + ((int)(cls.realtime >> 8) & 1);
|
||||
|
||||
// fill out remainder with spaces
|
||||
for (i = key_linepos + 1; i < con.linewidth; i++)
|
||||
{
|
||||
text[i] = ' ';
|
||||
}
|
||||
|
||||
// prestep if horizontally scrolling
|
||||
if (key_linepos >= con.linewidth)
|
||||
{
|
||||
text += 1 + key_linepos - con.linewidth;
|
||||
}
|
||||
|
||||
// draw it
|
||||
y = con.vislines - 16;
|
||||
for (i = 0; i < con.linewidth; i++)
|
||||
{
|
||||
re.DrawChar((i + 1) << 3, con.vislines - 22, text[i]);
|
||||
}
|
||||
|
||||
// remove cursor
|
||||
key_lines[edit_line][key_linepos] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawNotify
|
||||
|
||||
Draws the last few lines of output transparently over the game top
|
||||
================
|
||||
*/
|
||||
void Con_DrawNotify(void)
|
||||
{
|
||||
int x, v;
|
||||
char * text;
|
||||
int i;
|
||||
int time;
|
||||
char * s;
|
||||
int skip;
|
||||
|
||||
v = 0;
|
||||
for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++)
|
||||
{
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
time = con.times[i % NUM_CON_TIMES];
|
||||
if (time == 0)
|
||||
continue;
|
||||
|
||||
time = cls.realtime - time;
|
||||
if (time > con_notifytime->value * 1000)
|
||||
continue;
|
||||
|
||||
text = con.text + (i % con.totallines) * con.linewidth;
|
||||
|
||||
for (x = 0; x < con.linewidth; x++)
|
||||
{
|
||||
re.DrawChar((x + 1) << 3, v, text[x]);
|
||||
}
|
||||
|
||||
v += 8;
|
||||
}
|
||||
|
||||
if (cls.key_dest == key_message)
|
||||
{
|
||||
if (chat_team)
|
||||
{
|
||||
DrawString(8, v, "say_team:");
|
||||
skip = 11;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawString(8, v, "say:");
|
||||
skip = 5;
|
||||
}
|
||||
|
||||
s = chat_buffer;
|
||||
if (chat_bufferlen > (viddef.width >> 3) - (skip + 1))
|
||||
{
|
||||
s += chat_bufferlen - ((viddef.width >> 3) - (skip + 1));
|
||||
}
|
||||
|
||||
x = 0;
|
||||
while (s[x])
|
||||
{
|
||||
re.DrawChar((x + skip) << 3, v, s[x]);
|
||||
x++;
|
||||
}
|
||||
|
||||
re.DrawChar((x + skip) << 3, v, 10 + ((cls.realtime >> 8) & 1));
|
||||
v += 8;
|
||||
}
|
||||
|
||||
if (v)
|
||||
{
|
||||
SCR_AddDirtyPoint(0, 0);
|
||||
SCR_AddDirtyPoint(viddef.width - 1, v);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Con_DrawConsole
|
||||
|
||||
Draws the console with the solid background
|
||||
================
|
||||
*/
|
||||
void Con_DrawConsole(float frac)
|
||||
{
|
||||
int i, j, x, y, n;
|
||||
int rows;
|
||||
char * text;
|
||||
int row;
|
||||
int lines;
|
||||
char version[64];
|
||||
char dlbar[1024];
|
||||
|
||||
// LAMPERT 2015-10-26
|
||||
// 2 extra pixels of spacing between lines of text.
|
||||
enum
|
||||
{
|
||||
LINE_SPACING = 2
|
||||
};
|
||||
|
||||
lines = viddef.height * frac;
|
||||
if (lines <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (lines > viddef.height)
|
||||
{
|
||||
lines = viddef.height;
|
||||
}
|
||||
|
||||
// draw the background
|
||||
re.DrawStretchPic(0, -viddef.height + lines, viddef.width, viddef.height, "conback");
|
||||
SCR_AddDirtyPoint(0, 0);
|
||||
SCR_AddDirtyPoint(viddef.width - 1, lines - 1);
|
||||
|
||||
Com_sprintf(version, sizeof(version), "v%4.2f", VERSION);
|
||||
for (x = 0; x < 5; x++)
|
||||
{
|
||||
re.DrawChar(viddef.width - 44 + x * 8, lines - 12, 128 + version[x]);
|
||||
}
|
||||
|
||||
// draw the text
|
||||
con.vislines = lines;
|
||||
|
||||
#if 0
|
||||
rows = (lines-8)>>3; // rows of text to draw
|
||||
y = lines - 24;
|
||||
#else
|
||||
rows = (lines - 22) >> 3; // rows of text to draw
|
||||
y = lines - 30;
|
||||
#endif
|
||||
|
||||
// draw from the bottom up
|
||||
if (con.display != con.current)
|
||||
{
|
||||
// draw arrows to show the buffer is backscrolled
|
||||
for (x = 0; x < con.linewidth; x += 4)
|
||||
{
|
||||
re.DrawChar((x + 1) << 3, y, '^');
|
||||
}
|
||||
|
||||
y -= (8 + LINE_SPACING);
|
||||
rows--;
|
||||
}
|
||||
|
||||
row = con.display;
|
||||
for (i = 0; i < rows; i++, y -= (8 + LINE_SPACING), row--)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (con.current - row >= con.totallines)
|
||||
{
|
||||
break; // past scrollback wrap point
|
||||
}
|
||||
|
||||
text = con.text + (row % con.totallines) * con.linewidth;
|
||||
|
||||
for (x = 0; x < con.linewidth; x++)
|
||||
{
|
||||
re.DrawChar((x + 1) << 3, y, text[x]);
|
||||
}
|
||||
}
|
||||
|
||||
//ZOID
|
||||
// draw the download bar
|
||||
// figure out width
|
||||
if (cls.download)
|
||||
{
|
||||
if ((text = strrchr(cls.downloadname, '/')) != NULL)
|
||||
text++;
|
||||
else
|
||||
text = cls.downloadname;
|
||||
|
||||
x = con.linewidth - ((con.linewidth * 7) / 40);
|
||||
y = x - strlen(text) - 8;
|
||||
i = con.linewidth / 3;
|
||||
|
||||
if (strlen(text) > i)
|
||||
{
|
||||
y = x - i - 11;
|
||||
strncpy(dlbar, text, i);
|
||||
dlbar[i] = 0;
|
||||
strcat(dlbar, "...");
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(dlbar, text);
|
||||
}
|
||||
|
||||
strcat(dlbar, ": ");
|
||||
i = strlen(dlbar);
|
||||
dlbar[i++] = '\x80';
|
||||
|
||||
// where's the dot go?
|
||||
if (cls.downloadpercent == 0)
|
||||
n = 0;
|
||||
else
|
||||
n = y * cls.downloadpercent / 100;
|
||||
|
||||
for (j = 0; j < y; j++)
|
||||
{
|
||||
if (j == n)
|
||||
dlbar[i++] = '\x83';
|
||||
else
|
||||
dlbar[i++] = '\x81';
|
||||
}
|
||||
|
||||
dlbar[i++] = '\x82';
|
||||
dlbar[i] = 0;
|
||||
|
||||
sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
|
||||
|
||||
// draw it
|
||||
y = con.vislines - 12;
|
||||
for (i = 0; i < strlen(dlbar); i++)
|
||||
{
|
||||
re.DrawChar((i + 1) << 3, y, dlbar[i]);
|
||||
}
|
||||
}
|
||||
//ZOID
|
||||
|
||||
// draw the input prompt, user text, and cursor if desired
|
||||
Con_DrawInput();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_CONSOLE_H
|
||||
#define CL_CONSOLE_H
|
||||
|
||||
//
|
||||
// console
|
||||
//
|
||||
|
||||
enum
|
||||
{
|
||||
NUM_CON_TIMES = 4,
|
||||
CON_TEXTSIZE = 32768
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean initialized;
|
||||
char text[CON_TEXTSIZE];
|
||||
int current; // line where next message will be printed
|
||||
int x; // offset in current line for next print
|
||||
int display; // bottom of console displays this line
|
||||
int ormask; // high bit mask for colored characters
|
||||
int linewidth; // characters across screen
|
||||
int totallines; // total lines in console scrollback
|
||||
float cursorspeed;
|
||||
int vislines;
|
||||
float times[NUM_CON_TIMES]; // cls.realtime time the line was generated
|
||||
// for transparent notify lines
|
||||
} console_t;
|
||||
|
||||
extern console_t con;
|
||||
|
||||
void Con_DrawCharacter(int cx, int line, int num);
|
||||
void Con_CheckResize(void);
|
||||
void Con_Init(void);
|
||||
void Con_DrawConsole(float frac);
|
||||
void Con_Print(char * txt);
|
||||
void Con_CenteredPrint(char * text);
|
||||
void Con_Clear_f(void);
|
||||
void Con_DrawNotify(void);
|
||||
void Con_ClearNotify(void);
|
||||
void Con_ToggleConsole_f(void);
|
||||
|
||||
#endif // CL_CONSOLE_H
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_INPUT_H
|
||||
#define CL_INPUT_H
|
||||
|
||||
// input.h -- external (non-keyboard) input devices
|
||||
|
||||
void IN_Init(void);
|
||||
void IN_Shutdown(void);
|
||||
void IN_Activate(qboolean active);
|
||||
|
||||
void IN_Frame(void);
|
||||
void IN_Commands(void); // opportunity for devices to stick commands on the script buffer
|
||||
void IN_Move(usercmd_t * cmd); // add additional movement on top of the keyboard move cmd
|
||||
|
||||
#endif // CL_INPUT_H
|
||||
@@ -0,0 +1,980 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
|
||||
/*
|
||||
key up events are sent even if in console mode
|
||||
*/
|
||||
|
||||
enum
|
||||
{
|
||||
MAXCMDLINE = 256
|
||||
};
|
||||
|
||||
char key_lines[32][MAXCMDLINE];
|
||||
int key_linepos;
|
||||
int shift_down = false;
|
||||
int anykeydown;
|
||||
|
||||
int edit_line = 0;
|
||||
int history_line = 0;
|
||||
|
||||
int key_waiting;
|
||||
char * keybindings[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[] =
|
||||
{
|
||||
{ "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 },
|
||||
|
||||
{ "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 },
|
||||
|
||||
{ "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 },
|
||||
|
||||
{ "KP_HOME", K_KP_HOME },
|
||||
{ "KP_UPARROW", K_KP_UPARROW },
|
||||
{ "KP_PGUP", K_KP_PGUP },
|
||||
{ "KP_LEFTARROW", K_KP_LEFTARROW },
|
||||
{ "KP_5", K_KP_5 },
|
||||
{ "KP_RIGHTARROW", K_KP_RIGHTARROW },
|
||||
{ "KP_END", K_KP_END },
|
||||
{ "KP_DOWNARROW", K_KP_DOWNARROW },
|
||||
{ "KP_PGDN", K_KP_PGDN },
|
||||
{ "KP_ENTER", K_KP_ENTER },
|
||||
{ "KP_INS", K_KP_INS },
|
||||
{ "KP_DEL", K_KP_DEL },
|
||||
{ "KP_SLASH", K_KP_SLASH },
|
||||
{ "KP_MINUS", K_KP_MINUS },
|
||||
{ "KP_PLUS", K_KP_PLUS },
|
||||
|
||||
{ "MWHEELUP", K_MWHEELUP },
|
||||
{ "MWHEELDOWN", K_MWHEELDOWN },
|
||||
|
||||
{ "PAUSE", K_PAUSE },
|
||||
|
||||
{ "SEMICOLON", ';' }, // because a raw semicolon seperates commands
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
LINE TYPING INTO THE CONSOLE
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
void CompleteCommand(void)
|
||||
{
|
||||
char *cmd, *s;
|
||||
|
||||
s = key_lines[edit_line] + 1;
|
||||
if (*s == '\\' || *s == '/')
|
||||
{
|
||||
s++;
|
||||
}
|
||||
|
||||
cmd = Cmd_CompleteCommand(s);
|
||||
if (!cmd)
|
||||
{
|
||||
cmd = Cvar_CompleteVariable(s);
|
||||
}
|
||||
|
||||
if (cmd)
|
||||
{
|
||||
key_lines[edit_line][1] = '/';
|
||||
strcpy(key_lines[edit_line] + 2, cmd);
|
||||
key_linepos = strlen(cmd) + 2;
|
||||
key_lines[edit_line][key_linepos] = ' ';
|
||||
key_linepos++;
|
||||
key_lines[edit_line][key_linepos] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
Key_Console
|
||||
|
||||
Interactive line editing and console scrollback
|
||||
====================
|
||||
*/
|
||||
void Key_Console(int key)
|
||||
{
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case K_KP_SLASH:
|
||||
key = '/';
|
||||
break;
|
||||
case K_KP_MINUS:
|
||||
key = '-';
|
||||
break;
|
||||
case K_KP_PLUS:
|
||||
key = '+';
|
||||
break;
|
||||
case K_KP_HOME:
|
||||
key = '7';
|
||||
break;
|
||||
case K_KP_UPARROW:
|
||||
key = '8';
|
||||
break;
|
||||
case K_KP_PGUP:
|
||||
key = '9';
|
||||
break;
|
||||
case K_KP_LEFTARROW:
|
||||
key = '4';
|
||||
break;
|
||||
case K_KP_5:
|
||||
key = '5';
|
||||
break;
|
||||
case K_KP_RIGHTARROW:
|
||||
key = '6';
|
||||
break;
|
||||
case K_KP_END:
|
||||
key = '1';
|
||||
break;
|
||||
case K_KP_DOWNARROW:
|
||||
key = '2';
|
||||
break;
|
||||
case K_KP_PGDN:
|
||||
key = '3';
|
||||
break;
|
||||
case K_KP_INS:
|
||||
key = '0';
|
||||
break;
|
||||
case K_KP_DEL:
|
||||
key = '.';
|
||||
break;
|
||||
}
|
||||
|
||||
if ((toupper(key) == 'V' && keydown[K_CTRL]) ||
|
||||
(((key == K_INS) || (key == K_KP_INS)) && keydown[K_SHIFT]))
|
||||
{
|
||||
char * cbd;
|
||||
|
||||
if ((cbd = Sys_GetClipboardData()) != 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
strtok(cbd, "\n\r\b");
|
||||
|
||||
i = strlen(cbd);
|
||||
if (i + key_linepos >= MAXCMDLINE)
|
||||
i = MAXCMDLINE - key_linepos;
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
cbd[i] = 0;
|
||||
strcat(key_lines[edit_line], cbd);
|
||||
key_linepos += i;
|
||||
}
|
||||
free(cbd);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == 'l')
|
||||
{
|
||||
if (keydown[K_CTRL])
|
||||
{
|
||||
Cbuf_AddText("clear\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == K_ENTER || key == K_KP_ENTER)
|
||||
{ // backslash text are commands, else chat
|
||||
if (key_lines[edit_line][1] == '\\' || key_lines[edit_line][1] == '/')
|
||||
Cbuf_AddText(key_lines[edit_line] + 2); // skip the >
|
||||
else
|
||||
Cbuf_AddText(key_lines[edit_line] + 1); // valid command
|
||||
|
||||
Cbuf_AddText("\n");
|
||||
Com_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
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_TAB)
|
||||
{ // command completion
|
||||
CompleteCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key == K_BACKSPACE) || (key == K_LEFTARROW) || (key == K_KP_LEFTARROW) || ((key == 'h') && (keydown[K_CTRL])))
|
||||
{
|
||||
if (key_linepos > 1)
|
||||
key_linepos--;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key == K_UPARROW) || (key == K_KP_UPARROW) ||
|
||||
((key == 'p') && keydown[K_CTRL]))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
strcpy(key_lines[edit_line], key_lines[history_line]);
|
||||
key_linepos = strlen(key_lines[edit_line]);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key == K_DOWNARROW) || (key == K_KP_DOWNARROW) ||
|
||||
((key == 'n') && keydown[K_CTRL]))
|
||||
{
|
||||
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
|
||||
{
|
||||
strcpy(key_lines[edit_line], key_lines[history_line]);
|
||||
key_linepos = strlen(key_lines[edit_line]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_PGUP || key == K_KP_PGUP)
|
||||
{
|
||||
con.display -= 2;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_PGDN || key == K_KP_PGDN)
|
||||
{
|
||||
con.display += 2;
|
||||
if (con.display > con.current)
|
||||
con.display = con.current;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_HOME || key == K_KP_HOME)
|
||||
{
|
||||
con.display = con.current - con.totallines + 10;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_END || key == K_KP_END)
|
||||
{
|
||||
con.display = con.current;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
qboolean chat_team;
|
||||
char chat_buffer[MAXCMDLINE];
|
||||
int chat_bufferlen = 0;
|
||||
|
||||
void Key_Message(int key)
|
||||
{
|
||||
if (key == K_ENTER || key == K_KP_ENTER)
|
||||
{
|
||||
if (chat_team)
|
||||
{
|
||||
Cbuf_AddText("say_team \"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Cbuf_AddText("say \"");
|
||||
}
|
||||
|
||||
Cbuf_AddText(chat_buffer);
|
||||
Cbuf_AddText("\"\n");
|
||||
|
||||
cls.key_dest = key_game;
|
||||
chat_bufferlen = 0;
|
||||
chat_buffer[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == K_ESCAPE)
|
||||
{
|
||||
cls.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 == sizeof(chat_buffer) - 1)
|
||||
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(const 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?)
|
||||
===================
|
||||
*/
|
||||
const char * Key_KeynumToString(int keynum)
|
||||
{
|
||||
keyname_t * kn;
|
||||
static char tinystr[2];
|
||||
|
||||
if (keynum == -1)
|
||||
{
|
||||
return "<KEY NOT FOUND>";
|
||||
}
|
||||
|
||||
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 "<UNKNOWN KEYNUM>";
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
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 = strlen(binding);
|
||||
new = Z_Malloc(l + 1);
|
||||
strcpy(new, binding);
|
||||
new[l] = 0;
|
||||
keybindings[keynum] = new;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Key_Unbind_f
|
||||
===================
|
||||
*/
|
||||
void Key_Unbind_f(void)
|
||||
{
|
||||
int b;
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
Com_Printf("unbind <key> : remove commands from a key\n");
|
||||
return;
|
||||
}
|
||||
|
||||
b = Key_StringToKeynum(Cmd_Argv(1));
|
||||
if (b == -1)
|
||||
{
|
||||
Com_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)
|
||||
{
|
||||
Com_Printf("bind <key> [command] : attach a command to a key\n");
|
||||
return;
|
||||
}
|
||||
b = Key_StringToKeynum(Cmd_Argv(1));
|
||||
if (b == -1)
|
||||
{
|
||||
Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 2)
|
||||
{
|
||||
if (keybindings[b])
|
||||
Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b]);
|
||||
else
|
||||
Com_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++)
|
||||
{
|
||||
strcat(cmd, Cmd_Argv(i));
|
||||
if (i != (c - 1))
|
||||
strcat(cmd, " ");
|
||||
}
|
||||
|
||||
Key_SetBinding(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] && keybindings[i][0])
|
||||
{
|
||||
fprintf(f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Key_Bindlist_f
|
||||
|
||||
============
|
||||
*/
|
||||
void Key_Bindlist_f(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if (keybindings[i] && keybindings[i][0])
|
||||
{
|
||||
Com_Printf("%s \"%s\"\n", Key_KeynumToString(i), keybindings[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_KP_ENTER] = true;
|
||||
consolekeys[K_TAB] = true;
|
||||
consolekeys[K_LEFTARROW] = true;
|
||||
consolekeys[K_KP_LEFTARROW] = true;
|
||||
consolekeys[K_RIGHTARROW] = true;
|
||||
consolekeys[K_KP_RIGHTARROW] = true;
|
||||
consolekeys[K_UPARROW] = true;
|
||||
consolekeys[K_KP_UPARROW] = true;
|
||||
consolekeys[K_DOWNARROW] = true;
|
||||
consolekeys[K_KP_DOWNARROW] = true;
|
||||
consolekeys[K_BACKSPACE] = true;
|
||||
consolekeys[K_HOME] = true;
|
||||
consolekeys[K_KP_HOME] = true;
|
||||
consolekeys[K_END] = true;
|
||||
consolekeys[K_KP_END] = true;
|
||||
consolekeys[K_PGUP] = true;
|
||||
consolekeys[K_KP_PGUP] = true;
|
||||
consolekeys[K_PGDN] = true;
|
||||
consolekeys[K_KP_PGDN] = true;
|
||||
consolekeys[K_SHIFT] = true;
|
||||
consolekeys[K_INS] = true;
|
||||
consolekeys[K_KP_INS] = true;
|
||||
consolekeys[K_KP_DEL] = true;
|
||||
consolekeys[K_KP_SLASH] = true;
|
||||
consolekeys[K_KP_PLUS] = true;
|
||||
consolekeys[K_KP_MINUS] = true;
|
||||
consolekeys[K_KP_5] = 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("unbind", Key_Unbind_f);
|
||||
Cmd_AddCommand("unbindall", Key_Unbindall_f);
|
||||
Cmd_AddCommand("bindlist", Key_Bindlist_f);
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Key_Event
|
||||
|
||||
Called by the system between frames for both key up and key down events
|
||||
Should NOT be called during an interrupt!
|
||||
===================
|
||||
*/
|
||||
void Key_Event(int key, qboolean down, unsigned time)
|
||||
{
|
||||
char * kb;
|
||||
char cmd[1024];
|
||||
|
||||
// hack for modal presses
|
||||
if (key_waiting == -1)
|
||||
{
|
||||
if (down)
|
||||
key_waiting = key;
|
||||
return;
|
||||
}
|
||||
|
||||
// update auto-repeat status
|
||||
if (down)
|
||||
{
|
||||
key_repeats[key]++;
|
||||
if (key != K_BACKSPACE && key != K_PAUSE && key != K_PGUP && key != K_KP_PGUP && key != K_PGDN && key != K_KP_PGDN && key_repeats[key] > 1)
|
||||
{
|
||||
return; // ignore most autorepeats
|
||||
}
|
||||
|
||||
if (key >= 200 && !keybindings[key])
|
||||
{
|
||||
Com_Printf("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key_repeats[key] = 0;
|
||||
}
|
||||
|
||||
if (key == K_SHIFT)
|
||||
shift_down = down;
|
||||
|
||||
// console key is hardcoded, so the user can never unbind it
|
||||
if (key == '`' || key == '~')
|
||||
{
|
||||
if (!down)
|
||||
return;
|
||||
Con_ToggleConsole_f();
|
||||
return;
|
||||
}
|
||||
|
||||
// any key during the attract mode will bring up the menu
|
||||
if (cl.attractloop && cls.key_dest != key_menu)
|
||||
key = K_ESCAPE;
|
||||
|
||||
// menu key is hardcoded, so the user can never unbind it
|
||||
if (key == K_ESCAPE)
|
||||
{
|
||||
if (!down)
|
||||
return;
|
||||
|
||||
if (cl.frame.playerstate.stats[STAT_LAYOUTS] && cls.key_dest == key_game)
|
||||
{ // put away help computer / inventory
|
||||
Cbuf_AddText("cmd putaway\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cls.key_dest)
|
||||
{
|
||||
case key_message:
|
||||
Key_Message(key);
|
||||
break;
|
||||
case key_menu:
|
||||
M_Keydown(key);
|
||||
break;
|
||||
case key_game:
|
||||
case key_console:
|
||||
M_Menu_Main_f();
|
||||
break;
|
||||
default:
|
||||
Com_Error(ERR_FATAL, "Bad cls.key_dest");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// track if any key is down for BUTTON_ANY
|
||||
keydown[key] = down;
|
||||
if (down)
|
||||
{
|
||||
if (key_repeats[key] == 1)
|
||||
anykeydown++;
|
||||
}
|
||||
else
|
||||
{
|
||||
anykeydown--;
|
||||
if (anykeydown < 0)
|
||||
anykeydown = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// 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] == '+')
|
||||
{
|
||||
Com_sprintf(cmd, sizeof(cmd), "-%s %i %i\n", kb + 1, key, time);
|
||||
Cbuf_AddText(cmd);
|
||||
}
|
||||
if (keyshift[key] != key)
|
||||
{
|
||||
kb = keybindings[keyshift[key]];
|
||||
if (kb && kb[0] == '+')
|
||||
{
|
||||
Com_sprintf(cmd, sizeof(cmd), "-%s %i %i\n", kb + 1, key, time);
|
||||
Cbuf_AddText(cmd);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// if not a consolekey, send to the interpreter no matter what mode is
|
||||
//
|
||||
if ((cls.key_dest == key_menu && menubound[key]) || (cls.key_dest == key_console && !consolekeys[key]) || (cls.key_dest == key_game && (cls.state == ca_active || !consolekeys[key])))
|
||||
{
|
||||
kb = keybindings[key];
|
||||
if (kb)
|
||||
{
|
||||
if (kb[0] == '+')
|
||||
{ // button commands add keynum and time as a parm
|
||||
Com_sprintf(cmd, sizeof(cmd), "%s %i %i\n", kb, key, time);
|
||||
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 (cls.key_dest)
|
||||
{
|
||||
case key_message:
|
||||
Key_Message(key);
|
||||
break;
|
||||
case key_menu:
|
||||
M_Keydown(key);
|
||||
break;
|
||||
|
||||
case key_game:
|
||||
case key_console:
|
||||
Key_Console(key);
|
||||
break;
|
||||
default:
|
||||
Com_Error(ERR_FATAL, "Bad cls.key_dest");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Key_ClearStates
|
||||
===================
|
||||
*/
|
||||
void Key_ClearStates(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
anykeydown = false;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
if (keydown[i] || key_repeats[i])
|
||||
Key_Event(i, false, 0);
|
||||
keydown[i] = 0;
|
||||
key_repeats[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Key_GetKey
|
||||
===================
|
||||
*/
|
||||
int Key_GetKey(void)
|
||||
{
|
||||
key_waiting = -1;
|
||||
|
||||
while (key_waiting == -1)
|
||||
{
|
||||
Sys_SendKeyEvents();
|
||||
}
|
||||
|
||||
return key_waiting;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_KEYS_H
|
||||
#define CL_KEYS_H
|
||||
|
||||
//
|
||||
// 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_KP_HOME 160
|
||||
#define K_KP_UPARROW 161
|
||||
#define K_KP_PGUP 162
|
||||
#define K_KP_LEFTARROW 163
|
||||
#define K_KP_5 164
|
||||
#define K_KP_RIGHTARROW 165
|
||||
#define K_KP_END 166
|
||||
#define K_KP_DOWNARROW 167
|
||||
#define K_KP_PGDN 168
|
||||
#define K_KP_ENTER 169
|
||||
#define K_KP_INS 170
|
||||
#define K_KP_DEL 171
|
||||
#define K_KP_SLASH 172
|
||||
#define K_KP_MINUS 173
|
||||
#define K_KP_PLUS 174
|
||||
#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
|
||||
|
||||
#define K_MWHEELDOWN 239
|
||||
#define K_MWHEELUP 240
|
||||
|
||||
extern char * keybindings[256];
|
||||
extern int key_repeats[256];
|
||||
|
||||
extern int anykeydown;
|
||||
extern char chat_buffer[];
|
||||
extern int chat_bufferlen;
|
||||
extern qboolean chat_team;
|
||||
|
||||
void Key_Event(int key, qboolean down, unsigned time);
|
||||
void Key_Init(void);
|
||||
void Key_WriteBindings(FILE * f);
|
||||
void Key_SetBinding(int keynum, char * binding);
|
||||
void Key_ClearStates(void);
|
||||
int Key_GetKey(void);
|
||||
|
||||
#endif // CL_KEYS_H
|
||||
+4016
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "qmenu.h"
|
||||
|
||||
void Action_DoEnter(menuaction_s * a);
|
||||
void Action_Draw(menuaction_s * a);
|
||||
void Menu_DrawStatusBar(const char * string);
|
||||
void Menulist_DoEnter(menulist_s * l);
|
||||
void MenuList_Draw(menulist_s * l);
|
||||
void Separator_Draw(menuseparator_s * s);
|
||||
void Slider_DoSlide(menuslider_s * s, int dir);
|
||||
void Slider_Draw(menuslider_s * s);
|
||||
void SpinControl_DoEnter(menulist_s * s);
|
||||
void SpinControl_Draw(menulist_s * s);
|
||||
void SpinControl_DoSlide(menulist_s * s, int dir);
|
||||
|
||||
extern refexport_t re;
|
||||
extern viddef_t viddef;
|
||||
|
||||
#define RCOLUMN_OFFSET 16
|
||||
#define LCOLUMN_OFFSET -16
|
||||
|
||||
void Action_DoEnter(menuaction_s * a)
|
||||
{
|
||||
if (a->generic.callback)
|
||||
a->generic.callback(a);
|
||||
}
|
||||
|
||||
void Action_Draw(menuaction_s * a)
|
||||
{
|
||||
if (a->generic.flags & QMF_LEFT_JUSTIFY)
|
||||
{
|
||||
if (a->generic.flags & QMF_GRAYED)
|
||||
Menu_DrawStringDark(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name);
|
||||
else
|
||||
Menu_DrawString(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a->generic.flags & QMF_GRAYED)
|
||||
Menu_DrawStringR2LDark(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name);
|
||||
else
|
||||
Menu_DrawStringR2L(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name);
|
||||
}
|
||||
if (a->generic.ownerdraw)
|
||||
a->generic.ownerdraw(a);
|
||||
}
|
||||
|
||||
qboolean Field_DoEnter(menufield_s * f)
|
||||
{
|
||||
if (f->generic.callback)
|
||||
{
|
||||
f->generic.callback(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Field_Draw(menufield_s * f)
|
||||
{
|
||||
int i;
|
||||
char tempbuffer[128] = "";
|
||||
|
||||
if (f->generic.name)
|
||||
Menu_DrawStringR2LDark(f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y, f->generic.name);
|
||||
|
||||
strncpy(tempbuffer, f->buffer + f->visible_offset, f->visible_length);
|
||||
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18);
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24);
|
||||
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4, 20);
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4, 26);
|
||||
|
||||
for (i = 0; i < f->visible_length; i++)
|
||||
{
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19);
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25);
|
||||
}
|
||||
|
||||
Menu_DrawString(f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer);
|
||||
|
||||
if (Menu_ItemAtCursor(f->generic.parent) == f)
|
||||
{
|
||||
int offset;
|
||||
|
||||
if (f->visible_offset)
|
||||
offset = f->visible_length;
|
||||
else
|
||||
offset = f->cursor;
|
||||
|
||||
if (((int)(Sys_Milliseconds() / 250)) & 1)
|
||||
{
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + (offset + 2) * 8 + 8,
|
||||
f->generic.y + f->generic.parent->y,
|
||||
11);
|
||||
}
|
||||
else
|
||||
{
|
||||
re.DrawChar(f->generic.x + f->generic.parent->x + (offset + 2) * 8 + 8,
|
||||
f->generic.y + f->generic.parent->y,
|
||||
' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qboolean Field_Key(menufield_s * f, int key)
|
||||
{
|
||||
extern int keydown[];
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case K_KP_SLASH:
|
||||
key = '/';
|
||||
break;
|
||||
case K_KP_MINUS:
|
||||
key = '-';
|
||||
break;
|
||||
case K_KP_PLUS:
|
||||
key = '+';
|
||||
break;
|
||||
case K_KP_HOME:
|
||||
key = '7';
|
||||
break;
|
||||
case K_KP_UPARROW:
|
||||
key = '8';
|
||||
break;
|
||||
case K_KP_PGUP:
|
||||
key = '9';
|
||||
break;
|
||||
case K_KP_LEFTARROW:
|
||||
key = '4';
|
||||
break;
|
||||
case K_KP_5:
|
||||
key = '5';
|
||||
break;
|
||||
case K_KP_RIGHTARROW:
|
||||
key = '6';
|
||||
break;
|
||||
case K_KP_END:
|
||||
key = '1';
|
||||
break;
|
||||
case K_KP_DOWNARROW:
|
||||
key = '2';
|
||||
break;
|
||||
case K_KP_PGDN:
|
||||
key = '3';
|
||||
break;
|
||||
case K_KP_INS:
|
||||
key = '0';
|
||||
break;
|
||||
case K_KP_DEL:
|
||||
key = '.';
|
||||
break;
|
||||
}
|
||||
|
||||
if (key > 127)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case K_DEL:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** support pasting from the clipboard
|
||||
*/
|
||||
if ((toupper(key) == 'V' && keydown[K_CTRL]) ||
|
||||
(((key == K_INS) || (key == K_KP_INS)) && keydown[K_SHIFT]))
|
||||
{
|
||||
char * cbd;
|
||||
|
||||
if ((cbd = Sys_GetClipboardData()) != 0)
|
||||
{
|
||||
strtok(cbd, "\n\r\b");
|
||||
|
||||
strncpy(f->buffer, cbd, f->length - 1);
|
||||
f->cursor = strlen(f->buffer);
|
||||
f->visible_offset = f->cursor - f->visible_length;
|
||||
if (f->visible_offset < 0)
|
||||
f->visible_offset = 0;
|
||||
|
||||
free(cbd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case K_KP_LEFTARROW:
|
||||
case K_LEFTARROW:
|
||||
case K_BACKSPACE:
|
||||
if (f->cursor > 0)
|
||||
{
|
||||
memmove(&f->buffer[f->cursor - 1], &f->buffer[f->cursor], strlen(&f->buffer[f->cursor]) + 1);
|
||||
f->cursor--;
|
||||
|
||||
if (f->visible_offset)
|
||||
{
|
||||
f->visible_offset--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case K_KP_DEL:
|
||||
case K_DEL:
|
||||
memmove(&f->buffer[f->cursor], &f->buffer[f->cursor + 1], strlen(&f->buffer[f->cursor + 1]) + 1);
|
||||
break;
|
||||
|
||||
case K_KP_ENTER:
|
||||
case K_ENTER:
|
||||
case K_ESCAPE:
|
||||
case K_TAB:
|
||||
return false;
|
||||
|
||||
case K_SPACE:
|
||||
default:
|
||||
if (!isdigit(key) && (f->generic.flags & QMF_NUMBERSONLY))
|
||||
return false;
|
||||
|
||||
if (f->cursor < f->length)
|
||||
{
|
||||
f->buffer[f->cursor++] = key;
|
||||
f->buffer[f->cursor] = 0;
|
||||
|
||||
if (f->cursor > f->visible_length)
|
||||
{
|
||||
f->visible_offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Menu_AddItem(menuframework_s * menu, void * item)
|
||||
{
|
||||
if (menu->nitems == 0)
|
||||
menu->nslots = 0;
|
||||
|
||||
if (menu->nitems < MAXMENUITEMS)
|
||||
{
|
||||
menu->items[menu->nitems] = item;
|
||||
((menucommon_s *)menu->items[menu->nitems])->parent = menu;
|
||||
menu->nitems++;
|
||||
}
|
||||
|
||||
menu->nslots = Menu_TallySlots(menu);
|
||||
}
|
||||
|
||||
/*
|
||||
** Menu_AdjustCursor
|
||||
**
|
||||
** This function takes the given menu, the direction, and attempts
|
||||
** to adjust the menu's cursor so that it's at the next available
|
||||
** slot.
|
||||
*/
|
||||
void Menu_AdjustCursor(menuframework_s * m, int dir)
|
||||
{
|
||||
menucommon_s * citem;
|
||||
|
||||
/*
|
||||
** see if it's in a valid spot
|
||||
*/
|
||||
if (m->cursor >= 0 && m->cursor < m->nitems)
|
||||
{
|
||||
if ((citem = Menu_ItemAtCursor(m)) != 0)
|
||||
{
|
||||
if (citem->type != MTYPE_SEPARATOR)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** it's not in a valid spot, so crawl in the direction indicated until we
|
||||
** find a valid spot
|
||||
*/
|
||||
if (dir == 1)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
citem = Menu_ItemAtCursor(m);
|
||||
if (citem)
|
||||
if (citem->type != MTYPE_SEPARATOR)
|
||||
break;
|
||||
m->cursor += dir;
|
||||
if (m->cursor >= m->nitems)
|
||||
m->cursor = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
citem = Menu_ItemAtCursor(m);
|
||||
if (citem)
|
||||
if (citem->type != MTYPE_SEPARATOR)
|
||||
break;
|
||||
m->cursor += dir;
|
||||
if (m->cursor < 0)
|
||||
m->cursor = m->nitems - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_Center(menuframework_s * menu)
|
||||
{
|
||||
int height;
|
||||
|
||||
height = ((menucommon_s *)menu->items[menu->nitems - 1])->y;
|
||||
height += 10;
|
||||
|
||||
menu->y = (viddef.height - height) / 2;
|
||||
}
|
||||
|
||||
void Menu_Draw(menuframework_s * menu)
|
||||
{
|
||||
int i;
|
||||
menucommon_s * item;
|
||||
|
||||
/*
|
||||
** draw contents
|
||||
*/
|
||||
for (i = 0; i < menu->nitems; i++)
|
||||
{
|
||||
switch (((menucommon_s *)menu->items[i])->type)
|
||||
{
|
||||
case MTYPE_FIELD:
|
||||
Field_Draw((menufield_s *)menu->items[i]);
|
||||
break;
|
||||
case MTYPE_SLIDER:
|
||||
Slider_Draw((menuslider_s *)menu->items[i]);
|
||||
break;
|
||||
case MTYPE_LIST:
|
||||
MenuList_Draw((menulist_s *)menu->items[i]);
|
||||
break;
|
||||
case MTYPE_SPINCONTROL:
|
||||
SpinControl_Draw((menulist_s *)menu->items[i]);
|
||||
break;
|
||||
case MTYPE_ACTION:
|
||||
Action_Draw((menuaction_s *)menu->items[i]);
|
||||
break;
|
||||
case MTYPE_SEPARATOR:
|
||||
Separator_Draw((menuseparator_s *)menu->items[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
item = Menu_ItemAtCursor(menu);
|
||||
|
||||
if (item && item->cursordraw)
|
||||
{
|
||||
item->cursordraw(item);
|
||||
}
|
||||
else if (menu->cursordraw)
|
||||
{
|
||||
menu->cursordraw(menu);
|
||||
}
|
||||
else if (item && item->type != MTYPE_FIELD)
|
||||
{
|
||||
if (item->flags & QMF_LEFT_JUSTIFY)
|
||||
{
|
||||
re.DrawChar(menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ((int)(Sys_Milliseconds() / 250) & 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
re.DrawChar(menu->x + item->cursor_offset, menu->y + item->y, 12 + ((int)(Sys_Milliseconds() / 250) & 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (item)
|
||||
{
|
||||
if (item->statusbarfunc)
|
||||
item->statusbarfunc((void *)item);
|
||||
else if (item->statusbar)
|
||||
Menu_DrawStatusBar(item->statusbar);
|
||||
else
|
||||
Menu_DrawStatusBar(menu->statusbar);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menu_DrawStatusBar(menu->statusbar);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_DrawStatusBar(const char * string)
|
||||
{
|
||||
if (string)
|
||||
{
|
||||
int l = strlen(string);
|
||||
int maxrow = viddef.height / 8;
|
||||
int maxcol = viddef.width / 8;
|
||||
int col = maxcol / 2 - l / 2;
|
||||
|
||||
re.DrawFill(0, viddef.height - 8, viddef.width, 8, 4);
|
||||
Menu_DrawString(col * 8, viddef.height - 8, string);
|
||||
}
|
||||
else
|
||||
{
|
||||
re.DrawFill(0, viddef.height - 8, viddef.width, 8, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_DrawString(int x, int y, const char * string)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
{
|
||||
re.DrawChar((x + i * 8), y, string[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_DrawStringDark(int x, int y, const char * string)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
{
|
||||
re.DrawChar((x + i * 8), y, string[i] + 128);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_DrawStringR2L(int x, int y, const char * string)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
{
|
||||
re.DrawChar((x - i * 8), y, string[strlen(string) - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu_DrawStringR2LDark(int x, int y, const char * string)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
{
|
||||
re.DrawChar((x - i * 8), y, string[strlen(string) - i - 1] + 128);
|
||||
}
|
||||
}
|
||||
|
||||
void * Menu_ItemAtCursor(menuframework_s * m)
|
||||
{
|
||||
if (m->cursor < 0 || m->cursor >= m->nitems)
|
||||
return 0;
|
||||
|
||||
return m->items[m->cursor];
|
||||
}
|
||||
|
||||
qboolean Menu_SelectItem(menuframework_s * s)
|
||||
{
|
||||
menucommon_s * item = (menucommon_s *)Menu_ItemAtCursor(s);
|
||||
|
||||
if (item)
|
||||
{
|
||||
switch (item->type)
|
||||
{
|
||||
case MTYPE_FIELD:
|
||||
return Field_DoEnter((menufield_s *)item);
|
||||
case MTYPE_ACTION:
|
||||
Action_DoEnter((menuaction_s *)item);
|
||||
return true;
|
||||
case MTYPE_LIST:
|
||||
// Menulist_DoEnter( ( menulist_s * ) item );
|
||||
return false;
|
||||
case MTYPE_SPINCONTROL:
|
||||
// SpinControl_DoEnter( ( menulist_s * ) item );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Menu_SetStatusBar(menuframework_s * m, const char * string)
|
||||
{
|
||||
m->statusbar = string;
|
||||
}
|
||||
|
||||
void Menu_SlideItem(menuframework_s * s, int dir)
|
||||
{
|
||||
menucommon_s * item = (menucommon_s *)Menu_ItemAtCursor(s);
|
||||
|
||||
if (item)
|
||||
{
|
||||
switch (item->type)
|
||||
{
|
||||
case MTYPE_SLIDER:
|
||||
Slider_DoSlide((menuslider_s *)item, dir);
|
||||
break;
|
||||
case MTYPE_SPINCONTROL:
|
||||
SpinControl_DoSlide((menulist_s *)item, dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Menu_TallySlots(menuframework_s * menu)
|
||||
{
|
||||
int i;
|
||||
int total = 0;
|
||||
|
||||
for (i = 0; i < menu->nitems; i++)
|
||||
{
|
||||
if (((menucommon_s *)menu->items[i])->type == MTYPE_LIST)
|
||||
{
|
||||
int nitems = 0;
|
||||
const char ** n = ((menulist_s *)menu->items[i])->itemnames;
|
||||
|
||||
while (*n)
|
||||
nitems++, n++;
|
||||
|
||||
total += nitems;
|
||||
}
|
||||
else
|
||||
{
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void Menulist_DoEnter(menulist_s * l)
|
||||
{
|
||||
int start;
|
||||
|
||||
start = l->generic.y / 10 + 1;
|
||||
|
||||
l->curvalue = l->generic.parent->cursor - start;
|
||||
|
||||
if (l->generic.callback)
|
||||
l->generic.callback(l);
|
||||
}
|
||||
|
||||
void MenuList_Draw(menulist_s * l)
|
||||
{
|
||||
const char ** n;
|
||||
int y = 0;
|
||||
|
||||
Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y, l->generic.name);
|
||||
|
||||
n = l->itemnames;
|
||||
|
||||
re.DrawFill(l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue * 10 + 10, 128, 10, 16);
|
||||
while (*n)
|
||||
{
|
||||
Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y + y + 10, *n);
|
||||
|
||||
n++;
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
void Separator_Draw(menuseparator_s * s)
|
||||
{
|
||||
if (s->generic.name)
|
||||
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name);
|
||||
}
|
||||
|
||||
void Slider_DoSlide(menuslider_s * s, int dir)
|
||||
{
|
||||
s->curvalue += dir;
|
||||
|
||||
if (s->curvalue > s->maxvalue)
|
||||
s->curvalue = s->maxvalue;
|
||||
else if (s->curvalue < s->minvalue)
|
||||
s->curvalue = s->minvalue;
|
||||
|
||||
if (s->generic.callback)
|
||||
s->generic.callback(s);
|
||||
}
|
||||
|
||||
#define SLIDER_RANGE 10
|
||||
|
||||
void Slider_Draw(menuslider_s * s)
|
||||
{
|
||||
int i;
|
||||
|
||||
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
|
||||
s->generic.y + s->generic.parent->y,
|
||||
s->generic.name);
|
||||
|
||||
s->range = (s->curvalue - s->minvalue) / (float)(s->maxvalue - s->minvalue);
|
||||
|
||||
if (s->range < 0)
|
||||
s->range = 0;
|
||||
if (s->range > 1)
|
||||
s->range = 1;
|
||||
re.DrawChar(s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128);
|
||||
for (i = 0; i < SLIDER_RANGE; i++)
|
||||
re.DrawChar(RCOLUMN_OFFSET + s->generic.x + i * 8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 129);
|
||||
re.DrawChar(RCOLUMN_OFFSET + s->generic.x + i * 8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130);
|
||||
re.DrawChar((int)(8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE - 1) * 8 * s->range), s->generic.y + s->generic.parent->y, 131);
|
||||
}
|
||||
|
||||
void SpinControl_DoEnter(menulist_s * s)
|
||||
{
|
||||
s->curvalue++;
|
||||
if (s->itemnames[s->curvalue] == 0)
|
||||
s->curvalue = 0;
|
||||
|
||||
if (s->generic.callback)
|
||||
s->generic.callback(s);
|
||||
}
|
||||
|
||||
void SpinControl_DoSlide(menulist_s * s, int dir)
|
||||
{
|
||||
s->curvalue += dir;
|
||||
|
||||
if (s->curvalue < 0)
|
||||
s->curvalue = 0;
|
||||
else if (s->itemnames[s->curvalue] == 0)
|
||||
s->curvalue--;
|
||||
|
||||
if (s->generic.callback)
|
||||
s->generic.callback(s);
|
||||
}
|
||||
|
||||
void SpinControl_Draw(menulist_s * s)
|
||||
{
|
||||
char buffer[100];
|
||||
|
||||
if (s->generic.name)
|
||||
{
|
||||
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
|
||||
s->generic.y + s->generic.parent->y,
|
||||
s->generic.name);
|
||||
}
|
||||
if (!strchr(s->itemnames[s->curvalue], '\n'))
|
||||
{
|
||||
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue]);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(buffer, s->itemnames[s->curvalue]);
|
||||
*strchr(buffer, '\n') = 0;
|
||||
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer);
|
||||
strcpy(buffer, strchr(s->itemnames[s->curvalue], '\n') + 1);
|
||||
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_QMENU_H
|
||||
#define CL_QMENU_H
|
||||
|
||||
#define MAXMENUITEMS 64
|
||||
|
||||
#define MTYPE_SLIDER 0
|
||||
#define MTYPE_LIST 1
|
||||
#define MTYPE_ACTION 2
|
||||
#define MTYPE_SPINCONTROL 3
|
||||
#define MTYPE_SEPARATOR 4
|
||||
#define MTYPE_FIELD 5
|
||||
|
||||
#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 QMF_LEFT_JUSTIFY 0x00000001
|
||||
#define QMF_GRAYED 0x00000002
|
||||
#define QMF_NUMBERSONLY 0x00000004
|
||||
|
||||
typedef struct _tag_menuframework
|
||||
{
|
||||
int x, y;
|
||||
int cursor;
|
||||
|
||||
int nitems;
|
||||
int nslots;
|
||||
void * items[64];
|
||||
|
||||
const char * statusbar;
|
||||
|
||||
void (*cursordraw)(struct _tag_menuframework * m);
|
||||
|
||||
} menuframework_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
const char * name;
|
||||
int x, y;
|
||||
menuframework_s * parent;
|
||||
int cursor_offset;
|
||||
int localdata[4];
|
||||
unsigned flags;
|
||||
|
||||
const char * statusbar;
|
||||
|
||||
void (*callback)(void * self);
|
||||
void (*statusbarfunc)(void * self);
|
||||
void (*ownerdraw)(void * self);
|
||||
void (*cursordraw)(void * self);
|
||||
} menucommon_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
menucommon_s generic;
|
||||
|
||||
char buffer[80];
|
||||
int cursor;
|
||||
int length;
|
||||
int visible_length;
|
||||
int visible_offset;
|
||||
} menufield_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
menucommon_s generic;
|
||||
|
||||
float minvalue;
|
||||
float maxvalue;
|
||||
float curvalue;
|
||||
|
||||
float range;
|
||||
} menuslider_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
menucommon_s generic;
|
||||
|
||||
int curvalue;
|
||||
|
||||
const char ** itemnames;
|
||||
} menulist_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
menucommon_s generic;
|
||||
} menuaction_s;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
menucommon_s generic;
|
||||
} menuseparator_s;
|
||||
|
||||
qboolean Field_Key(menufield_s * field, int key);
|
||||
|
||||
void Menu_AddItem(menuframework_s * menu, void * item);
|
||||
void Menu_AdjustCursor(menuframework_s * menu, int dir);
|
||||
void Menu_Center(menuframework_s * menu);
|
||||
void Menu_Draw(menuframework_s * menu);
|
||||
void * Menu_ItemAtCursor(menuframework_s * m);
|
||||
qboolean Menu_SelectItem(menuframework_s * s);
|
||||
void Menu_SetStatusBar(menuframework_s * s, const char * string);
|
||||
void Menu_SlideItem(menuframework_s * s, int dir);
|
||||
int Menu_TallySlots(menuframework_s * menu);
|
||||
|
||||
void Menu_DrawString(int, int, const char *);
|
||||
void Menu_DrawStringDark(int, int, const char *);
|
||||
void Menu_DrawStringR2L(int, int, const char *);
|
||||
void Menu_DrawStringR2LDark(int, int, const char *);
|
||||
|
||||
#endif // CL_QMENU_H
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_REF_H
|
||||
#define CL_REF_H
|
||||
|
||||
#include "common/q_common.h"
|
||||
|
||||
#define REF_API_VERSION 3
|
||||
|
||||
#define MAX_DLIGHTS 32
|
||||
#define MAX_ENTITIES 128
|
||||
#define MAX_PARTICLES 4096
|
||||
#define MAX_LIGHTSTYLES 256
|
||||
|
||||
#define ENTITY_FLAGS 68
|
||||
#define POWERSUIT_SCALE 4.0f
|
||||
|
||||
#define SHELL_RED_COLOR 0xF2
|
||||
#define SHELL_GREEN_COLOR 0xD0
|
||||
#define SHELL_BLUE_COLOR 0xF3
|
||||
#define SHELL_RG_COLOR 0xDC
|
||||
#define SHELL_RB_COLOR 0x68
|
||||
#define SHELL_BG_COLOR 0x78
|
||||
#define SHELL_DOUBLE_COLOR 0xDF // 223
|
||||
#define SHELL_HALF_DAM_COLOR 0x90
|
||||
#define SHELL_CYAN_COLOR 0x72
|
||||
#define SHELL_WHITE_COLOR 0xD7
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Renderer representation of the game entities/lights/particles:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
typedef struct entity_s
|
||||
{
|
||||
struct model_s * model; // opaque type outside refresh
|
||||
float angles[3];
|
||||
|
||||
/*
|
||||
** most recent data
|
||||
*/
|
||||
float origin[3]; // also used as RF_BEAM's "from"
|
||||
int frame; // also used as RF_BEAM's diameter
|
||||
|
||||
/*
|
||||
** previous data for lerping
|
||||
*/
|
||||
float oldorigin[3]; // also used as RF_BEAM's "to"
|
||||
int oldframe;
|
||||
|
||||
/*
|
||||
** misc
|
||||
*/
|
||||
float backlerp; // 0.0 = current, 1.0 = old
|
||||
int skinnum; // also used as RF_BEAM's palette index
|
||||
|
||||
int lightstyle; // for flashing entities
|
||||
float alpha; // ignore if RF_TRANSLUCENT isn't set
|
||||
|
||||
struct image_s * skin; // NULL for inline skin
|
||||
int flags;
|
||||
|
||||
} entity_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t origin;
|
||||
vec3_t color;
|
||||
float intensity;
|
||||
} dlight_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t origin;
|
||||
int color;
|
||||
float alpha;
|
||||
} particle_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float rgb[3]; // 0.0 - 2.0
|
||||
float white; // highest of rgb
|
||||
} lightstyle_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x, y, width, height; // in virtual screen coordinates
|
||||
float fov_x, fov_y;
|
||||
float vieworg[3];
|
||||
float viewangles[3];
|
||||
float blend[4]; // rgba 0-1 full screen blend
|
||||
float time; // time is uesed to auto animate
|
||||
int rdflags; // RDF_UNDERWATER, etc
|
||||
|
||||
byte * areabits; // if not NULL, only areas with set bits will be drawn
|
||||
|
||||
lightstyle_t * lightstyles; // [MAX_LIGHTSTYLES]
|
||||
|
||||
int num_entities;
|
||||
entity_t * entities;
|
||||
|
||||
int num_dlights;
|
||||
dlight_t * dlights;
|
||||
|
||||
int num_particles;
|
||||
particle_t * particles;
|
||||
} refdef_t;
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// These are the functions exported by the refresh module
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// if api_version is different, the dll cannot be used
|
||||
int api_version;
|
||||
|
||||
// called when the library is loaded
|
||||
qboolean (*Init)(void * hinstance, void * wndproc);
|
||||
|
||||
// called before the library is unloaded
|
||||
void (*Shutdown)(void);
|
||||
|
||||
// All data that will be used in a level should be
|
||||
// registered before rendering any frames to prevent disk hits,
|
||||
// but they can still be registered at a later time
|
||||
// if necessary.
|
||||
//
|
||||
// EndRegistration will free any remaining data that wasn't registered.
|
||||
// Any model_s or skin_s pointers from before the BeginRegistration
|
||||
// are no longer valid after EndRegistration.
|
||||
//
|
||||
// Skins and images need to be differentiated, because skins
|
||||
// are flood filled to eliminate mip map edge errors, and pics have
|
||||
// an implicit "pics/" prepended to the name. (a pic name that starts with a
|
||||
// slash will not use the "pics/" prefix or the ".pcx" postfix)
|
||||
void (*BeginRegistration)(const char * map);
|
||||
struct model_s * (*RegisterModel)(const char * name);
|
||||
struct image_s * (*RegisterSkin)(const char * name);
|
||||
struct image_s * (*RegisterPic)(const char * name);
|
||||
void (*SetSky)(const char * name, float rotate, vec3_t axis);
|
||||
void (*EndRegistration)(void);
|
||||
|
||||
// renders a 3D game view.
|
||||
void (*RenderFrame)(refdef_t * fd);
|
||||
|
||||
// 2D overlay drawing:
|
||||
void (*DrawGetPicSize)(int * w, int * h, const char * name); // will return 0 0 if not found
|
||||
void (*DrawPic)(int x, int y, const char * name);
|
||||
void (*DrawStretchPic)(int x, int y, int w, int h, const char * name);
|
||||
void (*DrawChar)(int x, int y, int c);
|
||||
void (*DrawTileClear)(int x, int y, int w, int h, const char * name);
|
||||
void (*DrawFill)(int x, int y, int w, int h, int c);
|
||||
void (*DrawFadeScreen)(void);
|
||||
|
||||
// Draw images for cinematic rendering (which can have a different palette). Note that calls
|
||||
void (*DrawStretchRaw)(int x, int y, int w, int h, int cols, int rows, const byte * data);
|
||||
|
||||
// video mode and refresh state management entry points:
|
||||
void (*CinematicSetPalette)(const unsigned char * palette); // NULL = game palette
|
||||
void (*BeginFrame)(float camera_separation);
|
||||
void (*EndFrame)(void);
|
||||
|
||||
void (*AppActivate)(qboolean activate);
|
||||
|
||||
} refexport_t;
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// These are the functions imported by the refresh module
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void (*Sys_Error)(int err_level, const char * str, ...);
|
||||
void (*Con_Printf)(int print_level, const char * str, ...);
|
||||
|
||||
void (*Cmd_AddCommand)(char * name, void (*cmd)(void));
|
||||
void (*Cmd_RemoveCommand)(char * name);
|
||||
int (*Cmd_Argc)(void);
|
||||
char * (*Cmd_Argv)(int i);
|
||||
void (*Cmd_ExecuteText)(int exec_when, char * text);
|
||||
|
||||
// files will be memory mapped read only
|
||||
// the returned buffer may be part of a larger pak file,
|
||||
// or a discrete file from anywhere in the quake search path
|
||||
// a -1 return means the file does not exist
|
||||
// NULL can be passed for buf to just determine existance
|
||||
int (*FS_LoadFile)(char * name, void ** buf);
|
||||
void (*FS_FreeFile)(void * buf);
|
||||
|
||||
// gamedir will be the current directory that generated
|
||||
// files should be stored to, ie: "f:\quake\id1"
|
||||
char * (*FS_Gamedir)(void);
|
||||
|
||||
cvar_t * (*Cvar_Get)(char * name, char * value, int flags);
|
||||
cvar_t * (*Cvar_Set)(char * name, char * value);
|
||||
void (*Cvar_SetValue)(char * name, float value);
|
||||
|
||||
qboolean (*Vid_GetModeInfo)(int * width, int * height, int mode);
|
||||
void (*Vid_MenuInit)(void);
|
||||
void (*Vid_NewWindow)(int width, int height);
|
||||
|
||||
} refimport_t;
|
||||
|
||||
// This is the only function actually exported at the linker level
|
||||
typedef refexport_t (*GetRefAPI_t)(refimport_t);
|
||||
|
||||
#endif // CL_REF_H
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_SCREEN_H
|
||||
#define CL_SCREEN_H
|
||||
|
||||
// screen.h
|
||||
|
||||
extern vrect_t scr_vrect; // position of render window
|
||||
extern float scr_conlines; // lines of console to display
|
||||
extern float scr_con_current;
|
||||
extern int sb_lines;
|
||||
|
||||
extern cvar_t * scr_viewsize;
|
||||
extern cvar_t * crosshair;
|
||||
|
||||
extern char crosshair_pic[MAX_QPATH];
|
||||
extern int crosshair_width;
|
||||
extern int crosshair_height;
|
||||
|
||||
void SCR_Init(void);
|
||||
void SCR_UpdateScreen(void);
|
||||
void SCR_SizeUp(void);
|
||||
void SCR_SizeDown(void);
|
||||
void SCR_CenterPrint(const char * str);
|
||||
void SCR_BeginLoadingPlaque(void);
|
||||
void SCR_EndLoadingPlaque(void);
|
||||
void SCR_DebugGraph(float value, int color);
|
||||
void SCR_TouchPics(void);
|
||||
void SCR_RunConsole(void);
|
||||
void SCR_AddDirtyPoint(int x, int y);
|
||||
void SCR_DirtyScreen(void);
|
||||
|
||||
//
|
||||
// scr_cin.c
|
||||
//
|
||||
void SCR_PlayCinematic(const char * name);
|
||||
void SCR_RunCinematic(void);
|
||||
void SCR_StopCinematic(void);
|
||||
void SCR_FinishCinematic(void);
|
||||
qboolean SCR_DrawCinematic(void);
|
||||
|
||||
// LAMPERT: Added to QPS2 for testing.
|
||||
qboolean CinematicTest_PlayDirect(const char * filename);
|
||||
qboolean CinematicTest_RunFrame(void);
|
||||
|
||||
#endif // CL_SCREEN_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_SND_LOC_H
|
||||
#define CL_SND_LOC_H
|
||||
|
||||
// snd_loc.h -- private sound functions
|
||||
|
||||
// !!! if this is changed, the asm code must change !!!
|
||||
typedef struct
|
||||
{
|
||||
int left;
|
||||
int right;
|
||||
} portable_samplepair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int length;
|
||||
int loopstart;
|
||||
int speed; // not needed, because converted on load?
|
||||
int width;
|
||||
int stereo;
|
||||
byte data[1]; // variable sized
|
||||
} sfxcache_t;
|
||||
|
||||
typedef struct sfx_s
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
int registration_sequence;
|
||||
sfxcache_t * cache;
|
||||
char * truename;
|
||||
} sfx_t;
|
||||
|
||||
// a playsound_t will be generated by each call to S_StartSound,
|
||||
// when the mixer reaches playsound->begin, the playsound will
|
||||
// be assigned to a channel
|
||||
typedef struct playsound_s
|
||||
{
|
||||
struct playsound_s *prev, *next;
|
||||
sfx_t * sfx;
|
||||
float volume;
|
||||
float attenuation;
|
||||
int entnum;
|
||||
int entchannel;
|
||||
qboolean fixed_origin; // use origin field instead of entnum's origin
|
||||
vec3_t origin;
|
||||
unsigned begin; // begin on this sample
|
||||
} playsound_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int channels;
|
||||
int samples; // mono samples in buffer
|
||||
int submission_chunk; // don't mix less than this #
|
||||
int samplepos; // in mono samples
|
||||
int samplebits;
|
||||
int speed;
|
||||
byte * buffer;
|
||||
} dma_t;
|
||||
|
||||
// !!! if this is changed, the asm code must change !!!
|
||||
typedef struct
|
||||
{
|
||||
sfx_t * sfx; // sfx number
|
||||
int leftvol; // 0-255 volume
|
||||
int rightvol; // 0-255 volume
|
||||
int end; // end time in global paintsamples
|
||||
int pos; // sample position in sfx
|
||||
int looping; // where to loop, -1 = no looping OBSOLETE?
|
||||
int entnum; // to allow overriding a specific sound
|
||||
int entchannel; //
|
||||
vec3_t origin; // only use if fixed_origin is set
|
||||
vec_t dist_mult; // distance multiplier (attenuation/clipK)
|
||||
int master_vol; // 0-255 master volume
|
||||
qboolean fixed_origin; // use origin instead of fetching entnum's origin
|
||||
qboolean autosound; // from an entity->sound, cleared each frame
|
||||
} channel_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int rate;
|
||||
int width;
|
||||
int channels;
|
||||
int loopstart;
|
||||
int samples;
|
||||
int dataofs; // chunk starts this many bytes from file start
|
||||
} wavinfo_t;
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
SYSTEM SPECIFIC FUNCTIONS
|
||||
|
||||
====================================================================
|
||||
*/
|
||||
|
||||
qboolean SNDDMA_Init(void); // initializes cycling through a DMA buffer and returns information on it
|
||||
int SNDDMA_GetDMAPos(void); // gets the current DMA position
|
||||
void SNDDMA_Shutdown(void); // shutdown the DMA xfer.
|
||||
void SNDDMA_BeginPainting(void);
|
||||
void SNDDMA_Submit(void);
|
||||
|
||||
//====================================================================
|
||||
|
||||
#define MAX_CHANNELS 32
|
||||
extern channel_t channels[MAX_CHANNELS];
|
||||
|
||||
extern int paintedtime;
|
||||
extern int s_rawend;
|
||||
extern vec3_t listener_origin;
|
||||
extern vec3_t listener_forward;
|
||||
extern vec3_t listener_right;
|
||||
extern vec3_t listener_up;
|
||||
extern dma_t dma;
|
||||
extern playsound_t s_pendingplays;
|
||||
|
||||
#define MAX_RAW_SAMPLES 8192
|
||||
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
|
||||
|
||||
extern cvar_t * s_volume;
|
||||
extern cvar_t * s_nosound;
|
||||
extern cvar_t * s_loadas8bit;
|
||||
extern cvar_t * s_khz;
|
||||
extern cvar_t * s_show;
|
||||
extern cvar_t * s_mixahead;
|
||||
extern cvar_t * s_testsound;
|
||||
extern cvar_t * s_primary;
|
||||
|
||||
wavinfo_t GetWavinfo(char * name, byte * wav, int wavlength);
|
||||
void S_InitScaletable(void);
|
||||
sfxcache_t * S_LoadSound(sfx_t * s);
|
||||
void S_IssuePlaysound(playsound_t * ps);
|
||||
void S_PaintChannels(int endtime);
|
||||
|
||||
// picks a channel based on priorities, empty slots, number of channels
|
||||
channel_t * S_PickChannel(int entnum, int entchannel);
|
||||
|
||||
// spatializes a channel
|
||||
void S_Spatialize(channel_t * ch);
|
||||
|
||||
#endif // CL_SND_LOC_H
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// snd_mem.c: sound caching
|
||||
|
||||
#include "client.h"
|
||||
#include "snd_loc.h"
|
||||
|
||||
int cache_full_cycle;
|
||||
|
||||
byte * S_Alloc(int size);
|
||||
|
||||
/*
|
||||
================
|
||||
ResampleSfx
|
||||
================
|
||||
*/
|
||||
void ResampleSfx(sfx_t * sfx, int inrate, int inwidth, byte * data)
|
||||
{
|
||||
int outcount;
|
||||
int srcsample;
|
||||
float stepscale;
|
||||
int i;
|
||||
int sample, samplefrac, fracstep;
|
||||
sfxcache_t * sc;
|
||||
|
||||
sc = sfx->cache;
|
||||
if (!sc)
|
||||
return;
|
||||
|
||||
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
|
||||
|
||||
outcount = sc->length / stepscale;
|
||||
sc->length = outcount;
|
||||
if (sc->loopstart != -1)
|
||||
sc->loopstart = sc->loopstart / stepscale;
|
||||
|
||||
sc->speed = dma.speed;
|
||||
if (s_loadas8bit->value)
|
||||
sc->width = 1;
|
||||
else
|
||||
sc->width = inwidth;
|
||||
sc->stereo = 0;
|
||||
|
||||
// resample / decimate to the current source rate
|
||||
|
||||
if (stepscale == 1 && inwidth == 1 && sc->width == 1)
|
||||
{
|
||||
// fast special case
|
||||
for (i = 0; i < outcount; i++)
|
||||
((signed char *)sc->data)[i] = (int)((unsigned char)(data[i]) - 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
// general case
|
||||
samplefrac = 0;
|
||||
fracstep = stepscale * 256;
|
||||
for (i = 0; i < outcount; i++)
|
||||
{
|
||||
srcsample = samplefrac >> 8;
|
||||
samplefrac += fracstep;
|
||||
if (inwidth == 2)
|
||||
sample = LittleShort(((short *)data)[srcsample]);
|
||||
else
|
||||
sample = (int)((unsigned char)(data[srcsample]) - 128) << 8;
|
||||
if (sc->width == 2)
|
||||
((short *)sc->data)[i] = sample;
|
||||
else
|
||||
((signed char *)sc->data)[i] = sample >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
S_LoadSound
|
||||
==============
|
||||
*/
|
||||
sfxcache_t * S_LoadSound(sfx_t * s)
|
||||
{
|
||||
char namebuffer[MAX_QPATH];
|
||||
byte * data;
|
||||
wavinfo_t info;
|
||||
int len;
|
||||
float stepscale;
|
||||
sfxcache_t * sc;
|
||||
int size;
|
||||
char * name;
|
||||
|
||||
if (s->name[0] == '*')
|
||||
return NULL;
|
||||
|
||||
// see if still in memory
|
||||
sc = s->cache;
|
||||
if (sc)
|
||||
return sc;
|
||||
|
||||
//Com_Printf ("S_LoadSound: %x\n", (int)stackbuf);
|
||||
// load it in
|
||||
if (s->truename)
|
||||
name = s->truename;
|
||||
else
|
||||
name = s->name;
|
||||
|
||||
if (name[0] == '#')
|
||||
strcpy(namebuffer, &name[1]);
|
||||
else
|
||||
Com_sprintf(namebuffer, sizeof(namebuffer), "sound/%s", name);
|
||||
|
||||
// Com_Printf ("loading %s\n",namebuffer);
|
||||
|
||||
size = FS_LoadFile(namebuffer, (void **)&data);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
Com_DPrintf("Couldn't load %s\n", namebuffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = GetWavinfo(s->name, data, size);
|
||||
if (info.channels != 1)
|
||||
{
|
||||
Com_Printf("%s is a stereo sample\n", s->name);
|
||||
FS_FreeFile(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stepscale = (float)info.rate / dma.speed;
|
||||
len = info.samples / stepscale;
|
||||
|
||||
len = len * info.width * info.channels;
|
||||
|
||||
sc = s->cache = Z_Malloc(len + sizeof(sfxcache_t));
|
||||
if (!sc)
|
||||
{
|
||||
FS_FreeFile(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sc->length = info.samples;
|
||||
sc->loopstart = info.loopstart;
|
||||
sc->speed = info.rate;
|
||||
sc->width = info.width;
|
||||
sc->stereo = info.channels;
|
||||
|
||||
ResampleSfx(s, sc->speed, sc->width, data + info.dataofs);
|
||||
|
||||
FS_FreeFile(data);
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
WAV loading
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
byte * data_p;
|
||||
byte * iff_end;
|
||||
byte * last_chunk;
|
||||
byte * iff_data;
|
||||
int iff_chunk_len;
|
||||
|
||||
short GetLittleShort(void)
|
||||
{
|
||||
short val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p + 1) << 8);
|
||||
data_p += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
int GetLittleLong(void)
|
||||
{
|
||||
int val = 0;
|
||||
val = *data_p;
|
||||
val = val + (*(data_p + 1) << 8);
|
||||
val = val + (*(data_p + 2) << 16);
|
||||
val = val + (*(data_p + 3) << 24);
|
||||
data_p += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
void FindNextChunk(char * name)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
data_p = last_chunk;
|
||||
|
||||
if (data_p >= iff_end)
|
||||
{ // didn't find the chunk
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
iff_chunk_len = GetLittleLong();
|
||||
if (iff_chunk_len < 0)
|
||||
{
|
||||
data_p = NULL;
|
||||
return;
|
||||
}
|
||||
// if (iff_chunk_len > 1024*1024)
|
||||
// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
|
||||
data_p -= 8;
|
||||
last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1);
|
||||
if (!strncmp(data_p, name, 4))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FindChunk(char * name)
|
||||
{
|
||||
last_chunk = iff_data;
|
||||
FindNextChunk(name);
|
||||
}
|
||||
|
||||
void DumpChunks(void)
|
||||
{
|
||||
char str[5];
|
||||
|
||||
str[4] = 0;
|
||||
data_p = iff_data;
|
||||
do
|
||||
{
|
||||
memcpy(str, data_p, 4);
|
||||
data_p += 4;
|
||||
iff_chunk_len = GetLittleLong();
|
||||
Com_Printf("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
|
||||
data_p += (iff_chunk_len + 1) & ~1;
|
||||
} while (data_p < iff_end);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
GetWavinfo
|
||||
============
|
||||
*/
|
||||
wavinfo_t GetWavinfo(char * name, byte * wav, int wavlength)
|
||||
{
|
||||
wavinfo_t info;
|
||||
int i;
|
||||
int format;
|
||||
int samples;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
if (!wav)
|
||||
return info;
|
||||
|
||||
iff_data = wav;
|
||||
iff_end = wav + wavlength;
|
||||
|
||||
// find "RIFF" chunk
|
||||
FindChunk("RIFF");
|
||||
if (!(data_p && !strncmp(data_p + 8, "WAVE", 4)))
|
||||
{
|
||||
Com_Printf("Missing RIFF/WAVE chunks\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
// get "fmt " chunk
|
||||
iff_data = data_p + 12;
|
||||
// DumpChunks ();
|
||||
|
||||
FindChunk("fmt ");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing fmt chunk\n");
|
||||
return info;
|
||||
}
|
||||
data_p += 8;
|
||||
format = GetLittleShort();
|
||||
if (format != 1)
|
||||
{
|
||||
Com_Printf("Microsoft PCM format only\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
info.channels = GetLittleShort();
|
||||
info.rate = GetLittleLong();
|
||||
data_p += 4 + 2;
|
||||
info.width = GetLittleShort() / 8;
|
||||
|
||||
// get cue chunk
|
||||
FindChunk("cue ");
|
||||
if (data_p)
|
||||
{
|
||||
data_p += 32;
|
||||
info.loopstart = GetLittleLong();
|
||||
// Com_Printf("loopstart=%d\n", sfx->loopstart);
|
||||
|
||||
// if the next chunk is a LIST chunk, look for a cue length marker
|
||||
FindNextChunk("LIST");
|
||||
if (data_p)
|
||||
{
|
||||
if (!strncmp(data_p + 28, "mark", 4))
|
||||
{ // this is not a proper parse, but it works with cooledit...
|
||||
data_p += 24;
|
||||
i = GetLittleLong(); // samples in loop
|
||||
info.samples = info.loopstart + i;
|
||||
// Com_Printf("looped length: %i\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
info.loopstart = -1;
|
||||
|
||||
// find data chunk
|
||||
FindChunk("data");
|
||||
if (!data_p)
|
||||
{
|
||||
Com_Printf("Missing data chunk\n");
|
||||
return info;
|
||||
}
|
||||
|
||||
data_p += 4;
|
||||
samples = GetLittleLong() / info.width;
|
||||
|
||||
if (info.samples)
|
||||
{
|
||||
if (samples < info.samples)
|
||||
Com_Error(ERR_DROP, "Sound %s has a bad loop length", name);
|
||||
}
|
||||
else
|
||||
info.samples = samples;
|
||||
|
||||
info.dataofs = data_p - wav;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// snd_mix.c -- portable code to mix sounds for snd_dma.c
|
||||
|
||||
#include "client.h"
|
||||
#include "snd_loc.h"
|
||||
|
||||
#define PAINTBUFFER_SIZE 2048
|
||||
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
|
||||
int snd_scaletable[32][256];
|
||||
int *snd_p, snd_linear_count, snd_vol;
|
||||
short * snd_out;
|
||||
|
||||
void S_WriteLinearBlastStereo16(void);
|
||||
|
||||
#if !(defined __linux__ && defined __i386__)
|
||||
#if !id386
|
||||
|
||||
void S_WriteLinearBlastStereo16(void)
|
||||
{
|
||||
int i;
|
||||
int val;
|
||||
|
||||
for (i = 0; i < snd_linear_count; i += 2)
|
||||
{
|
||||
val = snd_p[i] >> 8;
|
||||
if (val > 0x7fff)
|
||||
snd_out[i] = 0x7fff;
|
||||
else if (val < (short)0x8000)
|
||||
snd_out[i] = (short)0x8000;
|
||||
else
|
||||
snd_out[i] = val;
|
||||
|
||||
val = snd_p[i + 1] >> 8;
|
||||
if (val > 0x7fff)
|
||||
snd_out[i + 1] = 0x7fff;
|
||||
else if (val < (short)0x8000)
|
||||
snd_out[i + 1] = (short)0x8000;
|
||||
else
|
||||
snd_out[i + 1] = val;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
__declspec(naked) void S_WriteLinearBlastStereo16(void)
|
||||
{
|
||||
__asm {
|
||||
push edi
|
||||
push ebx
|
||||
mov ecx,ds:dword ptr[snd_linear_count]
|
||||
mov ebx,ds:dword ptr[snd_p]
|
||||
mov edi,ds:dword ptr[snd_out]
|
||||
LWLBLoopTop:
|
||||
mov eax,ds:dword ptr[-8+ebx+ecx*4]
|
||||
sar eax,8
|
||||
cmp eax,07FFFh
|
||||
jg LClampHigh
|
||||
cmp eax,0FFFF8000h
|
||||
jnl LClampDone
|
||||
mov eax,0FFFF8000h
|
||||
jmp LClampDone
|
||||
LClampHigh:
|
||||
mov eax,07FFFh
|
||||
LClampDone:
|
||||
mov edx,ds:dword ptr[-4+ebx+ecx*4]
|
||||
sar edx,8
|
||||
cmp edx,07FFFh
|
||||
jg LClampHigh2
|
||||
cmp edx,0FFFF8000h
|
||||
jnl LClampDone2
|
||||
mov edx,0FFFF8000h
|
||||
jmp LClampDone2
|
||||
LClampHigh2:
|
||||
mov edx,07FFFh
|
||||
LClampDone2:
|
||||
shl edx,16
|
||||
and eax,0FFFFh
|
||||
or edx,eax
|
||||
mov ds:dword ptr[-4+edi+ecx*2],edx
|
||||
sub ecx,2
|
||||
jnz LWLBLoopTop
|
||||
pop ebx
|
||||
pop edi
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void S_TransferStereo16(unsigned long * pbuf, int endtime)
|
||||
{
|
||||
int lpos;
|
||||
int lpaintedtime;
|
||||
|
||||
snd_p = (int *)paintbuffer;
|
||||
lpaintedtime = paintedtime;
|
||||
|
||||
while (lpaintedtime < endtime)
|
||||
{
|
||||
// handle recirculating buffer issues
|
||||
lpos = lpaintedtime & ((dma.samples >> 1) - 1);
|
||||
|
||||
snd_out = (short *)pbuf + (lpos << 1);
|
||||
|
||||
snd_linear_count = (dma.samples >> 1) - lpos;
|
||||
if (lpaintedtime + snd_linear_count > endtime)
|
||||
snd_linear_count = endtime - lpaintedtime;
|
||||
|
||||
snd_linear_count <<= 1;
|
||||
|
||||
// write a linear blast of samples
|
||||
S_WriteLinearBlastStereo16();
|
||||
|
||||
snd_p += snd_linear_count;
|
||||
lpaintedtime += (snd_linear_count >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
S_TransferPaintBuffer
|
||||
|
||||
===================
|
||||
*/
|
||||
void S_TransferPaintBuffer(int endtime)
|
||||
{
|
||||
int out_idx;
|
||||
int count;
|
||||
int out_mask;
|
||||
int * p;
|
||||
int step;
|
||||
int val;
|
||||
unsigned long * pbuf;
|
||||
|
||||
pbuf = (unsigned long *)dma.buffer;
|
||||
|
||||
if (s_testsound->value)
|
||||
{
|
||||
int i;
|
||||
int count;
|
||||
|
||||
// write a fixed sine wave
|
||||
count = (endtime - paintedtime);
|
||||
for (i = 0; i < count; i++)
|
||||
paintbuffer[i].left = paintbuffer[i].right = sin((paintedtime + i) * 0.1) * 20000 * 256;
|
||||
}
|
||||
|
||||
if (dma.samplebits == 16 && dma.channels == 2)
|
||||
{ // optimized case
|
||||
S_TransferStereo16(pbuf, endtime);
|
||||
}
|
||||
else
|
||||
{ // general case
|
||||
p = (int *)paintbuffer;
|
||||
count = (endtime - paintedtime) * dma.channels;
|
||||
out_mask = dma.samples - 1;
|
||||
out_idx = paintedtime * dma.channels & out_mask;
|
||||
step = 3 - dma.channels;
|
||||
|
||||
if (dma.samplebits == 16)
|
||||
{
|
||||
short * out = (short *)pbuf;
|
||||
while (count--)
|
||||
{
|
||||
val = *p >> 8;
|
||||
p += step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < (short)0x8000)
|
||||
val = (short)0x8000;
|
||||
out[out_idx] = val;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
else if (dma.samplebits == 8)
|
||||
{
|
||||
unsigned char * out = (unsigned char *)pbuf;
|
||||
while (count--)
|
||||
{
|
||||
val = *p >> 8;
|
||||
p += step;
|
||||
if (val > 0x7fff)
|
||||
val = 0x7fff;
|
||||
else if (val < (short)0x8000)
|
||||
val = (short)0x8000;
|
||||
out[out_idx] = (val >> 8) + 128;
|
||||
out_idx = (out_idx + 1) & out_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CHANNEL MIXING
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
void S_PaintChannelFrom8(channel_t * ch, sfxcache_t * sc, int endtime, int offset);
|
||||
void S_PaintChannelFrom16(channel_t * ch, sfxcache_t * sc, int endtime, int offset);
|
||||
|
||||
void S_PaintChannels(int endtime)
|
||||
{
|
||||
int i;
|
||||
int end;
|
||||
channel_t * ch;
|
||||
sfxcache_t * sc;
|
||||
int ltime, count;
|
||||
playsound_t * ps;
|
||||
|
||||
snd_vol = s_volume->value * 256;
|
||||
|
||||
//Com_Printf ("%i to %i\n", paintedtime, endtime);
|
||||
while (paintedtime < endtime)
|
||||
{
|
||||
// if paintbuffer is smaller than DMA buffer
|
||||
end = endtime;
|
||||
if (endtime - paintedtime > PAINTBUFFER_SIZE)
|
||||
end = paintedtime + PAINTBUFFER_SIZE;
|
||||
|
||||
// start any playsounds
|
||||
while (1)
|
||||
{
|
||||
ps = s_pendingplays.next;
|
||||
if (ps == &s_pendingplays)
|
||||
break; // no more pending sounds
|
||||
if (ps->begin <= paintedtime)
|
||||
{
|
||||
S_IssuePlaysound(ps);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ps->begin < end)
|
||||
end = ps->begin; // stop here
|
||||
break;
|
||||
}
|
||||
|
||||
// clear the paint buffer
|
||||
if (s_rawend < paintedtime)
|
||||
{
|
||||
// Com_Printf ("clear\n");
|
||||
memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t));
|
||||
}
|
||||
else
|
||||
{ // copy from the streaming sound source
|
||||
int s;
|
||||
int stop;
|
||||
|
||||
stop = (end < s_rawend) ? end : s_rawend;
|
||||
|
||||
for (i = paintedtime; i < stop; i++)
|
||||
{
|
||||
s = i & (MAX_RAW_SAMPLES - 1);
|
||||
paintbuffer[i - paintedtime] = s_rawsamples[s];
|
||||
}
|
||||
// if (i != end)
|
||||
// Com_Printf ("partial stream\n");
|
||||
// else
|
||||
// Com_Printf ("full stream\n");
|
||||
for (; i < end; i++)
|
||||
{
|
||||
paintbuffer[i - paintedtime].left =
|
||||
paintbuffer[i - paintedtime].right = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// paint in the channels.
|
||||
ch = channels;
|
||||
for (i = 0; i < MAX_CHANNELS; i++, ch++)
|
||||
{
|
||||
ltime = paintedtime;
|
||||
|
||||
while (ltime < end)
|
||||
{
|
||||
if (!ch->sfx || (!ch->leftvol && !ch->rightvol))
|
||||
break;
|
||||
|
||||
// max painting is to the end of the buffer
|
||||
count = end - ltime;
|
||||
|
||||
// might be stopped by running out of data
|
||||
if (ch->end - ltime < count)
|
||||
count = ch->end - ltime;
|
||||
|
||||
sc = S_LoadSound(ch->sfx);
|
||||
if (!sc)
|
||||
break;
|
||||
|
||||
if (count > 0 && ch->sfx)
|
||||
{
|
||||
if (sc->width == 1) // FIXME; 8 bit asm is wrong now
|
||||
S_PaintChannelFrom8(ch, sc, count, ltime - paintedtime);
|
||||
else
|
||||
S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime);
|
||||
|
||||
ltime += count;
|
||||
}
|
||||
|
||||
// if at end of loop, restart
|
||||
if (ltime >= ch->end)
|
||||
{
|
||||
if (ch->autosound)
|
||||
{ // autolooping sounds always go back to start
|
||||
ch->pos = 0;
|
||||
ch->end = ltime + sc->length;
|
||||
}
|
||||
else if (sc->loopstart >= 0)
|
||||
{
|
||||
ch->pos = sc->loopstart;
|
||||
ch->end = ltime + sc->length - ch->pos;
|
||||
}
|
||||
else
|
||||
{ // channel just stopped
|
||||
ch->sfx = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transfer out according to DMA format
|
||||
S_TransferPaintBuffer(end);
|
||||
paintedtime = end;
|
||||
}
|
||||
}
|
||||
|
||||
void S_InitScaletable(void)
|
||||
{
|
||||
int i, j;
|
||||
int scale;
|
||||
|
||||
s_volume->modified = false;
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
scale = i * 8 * 256 * s_volume->value;
|
||||
for (j = 0; j < 256; j++)
|
||||
snd_scaletable[i][j] = ((signed char)j) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
#if !(defined __linux__ && defined __i386__)
|
||||
#if !id386
|
||||
|
||||
void S_PaintChannelFrom8(channel_t * ch, sfxcache_t * sc, int count, int offset)
|
||||
{
|
||||
int data;
|
||||
int *lscale, *rscale;
|
||||
unsigned char * sfx;
|
||||
int i;
|
||||
portable_samplepair_t * samp;
|
||||
|
||||
if (ch->leftvol > 255)
|
||||
ch->leftvol = 255;
|
||||
if (ch->rightvol > 255)
|
||||
ch->rightvol = 255;
|
||||
|
||||
lscale = snd_scaletable[ch->leftvol >> 11];
|
||||
rscale = snd_scaletable[ch->rightvol >> 11];
|
||||
sfx = (signed char *)sc->data + ch->pos;
|
||||
|
||||
samp = &paintbuffer[offset];
|
||||
|
||||
for (i = 0; i < count; i++, samp++)
|
||||
{
|
||||
data = sfx[i];
|
||||
samp->left += lscale[data];
|
||||
samp->right += rscale[data];
|
||||
}
|
||||
|
||||
ch->pos += count;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
__declspec(naked) void S_PaintChannelFrom8(channel_t * ch, sfxcache_t * sc, int count, int offset)
|
||||
{
|
||||
__asm {
|
||||
push esi
|
||||
push edi
|
||||
push ebx
|
||||
push ebp
|
||||
mov ebx,ds:dword ptr[4+16+esp]
|
||||
mov esi,ds:dword ptr[8+16+esp]
|
||||
mov eax,ds:dword ptr[4+ebx]
|
||||
mov edx,ds:dword ptr[8+ebx]
|
||||
cmp eax,255
|
||||
jna LLeftSet
|
||||
mov eax,255
|
||||
LLeftSet:
|
||||
cmp edx,255
|
||||
jna LRightSet
|
||||
mov edx,255
|
||||
LRightSet:
|
||||
and eax,0F8h
|
||||
add esi,20
|
||||
and edx,0F8h
|
||||
mov edi,ds:dword ptr[16+ebx]
|
||||
mov ecx,ds:dword ptr[12+16+esp]
|
||||
add esi,edi
|
||||
shl eax,7
|
||||
add edi,ecx
|
||||
shl edx,7
|
||||
mov ds:dword ptr[16+ebx],edi
|
||||
add eax,offset snd_scaletable
|
||||
add edx,offset snd_scaletable
|
||||
sub ebx,ebx
|
||||
mov bl,ds:byte ptr[-1+esi+ecx*1]
|
||||
test ecx,1
|
||||
jz LMix8Loop
|
||||
mov edi,ds:dword ptr[eax+ebx*4]
|
||||
mov ebp,ds:dword ptr[edx+ebx*4]
|
||||
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
|
||||
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
|
||||
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
|
||||
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
|
||||
mov bl,ds:byte ptr[-2+esi+ecx*1]
|
||||
dec ecx
|
||||
jz LDone
|
||||
LMix8Loop:
|
||||
mov edi,ds:dword ptr[eax+ebx*4]
|
||||
mov ebp,ds:dword ptr[edx+ebx*4]
|
||||
add edi,ds:dword ptr[paintbuffer+0-8+ecx*8]
|
||||
add ebp,ds:dword ptr[paintbuffer+4-8+ecx*8]
|
||||
mov bl,ds:byte ptr[-2+esi+ecx*1]
|
||||
mov ds:dword ptr[paintbuffer+0-8+ecx*8],edi
|
||||
mov ds:dword ptr[paintbuffer+4-8+ecx*8],ebp
|
||||
mov edi,ds:dword ptr[eax+ebx*4]
|
||||
mov ebp,ds:dword ptr[edx+ebx*4]
|
||||
mov bl,ds:byte ptr[-3+esi+ecx*1]
|
||||
add edi,ds:dword ptr[paintbuffer+0-8*2+ecx*8]
|
||||
add ebp,ds:dword ptr[paintbuffer+4-8*2+ecx*8]
|
||||
mov ds:dword ptr[paintbuffer+0-8*2+ecx*8],edi
|
||||
mov ds:dword ptr[paintbuffer+4-8*2+ecx*8],ebp
|
||||
sub ecx,2
|
||||
jnz LMix8Loop
|
||||
LDone:
|
||||
pop ebp
|
||||
pop ebx
|
||||
pop edi
|
||||
pop esi
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void S_PaintChannelFrom16(channel_t * ch, sfxcache_t * sc, int count, int offset)
|
||||
{
|
||||
int data;
|
||||
int left, right;
|
||||
int leftvol, rightvol;
|
||||
signed short * sfx;
|
||||
int i;
|
||||
portable_samplepair_t * samp;
|
||||
|
||||
leftvol = ch->leftvol * snd_vol;
|
||||
rightvol = ch->rightvol * snd_vol;
|
||||
sfx = (signed short *)sc->data + ch->pos;
|
||||
|
||||
samp = &paintbuffer[offset];
|
||||
for (i = 0; i < count; i++, samp++)
|
||||
{
|
||||
data = sfx[i];
|
||||
left = (data * leftvol) >> 8;
|
||||
right = (data * rightvol) >> 8;
|
||||
samp->left += left;
|
||||
samp->right += right;
|
||||
}
|
||||
|
||||
ch->pos += count;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_SOUND_H
|
||||
#define CL_SOUND_H
|
||||
|
||||
struct sfx_s;
|
||||
|
||||
void S_Init(void);
|
||||
void S_Shutdown(void);
|
||||
|
||||
// if origin is NULL, the sound will be dynamically sourced from the entity
|
||||
void S_StartSound(vec3_t origin, int entnum, int entchannel, struct sfx_s * sfx, float fvol, float attenuation, float timeofs);
|
||||
void S_StartLocalSound(char * s);
|
||||
|
||||
void S_RawSamples(int samples, int rate, int width, int channels, byte * data);
|
||||
|
||||
void S_StopAllSounds(void);
|
||||
void S_Update(vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up);
|
||||
|
||||
void S_Activate(qboolean active);
|
||||
|
||||
void S_BeginRegistration(void);
|
||||
struct sfx_s * S_RegisterSound(char * sample);
|
||||
void S_EndRegistration(void);
|
||||
|
||||
struct sfx_s * S_FindName(char * name, qboolean create);
|
||||
|
||||
// the sound code makes callbacks to the client for entitiy position
|
||||
// information, so entities can be dynamically re-spatialized
|
||||
void CL_GetEntitySoundOrigin(int ent, vec3_t org);
|
||||
|
||||
#endif // CL_SOUND_H
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 CL_VID_H
|
||||
#define CL_VID_H
|
||||
|
||||
// vid.h -- video driver defs
|
||||
|
||||
typedef struct vrect_s
|
||||
{
|
||||
int x, y;
|
||||
int width, height;
|
||||
} vrect_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
} viddef_t;
|
||||
|
||||
extern viddef_t viddef; // global video state
|
||||
|
||||
// Video module initialisation etc:
|
||||
void VID_Init(void);
|
||||
void VID_Shutdown(void);
|
||||
void VID_MenuInit(void);
|
||||
void VID_MenuDraw(void);
|
||||
const char * VID_MenuKey(int);
|
||||
|
||||
#endif // CL_VID_H
|
||||
@@ -0,0 +1,889 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "common/q_common.h"
|
||||
|
||||
//
|
||||
// Quake script command processing module
|
||||
//
|
||||
|
||||
void Cmd_ForwardToServer(void);
|
||||
|
||||
enum
|
||||
{
|
||||
ALIAS_LOOP_COUNT = 16,
|
||||
MAX_ALIAS_NAME = 32
|
||||
};
|
||||
|
||||
typedef struct cmdalias_s
|
||||
{
|
||||
struct cmdalias_s * next;
|
||||
char name[MAX_ALIAS_NAME];
|
||||
char * value;
|
||||
} cmdalias_t;
|
||||
|
||||
cmdalias_t * cmd_alias;
|
||||
qboolean cmd_wait;
|
||||
int alias_count; // for detecting runaway loops
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Wait_f
|
||||
|
||||
Causes execution of the remainder of the command buffer to be delayed until
|
||||
next frame. This allows commands like:
|
||||
bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
|
||||
============
|
||||
*/
|
||||
void Cmd_Wait_f(void)
|
||||
{
|
||||
cmd_wait = true;
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
COMMAND BUFFER
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
sizebuf_t cmd_text;
|
||||
byte cmd_text_buf[8192];
|
||||
byte defer_text_buf[8192];
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_Init
|
||||
============
|
||||
*/
|
||||
void Cbuf_Init(void)
|
||||
{
|
||||
SZ_Init(&cmd_text, cmd_text_buf, sizeof(cmd_text_buf));
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_AddText
|
||||
|
||||
Adds command text at the end of the buffer
|
||||
============
|
||||
*/
|
||||
void Cbuf_AddText(char * text)
|
||||
{
|
||||
int l;
|
||||
|
||||
l = strlen(text);
|
||||
|
||||
if (cmd_text.cursize + l >= cmd_text.maxsize)
|
||||
{
|
||||
Com_Printf("Cbuf_AddText: overflow\n");
|
||||
return;
|
||||
}
|
||||
SZ_Write(&cmd_text, text, strlen(text));
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_InsertText
|
||||
|
||||
Adds command text immediately after the current command
|
||||
Adds a \n to the text
|
||||
FIXME: actually change the command buffer to do less copying
|
||||
============
|
||||
*/
|
||||
void Cbuf_InsertText(char * text)
|
||||
{
|
||||
char * temp;
|
||||
int templen;
|
||||
|
||||
// copy off any commands still remaining in the exec buffer
|
||||
templen = cmd_text.cursize;
|
||||
if (templen)
|
||||
{
|
||||
temp = Z_Malloc(templen);
|
||||
memcpy(temp, cmd_text.data, templen);
|
||||
SZ_Clear(&cmd_text);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = NULL; // shut up compiler
|
||||
}
|
||||
|
||||
// add the entire text of the file
|
||||
Cbuf_AddText(text);
|
||||
|
||||
// add the copied off data
|
||||
if (templen)
|
||||
{
|
||||
SZ_Write(&cmd_text, temp, templen);
|
||||
Z_Free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_CopyToDefer
|
||||
============
|
||||
*/
|
||||
void Cbuf_CopyToDefer(void)
|
||||
{
|
||||
memcpy(defer_text_buf, cmd_text_buf, cmd_text.cursize);
|
||||
defer_text_buf[cmd_text.cursize] = 0;
|
||||
cmd_text.cursize = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_InsertFromDefer
|
||||
============
|
||||
*/
|
||||
void Cbuf_InsertFromDefer(void)
|
||||
{
|
||||
Cbuf_InsertText(defer_text_buf);
|
||||
defer_text_buf[0] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_ExecuteText
|
||||
============
|
||||
*/
|
||||
void Cbuf_ExecuteText(int exec_when, char * text)
|
||||
{
|
||||
switch (exec_when)
|
||||
{
|
||||
case EXEC_NOW:
|
||||
Cmd_ExecuteString(text);
|
||||
break;
|
||||
case EXEC_INSERT:
|
||||
Cbuf_InsertText(text);
|
||||
break;
|
||||
case EXEC_APPEND:
|
||||
Cbuf_AddText(text);
|
||||
break;
|
||||
default:
|
||||
Com_Error(ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cbuf_Execute
|
||||
============
|
||||
*/
|
||||
void Cbuf_Execute(void)
|
||||
{
|
||||
int i;
|
||||
char * text;
|
||||
char line[1024];
|
||||
int quotes;
|
||||
|
||||
alias_count = 0; // don't allow infinite alias loops
|
||||
|
||||
while (cmd_text.cursize)
|
||||
{
|
||||
// find a \n or ; line break
|
||||
text = (char *)cmd_text.data;
|
||||
|
||||
quotes = 0;
|
||||
for (i = 0; i < cmd_text.cursize; i++)
|
||||
{
|
||||
if (text[i] == '"')
|
||||
quotes++;
|
||||
if (!(quotes & 1) && text[i] == ';')
|
||||
break; // don't break if inside a quoted string
|
||||
if (text[i] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(line, text, i);
|
||||
line[i] = 0;
|
||||
|
||||
// delete the text from the command buffer and move remaining commands down
|
||||
// this is necessary because commands (exec, alias) can insert data at the
|
||||
// beginning of the text buffer
|
||||
|
||||
if (i == cmd_text.cursize)
|
||||
{
|
||||
cmd_text.cursize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
cmd_text.cursize -= i;
|
||||
memmove(text, text + i, cmd_text.cursize);
|
||||
}
|
||||
|
||||
// execute the command line
|
||||
Cmd_ExecuteString(line);
|
||||
|
||||
if (cmd_wait)
|
||||
{
|
||||
// skip out while text still remains in buffer, leaving it
|
||||
// for next frame
|
||||
cmd_wait = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Cbuf_AddEarlyCommands
|
||||
|
||||
Adds command line parameters as script statements
|
||||
Commands lead with a +, and continue until another +
|
||||
|
||||
Set commands are added early, so they are guaranteed to be set before
|
||||
the client and server initialize for the first time.
|
||||
|
||||
Other commands are added late, after all initialization is complete.
|
||||
===============
|
||||
*/
|
||||
void Cbuf_AddEarlyCommands(qboolean clear)
|
||||
{
|
||||
int i;
|
||||
char * s;
|
||||
|
||||
for (i = 0; i < COM_Argc(); i++)
|
||||
{
|
||||
s = COM_Argv(i);
|
||||
if (strcmp(s, "+set"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Cbuf_AddText(va("set %s %s\n", COM_Argv(i + 1), COM_Argv(i + 2)));
|
||||
|
||||
if (clear)
|
||||
{
|
||||
COM_ClearArgv(i);
|
||||
COM_ClearArgv(i + 1);
|
||||
COM_ClearArgv(i + 2);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cbuf_AddLateCommands
|
||||
|
||||
Adds command line parameters as script statements
|
||||
Commands lead with a + and continue until another + or -
|
||||
quake +vid_ref gl +map amlev1
|
||||
|
||||
Returns true if any late commands were added, which
|
||||
will keep the demoloop from immediately starting
|
||||
=================
|
||||
*/
|
||||
qboolean Cbuf_AddLateCommands(void)
|
||||
{
|
||||
int i, j;
|
||||
int s;
|
||||
char *text, *build, c;
|
||||
int argc;
|
||||
qboolean ret;
|
||||
|
||||
// build the combined string to parse from
|
||||
s = 0;
|
||||
argc = COM_Argc();
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
s += strlen(COM_Argv(i)) + 1;
|
||||
}
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
text = Z_Malloc(s + 1);
|
||||
text[0] = 0;
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
strcat(text, COM_Argv(i));
|
||||
if (i != argc - 1)
|
||||
strcat(text, " ");
|
||||
}
|
||||
|
||||
// pull out the commands
|
||||
build = Z_Malloc(s + 1);
|
||||
build[0] = 0;
|
||||
|
||||
for (i = 0; i < s - 1; i++)
|
||||
{
|
||||
if (text[i] == '+')
|
||||
{
|
||||
i++;
|
||||
|
||||
for (j = i; (text[j] != '+') && (text[j] != '-') && (text[j] != 0); j++)
|
||||
;
|
||||
|
||||
c = text[j];
|
||||
text[j] = 0;
|
||||
|
||||
strcat(build, text + i);
|
||||
strcat(build, "\n");
|
||||
text[j] = c;
|
||||
i = j - 1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = (build[0] != 0);
|
||||
if (ret)
|
||||
Cbuf_AddText(build);
|
||||
|
||||
Z_Free(text);
|
||||
Z_Free(build);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
SCRIPT COMMANDS
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
===============
|
||||
Cmd_Exec_f
|
||||
===============
|
||||
*/
|
||||
void Cmd_Exec_f(void)
|
||||
{
|
||||
char *f, *f2;
|
||||
int len;
|
||||
|
||||
if (Cmd_Argc() != 2)
|
||||
{
|
||||
Com_Printf("exec <filename> : execute a script file\n");
|
||||
return;
|
||||
}
|
||||
|
||||
len = FS_LoadFile(Cmd_Argv(1), (void **)&f);
|
||||
if (!f)
|
||||
{
|
||||
Com_Printf("couldn't exec %s\n", Cmd_Argv(1));
|
||||
return;
|
||||
}
|
||||
Com_Printf("execing %s\n", Cmd_Argv(1));
|
||||
|
||||
// the file doesn't have a trailing 0, so we need to copy it off
|
||||
f2 = Z_Malloc(len + 1);
|
||||
memcpy(f2, f, len);
|
||||
f2[len] = 0;
|
||||
|
||||
Cbuf_InsertText(f2);
|
||||
|
||||
Z_Free(f2);
|
||||
FS_FreeFile(f);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Cmd_Echo_f
|
||||
|
||||
Just prints the rest of the line to the console
|
||||
===============
|
||||
*/
|
||||
void Cmd_Echo_f(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < Cmd_Argc(); i++)
|
||||
{
|
||||
Com_Printf("%s ", Cmd_Argv(i));
|
||||
}
|
||||
Com_Printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Cmd_Alias_f
|
||||
|
||||
Creates a new command that executes a command string (possibly ; seperated)
|
||||
===============
|
||||
*/
|
||||
void Cmd_Alias_f(void)
|
||||
{
|
||||
cmdalias_t * a;
|
||||
char cmd[1024];
|
||||
int i, c;
|
||||
char * s;
|
||||
|
||||
if (Cmd_Argc() == 1)
|
||||
{
|
||||
Com_Printf("Current alias commands:\n");
|
||||
for (a = cmd_alias; a; a = a->next)
|
||||
Com_Printf("%s : %s\n", a->name, a->value);
|
||||
return;
|
||||
}
|
||||
|
||||
s = Cmd_Argv(1);
|
||||
if (strlen(s) >= MAX_ALIAS_NAME)
|
||||
{
|
||||
Com_Printf("Alias name is too long\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// if the alias already exists, reuse it
|
||||
for (a = cmd_alias; a; a = a->next)
|
||||
{
|
||||
if (!strcmp(s, a->name))
|
||||
{
|
||||
Z_Free(a->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!a)
|
||||
{
|
||||
a = Z_Malloc(sizeof(cmdalias_t));
|
||||
a->next = cmd_alias;
|
||||
cmd_alias = a;
|
||||
}
|
||||
strcpy(a->name, s);
|
||||
|
||||
// copy the rest of the command line
|
||||
cmd[0] = 0; // start out with a null string
|
||||
c = Cmd_Argc();
|
||||
for (i = 2; i < c; i++)
|
||||
{
|
||||
strcat(cmd, Cmd_Argv(i));
|
||||
if (i != (c - 1))
|
||||
strcat(cmd, " ");
|
||||
}
|
||||
strcat(cmd, "\n");
|
||||
|
||||
a->value = CopyString(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
COMMAND EXECUTION
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
typedef struct cmd_function_s
|
||||
{
|
||||
struct cmd_function_s * next;
|
||||
char * name;
|
||||
xcommand_t function;
|
||||
} cmd_function_t;
|
||||
|
||||
static int cmd_argc;
|
||||
static char * cmd_null_string = "";
|
||||
static char * cmd_argv[MAX_STRING_TOKENS];
|
||||
static char cmd_args[MAX_STRING_CHARS];
|
||||
static cmd_function_t * cmd_functions; // possible commands to execute
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Argc
|
||||
============
|
||||
*/
|
||||
int Cmd_Argc(void)
|
||||
{
|
||||
return cmd_argc;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Argv
|
||||
============
|
||||
*/
|
||||
char * Cmd_Argv(int arg)
|
||||
{
|
||||
if ((unsigned)arg >= cmd_argc)
|
||||
{
|
||||
return cmd_null_string;
|
||||
}
|
||||
return cmd_argv[arg];
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Args
|
||||
|
||||
Returns a single string containing argv(1) to argv(argc()-1)
|
||||
============
|
||||
*/
|
||||
char * Cmd_Args(void)
|
||||
{
|
||||
return cmd_args;
|
||||
}
|
||||
|
||||
/*
|
||||
======================
|
||||
Cmd_MacroExpandString
|
||||
======================
|
||||
*/
|
||||
char * Cmd_MacroExpandString(char * text)
|
||||
{
|
||||
int i, j, count, len;
|
||||
qboolean inquote;
|
||||
char * scan;
|
||||
static char expanded[MAX_STRING_CHARS];
|
||||
char temporary[MAX_STRING_CHARS];
|
||||
char *token, *start;
|
||||
|
||||
inquote = false;
|
||||
scan = text;
|
||||
|
||||
len = strlen(scan);
|
||||
if (len >= MAX_STRING_CHARS)
|
||||
{
|
||||
Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (scan[i] == '"')
|
||||
inquote ^= 1;
|
||||
if (inquote)
|
||||
continue; // don't expand inside quotes
|
||||
if (scan[i] != '$')
|
||||
continue;
|
||||
// scan out the complete macro
|
||||
start = scan + i + 1;
|
||||
token = COM_Parse(&start);
|
||||
if (!start)
|
||||
continue;
|
||||
|
||||
token = Cvar_VariableString(token);
|
||||
|
||||
j = strlen(token);
|
||||
len += j;
|
||||
if (len >= MAX_STRING_CHARS)
|
||||
{
|
||||
Com_Printf("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strncpy(temporary, scan, i);
|
||||
strcpy(temporary + i, token);
|
||||
strcpy(temporary + i + j, start);
|
||||
|
||||
strcpy(expanded, temporary);
|
||||
scan = expanded;
|
||||
i--;
|
||||
|
||||
if (++count == 100)
|
||||
{
|
||||
Com_Printf("Macro expansion loop, discarded.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (inquote)
|
||||
{
|
||||
Com_Printf("Line has unmatched quote, discarded.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return scan;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_TokenizeString
|
||||
|
||||
Parses the given string into command line tokens.
|
||||
$Cvars will be expanded unless they are in a quoted token
|
||||
============
|
||||
*/
|
||||
void Cmd_TokenizeString(char * text, qboolean macroExpand)
|
||||
{
|
||||
int i;
|
||||
char * com_token;
|
||||
|
||||
// clear the args from the last string
|
||||
for (i = 0; i < cmd_argc; i++)
|
||||
Z_Free(cmd_argv[i]);
|
||||
|
||||
cmd_argc = 0;
|
||||
cmd_args[0] = 0;
|
||||
|
||||
// macro expand the text
|
||||
if (macroExpand)
|
||||
text = Cmd_MacroExpandString(text);
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
while (1)
|
||||
{
|
||||
// skip whitespace up to a /n
|
||||
while (*text && *text <= ' ' && *text != '\n')
|
||||
{
|
||||
text++;
|
||||
}
|
||||
|
||||
if (*text == '\n')
|
||||
{ // a newline seperates commands in the buffer
|
||||
text++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!*text)
|
||||
return;
|
||||
|
||||
// set cmd_args to everything after the first arg
|
||||
if (cmd_argc == 1)
|
||||
{
|
||||
int l;
|
||||
|
||||
strcpy(cmd_args, text);
|
||||
|
||||
// strip off any trailing whitespace
|
||||
l = strlen(cmd_args) - 1;
|
||||
for (; l >= 0; l--)
|
||||
if (cmd_args[l] <= ' ')
|
||||
cmd_args[l] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
com_token = COM_Parse(&text);
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
if (cmd_argc < MAX_STRING_TOKENS)
|
||||
{
|
||||
cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token) + 1);
|
||||
strcpy(cmd_argv[cmd_argc], com_token);
|
||||
cmd_argc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddCommand
|
||||
============
|
||||
*/
|
||||
void Cmd_AddCommand(char * cmd_name, xcommand_t function)
|
||||
{
|
||||
cmd_function_t * cmd;
|
||||
|
||||
// fail if the command is a variable name
|
||||
if (Cvar_VariableString(cmd_name)[0])
|
||||
{
|
||||
Com_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
|
||||
return;
|
||||
}
|
||||
|
||||
// fail if the command already exists
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (!strcmp(cmd_name, cmd->name))
|
||||
{
|
||||
Com_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cmd = Z_Malloc(sizeof(cmd_function_t));
|
||||
cmd->name = cmd_name;
|
||||
cmd->function = function;
|
||||
cmd->next = cmd_functions;
|
||||
cmd_functions = cmd;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_RemoveCommand
|
||||
============
|
||||
*/
|
||||
void Cmd_RemoveCommand(char * cmd_name)
|
||||
{
|
||||
cmd_function_t *cmd, **back;
|
||||
|
||||
back = &cmd_functions;
|
||||
while (1)
|
||||
{
|
||||
cmd = *back;
|
||||
if (!cmd)
|
||||
{
|
||||
Com_Printf("Cmd_RemoveCommand: %s not added\n", cmd_name);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(cmd_name, cmd->name))
|
||||
{
|
||||
*back = cmd->next;
|
||||
Z_Free(cmd);
|
||||
return;
|
||||
}
|
||||
back = &cmd->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Exists
|
||||
============
|
||||
*/
|
||||
qboolean Cmd_Exists(const char * cmd_name)
|
||||
{
|
||||
cmd_function_t * cmd;
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (!strcmp(cmd_name, cmd->name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_CompleteCommand
|
||||
============
|
||||
*/
|
||||
char * Cmd_CompleteCommand(char * partial)
|
||||
{
|
||||
cmd_function_t * cmd;
|
||||
int len;
|
||||
cmdalias_t * a;
|
||||
|
||||
len = strlen(partial);
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
// check for exact match
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next)
|
||||
if (!strcmp(partial, cmd->name))
|
||||
return cmd->name;
|
||||
for (a = cmd_alias; a; a = a->next)
|
||||
if (!strcmp(partial, a->name))
|
||||
return a->name;
|
||||
|
||||
// check for partial match
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next)
|
||||
if (!strncmp(partial, cmd->name, len))
|
||||
return cmd->name;
|
||||
for (a = cmd_alias; a; a = a->next)
|
||||
if (!strncmp(partial, a->name, len))
|
||||
return a->name;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_ExecuteString
|
||||
|
||||
A complete command line has been parsed, so try to execute it
|
||||
FIXME: lookupnoadd the token to speed search?
|
||||
============
|
||||
*/
|
||||
void Cmd_ExecuteString(char * text)
|
||||
{
|
||||
cmd_function_t * cmd;
|
||||
cmdalias_t * a;
|
||||
|
||||
Cmd_TokenizeString(text, true);
|
||||
|
||||
// execute the command line
|
||||
if (!Cmd_Argc())
|
||||
return; // no tokens
|
||||
|
||||
// check functions
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next)
|
||||
{
|
||||
if (!Q_strcasecmp(cmd_argv[0], cmd->name))
|
||||
{
|
||||
if (!cmd->function)
|
||||
{ // forward to server command
|
||||
Cmd_ExecuteString(va("cmd %s", text));
|
||||
}
|
||||
else
|
||||
cmd->function();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check alias
|
||||
for (a = cmd_alias; a; a = a->next)
|
||||
{
|
||||
if (!Q_strcasecmp(cmd_argv[0], a->name))
|
||||
{
|
||||
if (++alias_count == ALIAS_LOOP_COUNT)
|
||||
{
|
||||
Com_Printf("ALIAS_LOOP_COUNT\n");
|
||||
return;
|
||||
}
|
||||
Cbuf_InsertText(a->value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check cvars
|
||||
if (Cvar_Command())
|
||||
return;
|
||||
|
||||
// send it as a server command if we are connected
|
||||
Cmd_ForwardToServer();
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_List_f
|
||||
============
|
||||
*/
|
||||
void Cmd_List_f(void)
|
||||
{
|
||||
cmd_function_t * cmd;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
for (cmd = cmd_functions; cmd; cmd = cmd->next, i++)
|
||||
{
|
||||
Com_Printf("%s\n", cmd->name);
|
||||
}
|
||||
|
||||
Com_Printf("%i commands\n", i);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_Init
|
||||
============
|
||||
*/
|
||||
void Cmd_Init(void)
|
||||
{
|
||||
//
|
||||
// register our commands
|
||||
//
|
||||
Cmd_AddCommand("cmdlist", Cmd_List_f);
|
||||
Cmd_AddCommand("exec", Cmd_Exec_f);
|
||||
Cmd_AddCommand("echo", Cmd_Echo_f);
|
||||
Cmd_AddCommand("alias", Cmd_Alias_f);
|
||||
Cmd_AddCommand("wait", Cmd_Wait_f);
|
||||
}
|
||||
+1732
File diff suppressed because it is too large.
Load diff
+1623
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "common/q_common.h"
|
||||
|
||||
// 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 const 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;
|
||||
}
|
||||
|
||||
unsigned short CRC_Block(byte * start, int count)
|
||||
{
|
||||
unsigned short crc;
|
||||
|
||||
CRC_Init(&crc);
|
||||
while (count--)
|
||||
{
|
||||
crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "common/q_common.h"
|
||||
|
||||
cvar_t * cvar_vars; // Global list of all CVars
|
||||
qboolean userinfo_modified;
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_InfoValidate
|
||||
============
|
||||
*/
|
||||
static qboolean Cvar_InfoValidate(const char * s)
|
||||
{
|
||||
if (strstr(s, "\\"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (strstr(s, "\""))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (strstr(s, ";"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_FindVar
|
||||
============
|
||||
*/
|
||||
static cvar_t * Cvar_FindVar(const char * var_name)
|
||||
{
|
||||
cvar_t * var;
|
||||
for (var = cvar_vars; var; var = var->next)
|
||||
{
|
||||
if (!strcmp(var_name, var->name))
|
||||
{
|
||||
return var;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_VariableValue
|
||||
============
|
||||
*/
|
||||
float Cvar_VariableValue(const char * var_name)
|
||||
{
|
||||
cvar_t * var;
|
||||
var = Cvar_FindVar(var_name);
|
||||
if (!var)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return atof(var->string);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_VariableString
|
||||
============
|
||||
*/
|
||||
char * Cvar_VariableString(const char * var_name)
|
||||
{
|
||||
cvar_t * var;
|
||||
var = Cvar_FindVar(var_name);
|
||||
if (!var)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return var->string;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_CompleteVariable
|
||||
============
|
||||
*/
|
||||
char * Cvar_CompleteVariable(const char * partial)
|
||||
{
|
||||
cvar_t * cvar;
|
||||
int len;
|
||||
|
||||
len = strlen(partial);
|
||||
if (!len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check exact match
|
||||
for (cvar = cvar_vars; cvar; cvar = cvar->next)
|
||||
{
|
||||
if (!strcmp(partial, cvar->name))
|
||||
{
|
||||
return cvar->name;
|
||||
}
|
||||
}
|
||||
|
||||
// check partial match
|
||||
for (cvar = cvar_vars; cvar; cvar = cvar->next)
|
||||
{
|
||||
if (!strncmp(partial, cvar->name, len))
|
||||
{
|
||||
return cvar->name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Get
|
||||
If the variable already exists, the value will not be set
|
||||
The flags will be or'ed in if the variable exists.
|
||||
============
|
||||
*/
|
||||
cvar_t * Cvar_Get(const char * var_name, const char * var_value, int flags)
|
||||
{
|
||||
cvar_t * var;
|
||||
|
||||
if (flags & (CVAR_USERINFO | CVAR_SERVERINFO))
|
||||
{
|
||||
if (!Cvar_InfoValidate(var_name))
|
||||
{
|
||||
Com_Printf("invalid info cvar name\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
var = Cvar_FindVar(var_name);
|
||||
if (var)
|
||||
{
|
||||
var->flags |= flags;
|
||||
return var;
|
||||
}
|
||||
|
||||
if (!var_value)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (flags & (CVAR_USERINFO | CVAR_SERVERINFO))
|
||||
{
|
||||
if (!Cvar_InfoValidate(var_value))
|
||||
{
|
||||
Com_Printf("invalid info cvar value\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
var = Z_Malloc(sizeof(*var));
|
||||
var->name = CopyString(var_name);
|
||||
var->string = CopyString(var_value);
|
||||
var->modified = true;
|
||||
var->value = atof(var->string);
|
||||
|
||||
// link the variable in
|
||||
var->next = cvar_vars;
|
||||
cvar_vars = var;
|
||||
var->flags = flags;
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Set2
|
||||
============
|
||||
*/
|
||||
cvar_t * Cvar_Set2(const char * var_name, const char * value, qboolean force)
|
||||
{
|
||||
cvar_t * var;
|
||||
|
||||
var = Cvar_FindVar(var_name);
|
||||
if (!var)
|
||||
{ // create it
|
||||
return Cvar_Get(var_name, value, 0);
|
||||
}
|
||||
|
||||
if (var->flags & (CVAR_USERINFO | CVAR_SERVERINFO))
|
||||
{
|
||||
if (!Cvar_InfoValidate(value))
|
||||
{
|
||||
Com_Printf("invalid info cvar value\n");
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
if (var->flags & CVAR_NOSET)
|
||||
{
|
||||
Com_Printf("%s is write protected.\n", var_name);
|
||||
return var;
|
||||
}
|
||||
|
||||
if (var->flags & CVAR_LATCH)
|
||||
{
|
||||
if (var->latched_string)
|
||||
{
|
||||
if (strcmp(value, var->latched_string) == 0)
|
||||
{
|
||||
return var;
|
||||
}
|
||||
Z_Free(var->latched_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(value, var->string) == 0)
|
||||
{
|
||||
return var;
|
||||
}
|
||||
}
|
||||
|
||||
if (Com_ServerState())
|
||||
{
|
||||
Com_Printf("%s will be changed for next game.\n", var_name);
|
||||
var->latched_string = CopyString(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var->string = CopyString(value);
|
||||
var->value = atof(var->string);
|
||||
if (!strcmp(var->name, "game"))
|
||||
{
|
||||
FS_SetGamedir(var->string);
|
||||
FS_ExecAutoexec();
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (var->latched_string)
|
||||
{
|
||||
Z_Free(var->latched_string);
|
||||
var->latched_string = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(value, var->string))
|
||||
{
|
||||
return var; // not changed
|
||||
}
|
||||
|
||||
var->modified = true;
|
||||
|
||||
if (var->flags & CVAR_USERINFO)
|
||||
{
|
||||
userinfo_modified = true; // transmit at next opportunity
|
||||
}
|
||||
|
||||
Z_Free(var->string); // free the old value string
|
||||
|
||||
var->string = CopyString(value);
|
||||
var->value = atof(var->string);
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_ForceSet
|
||||
============
|
||||
*/
|
||||
cvar_t * Cvar_ForceSet(const char * var_name, const char * value)
|
||||
{
|
||||
return Cvar_Set2(var_name, value, true);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Set
|
||||
============
|
||||
*/
|
||||
cvar_t * Cvar_Set(const char * var_name, const char * value)
|
||||
{
|
||||
return Cvar_Set2(var_name, value, false);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_FullSet
|
||||
============
|
||||
*/
|
||||
cvar_t * Cvar_FullSet(const char * var_name, const char * value, int flags)
|
||||
{
|
||||
cvar_t * var;
|
||||
|
||||
var = Cvar_FindVar(var_name);
|
||||
if (!var)
|
||||
{
|
||||
// create it
|
||||
return Cvar_Get(var_name, value, flags);
|
||||
}
|
||||
|
||||
var->modified = true;
|
||||
|
||||
if (var->flags & CVAR_USERINFO)
|
||||
{
|
||||
userinfo_modified = true; // transmit at next opportunity
|
||||
}
|
||||
|
||||
Z_Free(var->string); // free the old value string
|
||||
|
||||
var->string = CopyString(value);
|
||||
var->value = atof(var->string);
|
||||
var->flags = flags;
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_SetValue
|
||||
============
|
||||
*/
|
||||
void Cvar_SetValue(const char * var_name, float value)
|
||||
{
|
||||
char val[32];
|
||||
if (value == (int)value)
|
||||
{
|
||||
Com_sprintf(val, sizeof(val), "%i", (int)value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_sprintf(val, sizeof(val), "%f", value);
|
||||
}
|
||||
Cvar_Set(var_name, val);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_GetLatchedVars
|
||||
Any variables with latched values will now be updated
|
||||
============
|
||||
*/
|
||||
void Cvar_GetLatchedVars(void)
|
||||
{
|
||||
cvar_t * var;
|
||||
for (var = cvar_vars; var; var = var->next)
|
||||
{
|
||||
if (!var->latched_string)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Z_Free(var->string);
|
||||
var->string = var->latched_string;
|
||||
var->latched_string = NULL;
|
||||
var->value = atof(var->string);
|
||||
|
||||
if (!strcmp(var->name, "game"))
|
||||
{
|
||||
FS_SetGamedir(var->string);
|
||||
FS_ExecAutoexec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Command
|
||||
Handles variable inspection and changing from the console
|
||||
============
|
||||
*/
|
||||
qboolean Cvar_Command(void)
|
||||
{
|
||||
cvar_t * v;
|
||||
|
||||
// check variables
|
||||
v = Cvar_FindVar(Cmd_Argv(0));
|
||||
if (!v)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// perform a variable print or set
|
||||
if (Cmd_Argc() == 1)
|
||||
{
|
||||
Com_Printf("\"%s\" is \"%s\"\n", v->name, v->string);
|
||||
return true;
|
||||
}
|
||||
|
||||
Cvar_Set(v->name, Cmd_Argv(1));
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Set_f
|
||||
Allows setting and defining of arbitrary cvars from console
|
||||
============
|
||||
*/
|
||||
void Cvar_Set_f(void)
|
||||
{
|
||||
int c;
|
||||
int flags;
|
||||
|
||||
c = Cmd_Argc();
|
||||
if (c != 3 && c != 4)
|
||||
{
|
||||
Com_Printf("usage: set <variable> <value> [u / s]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 4)
|
||||
{
|
||||
if (!strcmp(Cmd_Argv(3), "u"))
|
||||
{
|
||||
flags = CVAR_USERINFO;
|
||||
}
|
||||
else if (!strcmp(Cmd_Argv(3), "s"))
|
||||
{
|
||||
flags = CVAR_SERVERINFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf("flags can only be 'u' or 's'\n");
|
||||
return;
|
||||
}
|
||||
Cvar_FullSet(Cmd_Argv(1), Cmd_Argv(2), flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cvar_Set(Cmd_Argv(1), Cmd_Argv(2));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_WriteVariables
|
||||
Appends lines containing "set variable value" for all variables
|
||||
with the archive flag set to true.
|
||||
============
|
||||
*/
|
||||
void Cvar_WriteVariables(const char * path)
|
||||
{
|
||||
cvar_t * var;
|
||||
char buffer[1024];
|
||||
FILE * f;
|
||||
|
||||
f = fopen(path, "a");
|
||||
for (var = cvar_vars; var; var = var->next)
|
||||
{
|
||||
if (var->flags & CVAR_ARCHIVE)
|
||||
{
|
||||
Com_sprintf(buffer, sizeof(buffer), "set %s \"%s\"\n", var->name, var->string);
|
||||
fprintf(f, "%s", buffer);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_List_f
|
||||
============
|
||||
*/
|
||||
void Cvar_List_f(void)
|
||||
{
|
||||
cvar_t * var;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
for (var = cvar_vars; var; var = var->next, i++)
|
||||
{
|
||||
if (var->flags & CVAR_ARCHIVE)
|
||||
{
|
||||
Com_Printf("*");
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf(" ");
|
||||
}
|
||||
|
||||
if (var->flags & CVAR_USERINFO)
|
||||
{
|
||||
Com_Printf("U");
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf(" ");
|
||||
}
|
||||
|
||||
if (var->flags & CVAR_SERVERINFO)
|
||||
{
|
||||
Com_Printf("S");
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf(" ");
|
||||
}
|
||||
|
||||
if (var->flags & CVAR_NOSET)
|
||||
{
|
||||
Com_Printf("-");
|
||||
}
|
||||
else if (var->flags & CVAR_LATCH)
|
||||
{
|
||||
Com_Printf("L");
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf(" ");
|
||||
}
|
||||
|
||||
Com_Printf(" %s \"%s\"\n", var->name, var->string);
|
||||
}
|
||||
Com_Printf("%i cvars\n", i);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_BitInfo
|
||||
============
|
||||
*/
|
||||
char * Cvar_BitInfo(int bit)
|
||||
{
|
||||
static char info[MAX_INFO_STRING];
|
||||
cvar_t * var;
|
||||
|
||||
info[0] = '\0';
|
||||
for (var = cvar_vars; var; var = var->next)
|
||||
{
|
||||
if (var->flags & bit)
|
||||
{
|
||||
Info_SetValueForKey(info, var->name, var->string);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Userinfo
|
||||
returns an info string containing all the CVAR_USERINFO cvars
|
||||
============
|
||||
*/
|
||||
char * Cvar_Userinfo(void)
|
||||
{
|
||||
return Cvar_BitInfo(CVAR_USERINFO);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Serverinfo
|
||||
returns an info string containing all the CVAR_SERVERINFO cvars
|
||||
============
|
||||
*/
|
||||
char * Cvar_Serverinfo(void)
|
||||
{
|
||||
return Cvar_BitInfo(CVAR_SERVERINFO);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cvar_Init
|
||||
Reads in all archived cvars
|
||||
============
|
||||
*/
|
||||
void Cvar_Init(void)
|
||||
{
|
||||
Cmd_AddCommand("set", Cvar_Set_f);
|
||||
Cmd_AddCommand("cvarlist", Cvar_List_f);
|
||||
}
|
||||
@@ -0,0 +1,972 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "common/q_common.h"
|
||||
|
||||
//===========================================================================
|
||||
|
||||
// define this to disallow any data but the demo pak file
|
||||
//#define NO_ADDONS
|
||||
|
||||
// if a packfile directory differs from this, it is assumed to be hacked
|
||||
|
||||
// Full version
|
||||
#define PAK0_CHECKSUM 0x40E614E0
|
||||
// Demo
|
||||
//#define PAK0_CHECKSUM 0xB2C6D7EA
|
||||
// OEM
|
||||
//#define PAK0_CHECKSUM 0x78E135C
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
QUAKE FILESYSTEM
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
int filepos;
|
||||
int filelen;
|
||||
} packfile_t;
|
||||
|
||||
typedef struct pack_s
|
||||
{
|
||||
char filename[MAX_OSPATH];
|
||||
FILE * handle;
|
||||
int numfiles;
|
||||
packfile_t * files;
|
||||
} pack_t;
|
||||
|
||||
typedef struct filelink_s
|
||||
{
|
||||
struct filelink_s * next;
|
||||
char * from;
|
||||
int fromlength;
|
||||
char * to;
|
||||
} filelink_t;
|
||||
|
||||
typedef struct searchpath_s
|
||||
{
|
||||
char filename[MAX_OSPATH];
|
||||
pack_t * pack; // only one of filename / pack will be used
|
||||
struct searchpath_s * next;
|
||||
} searchpath_t;
|
||||
|
||||
char fs_gamedir[MAX_OSPATH];
|
||||
cvar_t * fs_basedir;
|
||||
cvar_t * fs_cddir;
|
||||
cvar_t * fs_gamedirvar;
|
||||
|
||||
static filelink_t * fs_links;
|
||||
static searchpath_t * fs_searchpaths;
|
||||
static searchpath_t * fs_base_searchpaths; // without gamedirs
|
||||
static char fs_default_base_path[MAX_OSPATH] = ".";
|
||||
|
||||
/*
|
||||
All of Quake's data access is through a hierarchical 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 precaution against having a malicious server instruct clients to
|
||||
write files over areas they shouldn't.
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
FS_filelength
|
||||
================
|
||||
*/
|
||||
int FS_filelength(FILE * f)
|
||||
{
|
||||
int pos;
|
||||
int end;
|
||||
|
||||
pos = ftell(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
end = ftell(f);
|
||||
fseek(f, pos, SEEK_SET);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FS_CreatePath
|
||||
|
||||
Creates any directories needed to store the given filename
|
||||
============
|
||||
*/
|
||||
void FS_CreatePath(char * path)
|
||||
{
|
||||
char * ofs;
|
||||
for (ofs = path + 1; *ofs; ofs++)
|
||||
{
|
||||
if (*ofs == '/')
|
||||
{
|
||||
// create the directory
|
||||
*ofs = '\0';
|
||||
Sys_Mkdir(path);
|
||||
*ofs = '/';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
FS_FCloseFile
|
||||
|
||||
For some reason, other dll's can't just cal fclose()
|
||||
on files returned by FS_FOpenFile...
|
||||
==============
|
||||
*/
|
||||
void FS_FCloseFile(FILE * f)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// RAFAEL
|
||||
/*
|
||||
Developer_searchpath
|
||||
*/
|
||||
int Developer_searchpath(int who)
|
||||
{
|
||||
int ch;
|
||||
// PMM - warning removal
|
||||
// char *start;
|
||||
searchpath_t * search;
|
||||
|
||||
if (who == 1) // xatrix
|
||||
ch = 'x';
|
||||
else if (who == 2)
|
||||
ch = 'r';
|
||||
|
||||
for (search = fs_searchpaths; search; search = search->next)
|
||||
{
|
||||
if (strstr(search->filename, "xatrix"))
|
||||
return 1;
|
||||
|
||||
if (strstr(search->filename, "rogue"))
|
||||
return 2;
|
||||
/*
|
||||
start = strchr (search->filename, ch);
|
||||
|
||||
if (start == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp (start ,"xatrix") == 0)
|
||||
return (1);
|
||||
*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int file_from_pak = 0;
|
||||
|
||||
/*
|
||||
===========
|
||||
FS_FOpenFile
|
||||
|
||||
Finds the file in the search path.
|
||||
returns filesize and an open FILE *
|
||||
Used for streaming data out of either a pak file or
|
||||
a separate file.
|
||||
===========
|
||||
*/
|
||||
|
||||
#ifndef NO_ADDONS
|
||||
|
||||
int FS_FOpenFile(const char * filename, FILE ** file)
|
||||
{
|
||||
searchpath_t * search;
|
||||
char netpath[MAX_OSPATH];
|
||||
pack_t * pak;
|
||||
filelink_t * link;
|
||||
int i;
|
||||
|
||||
file_from_pak = 0;
|
||||
|
||||
// check for links first
|
||||
for (link = fs_links; link; link = link->next)
|
||||
{
|
||||
if (!strncmp(filename, link->from, link->fromlength))
|
||||
{
|
||||
Com_sprintf(netpath, sizeof(netpath), "%s%s", link->to, filename + link->fromlength);
|
||||
*file = fopen(netpath, "rb");
|
||||
if (*file)
|
||||
{
|
||||
Com_DPrintf("Link file: %s\n", netpath);
|
||||
return FS_filelength(*file);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// search through the path, one element at a time
|
||||
//
|
||||
for (search = fs_searchpaths; 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; i < pak->numfiles; i++)
|
||||
{
|
||||
if (!Q_strcasecmp(pak->files[i].name, filename))
|
||||
{
|
||||
// found it!
|
||||
file_from_pak = 1;
|
||||
Com_DPrintf("PackFile: %s : %s\n", pak->filename, filename);
|
||||
|
||||
// open a new file on the pakfile
|
||||
*file = fopen(pak->filename, "rb");
|
||||
if (!*file)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "Couldn't reopen %s", pak->filename);
|
||||
}
|
||||
|
||||
fseek(*file, pak->files[i].filepos, SEEK_SET);
|
||||
return pak->files[i].filelen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // check a file in the directory tree:
|
||||
{
|
||||
Com_sprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename);
|
||||
|
||||
*file = fopen(netpath, "rb");
|
||||
if (!*file)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Com_DPrintf("FS_FOpenFile: %s\n", netpath);
|
||||
|
||||
return FS_filelength(*file);
|
||||
}
|
||||
}
|
||||
|
||||
Com_DPrintf("FS_FOpenFile: can't find %s\n", filename);
|
||||
|
||||
*file = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else // NO_ADDONS
|
||||
|
||||
// this is just for demos to prevent add on hacking
|
||||
int FS_FOpenFile(const char * filename, FILE ** file)
|
||||
{
|
||||
searchpath_t * search;
|
||||
char netpath[MAX_OSPATH];
|
||||
pack_t * pak;
|
||||
int i;
|
||||
|
||||
file_from_pak = 0;
|
||||
|
||||
// get config from directory, everything else from pak
|
||||
if (!strcmp(filename, "config.cfg") || !strncmp(filename, "players/", 8))
|
||||
{
|
||||
Com_sprintf(netpath, sizeof(netpath), "%s/%s", FS_Gamedir(), filename);
|
||||
|
||||
*file = fopen(netpath, "rb");
|
||||
if (!*file)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Com_DPrintf("FS_FOpenFile (NO_ADDONS): %s\n", netpath);
|
||||
|
||||
return FS_filelength(*file);
|
||||
}
|
||||
|
||||
for (search = fs_searchpaths; search; search = search->next)
|
||||
{
|
||||
if (search->pack)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!search)
|
||||
{
|
||||
*file = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pak = search->pack;
|
||||
for (i = 0; i < pak->numfiles; i++)
|
||||
{
|
||||
if (!Q_strcasecmp(pak->files[i].name, filename))
|
||||
{
|
||||
// found it!
|
||||
file_from_pak = 1;
|
||||
Com_DPrintf("PackFile: %s : %s\n", pak->filename, filename);
|
||||
|
||||
// open a new file on the pakfile
|
||||
*file = fopen(pak->filename, "rb");
|
||||
if (!*file)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "Couldn't reopen %s", pak->filename);
|
||||
}
|
||||
|
||||
fseek(*file, pak->files[i].filepos, SEEK_SET);
|
||||
return pak->files[i].filelen;
|
||||
}
|
||||
}
|
||||
|
||||
Com_DPrintf("FS_FOpenFile (NO_ADDONS): can't find %s\n", filename);
|
||||
|
||||
*file = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif // NO_ADDONS
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_ReadFile
|
||||
|
||||
Properly handles partial reads
|
||||
=================
|
||||
*/
|
||||
void FS_Read(void * buffer, int len, FILE * f)
|
||||
{
|
||||
extern void CDAudio_Stop(void);
|
||||
enum
|
||||
{
|
||||
MAX_READ = 0x10000
|
||||
}; // read in blocks of 64K
|
||||
|
||||
int block, remaining;
|
||||
int read;
|
||||
byte * buf;
|
||||
int tries;
|
||||
|
||||
buf = (byte *)buffer;
|
||||
|
||||
// read in chunks for progress bar
|
||||
remaining = len;
|
||||
tries = 0;
|
||||
while (remaining)
|
||||
{
|
||||
block = remaining;
|
||||
if (block > MAX_READ)
|
||||
{
|
||||
block = MAX_READ;
|
||||
}
|
||||
|
||||
read = fread(buf, 1, block, f);
|
||||
if (read == 0)
|
||||
{
|
||||
// we might have been trying to read from a CD
|
||||
if (!tries)
|
||||
{
|
||||
tries = 1;
|
||||
CDAudio_Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Error(ERR_FATAL, "FS_Read: 0 bytes read");
|
||||
}
|
||||
}
|
||||
|
||||
if (read == -1)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "FS_Read: -1 bytes read");
|
||||
}
|
||||
|
||||
// do some progress bar thing here...
|
||||
|
||||
remaining -= read;
|
||||
buf += read;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FS_LoadFile
|
||||
|
||||
Filename are reletive to the quake search path
|
||||
a null buffer will just return the file length without loading
|
||||
============
|
||||
*/
|
||||
int FS_LoadFile(const char * path, void ** buffer)
|
||||
{
|
||||
FILE * h;
|
||||
byte * buf;
|
||||
int len;
|
||||
|
||||
buf = NULL; // quiet compiler warning
|
||||
|
||||
// look for it in the filesystem or pack files
|
||||
len = FS_FOpenFile(path, &h);
|
||||
if (!h)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
*buffer = NULL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
fclose(h);
|
||||
return len;
|
||||
}
|
||||
|
||||
buf = Z_Malloc(len);
|
||||
*buffer = buf;
|
||||
|
||||
FS_Read(buf, len, h);
|
||||
fclose(h);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FS_FreeFile
|
||||
=============
|
||||
*/
|
||||
void FS_FreeFile(void * buffer)
|
||||
{
|
||||
Z_Free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_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 * FS_LoadPackFile(char * packfile)
|
||||
{
|
||||
dpackheader_t header;
|
||||
int i;
|
||||
packfile_t * newfiles;
|
||||
int numpackfiles;
|
||||
pack_t * pack;
|
||||
FILE * packhandle;
|
||||
dpackfile_t info[MAX_FILES_IN_PACK];
|
||||
unsigned checksum;
|
||||
|
||||
packhandle = fopen(packfile, "rb");
|
||||
if (!packhandle)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fread(&header, 1, sizeof(header), packhandle);
|
||||
if (LittleLong(header.ident) != IDPAKHEADER)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "%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)
|
||||
{
|
||||
Com_Error(ERR_FATAL, "%s has %i files", packfile, numpackfiles);
|
||||
}
|
||||
|
||||
newfiles = Z_Malloc(numpackfiles * sizeof(packfile_t));
|
||||
|
||||
fseek(packhandle, header.dirofs, SEEK_SET);
|
||||
fread(info, 1, header.dirlen, packhandle);
|
||||
|
||||
// crc the directory to check for modifications
|
||||
checksum = Com_BlockChecksum((void *)info, header.dirlen);
|
||||
|
||||
#ifdef NO_ADDONS
|
||||
if (checksum != PAK0_CHECKSUM)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// parse the directory
|
||||
for (i = 0; i < numpackfiles; i++)
|
||||
{
|
||||
strcpy(newfiles[i].name, info[i].name);
|
||||
newfiles[i].filepos = LittleLong(info[i].filepos);
|
||||
newfiles[i].filelen = LittleLong(info[i].filelen);
|
||||
}
|
||||
|
||||
pack = Z_Malloc(sizeof(pack_t));
|
||||
strcpy(pack->filename, packfile);
|
||||
pack->handle = packhandle;
|
||||
pack->numfiles = numpackfiles;
|
||||
pack->files = newfiles;
|
||||
|
||||
Com_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles);
|
||||
return pack;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_AddGameDirectory
|
||||
|
||||
Sets fs_gamedir, adds the directory to the head of the path,
|
||||
then loads and adds pak1.pak pak2.pak ...
|
||||
================
|
||||
*/
|
||||
void FS_AddGameDirectory(char * dir)
|
||||
{
|
||||
int i;
|
||||
searchpath_t * search;
|
||||
pack_t * pak;
|
||||
char pakfile[MAX_OSPATH];
|
||||
|
||||
strcpy(fs_gamedir, dir);
|
||||
|
||||
//
|
||||
// add the directory to the search path
|
||||
//
|
||||
search = Z_Malloc(sizeof(searchpath_t));
|
||||
strcpy(search->filename, dir);
|
||||
search->next = fs_searchpaths;
|
||||
fs_searchpaths = search;
|
||||
|
||||
//
|
||||
// add any pak files in the format pak0.pak pak1.pak, ...
|
||||
//
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
Com_sprintf(pakfile, sizeof(pakfile), "%s/pak%i.pak", dir, i);
|
||||
|
||||
pak = FS_LoadPackFile(pakfile);
|
||||
if (!pak)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Com_DPrintf("FS_AddGameDirectory added pak '%s'\n", pakfile);
|
||||
|
||||
search = Z_Malloc(sizeof(searchpath_t));
|
||||
search->pack = pak;
|
||||
search->next = fs_searchpaths;
|
||||
fs_searchpaths = search;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FS_Gamedir
|
||||
|
||||
Called to find where to write a file (demos, savegames, etc)
|
||||
============
|
||||
*/
|
||||
char * FS_Gamedir(void)
|
||||
{
|
||||
return fs_gamedir;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FS_ExecAutoexec
|
||||
=============
|
||||
*/
|
||||
void FS_ExecAutoexec(void)
|
||||
{
|
||||
char * dir;
|
||||
char name[MAX_QPATH];
|
||||
|
||||
dir = Cvar_VariableString("gamedir");
|
||||
if (*dir)
|
||||
{
|
||||
Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME);
|
||||
}
|
||||
|
||||
if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM))
|
||||
{
|
||||
Cbuf_AddText("exec autoexec.cfg\n");
|
||||
}
|
||||
|
||||
Sys_FindClose();
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_SetGamedir
|
||||
|
||||
Sets the gamedir and path to a different directory.
|
||||
================
|
||||
*/
|
||||
void FS_SetGamedir(const char * dir)
|
||||
{
|
||||
searchpath_t * next;
|
||||
|
||||
if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":"))
|
||||
{
|
||||
Com_Printf("Gamedir should be a single filename, not a path\n");
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// free up any current game dir info
|
||||
//
|
||||
while (fs_searchpaths != fs_base_searchpaths)
|
||||
{
|
||||
if (fs_searchpaths->pack)
|
||||
{
|
||||
fclose(fs_searchpaths->pack->handle);
|
||||
Z_Free(fs_searchpaths->pack->files);
|
||||
Z_Free(fs_searchpaths->pack);
|
||||
}
|
||||
next = fs_searchpaths->next;
|
||||
Z_Free(fs_searchpaths);
|
||||
fs_searchpaths = next;
|
||||
}
|
||||
|
||||
//
|
||||
// flush all data, so it will be forced to reload
|
||||
//
|
||||
if (dedicated && !dedicated->value)
|
||||
{
|
||||
Cbuf_AddText("vid_restart\nsnd_restart\n");
|
||||
}
|
||||
|
||||
Com_sprintf(fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir);
|
||||
|
||||
if (!strcmp(dir, BASEDIRNAME) || (*dir == '\0'))
|
||||
{
|
||||
Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET);
|
||||
Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET);
|
||||
if (fs_cddir->string[0])
|
||||
{
|
||||
FS_AddGameDirectory(va("%s/%s", fs_cddir->string, dir));
|
||||
}
|
||||
FS_AddGameDirectory(va("%s/%s", fs_basedir->string, dir));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_Link_f
|
||||
|
||||
Creates a filelink_t
|
||||
================
|
||||
*/
|
||||
void FS_Link_f(void)
|
||||
{
|
||||
filelink_t *l, **prev;
|
||||
|
||||
if (Cmd_Argc() != 3)
|
||||
{
|
||||
Com_Printf("USAGE: link <from> <to>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// see if the link already exists
|
||||
prev = &fs_links;
|
||||
for (l = fs_links; l; l = l->next)
|
||||
{
|
||||
if (!strcmp(l->from, Cmd_Argv(1)))
|
||||
{
|
||||
Z_Free(l->to);
|
||||
if (!strlen(Cmd_Argv(2)))
|
||||
{ // delete it
|
||||
*prev = l->next;
|
||||
Z_Free(l->from);
|
||||
Z_Free(l);
|
||||
return;
|
||||
}
|
||||
l->to = CopyString(Cmd_Argv(2));
|
||||
return;
|
||||
}
|
||||
prev = &l->next;
|
||||
}
|
||||
|
||||
// create a new link
|
||||
l = Z_Malloc(sizeof(*l));
|
||||
l->next = fs_links;
|
||||
fs_links = l;
|
||||
l->from = CopyString(Cmd_Argv(1));
|
||||
l->fromlength = strlen(l->from);
|
||||
l->to = CopyString(Cmd_Argv(2));
|
||||
}
|
||||
|
||||
/*
|
||||
** FS_ListFiles
|
||||
*/
|
||||
char ** FS_ListFiles(char * findname, int * numfiles, unsigned musthave, unsigned canthave)
|
||||
{
|
||||
char * s;
|
||||
int nfiles = 0;
|
||||
char ** list = 0;
|
||||
|
||||
s = Sys_FindFirst(findname, musthave, canthave);
|
||||
while (s)
|
||||
{
|
||||
if (s[strlen(s) - 1] != '.')
|
||||
{
|
||||
nfiles++;
|
||||
}
|
||||
s = Sys_FindNext(musthave, canthave);
|
||||
}
|
||||
Sys_FindClose();
|
||||
|
||||
if (!nfiles)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nfiles++; // add space for a guard
|
||||
*numfiles = nfiles;
|
||||
|
||||
list = malloc(sizeof(char *) * nfiles);
|
||||
memset(list, 0, sizeof(char *) * nfiles);
|
||||
|
||||
s = Sys_FindFirst(findname, musthave, canthave);
|
||||
nfiles = 0;
|
||||
while (s)
|
||||
{
|
||||
if (s[strlen(s) - 1] != '.')
|
||||
{
|
||||
list[nfiles] = strdup(s);
|
||||
#ifdef _WIN32
|
||||
strlwr(list[nfiles]);
|
||||
#endif // _WIN32
|
||||
nfiles++;
|
||||
}
|
||||
s = Sys_FindNext(musthave, canthave);
|
||||
}
|
||||
Sys_FindClose();
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
** FS_Dir_f
|
||||
*/
|
||||
void FS_Dir_f(void)
|
||||
{
|
||||
char * path = NULL;
|
||||
char findname[1024];
|
||||
char wildcard[1024] = "*.*";
|
||||
char ** dirnames;
|
||||
int ndirs;
|
||||
|
||||
if (Cmd_Argc() != 1)
|
||||
{
|
||||
strcpy(wildcard, Cmd_Argv(1));
|
||||
}
|
||||
|
||||
while ((path = FS_NextPath(path)) != NULL)
|
||||
{
|
||||
char * tmp = findname;
|
||||
|
||||
Com_sprintf(findname, sizeof(findname), "%s/%s", path, wildcard);
|
||||
|
||||
while (*tmp != 0)
|
||||
{
|
||||
if (*tmp == '\\')
|
||||
{
|
||||
*tmp = '/';
|
||||
}
|
||||
tmp++;
|
||||
}
|
||||
Com_Printf("Directory of %s\n", findname);
|
||||
Com_Printf("----\n");
|
||||
|
||||
if ((dirnames = FS_ListFiles(findname, &ndirs, 0, 0)) != 0)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ndirs - 1; i++)
|
||||
{
|
||||
if (strrchr(dirnames[i], '/'))
|
||||
{
|
||||
Com_Printf("%s\n", strrchr(dirnames[i], '/') + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf("%s\n", dirnames[i]);
|
||||
}
|
||||
free(dirnames[i]);
|
||||
}
|
||||
free(dirnames);
|
||||
}
|
||||
Com_Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
FS_Path_f
|
||||
|
||||
============
|
||||
*/
|
||||
void FS_Path_f(void)
|
||||
{
|
||||
searchpath_t * s;
|
||||
filelink_t * l;
|
||||
|
||||
Com_Printf("Current search path:\n");
|
||||
for (s = fs_searchpaths; s; s = s->next)
|
||||
{
|
||||
if (s == fs_base_searchpaths)
|
||||
{
|
||||
Com_Printf("----------\n");
|
||||
}
|
||||
|
||||
if (s->pack)
|
||||
{
|
||||
Com_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles);
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_Printf("%s\n", s->filename);
|
||||
}
|
||||
}
|
||||
|
||||
Com_Printf("\nLinks:\n");
|
||||
for (l = fs_links; l; l = l->next)
|
||||
{
|
||||
Com_Printf("%s : %s\n", l->from, l->to);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_NextPath
|
||||
|
||||
Allows enumerating all of the directories in the search path
|
||||
================
|
||||
*/
|
||||
char * FS_NextPath(char * prevpath)
|
||||
{
|
||||
searchpath_t * s;
|
||||
char * prev;
|
||||
|
||||
if (!prevpath)
|
||||
{
|
||||
return fs_gamedir;
|
||||
}
|
||||
|
||||
prev = fs_gamedir;
|
||||
for (s = fs_searchpaths; s; s = s->next)
|
||||
{
|
||||
if (s->pack)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (prevpath == prev)
|
||||
{
|
||||
return s->filename;
|
||||
}
|
||||
prev = s->filename;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_SetDefaultBasePath
|
||||
|
||||
Added for QPS2 so we can change the game path prior to
|
||||
FS init and look for files in the USB drive or CDFS.
|
||||
================
|
||||
*/
|
||||
void FS_SetDefaultBasePath(const char * path)
|
||||
{
|
||||
if (path != NULL && *path != '\0')
|
||||
{
|
||||
strncpy(fs_default_base_path, path, sizeof(fs_default_base_path));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_InitFilesystem
|
||||
================
|
||||
*/
|
||||
void FS_InitFilesystem(void)
|
||||
{
|
||||
Com_DPrintf("---- FS_InitFilesystem ----\n");
|
||||
|
||||
Cmd_AddCommand("path", FS_Path_f);
|
||||
Cmd_AddCommand("link", FS_Link_f);
|
||||
Cmd_AddCommand("dir", FS_Dir_f);
|
||||
|
||||
//
|
||||
// basedir <path>
|
||||
// allows the game to run from outside the data tree
|
||||
//
|
||||
fs_basedir = Cvar_Get("basedir", fs_default_base_path, CVAR_NOSET);
|
||||
|
||||
//
|
||||
// cddir <path>
|
||||
// Logically concatenates the cddir after the basedir for
|
||||
// allows the game to run from outside the data tree
|
||||
//
|
||||
fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET);
|
||||
if (fs_cddir->string[0])
|
||||
{
|
||||
FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_cddir->string));
|
||||
}
|
||||
|
||||
//
|
||||
// start up with baseq2 by default
|
||||
//
|
||||
FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_basedir->string));
|
||||
|
||||
// any set gamedirs will be freed up to here
|
||||
fs_base_searchpaths = fs_searchpaths;
|
||||
|
||||
// check for game override
|
||||
fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO);
|
||||
if (fs_gamedirvar->string[0])
|
||||
{
|
||||
FS_SetGamedir(fs_gamedirvar->string);
|
||||
}
|
||||
|
||||
Com_DPrintf("fs_gamedir: %s\n", fs_gamedir);
|
||||
Com_DPrintf("fs_basedir: %s\n", fs_basedir->string);
|
||||
Com_DPrintf("fs_cddir: %s\n", fs_cddir->string);
|
||||
|
||||
Com_DPrintf("---- FS_InitFilesystem completed! ----\n");
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/* GLOBAL.H - RSAREF types and constants */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* POINTER defines a generic pointer type */
|
||||
typedef unsigned char * POINTER;
|
||||
|
||||
/* UINT2 defines a two byte word */
|
||||
typedef unsigned short int UINT2;
|
||||
|
||||
/* UINT4 defines a four byte word */
|
||||
#ifdef __alpha__
|
||||
typedef unsigned int UINT4;
|
||||
#else
|
||||
typedef unsigned long int UINT4;
|
||||
#endif
|
||||
|
||||
/* MD4.H - header file for MD4C.C */
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it is identified as the “RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing this software or this function.
|
||||
License is also granted to make and use derivative works provided that such works are identified as “derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm” in all material mentioning or referencing the derived work.
|
||||
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided “as is” without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this documentation and/or software. */
|
||||
|
||||
/* MD4 context. */
|
||||
typedef struct
|
||||
{
|
||||
UINT4 state[4]; /* state (ABCD) */
|
||||
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||
unsigned char buffer[64]; /* input buffer */
|
||||
} MD4_CTX;
|
||||
|
||||
void MD4Init(MD4_CTX *);
|
||||
void MD4Update(MD4_CTX *, unsigned char *, unsigned int);
|
||||
void MD4Final(unsigned char[16], MD4_CTX *);
|
||||
|
||||
/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */
|
||||
/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it is identified as the
|
||||
RSA Data Security, Inc. MD4 Message-Digest Algorithm
|
||||
in all material mentioning or referencing this software or this function.
|
||||
License is also granted to make and use derivative works provided that such works are identified as
|
||||
derived from the RSA Data Security, Inc. MD4 Message-Digest Algorithm
|
||||
in all material mentioning or referencing the derived work.
|
||||
RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided
|
||||
as is without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this documentation and/or software. */
|
||||
|
||||
/* Constants for MD4Transform routine. */
|
||||
#define S11 3
|
||||
#define S12 7
|
||||
#define S13 11
|
||||
#define S14 19
|
||||
#define S21 3
|
||||
#define S22 5
|
||||
#define S23 9
|
||||
#define S24 13
|
||||
#define S31 3
|
||||
#define S32 9
|
||||
#define S33 11
|
||||
#define S34 15
|
||||
|
||||
static void MD4Transform(UINT4[4], unsigned char[64]);
|
||||
static void Encode(unsigned char *, UINT4 *, unsigned int);
|
||||
static void Decode(UINT4 *, unsigned char *, unsigned int);
|
||||
|
||||
static unsigned char PADDING[64] = {
|
||||
0x80, 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, 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
|
||||
};
|
||||
|
||||
/* F, G and H are basic MD4 functions. */
|
||||
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||
#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
|
||||
/* ROTATE_LEFT rotates x left n bits. */
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
|
||||
/* Rotation is separate from addition to prevent recomputation */
|
||||
#define FF(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += F((b), (c), (d)) + (x); \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
}
|
||||
|
||||
#define GG(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += G((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
}
|
||||
|
||||
#define HH(a, b, c, d, x, s) \
|
||||
{ \
|
||||
(a) += H((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
|
||||
(a) = ROTATE_LEFT((a), (s)); \
|
||||
}
|
||||
|
||||
/* MD4 initialization. Begins an MD4 operation, writing a new context. */
|
||||
void MD4Init(MD4_CTX * context)
|
||||
{
|
||||
context->count[0] = context->count[1] = 0;
|
||||
|
||||
/* Load magic initialization constants.*/
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xefcdab89;
|
||||
context->state[2] = 0x98badcfe;
|
||||
context->state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
|
||||
void MD4Update(MD4_CTX * context, unsigned char * input, unsigned int inputLen)
|
||||
{
|
||||
unsigned int i, index, partLen;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
|
||||
|
||||
/* Update number of bits */
|
||||
if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
|
||||
context->count[1]++;
|
||||
|
||||
context->count[1] += ((UINT4)inputLen >> 29);
|
||||
|
||||
partLen = 64 - index;
|
||||
|
||||
/* Transform as many times as possible.*/
|
||||
if (inputLen >= partLen)
|
||||
{
|
||||
memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
|
||||
MD4Transform(context->state, context->buffer);
|
||||
|
||||
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||
MD4Transform(context->state, &input[i]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
/* Buffer remaining input */
|
||||
memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen - i);
|
||||
}
|
||||
|
||||
/* MD4 finalization. Ends an MD4 message-digest operation, writing the the message digest and zeroizing the context. */
|
||||
void MD4Final(unsigned char digest[16], MD4_CTX * context)
|
||||
{
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
|
||||
/* Save number of bits */
|
||||
Encode(bits, context->count, 8);
|
||||
|
||||
/* Pad out to 56 mod 64.*/
|
||||
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
MD4Update(context, PADDING, padLen);
|
||||
|
||||
/* Append length (before padding) */
|
||||
MD4Update(context, bits, 8);
|
||||
|
||||
/* Store state in digest */
|
||||
Encode(digest, context->state, 16);
|
||||
|
||||
/* Zeroize sensitive information.*/
|
||||
memset((POINTER)context, 0, sizeof(*context));
|
||||
}
|
||||
|
||||
/* MD4 basic transformation. Transforms state based on block. */
|
||||
static void MD4Transform(UINT4 state[4], unsigned char block[64])
|
||||
{
|
||||
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
Decode(x, block, 64);
|
||||
|
||||
/* Round 1 */
|
||||
FF(a, b, c, d, x[0], S11); /* 1 */
|
||||
FF(d, a, b, c, x[1], S12); /* 2 */
|
||||
FF(c, d, a, b, x[2], S13); /* 3 */
|
||||
FF(b, c, d, a, x[3], S14); /* 4 */
|
||||
FF(a, b, c, d, x[4], S11); /* 5 */
|
||||
FF(d, a, b, c, x[5], S12); /* 6 */
|
||||
FF(c, d, a, b, x[6], S13); /* 7 */
|
||||
FF(b, c, d, a, x[7], S14); /* 8 */
|
||||
FF(a, b, c, d, x[8], S11); /* 9 */
|
||||
FF(d, a, b, c, x[9], S12); /* 10 */
|
||||
FF(c, d, a, b, x[10], S13); /* 11 */
|
||||
FF(b, c, d, a, x[11], S14); /* 12 */
|
||||
FF(a, b, c, d, x[12], S11); /* 13 */
|
||||
FF(d, a, b, c, x[13], S12); /* 14 */
|
||||
FF(c, d, a, b, x[14], S13); /* 15 */
|
||||
FF(b, c, d, a, x[15], S14); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG(a, b, c, d, x[0], S21); /* 17 */
|
||||
GG(d, a, b, c, x[4], S22); /* 18 */
|
||||
GG(c, d, a, b, x[8], S23); /* 19 */
|
||||
GG(b, c, d, a, x[12], S24); /* 20 */
|
||||
GG(a, b, c, d, x[1], S21); /* 21 */
|
||||
GG(d, a, b, c, x[5], S22); /* 22 */
|
||||
GG(c, d, a, b, x[9], S23); /* 23 */
|
||||
GG(b, c, d, a, x[13], S24); /* 24 */
|
||||
GG(a, b, c, d, x[2], S21); /* 25 */
|
||||
GG(d, a, b, c, x[6], S22); /* 26 */
|
||||
GG(c, d, a, b, x[10], S23); /* 27 */
|
||||
GG(b, c, d, a, x[14], S24); /* 28 */
|
||||
GG(a, b, c, d, x[3], S21); /* 29 */
|
||||
GG(d, a, b, c, x[7], S22); /* 30 */
|
||||
GG(c, d, a, b, x[11], S23); /* 31 */
|
||||
GG(b, c, d, a, x[15], S24); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH(a, b, c, d, x[0], S31); /* 33 */
|
||||
HH(d, a, b, c, x[8], S32); /* 34 */
|
||||
HH(c, d, a, b, x[4], S33); /* 35 */
|
||||
HH(b, c, d, a, x[12], S34); /* 36 */
|
||||
HH(a, b, c, d, x[2], S31); /* 37 */
|
||||
HH(d, a, b, c, x[10], S32); /* 38 */
|
||||
HH(c, d, a, b, x[6], S33); /* 39 */
|
||||
HH(b, c, d, a, x[14], S34); /* 40 */
|
||||
HH(a, b, c, d, x[1], S31); /* 41 */
|
||||
HH(d, a, b, c, x[9], S32); /* 42 */
|
||||
HH(c, d, a, b, x[5], S33); /* 43 */
|
||||
HH(b, c, d, a, x[13], S34); /* 44 */
|
||||
HH(a, b, c, d, x[3], S31); /* 45 */
|
||||
HH(d, a, b, c, x[11], S32); /* 46 */
|
||||
HH(c, d, a, b, x[7], S33); /* 47 */
|
||||
HH(b, c, d, a, x[15], S34); /* 48 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
/* Zeroize sensitive information.*/
|
||||
memset((POINTER)x, 0, sizeof(x));
|
||||
}
|
||||
|
||||
/* Encodes input (UINT4) into output (unsigned char). Assumes len is a multiple of 4. */
|
||||
static void Encode(unsigned char * output, UINT4 * input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
{
|
||||
output[j] = (unsigned char)(input[i] & 0xff);
|
||||
output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff);
|
||||
output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff);
|
||||
output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
|
||||
static void Decode(UINT4 * output, unsigned char * input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((UINT4)input[j]) | (((UINT4)input[j + 1]) << 8) | (((UINT4)input[j + 2]) << 16) | (((UINT4)input[j + 3]) << 24);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
unsigned Com_BlockChecksum(void * buffer, int length)
|
||||
{
|
||||
int digest[4];
|
||||
unsigned val;
|
||||
MD4_CTX ctx;
|
||||
|
||||
MD4Init(&ctx);
|
||||
MD4Update(&ctx, (unsigned char *)buffer, length);
|
||||
MD4Final((unsigned char *)digest, &ctx);
|
||||
|
||||
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "common/q_common.h"
|
||||
|
||||
/*
|
||||
|
||||
packet header
|
||||
-------------
|
||||
31 sequence
|
||||
1 does this message contain a reliable payload
|
||||
31 acknowledge sequence
|
||||
1 acknowledge receipt of even/odd message
|
||||
16 qport
|
||||
|
||||
The remote connection never knows if it missed a reliable message, the
|
||||
local side detects that it has been dropped by seeing a sequence acknowledge
|
||||
higher thatn the last reliable sequence, but without the correct evon/odd
|
||||
bit for the reliable set.
|
||||
|
||||
If the sender notices that a reliable message has been dropped, it will be
|
||||
retransmitted. It will not be retransmitted again until a message after
|
||||
the retransmit has been acknowledged and the reliable still failed to get there.
|
||||
|
||||
if the sequence number is -1, the packet should be handled without a netcon
|
||||
|
||||
The reliable message can be added to at any time by doing
|
||||
MSG_Write* (&netchan->message, <data>).
|
||||
|
||||
If the message buffer is overflowed, either by a single message, or by
|
||||
multiple frames worth piling up while the last reliable transmit goes
|
||||
unacknowledged, the netchan signals a fatal error.
|
||||
|
||||
Reliable messages are always placed first in a packet, then the unreliable
|
||||
message is included if there is sufficient room.
|
||||
|
||||
To the receiver, there is no distinction between the reliable and unreliable
|
||||
parts of the message, they are just processed out as a single larger message.
|
||||
|
||||
Illogical packet sequence numbers cause the packet to be dropped, but do
|
||||
not kill the connection. This, combined with the tight window of valid
|
||||
reliable acknowledgement numbers provides protection against malicious
|
||||
address spoofing.
|
||||
|
||||
|
||||
The qport field is a workaround for bad address translating routers that
|
||||
sometimes remap the client's source port on a packet during gameplay.
|
||||
|
||||
If the base part of the net address matches and the qport matches, then the
|
||||
channel matches even if the IP port differs. The IP port should be updated
|
||||
to the new value before sending out any replies.
|
||||
|
||||
|
||||
If there is no information that needs to be transfered on a given frame,
|
||||
such as during the connection stage while waiting for the client to load,
|
||||
then a packet only needs to be delivered if there is something in the
|
||||
unacknowledged reliable
|
||||
*/
|
||||
|
||||
cvar_t * showpackets;
|
||||
cvar_t * showdrop;
|
||||
cvar_t * qport;
|
||||
|
||||
netadr_t net_from;
|
||||
sizebuf_t net_message;
|
||||
byte net_message_buffer[MAX_MSGLEN];
|
||||
|
||||
/*
|
||||
===============
|
||||
Netchan_Init
|
||||
|
||||
===============
|
||||
*/
|
||||
void Netchan_Init(void)
|
||||
{
|
||||
int port;
|
||||
|
||||
// pick a port value that should be nice and random
|
||||
port = Sys_Milliseconds() & 0xffff;
|
||||
|
||||
showpackets = Cvar_Get("showpackets", "0", 0);
|
||||
showdrop = Cvar_Get("showdrop", "0", 0);
|
||||
qport = Cvar_Get("qport", va("%i", port), CVAR_NOSET);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Netchan_OutOfBand
|
||||
|
||||
Sends an out-of-band datagram
|
||||
================
|
||||
*/
|
||||
void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte * data)
|
||||
{
|
||||
sizebuf_t send;
|
||||
byte send_buf[MAX_MSGLEN];
|
||||
|
||||
// write the packet header
|
||||
SZ_Init(&send, send_buf, sizeof(send_buf));
|
||||
|
||||
MSG_WriteLong(&send, -1); // -1 sequence means out of band
|
||||
SZ_Write(&send, data, length);
|
||||
|
||||
// send the datagram
|
||||
NET_SendPacket(net_socket, send.cursize, send.data, adr);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Netchan_OutOfBandPrint
|
||||
|
||||
Sends a text message in an out-of-band datagram
|
||||
================
|
||||
*/
|
||||
void Netchan_OutOfBandPrint(int net_socket, netadr_t adr, char * format, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
static char string[MAX_MSGLEN - 4];
|
||||
|
||||
va_start(argptr, format);
|
||||
vsprintf(string, format, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
Netchan_OutOfBand(net_socket, adr, strlen(string), (byte *)string);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
Netchan_Setup
|
||||
|
||||
called to open a channel to a remote system
|
||||
==============
|
||||
*/
|
||||
void Netchan_Setup(netsrc_t sock, netchan_t * chan, netadr_t adr, int qport)
|
||||
{
|
||||
memset(chan, 0, sizeof(*chan));
|
||||
|
||||
chan->sock = sock;
|
||||
chan->remote_address = adr;
|
||||
chan->qport = qport;
|
||||
chan->last_received = curtime;
|
||||
chan->incoming_sequence = 0;
|
||||
chan->outgoing_sequence = 1;
|
||||
|
||||
SZ_Init(&chan->message, chan->message_buf, sizeof(chan->message_buf));
|
||||
chan->message.allowoverflow = true;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Netchan_CanReliable
|
||||
|
||||
Returns true if the last reliable message has acked
|
||||
================
|
||||
*/
|
||||
qboolean Netchan_CanReliable(netchan_t * chan)
|
||||
{
|
||||
if (chan->reliable_length)
|
||||
return false; // waiting for ack
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean Netchan_NeedReliable(netchan_t * chan)
|
||||
{
|
||||
qboolean send_reliable;
|
||||
|
||||
// if the remote side dropped the last reliable message, resend it
|
||||
send_reliable = false;
|
||||
|
||||
if (chan->incoming_acknowledged > chan->last_reliable_sequence && chan->incoming_reliable_acknowledged != chan->reliable_sequence)
|
||||
send_reliable = true;
|
||||
|
||||
// if the reliable transmit buffer is empty, copy the current message out
|
||||
if (!chan->reliable_length && chan->message.cursize)
|
||||
{
|
||||
send_reliable = true;
|
||||
}
|
||||
|
||||
return send_reliable;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
Netchan_Transmit
|
||||
|
||||
tries to send an unreliable message to a connection, and handles the
|
||||
transmition / retransmition of the reliable messages.
|
||||
|
||||
A 0 length will still generate a packet and deal with the reliable messages.
|
||||
================
|
||||
*/
|
||||
void Netchan_Transmit(netchan_t * chan, int length, byte * data)
|
||||
{
|
||||
sizebuf_t send;
|
||||
byte send_buf[MAX_MSGLEN];
|
||||
qboolean send_reliable;
|
||||
unsigned w1, w2;
|
||||
|
||||
// check for message overflow
|
||||
if (chan->message.overflowed)
|
||||
{
|
||||
chan->fatal_error = true;
|
||||
Com_Printf("%s:Outgoing message overflow\n", NET_AdrToString(chan->remote_address));
|
||||
return;
|
||||
}
|
||||
|
||||
send_reliable = Netchan_NeedReliable(chan);
|
||||
|
||||
if (!chan->reliable_length && chan->message.cursize)
|
||||
{
|
||||
memcpy(chan->reliable_buf, chan->message_buf, chan->message.cursize);
|
||||
chan->reliable_length = chan->message.cursize;
|
||||
chan->message.cursize = 0;
|
||||
chan->reliable_sequence ^= 1;
|
||||
}
|
||||
|
||||
// write the packet header
|
||||
SZ_Init(&send, send_buf, sizeof(send_buf));
|
||||
|
||||
w1 = (chan->outgoing_sequence & ~(1 << 31)) | (send_reliable << 31);
|
||||
w2 = (chan->incoming_sequence & ~(1 << 31)) | (chan->incoming_reliable_sequence << 31);
|
||||
|
||||
chan->outgoing_sequence++;
|
||||
chan->last_sent = curtime;
|
||||
|
||||
MSG_WriteLong(&send, w1);
|
||||
MSG_WriteLong(&send, w2);
|
||||
|
||||
// send the qport if we are a client
|
||||
if (chan->sock == NS_CLIENT)
|
||||
MSG_WriteShort(&send, qport->value);
|
||||
|
||||
// copy the reliable message to the packet first
|
||||
if (send_reliable)
|
||||
{
|
||||
SZ_Write(&send, chan->reliable_buf, chan->reliable_length);
|
||||
chan->last_reliable_sequence = chan->outgoing_sequence;
|
||||
}
|
||||
|
||||
// add the unreliable part if space is available
|
||||
if (send.maxsize - send.cursize >= length)
|
||||
SZ_Write(&send, data, length);
|
||||
else
|
||||
Com_Printf("Netchan_Transmit: dumped unreliable\n");
|
||||
|
||||
// send the datagram
|
||||
NET_SendPacket(chan->sock, send.cursize, send.data, chan->remote_address);
|
||||
|
||||
if (showpackets->value)
|
||||
{
|
||||
if (send_reliable)
|
||||
Com_Printf("send %4i : s=%i reliable=%i ack=%i rack=%i\n", send.cursize, chan->outgoing_sequence - 1, chan->reliable_sequence, chan->incoming_sequence, chan->incoming_reliable_sequence);
|
||||
else
|
||||
Com_Printf("send %4i : s=%i ack=%i rack=%i\n", send.cursize, chan->outgoing_sequence - 1, chan->incoming_sequence, chan->incoming_reliable_sequence);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Netchan_Process
|
||||
|
||||
called when the current net_message is from remote_address
|
||||
modifies net_message so that it points to the packet payload
|
||||
=================
|
||||
*/
|
||||
qboolean Netchan_Process(netchan_t * chan, sizebuf_t * msg)
|
||||
{
|
||||
unsigned sequence, sequence_ack;
|
||||
unsigned reliable_ack, reliable_message;
|
||||
int qport;
|
||||
|
||||
// get sequence numbers
|
||||
MSG_BeginReading(msg);
|
||||
sequence = MSG_ReadLong(msg);
|
||||
sequence_ack = MSG_ReadLong(msg);
|
||||
|
||||
// read the qport if we are a server
|
||||
if (chan->sock == NS_SERVER)
|
||||
qport = MSG_ReadShort(msg);
|
||||
|
||||
reliable_message = sequence >> 31;
|
||||
reliable_ack = sequence_ack >> 31;
|
||||
|
||||
sequence &= ~(1 << 31);
|
||||
sequence_ack &= ~(1 << 31);
|
||||
|
||||
if (showpackets->value)
|
||||
{
|
||||
if (reliable_message)
|
||||
Com_Printf("recv %4i : s=%i reliable=%i ack=%i rack=%i\n", msg->cursize, sequence, chan->incoming_reliable_sequence ^ 1, sequence_ack, reliable_ack);
|
||||
else
|
||||
Com_Printf("recv %4i : s=%i ack=%i rack=%i\n", msg->cursize, sequence, sequence_ack, reliable_ack);
|
||||
}
|
||||
|
||||
//
|
||||
// discard stale or duplicated packets
|
||||
//
|
||||
if (sequence <= chan->incoming_sequence)
|
||||
{
|
||||
if (showdrop->value)
|
||||
Com_Printf("%s:Out of order packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence);
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// dropped packets don't keep the message from being used
|
||||
//
|
||||
chan->dropped = sequence - (chan->incoming_sequence + 1);
|
||||
if (chan->dropped > 0)
|
||||
{
|
||||
if (showdrop->value)
|
||||
Com_Printf("%s:Dropped %i packets at %i\n", NET_AdrToString(chan->remote_address), chan->dropped, sequence);
|
||||
}
|
||||
|
||||
//
|
||||
// if the current outgoing reliable message has been acknowledged
|
||||
// clear the buffer to make way for the next
|
||||
//
|
||||
if (reliable_ack == chan->reliable_sequence)
|
||||
chan->reliable_length = 0; // it has been received
|
||||
|
||||
//
|
||||
// if this message contains a reliable message, bump incoming_reliable_sequence
|
||||
//
|
||||
chan->incoming_sequence = sequence;
|
||||
chan->incoming_acknowledged = sequence_ack;
|
||||
chan->incoming_reliable_acknowledged = reliable_ack;
|
||||
if (reliable_message)
|
||||
{
|
||||
chan->incoming_reliable_sequence ^= 1;
|
||||
}
|
||||
|
||||
//
|
||||
// the message can now be read from the current message pointer
|
||||
//
|
||||
chan->last_received = curtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
+1356
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,828 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
// Definitions common between client and server, but not game.dll
|
||||
//
|
||||
#ifndef Q_COMMON_H
|
||||
#define Q_COMMON_H
|
||||
|
||||
#include "ps2/math_funcs.h"
|
||||
#include "game/q_shared.h"
|
||||
|
||||
#define VERSION 3.19
|
||||
#define BASEDIRNAME "baseq2"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define BUILDSTRING "Win32 RELEASE"
|
||||
#else
|
||||
#define BUILDSTRING "Win32 DEBUG"
|
||||
#endif
|
||||
|
||||
#ifdef _M_IX86
|
||||
#define CPUSTRING "x86"
|
||||
#elif defined _M_ALPHA
|
||||
#define CPUSTRING "AXP"
|
||||
#endif
|
||||
|
||||
#elif defined __linux__
|
||||
|
||||
#define BUILDSTRING "Linux"
|
||||
|
||||
#ifdef __i386__
|
||||
#define CPUSTRING "i386"
|
||||
#elif defined __alpha__
|
||||
#define CPUSTRING "axp"
|
||||
#else
|
||||
#define CPUSTRING "Unknown"
|
||||
#endif
|
||||
|
||||
#elif defined __sun__
|
||||
|
||||
#define BUILDSTRING "Solaris"
|
||||
|
||||
#ifdef __i386__
|
||||
#define CPUSTRING "i386"
|
||||
#else
|
||||
#define CPUSTRING "sparc"
|
||||
#endif
|
||||
|
||||
#elif defined PS2_QUAKE
|
||||
|
||||
// LAMPERT: 2015-10-26
|
||||
#define BUILDSTRING "PS2-QUAKE"
|
||||
#define CPUSTRING "EE-MIPS-R5900-LE"
|
||||
|
||||
#else // !WIN32
|
||||
|
||||
#define BUILDSTRING "NON-WIN32"
|
||||
#define CPUSTRING "NON-WIN32"
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(RESTRICT_DEFINED) && !defined(restrict)
|
||||
#define restrict __restrict
|
||||
#define RESTRICT_DEFINED 1
|
||||
#endif // RESTRICT_DEFINED
|
||||
|
||||
//============================================================================
|
||||
|
||||
typedef struct sizebuf_s
|
||||
{
|
||||
qboolean allowoverflow; // if false, do a Com_Error
|
||||
qboolean overflowed; // set to true if the buffer size failed
|
||||
byte * data;
|
||||
int maxsize;
|
||||
int cursize;
|
||||
int readcount;
|
||||
} sizebuf_t;
|
||||
|
||||
void SZ_Init(sizebuf_t * buf, byte * data, int length);
|
||||
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
|
||||
|
||||
//============================================================================
|
||||
|
||||
struct usercmd_s;
|
||||
struct entity_state_s;
|
||||
|
||||
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_WritePos(sizebuf_t * sb, vec3_t pos);
|
||||
void MSG_WriteAngle(sizebuf_t * sb, float f);
|
||||
void MSG_WriteAngle16(sizebuf_t * sb, float f);
|
||||
void MSG_WriteDeltaUsercmd(sizebuf_t * sb, struct usercmd_s * from, struct usercmd_s * cmd);
|
||||
void MSG_WriteDeltaEntity(struct entity_state_s * from, struct entity_state_s * to, sizebuf_t * msg, qboolean force, qboolean newentity);
|
||||
void MSG_WriteDir(sizebuf_t * sb, vec3_t vector);
|
||||
void MSG_BeginReading(sizebuf_t * sb);
|
||||
int MSG_ReadChar(sizebuf_t * sb);
|
||||
int MSG_ReadByte(sizebuf_t * sb);
|
||||
int MSG_ReadShort(sizebuf_t * sb);
|
||||
int MSG_ReadLong(sizebuf_t * sb);
|
||||
float MSG_ReadFloat(sizebuf_t * sb);
|
||||
char * MSG_ReadString(sizebuf_t * sb);
|
||||
char * MSG_ReadStringLine(sizebuf_t * sb);
|
||||
float MSG_ReadCoord(sizebuf_t * sb);
|
||||
void MSG_ReadPos(sizebuf_t * sb, vec3_t pos);
|
||||
float MSG_ReadAngle(sizebuf_t * sb);
|
||||
float MSG_ReadAngle16(sizebuf_t * sb);
|
||||
void MSG_ReadDeltaUsercmd(sizebuf_t * sb, struct usercmd_s * from, struct usercmd_s * cmd);
|
||||
void MSG_ReadDir(sizebuf_t * sb, vec3_t vector);
|
||||
void MSG_ReadData(sizebuf_t * sb, void * buffer, int size);
|
||||
|
||||
//============================================================================
|
||||
|
||||
extern qboolean bigendien;
|
||||
|
||||
short BigShort(short l);
|
||||
short LittleShort(short l);
|
||||
int BigLong(int l);
|
||||
int LittleLong(int l);
|
||||
float BigFloat(float l);
|
||||
float LittleFloat(float l);
|
||||
|
||||
//============================================================================
|
||||
|
||||
int COM_Argc(void);
|
||||
char * COM_Argv(int arg); // range and null checked
|
||||
void COM_ClearArgv(int arg);
|
||||
int COM_CheckParm(const char * parm);
|
||||
void COM_AddParm(char * parm);
|
||||
|
||||
void COM_Init(void);
|
||||
void COM_InitArgv(int argc, char ** argv);
|
||||
|
||||
char * CopyString(const char * in);
|
||||
|
||||
//============================================================================
|
||||
|
||||
void Info_Print(char * s);
|
||||
|
||||
/* crc.h */
|
||||
void CRC_Init(unsigned short * crcvalue);
|
||||
void CRC_ProcessByte(unsigned short * crcvalue, byte data);
|
||||
unsigned short CRC_Value(unsigned short crcvalue);
|
||||
unsigned short CRC_Block(byte * start, int count);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
PROTOCOL
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
// protocol.h -- communications protocols
|
||||
|
||||
#define PROTOCOL_VERSION 34
|
||||
|
||||
//=========================================
|
||||
|
||||
#define PORT_MASTER 27900
|
||||
#define PORT_CLIENT 27901
|
||||
#define PORT_SERVER 27910
|
||||
|
||||
//=========================================
|
||||
|
||||
#define UPDATE_BACKUP 16 // copies of entity_state_t to keep buffered must be power of two
|
||||
#define UPDATE_MASK (UPDATE_BACKUP - 1)
|
||||
|
||||
//==================
|
||||
// the svc_strings[] array in cl_parse.c should mirror this
|
||||
//==================
|
||||
|
||||
//
|
||||
// server to client
|
||||
//
|
||||
enum svc_ops_e
|
||||
{
|
||||
svc_bad,
|
||||
|
||||
// these ops are known to the game dll
|
||||
svc_muzzleflash,
|
||||
svc_muzzleflash2,
|
||||
svc_temp_entity,
|
||||
svc_layout,
|
||||
svc_inventory,
|
||||
|
||||
// the rest are private to the client and server
|
||||
svc_nop,
|
||||
svc_disconnect,
|
||||
svc_reconnect,
|
||||
svc_sound, // <see code>
|
||||
svc_print, // [byte] id [string] null terminated string
|
||||
svc_stufftext, // [string] stuffed into client's console buffer, should be \n terminated
|
||||
svc_serverdata, // [long] protocol ...
|
||||
svc_configstring, // [short] [string]
|
||||
svc_spawnbaseline,
|
||||
svc_centerprint, // [string] to put in center of the screen
|
||||
svc_download, // [short] size [size bytes]
|
||||
svc_playerinfo, // variable
|
||||
svc_packetentities, // [...]
|
||||
svc_deltapacketentities, // [...]
|
||||
svc_frame
|
||||
};
|
||||
|
||||
//==============================================
|
||||
|
||||
//
|
||||
// client to server
|
||||
//
|
||||
enum clc_ops_e
|
||||
{
|
||||
clc_bad,
|
||||
clc_nop,
|
||||
clc_move, // [[usercmd_t]
|
||||
clc_userinfo, // [[userinfo string]
|
||||
clc_stringcmd // [string] message
|
||||
};
|
||||
|
||||
//==============================================
|
||||
|
||||
// plyer_state_t communication
|
||||
|
||||
#define PS_M_TYPE (1 << 0)
|
||||
#define PS_M_ORIGIN (1 << 1)
|
||||
#define PS_M_VELOCITY (1 << 2)
|
||||
#define PS_M_TIME (1 << 3)
|
||||
#define PS_M_FLAGS (1 << 4)
|
||||
#define PS_M_GRAVITY (1 << 5)
|
||||
#define PS_M_DELTA_ANGLES (1 << 6)
|
||||
|
||||
#define PS_VIEWOFFSET (1 << 7)
|
||||
#define PS_VIEWANGLES (1 << 8)
|
||||
#define PS_KICKANGLES (1 << 9)
|
||||
#define PS_BLEND (1 << 10)
|
||||
#define PS_FOV (1 << 11)
|
||||
#define PS_WEAPONINDEX (1 << 12)
|
||||
#define PS_WEAPONFRAME (1 << 13)
|
||||
#define PS_RDFLAGS (1 << 14)
|
||||
|
||||
//==============================================
|
||||
|
||||
// user_cmd_t communication
|
||||
|
||||
// ms and light always sent, the others are optional
|
||||
#define CM_ANGLE1 (1 << 0)
|
||||
#define CM_ANGLE2 (1 << 1)
|
||||
#define CM_ANGLE3 (1 << 2)
|
||||
#define CM_FORWARD (1 << 3)
|
||||
#define CM_SIDE (1 << 4)
|
||||
#define CM_UP (1 << 5)
|
||||
#define CM_BUTTONS (1 << 6)
|
||||
#define CM_IMPULSE (1 << 7)
|
||||
|
||||
//==============================================
|
||||
|
||||
// a sound without an ent or pos will be a local only sound
|
||||
#define SND_VOLUME (1 << 0) // a byte
|
||||
#define SND_ATTENUATION (1 << 1) // a byte
|
||||
#define SND_POS (1 << 2) // three coordinates
|
||||
#define SND_ENT (1 << 3) // a short 0-2: channel, 3-12: entity
|
||||
#define SND_OFFSET (1 << 4) // a byte, msec offset from frame start
|
||||
|
||||
#define DEFAULT_SOUND_PACKET_VOLUME 1.0
|
||||
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
|
||||
|
||||
//==============================================
|
||||
|
||||
// entity_state_t communication
|
||||
|
||||
// try to pack the common update flags into the first byte
|
||||
#define U_ORIGIN1 (1 << 0)
|
||||
#define U_ORIGIN2 (1 << 1)
|
||||
#define U_ANGLE2 (1 << 2)
|
||||
#define U_ANGLE3 (1 << 3)
|
||||
#define U_FRAME8 (1 << 4) // frame is a byte
|
||||
#define U_EVENT (1 << 5)
|
||||
#define U_REMOVE (1 << 6) // REMOVE this entity, don't add it
|
||||
#define U_MOREBITS1 (1 << 7) // read one additional byte
|
||||
|
||||
// second byte
|
||||
#define U_NUMBER16 (1 << 8) // NUMBER8 is implicit if not set
|
||||
#define U_ORIGIN3 (1 << 9)
|
||||
#define U_ANGLE1 (1 << 10)
|
||||
#define U_MODEL (1 << 11)
|
||||
#define U_RENDERFX8 (1 << 12) // fullbright, etc
|
||||
#define U_EFFECTS8 (1 << 14) // autorotate, trails, etc
|
||||
#define U_MOREBITS2 (1 << 15) // read one additional byte
|
||||
|
||||
// third byte
|
||||
#define U_SKIN8 (1 << 16)
|
||||
#define U_FRAME16 (1 << 17) // frame is a short
|
||||
#define U_RENDERFX16 (1 << 18) // 8 + 16 = 32
|
||||
#define U_EFFECTS16 (1 << 19) // 8 + 16 = 32
|
||||
#define U_MODEL2 (1 << 20) // weapons, flags, etc
|
||||
#define U_MODEL3 (1 << 21)
|
||||
#define U_MODEL4 (1 << 22)
|
||||
#define U_MOREBITS3 (1 << 23) // read one additional byte
|
||||
|
||||
// fourth byte
|
||||
#define U_OLDORIGIN (1 << 24) // FIXME: get rid of this
|
||||
#define U_SKIN16 (1 << 25)
|
||||
#define U_SOUND (1 << 26)
|
||||
#define U_SOLID (1 << 27)
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
CMD
|
||||
|
||||
Command text buffering and command execution
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Any number of commands can be added in a frame, from several different sources.
|
||||
Most commands come from either keybindings or console line input, but remote
|
||||
servers can also send across commands and entire text files can be execed.
|
||||
|
||||
The + command line options are also added to the command buffer.
|
||||
|
||||
The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
|
||||
|
||||
*/
|
||||
|
||||
#define EXEC_NOW 0 // don't return until completed
|
||||
#define EXEC_INSERT 1 // insert at current position, but don't run yet
|
||||
#define EXEC_APPEND 2 // add to end of the command buffer
|
||||
|
||||
void Cbuf_Init(void);
|
||||
// allocates an initial text buffer that will grow as needed
|
||||
|
||||
void Cbuf_AddText(char * text);
|
||||
// as new commands are generated from the console or keybindings,
|
||||
// the text is added to the end of the command buffer.
|
||||
|
||||
void Cbuf_InsertText(char * text);
|
||||
// when a command wants to issue other commands immediately, the text is
|
||||
// inserted at the beginning of the buffer, before any remaining unexecuted
|
||||
// commands.
|
||||
|
||||
void Cbuf_ExecuteText(int exec_when, char * text);
|
||||
// this can be used in place of either Cbuf_AddText or Cbuf_InsertText
|
||||
|
||||
void Cbuf_AddEarlyCommands(qboolean clear);
|
||||
// adds all the +set commands from the command line
|
||||
|
||||
qboolean Cbuf_AddLateCommands(void);
|
||||
// adds all the remaining + commands from the command line
|
||||
// Returns true if any late commands were added, which
|
||||
// will keep the demoloop from immediately starting
|
||||
|
||||
void Cbuf_Execute(void);
|
||||
// Pulls off \n terminated lines of text from the command buffer and sends
|
||||
// them through Cmd_ExecuteString. Stops when the buffer is empty.
|
||||
// Normally called once per frame, but may be explicitly invoked.
|
||||
// Do not call inside a command function!
|
||||
|
||||
void Cbuf_CopyToDefer(void);
|
||||
void Cbuf_InsertFromDefer(void);
|
||||
// These two functions are used to defer any pending commands while a map
|
||||
// is being loaded
|
||||
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
|
||||
Command execution takes a null terminated string, breaks it into tokens,
|
||||
then searches for a command or variable that matches the first token.
|
||||
|
||||
*/
|
||||
|
||||
typedef void (*xcommand_t)(void);
|
||||
|
||||
void Cmd_Init(void);
|
||||
|
||||
void Cmd_AddCommand(char * cmd_name, xcommand_t function);
|
||||
// called by the init functions of other parts of the program to
|
||||
// register commands and functions to call for them.
|
||||
// The cmd_name is referenced later, so it should not be in temp memory
|
||||
// if function is NULL, the command will be forwarded to the server
|
||||
// as a clc_stringcmd instead of executed locally
|
||||
void Cmd_RemoveCommand(char * cmd_name);
|
||||
|
||||
qboolean Cmd_Exists(const char * cmd_name);
|
||||
// used by the cvar code to check for cvar / command name overlap
|
||||
|
||||
char * Cmd_CompleteCommand(char * partial);
|
||||
// attempts to match a partial command for automatic command line completion
|
||||
// returns NULL if nothing fits
|
||||
|
||||
int Cmd_Argc(void);
|
||||
char * Cmd_Argv(int arg);
|
||||
char * Cmd_Args(void);
|
||||
// The functions that execute commands get their parameters with these
|
||||
// functions. Cmd_Argv () will return an empty string, not a NULL
|
||||
// if arg > argc, so string operations are always safe.
|
||||
|
||||
void Cmd_TokenizeString(char * text, qboolean macroExpand);
|
||||
// Takes a null terminated string. Does not need to be /n terminated.
|
||||
// breaks the string up into arg tokens.
|
||||
|
||||
void Cmd_ExecuteString(char * text);
|
||||
// Parses a single line of text into arguments and tries to execute it
|
||||
// as if it was typed at the console
|
||||
|
||||
void Cmd_ForwardToServer(void);
|
||||
// adds the current command line as a clc_stringcmd to the client message.
|
||||
// things like godmode, noclip, etc, are commands directed to the server,
|
||||
// so when they are typed in at the console, they will need to be forwarded.
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
CVAR
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
cvar_t variables are used to hold scalar or string variables that can be changed or
|
||||
displayed at the console or prog code as well as accessed directly
|
||||
in C code.
|
||||
|
||||
The user can access cvars from the console in three ways:
|
||||
r_draworder prints the current value
|
||||
r_draworder 0 sets the current value to 0
|
||||
set r_draworder 0 as above, but creates the cvar if not present
|
||||
Cvars are restricted from having the same names as commands to keep this
|
||||
interface from being ambiguous.
|
||||
*/
|
||||
extern cvar_t * cvar_vars;
|
||||
|
||||
cvar_t * Cvar_Get(const char * var_name, const char * value, int flags);
|
||||
// creates the variable if it doesn't exist, or returns the existing one
|
||||
// if it exists, the value will not be changed, but flags will be ORed in
|
||||
// that allows variables to be unarchived without needing bitflags
|
||||
|
||||
cvar_t * Cvar_Set(const char * var_name, const char * value);
|
||||
// will create the variable if it doesn't exist
|
||||
|
||||
cvar_t * Cvar_ForceSet(const char * var_name, const char * value);
|
||||
// will set the variable even if NOSET or LATCH
|
||||
|
||||
cvar_t * Cvar_FullSet(const char * var_name, const char * value, int flags);
|
||||
|
||||
void Cvar_SetValue(const char * var_name, float value);
|
||||
// expands value to a string and calls Cvar_Set
|
||||
|
||||
float Cvar_VariableValue(const char * var_name);
|
||||
// returns 0 if not defined or non numeric
|
||||
|
||||
char * Cvar_VariableString(const char * var_name);
|
||||
// returns an empty string if not defined
|
||||
|
||||
char * Cvar_CompleteVariable(const char * partial);
|
||||
// attempts to match a partial variable name for command line completion
|
||||
// returns NULL if nothing fits
|
||||
|
||||
void Cvar_GetLatchedVars(void);
|
||||
// any CVAR_LATCHED variables that have been set will now take effect
|
||||
|
||||
qboolean Cvar_Command(void);
|
||||
// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
|
||||
// command. Returns true if the command was a variable reference that
|
||||
// was handled. (print or change)
|
||||
|
||||
void Cvar_WriteVariables(const char * path);
|
||||
// appends lines containing "set variable value" for all variables
|
||||
// with the archive flag set to true.
|
||||
|
||||
void Cvar_Init(void);
|
||||
|
||||
char * Cvar_Userinfo(void);
|
||||
// returns an info string containing all the CVAR_USERINFO cvars
|
||||
|
||||
char * Cvar_Serverinfo(void);
|
||||
// returns an info string containing all the CVAR_SERVERINFO cvars
|
||||
|
||||
extern qboolean userinfo_modified;
|
||||
// this is set each time a CVAR_USERINFO variable is changed
|
||||
// so that the client knows to send it to the server
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
NET
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
// net.h -- quake's interface to the networking layer
|
||||
|
||||
enum
|
||||
{
|
||||
PORT_ANY = -1,
|
||||
MAX_MSGLEN = 1400, // max length of a message
|
||||
PACKET_HEADER = 10 // two ints and a short
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NA_LOOPBACK,
|
||||
NA_BROADCAST,
|
||||
NA_IP,
|
||||
NA_IPX,
|
||||
NA_BROADCAST_IPX
|
||||
} netadrtype_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NS_CLIENT,
|
||||
NS_SERVER
|
||||
} netsrc_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
netadrtype_t type;
|
||||
|
||||
byte ip[4];
|
||||
byte ipx[10];
|
||||
|
||||
unsigned short port;
|
||||
} netadr_t;
|
||||
|
||||
void NET_Init(void);
|
||||
void NET_Shutdown(void);
|
||||
void NET_Config(qboolean multiplayer);
|
||||
|
||||
qboolean NET_GetPacket(netsrc_t sock, netadr_t * net_from, sizebuf_t * net_message);
|
||||
void NET_SendPacket(netsrc_t sock, int length, void * data, netadr_t to);
|
||||
|
||||
qboolean NET_CompareAdr(netadr_t a, netadr_t b);
|
||||
qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b);
|
||||
qboolean NET_IsLocalAddress(netadr_t adr);
|
||||
char * NET_AdrToString(netadr_t a);
|
||||
qboolean NET_StringToAdr(char * s, netadr_t * a);
|
||||
void NET_Sleep(int msec);
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG)
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_LATENT = 32
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean fatal_error;
|
||||
|
||||
netsrc_t sock;
|
||||
int dropped; // between last packet and previous
|
||||
|
||||
int last_received; // for timeouts
|
||||
int last_sent; // for retransmits
|
||||
|
||||
netadr_t remote_address;
|
||||
int qport; // qport value to write when transmitting
|
||||
|
||||
// sequencing variables
|
||||
int incoming_sequence;
|
||||
int incoming_acknowledged;
|
||||
int incoming_reliable_acknowledged; // single bit
|
||||
int incoming_reliable_sequence; // single bit, maintained local
|
||||
|
||||
int outgoing_sequence;
|
||||
int reliable_sequence; // single bit
|
||||
int last_reliable_sequence; // sequence number of last send
|
||||
|
||||
// reliable staging and holding areas
|
||||
sizebuf_t message; // writing buffer to send to server
|
||||
byte message_buf[MAX_MSGLEN - 16]; // leave space for header
|
||||
|
||||
// message is copied to this buffer when it is first transfered
|
||||
int reliable_length;
|
||||
byte reliable_buf[MAX_MSGLEN - 16]; // unacked reliable message
|
||||
} netchan_t;
|
||||
|
||||
extern netadr_t net_from;
|
||||
extern sizebuf_t net_message;
|
||||
extern byte net_message_buffer[MAX_MSGLEN];
|
||||
|
||||
void Netchan_Init(void);
|
||||
void Netchan_Setup(netsrc_t sock, netchan_t * chan, netadr_t adr, int qport);
|
||||
|
||||
qboolean Netchan_NeedReliable(netchan_t * chan);
|
||||
void Netchan_Transmit(netchan_t * chan, int length, byte * data);
|
||||
void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte * data);
|
||||
void Netchan_OutOfBandPrint(int net_socket, netadr_t adr, char * format, ...);
|
||||
qboolean Netchan_Process(netchan_t * chan, sizebuf_t * msg);
|
||||
qboolean Netchan_CanReliable(netchan_t * chan);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
CMODEL
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
#include "common/q_files.h"
|
||||
|
||||
cmodel_t * CM_LoadMap(char * name, qboolean clientload, unsigned * checksum);
|
||||
cmodel_t * CM_InlineModel(char * name); // *1, *2, etc
|
||||
|
||||
int CM_NumClusters(void);
|
||||
int CM_NumInlineModels(void);
|
||||
char * CM_EntityString(void);
|
||||
|
||||
// creates a clipping hull for an arbitrary box
|
||||
int CM_HeadnodeForBox(vec3_t mins, vec3_t maxs);
|
||||
|
||||
// returns an ORed contents mask
|
||||
int CM_PointContents(vec3_t p, int headnode);
|
||||
int CM_TransformedPointContents(vec3_t p, int headnode, vec3_t origin, vec3_t angles);
|
||||
|
||||
trace_t CM_BoxTrace(vec3_t start, vec3_t end,
|
||||
vec3_t mins, vec3_t maxs,
|
||||
int headnode, int brushmask);
|
||||
|
||||
trace_t CM_TransformedBoxTrace(vec3_t start, vec3_t end,
|
||||
vec3_t mins, vec3_t maxs,
|
||||
int headnode, int brushmask,
|
||||
vec3_t origin, vec3_t angles);
|
||||
|
||||
byte * CM_ClusterPVS(int cluster);
|
||||
byte * CM_ClusterPHS(int cluster);
|
||||
|
||||
int CM_PointLeafnum(vec3_t p);
|
||||
|
||||
// call with topnode set to the headnode, returns with topnode
|
||||
// set to the first node that splits the box
|
||||
int CM_BoxLeafnums(vec3_t mins, vec3_t maxs, int * list,
|
||||
int listsize, int * topnode);
|
||||
|
||||
int CM_LeafContents(int leafnum);
|
||||
int CM_LeafCluster(int leafnum);
|
||||
int CM_LeafArea(int leafnum);
|
||||
|
||||
void CM_SetAreaPortalState(int portalnum, qboolean open);
|
||||
qboolean CM_AreasConnected(int area1, int area2);
|
||||
|
||||
int CM_WriteAreaBits(byte * buffer, int area);
|
||||
qboolean CM_HeadnodeVisible(int headnode, byte * visbits);
|
||||
|
||||
void CM_WritePortalState(FILE * f);
|
||||
void CM_ReadPortalState(FILE * f);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
PLAYER MOVEMENT CODE
|
||||
|
||||
Common between server and client so prediction matches
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
extern float pm_airaccelerate;
|
||||
void Pmove(pmove_t * pmove);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
FILESYSTEM
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
void FS_InitFilesystem(void);
|
||||
void FS_SetGamedir(const char * dir);
|
||||
void FS_SetDefaultBasePath(const char * path);
|
||||
char * FS_Gamedir(void);
|
||||
char * FS_NextPath(char * prevpath);
|
||||
void FS_ExecAutoexec(void);
|
||||
|
||||
int FS_FOpenFile(const char * filename, FILE ** file);
|
||||
void FS_FCloseFile(FILE * f);
|
||||
// note: this can't be called from another DLL, due to MS libc issues
|
||||
|
||||
int FS_LoadFile(const char * path, void ** buffer);
|
||||
// a null buffer will just return the file length without loading
|
||||
// a -1 length is not present
|
||||
|
||||
void FS_Read(void * buffer, int len, FILE * f);
|
||||
// properly handles partial reads
|
||||
|
||||
void FS_FreeFile(void * buffer);
|
||||
void FS_CreatePath(char * path);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
MISC
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
#define ERR_FATAL 0 // exit the entire game with a popup window
|
||||
#define ERR_DROP 1 // print to console and disconnect from game
|
||||
#define ERR_QUIT 2 // not an error, just a normal exit
|
||||
|
||||
#define EXEC_NOW 0 // don't return until completed
|
||||
#define EXEC_INSERT 1 // insert at current position, but don't run yet
|
||||
#define EXEC_APPEND 2 // add to end of the command buffer
|
||||
|
||||
#define PRINT_ALL 0
|
||||
#define PRINT_DEVELOPER 1 // only print when "developer 1"
|
||||
|
||||
void Com_BeginRedirect(int target, char * buffer, int buffersize, void(*flush));
|
||||
void Com_EndRedirect(void);
|
||||
void Com_Printf(const char * fmt, ...);
|
||||
void Com_DPrintf(const char * fmt, ...);
|
||||
void Com_Error(int code, const char * fmt, ...);
|
||||
void Com_Quit(void);
|
||||
|
||||
int Com_ServerState(void); // this should have just been a cvar...
|
||||
void Com_SetServerState(int state);
|
||||
|
||||
unsigned Com_BlockChecksum(void * buffer, int length);
|
||||
byte COM_BlockSequenceCRCByte(byte * base, int length, int sequence);
|
||||
|
||||
float frand(void); // 0 to 1
|
||||
float crand(void); // -1 to 1
|
||||
|
||||
extern cvar_t * developer;
|
||||
extern cvar_t * dedicated;
|
||||
extern cvar_t * host_speeds;
|
||||
extern cvar_t * log_stats;
|
||||
|
||||
extern FILE * log_stats_file;
|
||||
|
||||
// host_speeds times
|
||||
extern int time_before_game;
|
||||
extern int time_after_game;
|
||||
extern int time_before_ref;
|
||||
extern int time_after_ref;
|
||||
|
||||
void Z_Free(void * ptr);
|
||||
void * Z_Malloc(int size); // returns 0 filled memory
|
||||
void * Z_TagMalloc(int size, int tag);
|
||||
void Z_FreeTags(int tag);
|
||||
|
||||
void Qcommon_Init(int argc, char ** argv);
|
||||
void Qcommon_Frame(int msec);
|
||||
void Qcommon_Shutdown(void);
|
||||
|
||||
#define NUMVERTEXNORMALS 162
|
||||
extern vec3_t bytedirs[NUMVERTEXNORMALS];
|
||||
|
||||
// this is in the client code, but can be used for debugging from server
|
||||
void SCR_DebugGraph(float value, int color);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
NON-PORTABLE SYSTEM SERVICES
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
void Sys_Init(void);
|
||||
void Sys_AppActivate(void);
|
||||
|
||||
// loads the game dll and calls the api init function
|
||||
void * Sys_GetGameAPI(void * parms);
|
||||
void Sys_UnloadGame(void);
|
||||
|
||||
char * Sys_ConsoleInput(void);
|
||||
void Sys_ConsoleOutput(const char * string);
|
||||
void Sys_SendKeyEvents(void);
|
||||
|
||||
void Sys_Error(const char * error, ...);
|
||||
void Sys_Quit(void);
|
||||
|
||||
char * Sys_GetClipboardData(void);
|
||||
void Sys_CopyProtect(void);
|
||||
|
||||
/*
|
||||
==============================================================
|
||||
|
||||
CLIENT / SERVER SYSTEMS
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
|
||||
void CL_Init(void);
|
||||
void CL_Drop(void);
|
||||
void CL_Shutdown(void);
|
||||
void CL_Frame(int msec);
|
||||
void Con_Print(char * text);
|
||||
void SCR_BeginLoadingPlaque(void);
|
||||
|
||||
void SV_Init(void);
|
||||
void SV_Shutdown(char * finalmsg, qboolean reconnect);
|
||||
void SV_Frame(int msec);
|
||||
|
||||
#endif // Q_COMMON_H
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 Q_CRC_H
|
||||
#define Q_CRC_H
|
||||
|
||||
void CRC_Init(unsigned short * crcvalue);
|
||||
void CRC_ProcessByte(unsigned short * crcvalue, byte data);
|
||||
unsigned short CRC_Value(unsigned short crcvalue);
|
||||
unsigned short CRC_Block(byte * start, int count);
|
||||
|
||||
#endif // Q_CRC_H
|
||||
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 Q_FILES_H
|
||||
#define Q_FILES_H
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
The .pak files are just a linear collapse of a directory tree
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define MAX_FILES_IN_PACK 4096
|
||||
#define IDPAKHEADER (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P')
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[56];
|
||||
int filepos, filelen;
|
||||
} dpackfile_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident; // == IDPAKHEADER
|
||||
int dirofs;
|
||||
int dirlen;
|
||||
} dpackheader_t;
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
PCX files are used for as many images as possible
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char manufacturer;
|
||||
char version;
|
||||
char encoding;
|
||||
char bits_per_pixel;
|
||||
unsigned short xmin, ymin, xmax, ymax;
|
||||
unsigned short hres, vres;
|
||||
unsigned char palette[48];
|
||||
char reserved;
|
||||
char color_planes;
|
||||
unsigned short bytes_per_line;
|
||||
unsigned short palette_type;
|
||||
char filler[58];
|
||||
unsigned char data; // unbounded
|
||||
} pcx_t;
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
.MD2 triangle model file format
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define IDALIASHEADER (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')
|
||||
#define ALIAS_VERSION 8
|
||||
|
||||
#define MAX_TRIANGLES 4096
|
||||
#define MAX_VERTS 2048
|
||||
#define MAX_FRAMES 512
|
||||
#define MAX_MD2SKINS 32
|
||||
#define MAX_SKINNAME 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short s;
|
||||
short t;
|
||||
} dstvert_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
short index_xyz[3];
|
||||
short index_st[3];
|
||||
} dtriangle_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
byte v[3]; // scaled byte to fit in frame mins/maxs
|
||||
byte lightnormalindex;
|
||||
} dtrivertx_t;
|
||||
|
||||
#define DTRIVERTX_V0 0
|
||||
#define DTRIVERTX_V1 1
|
||||
#define DTRIVERTX_V2 2
|
||||
#define DTRIVERTX_LNI 3
|
||||
#define DTRIVERTX_SIZE 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float scale[3]; // multiply byte verts by this
|
||||
float translate[3]; // then add this
|
||||
char name[16]; // frame name from grabbing
|
||||
dtrivertx_t verts[1]; // variable sized
|
||||
} daliasframe_t;
|
||||
|
||||
// the glcmd format:
|
||||
// a positive integer starts a tristrip command, followed by that many
|
||||
// vertex structures.
|
||||
// a negative integer starts a trifan command, followed by -x vertexes
|
||||
// a zero indicates the end of the command list.
|
||||
// a vertex consists of a floating point s, a floating point t,
|
||||
// and an integer vertex index.
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident;
|
||||
int version;
|
||||
|
||||
int skinwidth;
|
||||
int skinheight;
|
||||
int framesize; // byte size of each frame
|
||||
|
||||
int num_skins;
|
||||
int num_xyz;
|
||||
int num_st; // greater than num_xyz for seams
|
||||
int num_tris;
|
||||
int num_glcmds; // dwords in strip/fan command list
|
||||
int num_frames;
|
||||
|
||||
int ofs_skins; // each skin is a MAX_SKINNAME string
|
||||
int ofs_st; // byte offset from start for stverts
|
||||
int ofs_tris; // offset for dtriangles
|
||||
int ofs_frames; // offset for first frame
|
||||
int ofs_glcmds;
|
||||
int ofs_end; // end of file
|
||||
|
||||
} dmdl_t;
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
|
||||
.SP2 sprite file format
|
||||
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
#define IDSPRITEHEADER (('2' << 24) + ('S' << 16) + ('D' << 8) + 'I') // little-endian "IDS2"
|
||||
#define SPRITE_VERSION 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int width, height;
|
||||
int origin_x, origin_y; // raster coordinates inside pic
|
||||
char name[MAX_SKINNAME]; // name of pcx file
|
||||
} dsprframe_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident;
|
||||
int version;
|
||||
int numframes;
|
||||
dsprframe_t frames[1]; // variable sized
|
||||
} dsprite_t;
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
.WAL texture file format
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#define MIPLEVELS 4
|
||||
|
||||
typedef struct miptex_s
|
||||
{
|
||||
char name[32];
|
||||
unsigned width, height;
|
||||
unsigned offsets[MIPLEVELS]; // four mip maps stored
|
||||
char animname[32]; // next frame in animation chain
|
||||
int flags;
|
||||
int contents;
|
||||
int value;
|
||||
} miptex_t;
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
.BSP file format
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#define IDBSPHEADER (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I') // little-endian "IBSP"
|
||||
#define BSPVERSION 38
|
||||
|
||||
// upper design bounds
|
||||
// leaffaces, leafbrushes, planes, and verts are still bounded by 16 bit short limits
|
||||
#define MAX_MAP_MODELS 1024
|
||||
#define MAX_MAP_BRUSHES 8192
|
||||
#define MAX_MAP_ENTITIES 2048
|
||||
#define MAX_MAP_ENTSTRING 0x40000
|
||||
#define MAX_MAP_TEXINFO 8192
|
||||
|
||||
#define MAX_MAP_AREAS 256
|
||||
#define MAX_MAP_AREAPORTALS 1024
|
||||
#define MAX_MAP_PLANES 65536
|
||||
#define MAX_MAP_NODES 65536
|
||||
#define MAX_MAP_BRUSHSIDES 65536
|
||||
#define MAX_MAP_LEAFS 65536
|
||||
#define MAX_MAP_VERTS 65536
|
||||
#define MAX_MAP_FACES 65536
|
||||
#define MAX_MAP_LEAFFACES 65536
|
||||
#define MAX_MAP_LEAFBRUSHES 65536
|
||||
#define MAX_MAP_PORTALS 65536
|
||||
#define MAX_MAP_EDGES 128000
|
||||
#define MAX_MAP_SURFEDGES 256000
|
||||
#define MAX_MAP_LIGHTING 0x200000
|
||||
#define MAX_MAP_VISIBILITY 0x100000
|
||||
|
||||
// key / value pair sizes
|
||||
#define MAX_KEY 32
|
||||
#define MAX_VALUE 1024
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fileofs;
|
||||
int filelen;
|
||||
} lump_t;
|
||||
|
||||
#define LUMP_ENTITIES 0
|
||||
#define LUMP_PLANES 1
|
||||
#define LUMP_VERTEXES 2
|
||||
#define LUMP_VISIBILITY 3
|
||||
#define LUMP_NODES 4
|
||||
#define LUMP_TEXINFO 5
|
||||
#define LUMP_FACES 6
|
||||
#define LUMP_LIGHTING 7
|
||||
#define LUMP_LEAFS 8
|
||||
#define LUMP_LEAFFACES 9
|
||||
#define LUMP_LEAFBRUSHES 10
|
||||
#define LUMP_EDGES 11
|
||||
#define LUMP_SURFEDGES 12
|
||||
#define LUMP_MODELS 13
|
||||
#define LUMP_BRUSHES 14
|
||||
#define LUMP_BRUSHSIDES 15
|
||||
#define LUMP_POP 16
|
||||
#define LUMP_AREAS 17
|
||||
#define LUMP_AREAPORTALS 18
|
||||
#define HEADER_LUMPS 19
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ident;
|
||||
int version;
|
||||
lump_t lumps[HEADER_LUMPS];
|
||||
} dheader_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float mins[3], maxs[3];
|
||||
float origin[3]; // for sounds or lights
|
||||
int headnode;
|
||||
int firstface, numfaces; // submodels just draw faces
|
||||
// without walking the bsp tree
|
||||
} dmodel_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float point[3];
|
||||
} dvertex_t;
|
||||
|
||||
// 0-2 are axial planes
|
||||
#define PLANE_X 0
|
||||
#define PLANE_Y 1
|
||||
#define PLANE_Z 2
|
||||
|
||||
// 3-5 are non-axial planes snapped to the nearest
|
||||
#define PLANE_ANYX 3
|
||||
#define PLANE_ANYY 4
|
||||
#define PLANE_ANYZ 5
|
||||
|
||||
// planes (x&~1) and (x&~1)+1 are always opposites
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float normal[3];
|
||||
float dist;
|
||||
int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
|
||||
} dplane_t;
|
||||
|
||||
// contents flags are seperate bits
|
||||
// a given brush can contribute multiple content bits
|
||||
// multiple brushes can be in a single leaf
|
||||
|
||||
// these definitions also need to be in q_shared.h!
|
||||
|
||||
// lower bits are stronger, and will eat weaker brushes completely
|
||||
#define CONTENTS_SOLID 1 // an eye is never valid in a solid
|
||||
#define CONTENTS_WINDOW 2 // translucent, but not watery
|
||||
#define CONTENTS_AUX 4
|
||||
#define CONTENTS_LAVA 8
|
||||
#define CONTENTS_SLIME 16
|
||||
#define CONTENTS_WATER 32
|
||||
#define CONTENTS_MIST 64
|
||||
#define LAST_VISIBLE_CONTENTS 64
|
||||
|
||||
// remaining contents are non-visible, and don't eat brushes
|
||||
#define CONTENTS_AREAPORTAL 0x8000
|
||||
#define CONTENTS_PLAYERCLIP 0x10000
|
||||
#define CONTENTS_MONSTERCLIP 0x20000
|
||||
|
||||
// currents can be added to any other contents, and may be mixed
|
||||
#define CONTENTS_CURRENT_0 0x40000
|
||||
#define CONTENTS_CURRENT_90 0x80000
|
||||
#define CONTENTS_CURRENT_180 0x100000
|
||||
#define CONTENTS_CURRENT_270 0x200000
|
||||
#define CONTENTS_CURRENT_UP 0x400000
|
||||
#define CONTENTS_CURRENT_DOWN 0x800000
|
||||
|
||||
#define CONTENTS_ORIGIN 0x1000000 // removed before bsping an entity
|
||||
#define CONTENTS_MONSTER 0x2000000 // should never be on a brush, only in game
|
||||
#define CONTENTS_DEADMONSTER 0x4000000
|
||||
#define CONTENTS_DETAIL 0x8000000 // brushes to be added after vis leafs
|
||||
#define CONTENTS_TRANSLUCENT 0x10000000 // auto set if any surface has trans
|
||||
#define CONTENTS_LADDER 0x20000000
|
||||
|
||||
#define SURF_LIGHT 0x1 // value will hold the light strength
|
||||
#define SURF_SLICK 0x2 // effects game physics
|
||||
#define SURF_SKY 0x4 // don't draw, but add to skybox
|
||||
#define SURF_WARP 0x8 // turbulent water warp
|
||||
#define SURF_TRANS33 0x10
|
||||
#define SURF_TRANS66 0x20
|
||||
#define SURF_FLOWING 0x40 // scroll towards angle
|
||||
#define SURF_NODRAW 0x80 // don't bother referencing the texture
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int planenum;
|
||||
int children[2]; // negative numbers are -(leafs+1), not nodes
|
||||
short mins[3]; // for frustom culling
|
||||
short maxs[3];
|
||||
unsigned short firstface;
|
||||
unsigned short numfaces; // counting both sides
|
||||
} dnode_t;
|
||||
|
||||
typedef struct textureinfo_s
|
||||
{
|
||||
float vecs[2][4]; // [s/t][xyz offset]
|
||||
int flags; // miptex flags + overrides
|
||||
int value; // light emission, etc
|
||||
char texture[32]; // texture name (textures/*.wal)
|
||||
int nexttexinfo; // for animations, -1 = end of chain
|
||||
} textureinfo_t;
|
||||
|
||||
// note that edge 0 is never used, because negative edge nums are used for
|
||||
// counterclockwise use of the edge in a face
|
||||
typedef struct
|
||||
{
|
||||
unsigned short v[2]; // vertex numbers
|
||||
} dedge_t;
|
||||
|
||||
#define MAXLIGHTMAPS 4
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short planenum;
|
||||
short side;
|
||||
|
||||
int firstedge; // we must support > 64k edges
|
||||
short numedges;
|
||||
short texinfo;
|
||||
|
||||
// lighting info
|
||||
byte styles[MAXLIGHTMAPS];
|
||||
int lightofs; // start of [numstyles*surfsize] samples
|
||||
} dface_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int contents; // OR of all brushes (not needed?)
|
||||
|
||||
short cluster;
|
||||
short area;
|
||||
|
||||
short mins[3]; // for frustum culling
|
||||
short maxs[3];
|
||||
|
||||
unsigned short firstleafface;
|
||||
unsigned short numleaffaces;
|
||||
|
||||
unsigned short firstleafbrush;
|
||||
unsigned short numleafbrushes;
|
||||
} dleaf_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned short planenum; // facing out of the leaf
|
||||
short texinfo;
|
||||
} dbrushside_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int firstside;
|
||||
int numsides;
|
||||
int contents;
|
||||
} dbrush_t;
|
||||
|
||||
#define ANGLE_UP -1
|
||||
#define ANGLE_DOWN -2
|
||||
|
||||
// the visibility lump consists of a header with a count, then
|
||||
// byte offsets for the PVS and PHS of each cluster, then the raw
|
||||
// compressed bit vectors
|
||||
#define DVIS_PVS 0
|
||||
#define DVIS_PHS 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numclusters;
|
||||
int bitofs[8][2]; // bitofs[numclusters][2]
|
||||
} dvis_t;
|
||||
|
||||
// each area has a list of portals that lead into other areas
|
||||
// when portals are closed, other areas may not be visible or
|
||||
// hearable even if the vis info says that it should be
|
||||
typedef struct
|
||||
{
|
||||
int portalnum;
|
||||
int otherarea;
|
||||
} dareaportal_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numareaportals;
|
||||
int firstareaportal;
|
||||
} darea_t;
|
||||
|
||||
#endif // Q_FILES_H
|
||||
+1098
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
void UpdateChaseCam(edict_t * ent)
|
||||
{
|
||||
vec3_t o, ownerv, goal;
|
||||
edict_t * targ;
|
||||
vec3_t forward, right;
|
||||
trace_t trace;
|
||||
int i;
|
||||
vec3_t oldgoal;
|
||||
vec3_t angles;
|
||||
|
||||
// is our chase target gone?
|
||||
if (!ent->client->chase_target->inuse || ent->client->chase_target->client->resp.spectator)
|
||||
{
|
||||
edict_t * old = ent->client->chase_target;
|
||||
ChaseNext(ent);
|
||||
if (ent->client->chase_target == old)
|
||||
{
|
||||
ent->client->chase_target = NULL;
|
||||
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targ = ent->client->chase_target;
|
||||
|
||||
VectorCopy(targ->s.origin, ownerv);
|
||||
VectorCopy(ent->s.origin, oldgoal);
|
||||
|
||||
ownerv[2] += targ->viewheight;
|
||||
|
||||
VectorCopy(targ->client->v_angle, angles);
|
||||
if (angles[PITCH] > 56)
|
||||
angles[PITCH] = 56;
|
||||
AngleVectors(angles, forward, right, NULL);
|
||||
VectorNormalize(forward);
|
||||
VectorMA(ownerv, -30, forward, o);
|
||||
|
||||
if (o[2] < targ->s.origin[2] + 20)
|
||||
o[2] = targ->s.origin[2] + 20;
|
||||
|
||||
// jump animation lifts
|
||||
if (!targ->groundentity)
|
||||
o[2] += 16;
|
||||
|
||||
trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
|
||||
VectorCopy(trace.endpos, goal);
|
||||
|
||||
VectorMA(goal, 2, forward, goal);
|
||||
|
||||
// pad for floors and ceilings
|
||||
VectorCopy(goal, o);
|
||||
o[2] += 6;
|
||||
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1)
|
||||
{
|
||||
VectorCopy(trace.endpos, goal);
|
||||
goal[2] -= 6;
|
||||
}
|
||||
|
||||
VectorCopy(goal, o);
|
||||
o[2] -= 6;
|
||||
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1)
|
||||
{
|
||||
VectorCopy(trace.endpos, goal);
|
||||
goal[2] += 6;
|
||||
}
|
||||
|
||||
if (targ->deadflag)
|
||||
ent->client->ps.pmove.pm_type = PM_DEAD;
|
||||
else
|
||||
ent->client->ps.pmove.pm_type = PM_FREEZE;
|
||||
|
||||
VectorCopy(goal, ent->s.origin);
|
||||
for (i = 0; i < 3; i++)
|
||||
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]);
|
||||
|
||||
if (targ->deadflag)
|
||||
{
|
||||
ent->client->ps.viewangles[ROLL] = 40;
|
||||
ent->client->ps.viewangles[PITCH] = -15;
|
||||
ent->client->ps.viewangles[YAW] = targ->client->killer_yaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(targ->client->v_angle, ent->client->ps.viewangles);
|
||||
VectorCopy(targ->client->v_angle, ent->client->v_angle);
|
||||
}
|
||||
|
||||
ent->viewheight = 0;
|
||||
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
void ChaseNext(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
edict_t * e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do
|
||||
{
|
||||
i++;
|
||||
if (i > maxclients->value)
|
||||
i = 1;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void ChasePrev(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
edict_t * e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do
|
||||
{
|
||||
i--;
|
||||
if (i < 1)
|
||||
i = maxclients->value;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void GetChaseTarget(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
edict_t * other;
|
||||
|
||||
for (i = 1; i <= maxclients->value; i++)
|
||||
{
|
||||
other = g_edicts + i;
|
||||
if (other->inuse && !other->client->resp.spectator)
|
||||
{
|
||||
ent->client->chase_target = other;
|
||||
ent->client->update_chase = true;
|
||||
UpdateChaseCam(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gi.centerprintf(ent, "No other players to chase.");
|
||||
}
|
||||
@@ -0,0 +1,988 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
#include "m_player.h"
|
||||
|
||||
char * ClientTeam(edict_t * ent)
|
||||
{
|
||||
char * p;
|
||||
static char value[512];
|
||||
|
||||
value[0] = 0;
|
||||
|
||||
if (!ent->client)
|
||||
return value;
|
||||
|
||||
strcpy(value, Info_ValueForKey(ent->client->pers.userinfo, "skin"));
|
||||
p = strchr(value, '/');
|
||||
if (!p)
|
||||
return value;
|
||||
|
||||
if ((int)(dmflags->value) & DF_MODELTEAMS)
|
||||
{
|
||||
*p = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
// if ((int)(dmflags->value) & DF_SKINTEAMS)
|
||||
return ++p;
|
||||
}
|
||||
|
||||
qboolean OnSameTeam(edict_t * ent1, edict_t * ent2)
|
||||
{
|
||||
char ent1Team[512];
|
||||
char ent2Team[512];
|
||||
|
||||
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
|
||||
return false;
|
||||
|
||||
strcpy(ent1Team, ClientTeam(ent1));
|
||||
strcpy(ent2Team, ClientTeam(ent2));
|
||||
|
||||
if (strcmp(ent1Team, ent2Team) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void SelectNextItem(edict_t * ent, int itflags)
|
||||
{
|
||||
gclient_t * cl;
|
||||
int i, index;
|
||||
gitem_t * it;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (cl->chase_target)
|
||||
{
|
||||
ChaseNext(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// scan for the next valid one
|
||||
for (i = 1; i <= MAX_ITEMS; i++)
|
||||
{
|
||||
index = (cl->pers.selected_item + i) % MAX_ITEMS;
|
||||
if (!cl->pers.inventory[index])
|
||||
continue;
|
||||
it = &itemlist[index];
|
||||
if (!it->use)
|
||||
continue;
|
||||
if (!(it->flags & itflags))
|
||||
continue;
|
||||
|
||||
cl->pers.selected_item = index;
|
||||
return;
|
||||
}
|
||||
|
||||
cl->pers.selected_item = -1;
|
||||
}
|
||||
|
||||
void SelectPrevItem(edict_t * ent, int itflags)
|
||||
{
|
||||
gclient_t * cl;
|
||||
int i, index;
|
||||
gitem_t * it;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (cl->chase_target)
|
||||
{
|
||||
ChasePrev(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// scan for the next valid one
|
||||
for (i = 1; i <= MAX_ITEMS; i++)
|
||||
{
|
||||
index = (cl->pers.selected_item + MAX_ITEMS - i) % MAX_ITEMS;
|
||||
if (!cl->pers.inventory[index])
|
||||
continue;
|
||||
it = &itemlist[index];
|
||||
if (!it->use)
|
||||
continue;
|
||||
if (!(it->flags & itflags))
|
||||
continue;
|
||||
|
||||
cl->pers.selected_item = index;
|
||||
return;
|
||||
}
|
||||
|
||||
cl->pers.selected_item = -1;
|
||||
}
|
||||
|
||||
void ValidateSelectedItem(edict_t * ent)
|
||||
{
|
||||
gclient_t * cl;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (cl->pers.inventory[cl->pers.selected_item])
|
||||
return; // valid
|
||||
|
||||
SelectNextItem(ent, -1);
|
||||
}
|
||||
|
||||
//=================================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Give_f
|
||||
|
||||
Give items to a client
|
||||
==================
|
||||
*/
|
||||
void Cmd_Give_f(edict_t * ent)
|
||||
{
|
||||
char * name;
|
||||
gitem_t * it;
|
||||
int index;
|
||||
int i;
|
||||
qboolean give_all;
|
||||
edict_t * it_ent;
|
||||
|
||||
if (deathmatch->value && !sv_cheats->value)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
name = gi.args();
|
||||
|
||||
if (Q_stricmp(name, "all") == 0)
|
||||
give_all = true;
|
||||
else
|
||||
give_all = false;
|
||||
|
||||
if (give_all || Q_stricmp(gi.argv(1), "health") == 0)
|
||||
{
|
||||
if (gi.argc() == 3)
|
||||
ent->health = atoi(gi.argv(2));
|
||||
else
|
||||
ent->health = ent->max_health;
|
||||
if (!give_all)
|
||||
return;
|
||||
}
|
||||
|
||||
if (give_all || Q_stricmp(name, "weapons") == 0)
|
||||
{
|
||||
for (i = 0; i < game.num_items; i++)
|
||||
{
|
||||
it = itemlist + i;
|
||||
if (!it->pickup)
|
||||
continue;
|
||||
if (!(it->flags & IT_WEAPON))
|
||||
continue;
|
||||
ent->client->pers.inventory[i] += 1;
|
||||
}
|
||||
if (!give_all)
|
||||
return;
|
||||
}
|
||||
|
||||
if (give_all || Q_stricmp(name, "ammo") == 0)
|
||||
{
|
||||
for (i = 0; i < game.num_items; i++)
|
||||
{
|
||||
it = itemlist + i;
|
||||
if (!it->pickup)
|
||||
continue;
|
||||
if (!(it->flags & IT_AMMO))
|
||||
continue;
|
||||
Add_Ammo(ent, it, 1000);
|
||||
}
|
||||
if (!give_all)
|
||||
return;
|
||||
}
|
||||
|
||||
if (give_all || Q_stricmp(name, "armor") == 0)
|
||||
{
|
||||
gitem_armor_t * info;
|
||||
|
||||
it = FindItem("Jacket Armor");
|
||||
ent->client->pers.inventory[ITEM_INDEX(it)] = 0;
|
||||
|
||||
it = FindItem("Combat Armor");
|
||||
ent->client->pers.inventory[ITEM_INDEX(it)] = 0;
|
||||
|
||||
it = FindItem("Body Armor");
|
||||
info = (gitem_armor_t *)it->info;
|
||||
ent->client->pers.inventory[ITEM_INDEX(it)] = info->max_count;
|
||||
|
||||
if (!give_all)
|
||||
return;
|
||||
}
|
||||
|
||||
if (give_all || Q_stricmp(name, "Power Shield") == 0)
|
||||
{
|
||||
it = FindItem("Power Shield");
|
||||
it_ent = G_Spawn();
|
||||
it_ent->classname = it->classname;
|
||||
SpawnItem(it_ent, it);
|
||||
Touch_Item(it_ent, ent, NULL, NULL);
|
||||
if (it_ent->inuse)
|
||||
G_FreeEdict(it_ent);
|
||||
|
||||
if (!give_all)
|
||||
return;
|
||||
}
|
||||
|
||||
if (give_all)
|
||||
{
|
||||
for (i = 0; i < game.num_items; i++)
|
||||
{
|
||||
it = itemlist + i;
|
||||
if (!it->pickup)
|
||||
continue;
|
||||
if (it->flags & (IT_ARMOR | IT_WEAPON | IT_AMMO))
|
||||
continue;
|
||||
ent->client->pers.inventory[i] = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
it = FindItem(name);
|
||||
if (!it)
|
||||
{
|
||||
name = gi.argv(1);
|
||||
it = FindItem(name);
|
||||
if (!it)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "unknown item\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!it->pickup)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "non-pickup item\n");
|
||||
return;
|
||||
}
|
||||
|
||||
index = ITEM_INDEX(it);
|
||||
|
||||
if (it->flags & IT_AMMO)
|
||||
{
|
||||
if (gi.argc() == 3)
|
||||
ent->client->pers.inventory[index] = atoi(gi.argv(2));
|
||||
else
|
||||
ent->client->pers.inventory[index] += it->quantity;
|
||||
}
|
||||
else
|
||||
{
|
||||
it_ent = G_Spawn();
|
||||
it_ent->classname = it->classname;
|
||||
SpawnItem(it_ent, it);
|
||||
Touch_Item(it_ent, ent, NULL, NULL);
|
||||
if (it_ent->inuse)
|
||||
G_FreeEdict(it_ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_God_f
|
||||
|
||||
Sets client to godmode
|
||||
|
||||
argv(0) god
|
||||
==================
|
||||
*/
|
||||
void Cmd_God_f(edict_t * ent)
|
||||
{
|
||||
char * msg;
|
||||
|
||||
if (deathmatch->value && !sv_cheats->value)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->flags ^= FL_GODMODE;
|
||||
if (!(ent->flags & FL_GODMODE))
|
||||
msg = "godmode OFF\n";
|
||||
else
|
||||
msg = "godmode ON\n";
|
||||
|
||||
gi.cprintf(ent, PRINT_HIGH, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Notarget_f
|
||||
|
||||
Sets client to notarget
|
||||
|
||||
argv(0) notarget
|
||||
==================
|
||||
*/
|
||||
void Cmd_Notarget_f(edict_t * ent)
|
||||
{
|
||||
char * msg;
|
||||
|
||||
if (deathmatch->value && !sv_cheats->value)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->flags ^= FL_NOTARGET;
|
||||
if (!(ent->flags & FL_NOTARGET))
|
||||
msg = "notarget OFF\n";
|
||||
else
|
||||
msg = "notarget ON\n";
|
||||
|
||||
gi.cprintf(ent, PRINT_HIGH, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Noclip_f
|
||||
|
||||
argv(0) noclip
|
||||
==================
|
||||
*/
|
||||
void Cmd_Noclip_f(edict_t * ent)
|
||||
{
|
||||
char * msg;
|
||||
|
||||
if (deathmatch->value && !sv_cheats->value)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->movetype == MOVETYPE_NOCLIP)
|
||||
{
|
||||
ent->movetype = MOVETYPE_WALK;
|
||||
msg = "noclip OFF\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->movetype = MOVETYPE_NOCLIP;
|
||||
msg = "noclip ON\n";
|
||||
}
|
||||
|
||||
gi.cprintf(ent, PRINT_HIGH, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Use_f
|
||||
|
||||
Use an inventory item
|
||||
==================
|
||||
*/
|
||||
void Cmd_Use_f(edict_t * ent)
|
||||
{
|
||||
int index;
|
||||
gitem_t * it;
|
||||
char * s;
|
||||
|
||||
s = gi.args();
|
||||
it = FindItem(s);
|
||||
if (!it)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s);
|
||||
return;
|
||||
}
|
||||
if (!it->use)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n");
|
||||
return;
|
||||
}
|
||||
index = ITEM_INDEX(it);
|
||||
if (!ent->client->pers.inventory[index])
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||
return;
|
||||
}
|
||||
|
||||
it->use(ent, it);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Drop_f
|
||||
|
||||
Drop an inventory item
|
||||
==================
|
||||
*/
|
||||
void Cmd_Drop_f(edict_t * ent)
|
||||
{
|
||||
int index;
|
||||
gitem_t * it;
|
||||
char * s;
|
||||
|
||||
s = gi.args();
|
||||
it = FindItem(s);
|
||||
if (!it)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s);
|
||||
return;
|
||||
}
|
||||
if (!it->drop)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n");
|
||||
return;
|
||||
}
|
||||
index = ITEM_INDEX(it);
|
||||
if (!ent->client->pers.inventory[index])
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||
return;
|
||||
}
|
||||
|
||||
it->drop(ent, it);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_Inven_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_Inven_f(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
gclient_t * cl;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
cl->showscores = false;
|
||||
cl->showhelp = false;
|
||||
|
||||
if (cl->showinventory)
|
||||
{
|
||||
cl->showinventory = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cl->showinventory = true;
|
||||
|
||||
gi.WriteByte(svc_inventory);
|
||||
for (i = 0; i < MAX_ITEMS; i++)
|
||||
{
|
||||
gi.WriteShort(cl->pers.inventory[i]);
|
||||
}
|
||||
gi.unicast(ent, true);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_InvUse_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_InvUse_f(edict_t * ent)
|
||||
{
|
||||
gitem_t * it;
|
||||
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
if (ent->client->pers.selected_item == -1)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "No item to use.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
it = &itemlist[ent->client->pers.selected_item];
|
||||
if (!it->use)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n");
|
||||
return;
|
||||
}
|
||||
it->use(ent, it);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_WeapPrev_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_WeapPrev_f(edict_t * ent)
|
||||
{
|
||||
gclient_t * cl;
|
||||
int i, index;
|
||||
gitem_t * it;
|
||||
int selected_weapon;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (!cl->pers.weapon)
|
||||
return;
|
||||
|
||||
selected_weapon = ITEM_INDEX(cl->pers.weapon);
|
||||
|
||||
// scan for the next valid one
|
||||
for (i = 1; i <= MAX_ITEMS; i++)
|
||||
{
|
||||
index = (selected_weapon + i) % MAX_ITEMS;
|
||||
if (!cl->pers.inventory[index])
|
||||
continue;
|
||||
it = &itemlist[index];
|
||||
if (!it->use)
|
||||
continue;
|
||||
if (!(it->flags & IT_WEAPON))
|
||||
continue;
|
||||
it->use(ent, it);
|
||||
if (cl->pers.weapon == it)
|
||||
return; // successful
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_WeapNext_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_WeapNext_f(edict_t * ent)
|
||||
{
|
||||
gclient_t * cl;
|
||||
int i, index;
|
||||
gitem_t * it;
|
||||
int selected_weapon;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (!cl->pers.weapon)
|
||||
return;
|
||||
|
||||
selected_weapon = ITEM_INDEX(cl->pers.weapon);
|
||||
|
||||
// scan for the next valid one
|
||||
for (i = 1; i <= MAX_ITEMS; i++)
|
||||
{
|
||||
index = (selected_weapon + MAX_ITEMS - i) % MAX_ITEMS;
|
||||
if (!cl->pers.inventory[index])
|
||||
continue;
|
||||
it = &itemlist[index];
|
||||
if (!it->use)
|
||||
continue;
|
||||
if (!(it->flags & IT_WEAPON))
|
||||
continue;
|
||||
it->use(ent, it);
|
||||
if (cl->pers.weapon == it)
|
||||
return; // successful
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_WeapLast_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_WeapLast_f(edict_t * ent)
|
||||
{
|
||||
gclient_t * cl;
|
||||
int index;
|
||||
gitem_t * it;
|
||||
|
||||
cl = ent->client;
|
||||
|
||||
if (!cl->pers.weapon || !cl->pers.lastweapon)
|
||||
return;
|
||||
|
||||
index = ITEM_INDEX(cl->pers.lastweapon);
|
||||
if (!cl->pers.inventory[index])
|
||||
return;
|
||||
it = &itemlist[index];
|
||||
if (!it->use)
|
||||
return;
|
||||
if (!(it->flags & IT_WEAPON))
|
||||
return;
|
||||
it->use(ent, it);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_InvDrop_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_InvDrop_f(edict_t * ent)
|
||||
{
|
||||
gitem_t * it;
|
||||
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
if (ent->client->pers.selected_item == -1)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "No item to drop.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
it = &itemlist[ent->client->pers.selected_item];
|
||||
if (!it->drop)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n");
|
||||
return;
|
||||
}
|
||||
it->drop(ent, it);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_Kill_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_Kill_f(edict_t * ent)
|
||||
{
|
||||
if ((level.time - ent->client->respawn_time) < 5)
|
||||
return;
|
||||
ent->flags &= ~FL_GODMODE;
|
||||
ent->health = 0;
|
||||
meansOfDeath = MOD_SUICIDE;
|
||||
player_die(ent, ent, ent, 100000, vec3_origin);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_PutAway_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_PutAway_f(edict_t * ent)
|
||||
{
|
||||
ent->client->showscores = false;
|
||||
ent->client->showhelp = false;
|
||||
ent->client->showinventory = false;
|
||||
}
|
||||
|
||||
int PlayerSort(void const * a, void const * b)
|
||||
{
|
||||
int anum, bnum;
|
||||
|
||||
anum = *(int *)a;
|
||||
bnum = *(int *)b;
|
||||
|
||||
anum = game.clients[anum].ps.stats[STAT_FRAGS];
|
||||
bnum = game.clients[bnum].ps.stats[STAT_FRAGS];
|
||||
|
||||
if (anum < bnum)
|
||||
return -1;
|
||||
if (anum > bnum)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_Players_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_Players_f(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
int count;
|
||||
char small[64];
|
||||
char large[1280];
|
||||
int index[256];
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < maxclients->value; i++)
|
||||
if (game.clients[i].pers.connected)
|
||||
{
|
||||
index[count] = i;
|
||||
count++;
|
||||
}
|
||||
|
||||
// sort by frags
|
||||
qsort(index, count, sizeof(index[0]), PlayerSort);
|
||||
|
||||
// print information
|
||||
large[0] = 0;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
Com_sprintf(small, sizeof(small), "%3i %s\n",
|
||||
game.clients[index[i]].ps.stats[STAT_FRAGS],
|
||||
game.clients[index[i]].pers.netname);
|
||||
if (strlen(small) + strlen(large) > sizeof(large) - 100)
|
||||
{ // can't print all of them in one packet
|
||||
strcat(large, "...\n");
|
||||
break;
|
||||
}
|
||||
strcat(large, small);
|
||||
}
|
||||
|
||||
gi.cprintf(ent, PRINT_HIGH, "%s\n%i players\n", large, count);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Cmd_Wave_f
|
||||
=================
|
||||
*/
|
||||
void Cmd_Wave_f(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = atoi(gi.argv(1));
|
||||
|
||||
// can't wave when ducked
|
||||
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
||||
return;
|
||||
|
||||
if (ent->client->anim_priority > ANIM_WAVE)
|
||||
return;
|
||||
|
||||
ent->client->anim_priority = ANIM_WAVE;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
gi.cprintf(ent, PRINT_HIGH, "flipoff\n");
|
||||
ent->s.frame = FRAME_flip01 - 1;
|
||||
ent->client->anim_end = FRAME_flip12;
|
||||
break;
|
||||
case 1:
|
||||
gi.cprintf(ent, PRINT_HIGH, "salute\n");
|
||||
ent->s.frame = FRAME_salute01 - 1;
|
||||
ent->client->anim_end = FRAME_salute11;
|
||||
break;
|
||||
case 2:
|
||||
gi.cprintf(ent, PRINT_HIGH, "taunt\n");
|
||||
ent->s.frame = FRAME_taunt01 - 1;
|
||||
ent->client->anim_end = FRAME_taunt17;
|
||||
break;
|
||||
case 3:
|
||||
gi.cprintf(ent, PRINT_HIGH, "wave\n");
|
||||
ent->s.frame = FRAME_wave01 - 1;
|
||||
ent->client->anim_end = FRAME_wave11;
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
gi.cprintf(ent, PRINT_HIGH, "point\n");
|
||||
ent->s.frame = FRAME_point01 - 1;
|
||||
ent->client->anim_end = FRAME_point12;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Cmd_Say_f
|
||||
==================
|
||||
*/
|
||||
void Cmd_Say_f(edict_t * ent, qboolean team, qboolean arg0)
|
||||
{
|
||||
int i, j;
|
||||
edict_t * other;
|
||||
char * p;
|
||||
char text[2048];
|
||||
gclient_t * cl;
|
||||
|
||||
if (gi.argc() < 2 && !arg0)
|
||||
return;
|
||||
|
||||
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
|
||||
team = false;
|
||||
|
||||
if (team)
|
||||
Com_sprintf(text, sizeof(text), "(%s): ", ent->client->pers.netname);
|
||||
else
|
||||
Com_sprintf(text, sizeof(text), "%s: ", ent->client->pers.netname);
|
||||
|
||||
if (arg0)
|
||||
{
|
||||
strcat(text, gi.argv(0));
|
||||
strcat(text, " ");
|
||||
strcat(text, gi.args());
|
||||
}
|
||||
else
|
||||
{
|
||||
p = gi.args();
|
||||
|
||||
if (*p == '"')
|
||||
{
|
||||
p++;
|
||||
p[strlen(p) - 1] = 0;
|
||||
}
|
||||
strcat(text, p);
|
||||
}
|
||||
|
||||
// don't let text be too long for malicious reasons
|
||||
if (strlen(text) > 150)
|
||||
text[150] = 0;
|
||||
|
||||
strcat(text, "\n");
|
||||
|
||||
if (flood_msgs->value)
|
||||
{
|
||||
cl = ent->client;
|
||||
|
||||
if (level.time < cl->flood_locktill)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "You can't talk for %d more seconds\n",
|
||||
(int)(cl->flood_locktill - level.time));
|
||||
return;
|
||||
}
|
||||
i = cl->flood_whenhead - flood_msgs->value + 1;
|
||||
if (i < 0)
|
||||
i = (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])) + i;
|
||||
if (cl->flood_when[i] &&
|
||||
level.time - cl->flood_when[i] < flood_persecond->value)
|
||||
{
|
||||
cl->flood_locktill = level.time + flood_waitdelay->value;
|
||||
gi.cprintf(ent, PRINT_CHAT, "Flood protection: You can't talk for %d seconds.\n",
|
||||
(int)flood_waitdelay->value);
|
||||
return;
|
||||
}
|
||||
cl->flood_whenhead = (cl->flood_whenhead + 1) %
|
||||
(sizeof(cl->flood_when) / sizeof(cl->flood_when[0]));
|
||||
cl->flood_when[cl->flood_whenhead] = level.time;
|
||||
}
|
||||
|
||||
if (dedicated->value)
|
||||
gi.cprintf(NULL, PRINT_CHAT, "%s", text);
|
||||
|
||||
for (j = 1; j <= game.maxclients; j++)
|
||||
{
|
||||
other = &g_edicts[j];
|
||||
if (!other->inuse)
|
||||
continue;
|
||||
if (!other->client)
|
||||
continue;
|
||||
if (team)
|
||||
{
|
||||
if (!OnSameTeam(ent, other))
|
||||
continue;
|
||||
}
|
||||
gi.cprintf(other, PRINT_CHAT, "%s", text);
|
||||
}
|
||||
}
|
||||
|
||||
void Cmd_PlayerList_f(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
char st[80];
|
||||
char text[1400];
|
||||
edict_t * e2;
|
||||
|
||||
// connect time, ping, score, name
|
||||
*text = 0;
|
||||
for (i = 0, e2 = g_edicts + 1; i < maxclients->value; i++, e2++)
|
||||
{
|
||||
if (!e2->inuse)
|
||||
continue;
|
||||
|
||||
sprintf(st, "%02d:%02d %4d %3d %s%s\n",
|
||||
(level.framenum - e2->client->resp.enterframe) / 600,
|
||||
((level.framenum - e2->client->resp.enterframe) % 600) / 10,
|
||||
e2->client->ping,
|
||||
e2->client->resp.score,
|
||||
e2->client->pers.netname,
|
||||
e2->client->resp.spectator ? " (spectator)" : "");
|
||||
if (strlen(text) + strlen(st) > sizeof(text) - 50)
|
||||
{
|
||||
sprintf(text + strlen(text), "And more...\n");
|
||||
gi.cprintf(ent, PRINT_HIGH, "%s", text);
|
||||
return;
|
||||
}
|
||||
strcat(text, st);
|
||||
}
|
||||
gi.cprintf(ent, PRINT_HIGH, "%s", text);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ClientCommand
|
||||
=================
|
||||
*/
|
||||
void ClientCommand(edict_t * ent)
|
||||
{
|
||||
char * cmd;
|
||||
|
||||
if (!ent->client)
|
||||
return; // not fully in game yet
|
||||
|
||||
cmd = gi.argv(0);
|
||||
|
||||
if (Q_stricmp(cmd, "players") == 0)
|
||||
{
|
||||
Cmd_Players_f(ent);
|
||||
return;
|
||||
}
|
||||
if (Q_stricmp(cmd, "say") == 0)
|
||||
{
|
||||
Cmd_Say_f(ent, false, false);
|
||||
return;
|
||||
}
|
||||
if (Q_stricmp(cmd, "say_team") == 0)
|
||||
{
|
||||
Cmd_Say_f(ent, true, false);
|
||||
return;
|
||||
}
|
||||
if (Q_stricmp(cmd, "score") == 0)
|
||||
{
|
||||
Cmd_Score_f(ent);
|
||||
return;
|
||||
}
|
||||
if (Q_stricmp(cmd, "help") == 0)
|
||||
{
|
||||
Cmd_Help_f(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.intermissiontime)
|
||||
return;
|
||||
|
||||
if (Q_stricmp(cmd, "use") == 0)
|
||||
Cmd_Use_f(ent);
|
||||
else if (Q_stricmp(cmd, "drop") == 0)
|
||||
Cmd_Drop_f(ent);
|
||||
else if (Q_stricmp(cmd, "give") == 0)
|
||||
Cmd_Give_f(ent);
|
||||
else if (Q_stricmp(cmd, "god") == 0)
|
||||
Cmd_God_f(ent);
|
||||
else if (Q_stricmp(cmd, "notarget") == 0)
|
||||
Cmd_Notarget_f(ent);
|
||||
else if (Q_stricmp(cmd, "noclip") == 0)
|
||||
Cmd_Noclip_f(ent);
|
||||
else if (Q_stricmp(cmd, "inven") == 0)
|
||||
Cmd_Inven_f(ent);
|
||||
else if (Q_stricmp(cmd, "invnext") == 0)
|
||||
SelectNextItem(ent, -1);
|
||||
else if (Q_stricmp(cmd, "invprev") == 0)
|
||||
SelectPrevItem(ent, -1);
|
||||
else if (Q_stricmp(cmd, "invnextw") == 0)
|
||||
SelectNextItem(ent, IT_WEAPON);
|
||||
else if (Q_stricmp(cmd, "invprevw") == 0)
|
||||
SelectPrevItem(ent, IT_WEAPON);
|
||||
else if (Q_stricmp(cmd, "invnextp") == 0)
|
||||
SelectNextItem(ent, IT_POWERUP);
|
||||
else if (Q_stricmp(cmd, "invprevp") == 0)
|
||||
SelectPrevItem(ent, IT_POWERUP);
|
||||
else if (Q_stricmp(cmd, "invuse") == 0)
|
||||
Cmd_InvUse_f(ent);
|
||||
else if (Q_stricmp(cmd, "invdrop") == 0)
|
||||
Cmd_InvDrop_f(ent);
|
||||
else if (Q_stricmp(cmd, "weapprev") == 0)
|
||||
Cmd_WeapPrev_f(ent);
|
||||
else if (Q_stricmp(cmd, "weapnext") == 0)
|
||||
Cmd_WeapNext_f(ent);
|
||||
else if (Q_stricmp(cmd, "weaplast") == 0)
|
||||
Cmd_WeapLast_f(ent);
|
||||
else if (Q_stricmp(cmd, "kill") == 0)
|
||||
Cmd_Kill_f(ent);
|
||||
else if (Q_stricmp(cmd, "putaway") == 0)
|
||||
Cmd_PutAway_f(ent);
|
||||
else if (Q_stricmp(cmd, "wave") == 0)
|
||||
Cmd_Wave_f(ent);
|
||||
else if (Q_stricmp(cmd, "playerlist") == 0)
|
||||
Cmd_PlayerList_f(ent);
|
||||
else // anything that doesn't match a command will be a chat
|
||||
Cmd_Say_f(ent, false, true);
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// g_combat.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
============
|
||||
CanDamage
|
||||
|
||||
Returns true if the inflictor can directly damage the target. Used for
|
||||
explosions and melee attacks.
|
||||
============
|
||||
*/
|
||||
qboolean CanDamage(edict_t * targ, edict_t * inflictor)
|
||||
{
|
||||
vec3_t dest;
|
||||
trace_t trace;
|
||||
|
||||
// bmodels need special checking because their origin is 0,0,0
|
||||
if (targ->movetype == MOVETYPE_PUSH)
|
||||
{
|
||||
VectorAdd(targ->absmin, targ->absmax, dest);
|
||||
VectorScale(dest, 0.5, dest);
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
if (trace.ent == targ)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy(targ->s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy(targ->s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy(targ->s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy(targ->s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Killed
|
||||
============
|
||||
*/
|
||||
void Killed(edict_t * targ, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
if (targ->health < -999)
|
||||
targ->health = -999;
|
||||
|
||||
targ->enemy = attacker;
|
||||
|
||||
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||
{
|
||||
// targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
|
||||
if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
{
|
||||
level.killed_monsters++;
|
||||
if (coop->value && attacker->client)
|
||||
attacker->client->resp.score++;
|
||||
// medics won't heal monsters that they kill themselves
|
||||
if (strcmp(attacker->classname, "monster_medic") == 0)
|
||||
targ->owner = attacker;
|
||||
}
|
||||
}
|
||||
|
||||
if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
|
||||
{ // doors, triggers, etc
|
||||
targ->die(targ, inflictor, attacker, damage, point);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||
{
|
||||
targ->touch = NULL;
|
||||
monster_death_use(targ);
|
||||
}
|
||||
|
||||
targ->die(targ, inflictor, attacker, damage, point);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SpawnDamage
|
||||
================
|
||||
*/
|
||||
void SpawnDamage(int type, vec3_t origin, vec3_t normal, int damage)
|
||||
{
|
||||
if (damage > 255)
|
||||
damage = 255;
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(type);
|
||||
// gi.WriteByte (damage);
|
||||
gi.WritePosition(origin);
|
||||
gi.WriteDir(normal);
|
||||
gi.multicast(origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
T_Damage
|
||||
|
||||
targ entity that is being damaged
|
||||
inflictor entity that is causing the damage
|
||||
attacker entity that caused the inflictor to damage targ
|
||||
example: targ=monster, inflictor=rocket, attacker=player
|
||||
|
||||
dir direction of the attack
|
||||
point point at which the damage is being inflicted
|
||||
normal normal vector from that point
|
||||
damage amount of damage being inflicted
|
||||
knockback force to be applied against targ as a result of the damage
|
||||
|
||||
dflags these flags are used to control how T_Damage works
|
||||
DAMAGE_RADIUS damage was indirect (from a nearby explosion)
|
||||
DAMAGE_NO_ARMOR armor does not protect from this damage
|
||||
DAMAGE_ENERGY damage is from an energy based weapon
|
||||
DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
|
||||
DAMAGE_BULLET damage is from a bullet (used for ricochets)
|
||||
DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
||||
============
|
||||
*/
|
||||
static int CheckPowerArmor(edict_t * ent, vec3_t point, vec3_t normal, int damage, int dflags)
|
||||
{
|
||||
gclient_t * client;
|
||||
int save;
|
||||
int power_armor_type;
|
||||
int index;
|
||||
int damagePerCell;
|
||||
int pa_te_type;
|
||||
int power;
|
||||
int power_used;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
|
||||
if (dflags & DAMAGE_NO_ARMOR)
|
||||
return 0;
|
||||
|
||||
if (client)
|
||||
{
|
||||
power_armor_type = PowerArmorType(ent);
|
||||
if (power_armor_type != POWER_ARMOR_NONE)
|
||||
{
|
||||
index = ITEM_INDEX(FindItem("Cells"));
|
||||
power = client->pers.inventory[index];
|
||||
}
|
||||
}
|
||||
else if (ent->svflags & SVF_MONSTER)
|
||||
{
|
||||
power_armor_type = ent->monsterinfo.power_armor_type;
|
||||
power = ent->monsterinfo.power_armor_power;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == POWER_ARMOR_NONE)
|
||||
return 0;
|
||||
if (!power)
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == POWER_ARMOR_SCREEN)
|
||||
{
|
||||
vec3_t vec;
|
||||
float dot;
|
||||
vec3_t forward;
|
||||
|
||||
// only works if damage point is in front
|
||||
AngleVectors(ent->s.angles, forward, NULL, NULL);
|
||||
VectorSubtract(point, ent->s.origin, vec);
|
||||
VectorNormalize(vec);
|
||||
dot = DotProduct(vec, forward);
|
||||
if (dot <= 0.3)
|
||||
return 0;
|
||||
|
||||
damagePerCell = 1;
|
||||
pa_te_type = TE_SCREEN_SPARKS;
|
||||
damage = damage / 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
damagePerCell = 2;
|
||||
pa_te_type = TE_SHIELD_SPARKS;
|
||||
damage = (2 * damage) / 3;
|
||||
}
|
||||
|
||||
save = power * damagePerCell;
|
||||
if (!save)
|
||||
return 0;
|
||||
if (save > damage)
|
||||
save = damage;
|
||||
|
||||
SpawnDamage(pa_te_type, point, normal, save);
|
||||
ent->powerarmor_time = level.time + 0.2;
|
||||
|
||||
power_used = save / damagePerCell;
|
||||
|
||||
if (client)
|
||||
client->pers.inventory[index] -= power_used;
|
||||
else
|
||||
ent->monsterinfo.power_armor_power -= power_used;
|
||||
return save;
|
||||
}
|
||||
|
||||
static int CheckArmor(edict_t * ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
|
||||
{
|
||||
gclient_t * client;
|
||||
int save;
|
||||
int index;
|
||||
gitem_t * armor;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
if (dflags & DAMAGE_NO_ARMOR)
|
||||
return 0;
|
||||
|
||||
index = ArmorIndex(ent);
|
||||
if (!index)
|
||||
return 0;
|
||||
|
||||
armor = GetItemByIndex(index);
|
||||
|
||||
if (dflags & DAMAGE_ENERGY)
|
||||
save = ceil(((gitem_armor_t *)armor->info)->energy_protection * damage);
|
||||
else
|
||||
save = ceil(((gitem_armor_t *)armor->info)->normal_protection * damage);
|
||||
if (save >= client->pers.inventory[index])
|
||||
save = client->pers.inventory[index];
|
||||
|
||||
if (!save)
|
||||
return 0;
|
||||
|
||||
client->pers.inventory[index] -= save;
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
void M_ReactToDamage(edict_t * targ, edict_t * attacker)
|
||||
{
|
||||
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
|
||||
return;
|
||||
|
||||
if (attacker == targ || attacker == targ->enemy)
|
||||
return;
|
||||
|
||||
// if we are a good guy monster and our attacker is a player
|
||||
// or another good guy, do not get mad at them
|
||||
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
|
||||
{
|
||||
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
return;
|
||||
}
|
||||
|
||||
// we now know that we are not both good guys
|
||||
|
||||
// if attacker is a client, get mad at them because he's good and we're not
|
||||
if (attacker->client)
|
||||
{
|
||||
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
||||
|
||||
// this can only happen in coop (both new and old enemies are clients)
|
||||
// only switch if can't see the current enemy
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
{
|
||||
if (visible(targ, targ->enemy))
|
||||
{
|
||||
targ->oldenemy = attacker;
|
||||
return;
|
||||
}
|
||||
targ->oldenemy = targ->enemy;
|
||||
}
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
return;
|
||||
}
|
||||
|
||||
// it's the same base (walk/swim/fly) type and a different classname and it's not a tank
|
||||
// (they spray too much), get mad at them
|
||||
if (((targ->flags & (FL_FLY | FL_SWIM)) == (attacker->flags & (FL_FLY | FL_SWIM))) &&
|
||||
(strcmp(targ->classname, attacker->classname) != 0) &&
|
||||
(strcmp(attacker->classname, "monster_tank") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_supertank") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_makron") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_jorg") != 0))
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
// if they *meant* to shoot us, then shoot back
|
||||
else if (attacker->enemy == targ)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
// otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
|
||||
else if (attacker->enemy && attacker->enemy != targ)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker->enemy;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget(targ);
|
||||
}
|
||||
}
|
||||
|
||||
qboolean CheckTeamDamage(edict_t * targ, edict_t * attacker)
|
||||
{
|
||||
//FIXME make the next line real and uncomment this block
|
||||
// if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
|
||||
return false;
|
||||
}
|
||||
|
||||
void T_Damage(edict_t * targ, edict_t * inflictor, edict_t * attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
|
||||
{
|
||||
gclient_t * client;
|
||||
int take;
|
||||
int save;
|
||||
int asave;
|
||||
int psave;
|
||||
int te_sparks;
|
||||
|
||||
if (!targ->takedamage)
|
||||
return;
|
||||
|
||||
// friendly fire avoidance
|
||||
// if enabled you can't hurt teammates (but you can hurt yourself)
|
||||
// knockback still occurs
|
||||
if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
|
||||
{
|
||||
if (OnSameTeam(targ, attacker))
|
||||
{
|
||||
if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
|
||||
damage = 0;
|
||||
else
|
||||
mod |= MOD_FRIENDLY_FIRE;
|
||||
}
|
||||
}
|
||||
meansOfDeath = mod;
|
||||
|
||||
// easy mode takes half damage
|
||||
if (skill->value == 0 && deathmatch->value == 0 && targ->client)
|
||||
{
|
||||
damage *= 0.5;
|
||||
if (!damage)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
client = targ->client;
|
||||
|
||||
if (dflags & DAMAGE_BULLET)
|
||||
te_sparks = TE_BULLET_SPARKS;
|
||||
else
|
||||
te_sparks = TE_SPARKS;
|
||||
|
||||
VectorNormalize(dir);
|
||||
|
||||
// bonus damage for suprising a monster
|
||||
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
|
||||
damage *= 2;
|
||||
|
||||
if (targ->flags & FL_NO_KNOCKBACK)
|
||||
knockback = 0;
|
||||
|
||||
// figure momentum add
|
||||
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
||||
{
|
||||
if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
|
||||
{
|
||||
vec3_t kvel;
|
||||
float mass;
|
||||
|
||||
if (targ->mass < 50)
|
||||
mass = 50;
|
||||
else
|
||||
mass = targ->mass;
|
||||
|
||||
if (targ->client && attacker == targ)
|
||||
VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
|
||||
else
|
||||
VectorScale(dir, 500.0 * (float)knockback / mass, kvel);
|
||||
|
||||
VectorAdd(targ->velocity, kvel, targ->velocity);
|
||||
}
|
||||
}
|
||||
|
||||
take = damage;
|
||||
save = 0;
|
||||
|
||||
// check for godmode
|
||||
if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
take = 0;
|
||||
save = damage;
|
||||
SpawnDamage(te_sparks, point, normal, save);
|
||||
}
|
||||
|
||||
// check for invincibility
|
||||
if ((client && client->invincible_framenum > level.framenum) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
if (targ->pain_debounce_time < level.time)
|
||||
{
|
||||
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||
targ->pain_debounce_time = level.time + 2;
|
||||
}
|
||||
take = 0;
|
||||
save = damage;
|
||||
}
|
||||
|
||||
psave = CheckPowerArmor(targ, point, normal, take, dflags);
|
||||
take -= psave;
|
||||
|
||||
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
|
||||
take -= asave;
|
||||
|
||||
//treat cheat/powerup savings the same as armor
|
||||
asave += save;
|
||||
|
||||
// team damage avoidance
|
||||
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage(targ, attacker))
|
||||
return;
|
||||
|
||||
// do the damage
|
||||
if (take)
|
||||
{
|
||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
SpawnDamage(TE_BLOOD, point, normal, take);
|
||||
else
|
||||
SpawnDamage(te_sparks, point, normal, take);
|
||||
|
||||
targ->health = targ->health - take;
|
||||
|
||||
if (targ->health <= 0)
|
||||
{
|
||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
targ->flags |= FL_NO_KNOCKBACK;
|
||||
Killed(targ, inflictor, attacker, take, point);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (targ->svflags & SVF_MONSTER)
|
||||
{
|
||||
M_ReactToDamage(targ, attacker);
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
|
||||
{
|
||||
targ->pain(targ, attacker, knockback, take);
|
||||
// nightmare mode monsters don't go into pain frames often
|
||||
if (skill->value == 3)
|
||||
targ->pain_debounce_time = level.time + 5;
|
||||
}
|
||||
}
|
||||
else if (client)
|
||||
{
|
||||
if (!(targ->flags & FL_GODMODE) && (take))
|
||||
targ->pain(targ, attacker, knockback, take);
|
||||
}
|
||||
else if (take)
|
||||
{
|
||||
if (targ->pain)
|
||||
targ->pain(targ, attacker, knockback, take);
|
||||
}
|
||||
|
||||
// add to the damage inflicted on a player this frame
|
||||
// the total will be turned into screen blends and view angle kicks
|
||||
// at the end of the frame
|
||||
if (client)
|
||||
{
|
||||
client->damage_parmor += psave;
|
||||
client->damage_armor += asave;
|
||||
client->damage_blood += take;
|
||||
client->damage_knockback += knockback;
|
||||
VectorCopy(point, client->damage_from);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusDamage
|
||||
============
|
||||
*/
|
||||
void T_RadiusDamage(edict_t * inflictor, edict_t * attacker, float damage, edict_t * ignore, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t * ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||
{
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
VectorAdd(ent->mins, ent->maxs, v);
|
||||
VectorMA(ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract(inflictor->s.origin, v, v);
|
||||
points = damage - 0.5 * VectorLength(v);
|
||||
if (ent == attacker)
|
||||
points = points * 0.5;
|
||||
if (points > 0)
|
||||
{
|
||||
if (CanDamage(ent, inflictor))
|
||||
{
|
||||
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2029
File diff suppressed because it is too large.
Load diff
+2160
File diff suppressed because it is too large.
Load diff
+1088
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
game_locals_t game;
|
||||
level_locals_t level;
|
||||
game_import_t gi;
|
||||
game_export_t globals;
|
||||
spawn_temp_t st;
|
||||
|
||||
int sm_meat_index;
|
||||
int snd_fry;
|
||||
int meansOfDeath;
|
||||
|
||||
edict_t * g_edicts;
|
||||
|
||||
cvar_t * deathmatch;
|
||||
cvar_t * coop;
|
||||
cvar_t * dmflags;
|
||||
cvar_t * skill;
|
||||
cvar_t * fraglimit;
|
||||
cvar_t * timelimit;
|
||||
cvar_t * password;
|
||||
cvar_t * spectator_password;
|
||||
cvar_t * maxclients;
|
||||
cvar_t * maxspectators;
|
||||
cvar_t * maxentities;
|
||||
cvar_t * g_select_empty;
|
||||
cvar_t * dedicated;
|
||||
|
||||
cvar_t * filterban;
|
||||
|
||||
cvar_t * sv_maxvelocity;
|
||||
cvar_t * sv_gravity;
|
||||
|
||||
cvar_t * sv_rollspeed;
|
||||
cvar_t * sv_rollangle;
|
||||
cvar_t * gun_x;
|
||||
cvar_t * gun_y;
|
||||
cvar_t * gun_z;
|
||||
|
||||
cvar_t * run_pitch;
|
||||
cvar_t * run_roll;
|
||||
cvar_t * bob_up;
|
||||
cvar_t * bob_pitch;
|
||||
cvar_t * bob_roll;
|
||||
|
||||
cvar_t * sv_cheats;
|
||||
|
||||
cvar_t * flood_msgs;
|
||||
cvar_t * flood_persecond;
|
||||
cvar_t * flood_waitdelay;
|
||||
|
||||
cvar_t * sv_maplist;
|
||||
|
||||
void SpawnEntities(char * mapname, char * entities, char * spawnpoint);
|
||||
void ClientThink(edict_t * ent, usercmd_t * cmd);
|
||||
qboolean ClientConnect(edict_t * ent, char * userinfo);
|
||||
void ClientUserinfoChanged(edict_t * ent, char * userinfo);
|
||||
void ClientDisconnect(edict_t * ent);
|
||||
void ClientBegin(edict_t * ent);
|
||||
void ClientCommand(edict_t * ent);
|
||||
void RunEntity(edict_t * ent);
|
||||
void WriteGame(char * filename, qboolean autosave);
|
||||
void ReadGame(char * filename);
|
||||
void WriteLevel(char * filename);
|
||||
void ReadLevel(char * filename);
|
||||
void InitGame(void);
|
||||
void G_RunFrame(void);
|
||||
|
||||
//===================================================================
|
||||
|
||||
void ShutdownGame(void)
|
||||
{
|
||||
gi.dprintf("==== ShutdownGame ====\n");
|
||||
|
||||
gi.FreeTags(TAG_LEVEL);
|
||||
gi.FreeTags(TAG_GAME);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
GetGameAPI
|
||||
|
||||
Returns a pointer to the structure with all entry points
|
||||
and global variables
|
||||
=================
|
||||
*/
|
||||
game_export_t * GetGameAPI(game_import_t * import)
|
||||
{
|
||||
gi = *import;
|
||||
|
||||
globals.apiversion = GAME_API_VERSION;
|
||||
globals.Init = InitGame;
|
||||
globals.Shutdown = ShutdownGame;
|
||||
globals.SpawnEntities = SpawnEntities;
|
||||
|
||||
globals.WriteGame = WriteGame;
|
||||
globals.ReadGame = ReadGame;
|
||||
globals.WriteLevel = WriteLevel;
|
||||
globals.ReadLevel = ReadLevel;
|
||||
|
||||
globals.ClientThink = ClientThink;
|
||||
globals.ClientConnect = ClientConnect;
|
||||
globals.ClientUserinfoChanged = ClientUserinfoChanged;
|
||||
globals.ClientDisconnect = ClientDisconnect;
|
||||
globals.ClientBegin = ClientBegin;
|
||||
globals.ClientCommand = ClientCommand;
|
||||
|
||||
globals.RunFrame = G_RunFrame;
|
||||
globals.ServerCommand = ServerCommand;
|
||||
globals.edict_size = sizeof(edict_t);
|
||||
|
||||
return &globals;
|
||||
}
|
||||
|
||||
#ifndef GAME_HARD_LINKED
|
||||
// this is only here so the functions in q_shared.c and q_shwin.c can link
|
||||
void Sys_Error(char * error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start(argptr, error);
|
||||
vsprintf(text, error, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
gi.error(ERR_FATAL, "%s", text);
|
||||
}
|
||||
|
||||
void Com_Printf(char * msg, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start(argptr, msg);
|
||||
vsprintf(text, msg, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
gi.dprintf("%s", text);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
ClientEndServerFrames
|
||||
=================
|
||||
*/
|
||||
void ClientEndServerFrames(void)
|
||||
{
|
||||
int i;
|
||||
edict_t * ent;
|
||||
|
||||
// calc the player views now that all pushing
|
||||
// and damage has been added
|
||||
for (i = 0; i < maxclients->value; i++)
|
||||
{
|
||||
ent = g_edicts + 1 + i;
|
||||
if (!ent->inuse || !ent->client)
|
||||
continue;
|
||||
ClientEndServerFrame(ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CreateTargetChangeLevel
|
||||
|
||||
Returns the created target changelevel
|
||||
=================
|
||||
*/
|
||||
edict_t * CreateTargetChangeLevel(char * map)
|
||||
{
|
||||
edict_t * ent;
|
||||
|
||||
ent = G_Spawn();
|
||||
ent->classname = "target_changelevel";
|
||||
Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map);
|
||||
ent->map = level.nextmap;
|
||||
return ent;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
EndDMLevel
|
||||
|
||||
The timelimit or fraglimit has been exceeded
|
||||
=================
|
||||
*/
|
||||
void EndDMLevel(void)
|
||||
{
|
||||
edict_t * ent;
|
||||
char *s, *t, *f;
|
||||
static const char * seps = " ,\n\r";
|
||||
|
||||
// stay on same level flag
|
||||
if ((int)dmflags->value & DF_SAME_LEVEL)
|
||||
{
|
||||
BeginIntermission(CreateTargetChangeLevel(level.mapname));
|
||||
return;
|
||||
}
|
||||
|
||||
// see if it's in the map list
|
||||
if (*sv_maplist->string)
|
||||
{
|
||||
s = strdup(sv_maplist->string);
|
||||
f = NULL;
|
||||
t = strtok(s, seps);
|
||||
while (t != NULL)
|
||||
{
|
||||
if (Q_stricmp(t, level.mapname) == 0)
|
||||
{
|
||||
// it's in the list, go to the next one
|
||||
t = strtok(NULL, seps);
|
||||
if (t == NULL)
|
||||
{ // end of list, go to first one
|
||||
if (f == NULL) // there isn't a first one, same level
|
||||
BeginIntermission(CreateTargetChangeLevel(level.mapname));
|
||||
else
|
||||
BeginIntermission(CreateTargetChangeLevel(f));
|
||||
}
|
||||
else
|
||||
BeginIntermission(CreateTargetChangeLevel(t));
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
if (!f)
|
||||
f = t;
|
||||
t = strtok(NULL, seps);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
if (level.nextmap[0]) // go to a specific map
|
||||
BeginIntermission(CreateTargetChangeLevel(level.nextmap));
|
||||
else
|
||||
{ // search for a changelevel
|
||||
ent = G_Find(NULL, FOFS(classname), "target_changelevel");
|
||||
if (!ent)
|
||||
{ // the map designer didn't include a changelevel,
|
||||
// so create a fake ent that goes back to the same level
|
||||
BeginIntermission(CreateTargetChangeLevel(level.mapname));
|
||||
return;
|
||||
}
|
||||
BeginIntermission(ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CheckDMRules
|
||||
=================
|
||||
*/
|
||||
void CheckDMRules(void)
|
||||
{
|
||||
int i;
|
||||
gclient_t * cl;
|
||||
|
||||
if (level.intermissiontime)
|
||||
return;
|
||||
|
||||
if (!deathmatch->value)
|
||||
return;
|
||||
|
||||
if (timelimit->value)
|
||||
{
|
||||
if (level.time >= timelimit->value * 60)
|
||||
{
|
||||
gi.bprintf(PRINT_HIGH, "Timelimit hit.\n");
|
||||
EndDMLevel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fraglimit->value)
|
||||
{
|
||||
for (i = 0; i < maxclients->value; i++)
|
||||
{
|
||||
cl = game.clients + i;
|
||||
if (!g_edicts[i + 1].inuse)
|
||||
continue;
|
||||
|
||||
if (cl->resp.score >= fraglimit->value)
|
||||
{
|
||||
gi.bprintf(PRINT_HIGH, "Fraglimit hit.\n");
|
||||
EndDMLevel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ExitLevel
|
||||
=============
|
||||
*/
|
||||
void ExitLevel(void)
|
||||
{
|
||||
int i;
|
||||
edict_t * ent;
|
||||
char command[256];
|
||||
|
||||
Com_sprintf(command, sizeof(command), "gamemap \"%s\"\n", level.changemap);
|
||||
gi.AddCommandString(command);
|
||||
level.changemap = NULL;
|
||||
level.exitintermission = 0;
|
||||
level.intermissiontime = 0;
|
||||
ClientEndServerFrames();
|
||||
|
||||
// clear some things before going to next level
|
||||
for (i = 0; i < maxclients->value; i++)
|
||||
{
|
||||
ent = g_edicts + 1 + i;
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (ent->health > ent->client->pers.max_health)
|
||||
ent->health = ent->client->pers.max_health;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_RunFrame
|
||||
|
||||
Advances the world by 0.1 seconds
|
||||
================
|
||||
*/
|
||||
void G_RunFrame(void)
|
||||
{
|
||||
int i;
|
||||
edict_t * ent;
|
||||
|
||||
level.framenum++;
|
||||
level.time = level.framenum * FRAMETIME;
|
||||
|
||||
// choose a client for monsters to target this frame
|
||||
AI_SetSightClient();
|
||||
|
||||
// exit intermissions
|
||||
if (level.exitintermission)
|
||||
{
|
||||
ExitLevel();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// treat each object in turn
|
||||
// even the world gets a chance to think
|
||||
//
|
||||
ent = &g_edicts[0];
|
||||
for (i = 0; i < globals.num_edicts; i++, ent++)
|
||||
{
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
|
||||
level.current_entity = ent;
|
||||
|
||||
VectorCopy(ent->s.origin, ent->s.old_origin);
|
||||
|
||||
// if the ground entity moved, make sure we are still on it
|
||||
if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
if (!(ent->flags & (FL_SWIM | FL_FLY)) && (ent->svflags & SVF_MONSTER))
|
||||
{
|
||||
M_CheckGround(ent);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 && i <= maxclients->value)
|
||||
{
|
||||
ClientBeginServerFrame(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
G_RunEntity(ent);
|
||||
}
|
||||
|
||||
// see if it is time to end a deathmatch
|
||||
CheckDMRules();
|
||||
|
||||
// build the playerstate_t structures for all players
|
||||
ClientEndServerFrames();
|
||||
}
|
||||
+1844
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
//
|
||||
// monster weapons
|
||||
//
|
||||
|
||||
//FIXME mosnters should call these with a totally accurate direction
|
||||
// and we can mess it up based on skill. Spread should be for normal
|
||||
// and we can tighten or loosen based on skill. We could muck with
|
||||
// the damages too, but I'm not sure that's such a good idea.
|
||||
void monster_fire_bullet(edict_t * self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype)
|
||||
{
|
||||
fire_bullet(self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_shotgun(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
|
||||
{
|
||||
fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_blaster(edict_t * self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
|
||||
{
|
||||
fire_blaster(self, start, dir, damage, speed, effect, false);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_grenade(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
|
||||
{
|
||||
fire_grenade(self, start, aimdir, damage, speed, 2.5, damage + 40);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_rocket(edict_t * self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
|
||||
{
|
||||
fire_rocket(self, start, dir, damage, speed, damage + 20, damage);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_railgun(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
|
||||
{
|
||||
fire_rail(self, start, aimdir, damage, kick);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_bfg(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
|
||||
{
|
||||
fire_bfg(self, start, aimdir, damage, speed, damage_radius);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
//
|
||||
// Monster utility functions
|
||||
//
|
||||
|
||||
static void M_FliesOff(edict_t * self)
|
||||
{
|
||||
self->s.effects &= ~EF_FLIES;
|
||||
self->s.sound = 0;
|
||||
}
|
||||
|
||||
static void M_FliesOn(edict_t * self)
|
||||
{
|
||||
if (self->waterlevel)
|
||||
return;
|
||||
self->s.effects |= EF_FLIES;
|
||||
self->s.sound = gi.soundindex("infantry/inflies1.wav");
|
||||
self->think = M_FliesOff;
|
||||
self->nextthink = level.time + 60;
|
||||
}
|
||||
|
||||
void M_FlyCheck(edict_t * self)
|
||||
{
|
||||
if (self->waterlevel)
|
||||
return;
|
||||
|
||||
if (random() > 0.5)
|
||||
return;
|
||||
|
||||
self->think = M_FliesOn;
|
||||
self->nextthink = level.time + 5 + 10 * random();
|
||||
}
|
||||
|
||||
void AttackFinished(edict_t * self, float time)
|
||||
{
|
||||
self->monsterinfo.attack_finished = level.time + time;
|
||||
}
|
||||
|
||||
void M_CheckGround(edict_t * ent)
|
||||
{
|
||||
vec3_t point;
|
||||
trace_t trace;
|
||||
|
||||
if (ent->flags & (FL_SWIM | FL_FLY))
|
||||
return;
|
||||
|
||||
if (ent->velocity[2] > 100)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// if the hull point one-quarter unit down is solid the entity is on ground
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
point[2] = ent->s.origin[2] - 0.25;
|
||||
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
|
||||
|
||||
// check steepness
|
||||
if (trace.plane.normal[2] < 0.7 && !trace.startsolid)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// ent->groundentity = trace.ent;
|
||||
// ent->groundentity_linkcount = trace.ent->linkcount;
|
||||
// if (!trace.startsolid && !trace.allsolid)
|
||||
// VectorCopy (trace.endpos, ent->s.origin);
|
||||
if (!trace.startsolid && !trace.allsolid)
|
||||
{
|
||||
VectorCopy(trace.endpos, ent->s.origin);
|
||||
ent->groundentity = trace.ent;
|
||||
ent->groundentity_linkcount = trace.ent->linkcount;
|
||||
ent->velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void M_CatagorizePosition(edict_t * ent)
|
||||
{
|
||||
vec3_t point;
|
||||
int cont;
|
||||
|
||||
//
|
||||
// get waterlevel
|
||||
//
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
point[2] = ent->s.origin[2] + ent->mins[2] + 1;
|
||||
cont = gi.pointcontents(point);
|
||||
|
||||
if (!(cont & MASK_WATER))
|
||||
{
|
||||
ent->waterlevel = 0;
|
||||
ent->watertype = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ent->watertype = cont;
|
||||
ent->waterlevel = 1;
|
||||
point[2] += 26;
|
||||
cont = gi.pointcontents(point);
|
||||
if (!(cont & MASK_WATER))
|
||||
return;
|
||||
|
||||
ent->waterlevel = 2;
|
||||
point[2] += 22;
|
||||
cont = gi.pointcontents(point);
|
||||
if (cont & MASK_WATER)
|
||||
ent->waterlevel = 3;
|
||||
}
|
||||
|
||||
void M_WorldEffects(edict_t * ent)
|
||||
{
|
||||
int dmg;
|
||||
|
||||
if (ent->health > 0)
|
||||
{
|
||||
if (!(ent->flags & FL_SWIM))
|
||||
{
|
||||
if (ent->waterlevel < 3)
|
||||
{
|
||||
ent->air_finished = level.time + 12;
|
||||
}
|
||||
else if (ent->air_finished < level.time)
|
||||
{ // drown!
|
||||
if (ent->pain_debounce_time < level.time)
|
||||
{
|
||||
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||
if (dmg > 15)
|
||||
dmg = 15;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||
ent->pain_debounce_time = level.time + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ent->waterlevel > 0)
|
||||
{
|
||||
ent->air_finished = level.time + 9;
|
||||
}
|
||||
else if (ent->air_finished < level.time)
|
||||
{ // suffocate!
|
||||
if (ent->pain_debounce_time < level.time)
|
||||
{
|
||||
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||
if (dmg > 15)
|
||||
dmg = 15;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||
ent->pain_debounce_time = level.time + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->waterlevel == 0)
|
||||
{
|
||||
if (ent->flags & FL_INWATER)
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
|
||||
ent->flags &= ~FL_INWATER;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
|
||||
{
|
||||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 0.2;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10 * ent->waterlevel, 0, 0, MOD_LAVA);
|
||||
}
|
||||
}
|
||||
if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
|
||||
{
|
||||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 1;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4 * ent->waterlevel, 0, 0, MOD_SLIME);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ent->flags & FL_INWATER))
|
||||
{
|
||||
if (!(ent->svflags & SVF_DEADMONSTER))
|
||||
{
|
||||
if (ent->watertype & CONTENTS_LAVA)
|
||||
{
|
||||
if (random() <= 0.5)
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
else if (ent->watertype & CONTENTS_SLIME)
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else if (ent->watertype & CONTENTS_WATER)
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ent->flags |= FL_INWATER;
|
||||
ent->damage_debounce_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void M_droptofloor(edict_t * ent)
|
||||
{
|
||||
vec3_t end;
|
||||
trace_t trace;
|
||||
|
||||
ent->s.origin[2] += 1;
|
||||
VectorCopy(ent->s.origin, end);
|
||||
end[2] -= 256;
|
||||
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
|
||||
|
||||
if (trace.fraction == 1 || trace.allsolid)
|
||||
return;
|
||||
|
||||
VectorCopy(trace.endpos, ent->s.origin);
|
||||
|
||||
gi.linkentity(ent);
|
||||
M_CheckGround(ent);
|
||||
M_CatagorizePosition(ent);
|
||||
}
|
||||
|
||||
void M_SetEffects(edict_t * ent)
|
||||
{
|
||||
ent->s.effects &= ~(EF_COLOR_SHELL | EF_POWERSCREEN);
|
||||
ent->s.renderfx &= ~(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE);
|
||||
|
||||
if (ent->monsterinfo.aiflags & AI_RESURRECTING)
|
||||
{
|
||||
ent->s.effects |= EF_COLOR_SHELL;
|
||||
ent->s.renderfx |= RF_SHELL_RED;
|
||||
}
|
||||
|
||||
if (ent->health <= 0)
|
||||
return;
|
||||
|
||||
if (ent->powerarmor_time > level.time)
|
||||
{
|
||||
if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
|
||||
{
|
||||
ent->s.effects |= EF_POWERSCREEN;
|
||||
}
|
||||
else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
|
||||
{
|
||||
ent->s.effects |= EF_COLOR_SHELL;
|
||||
ent->s.renderfx |= RF_SHELL_GREEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M_MoveFrame(edict_t * self)
|
||||
{
|
||||
mmove_t * move;
|
||||
int index;
|
||||
|
||||
move = self->monsterinfo.currentmove;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if ((self->monsterinfo.nextframe) &&
|
||||
(self->monsterinfo.nextframe >= move->firstframe) &&
|
||||
(self->monsterinfo.nextframe <= move->lastframe))
|
||||
{
|
||||
self->s.frame = self->monsterinfo.nextframe;
|
||||
self->monsterinfo.nextframe = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->s.frame == move->lastframe)
|
||||
{
|
||||
if (move->endfunc)
|
||||
{
|
||||
move->endfunc(self);
|
||||
|
||||
// regrab move, endfunc is very likely to change it
|
||||
move = self->monsterinfo.currentmove;
|
||||
|
||||
// check for death
|
||||
if (self->svflags & SVF_DEADMONSTER)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
self->s.frame = move->firstframe;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||
{
|
||||
self->s.frame++;
|
||||
if (self->s.frame > move->lastframe)
|
||||
{
|
||||
self->s.frame = move->firstframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index = self->s.frame - move->firstframe;
|
||||
if (move->frame[index].aifunc)
|
||||
{
|
||||
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||
{
|
||||
move->frame[index].aifunc(self, move->frame[index].dist * self->monsterinfo.scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
move->frame[index].aifunc(self, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (move->frame[index].thinkfunc)
|
||||
{
|
||||
move->frame[index].thinkfunc(self);
|
||||
}
|
||||
}
|
||||
|
||||
void monster_think(edict_t * self)
|
||||
{
|
||||
M_MoveFrame(self);
|
||||
if (self->linkcount != self->monsterinfo.linkcount)
|
||||
{
|
||||
self->monsterinfo.linkcount = self->linkcount;
|
||||
M_CheckGround(self);
|
||||
}
|
||||
M_CatagorizePosition(self);
|
||||
M_WorldEffects(self);
|
||||
M_SetEffects(self);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
monster_use
|
||||
|
||||
Using a monster makes it angry at the current activator
|
||||
================
|
||||
*/
|
||||
void monster_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (self->enemy)
|
||||
return;
|
||||
if (self->health <= 0)
|
||||
return;
|
||||
if (activator->flags & FL_NOTARGET)
|
||||
return;
|
||||
if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
return;
|
||||
|
||||
// delay reaction so if the monster is teleported, its sound is still heard
|
||||
self->enemy = activator;
|
||||
FoundTarget(self);
|
||||
}
|
||||
|
||||
void monster_start_go(edict_t * self);
|
||||
|
||||
void monster_triggered_spawn(edict_t * self)
|
||||
{
|
||||
self->s.origin[2] += 1;
|
||||
KillBox(self);
|
||||
|
||||
self->solid = SOLID_BBOX;
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->air_finished = level.time + 12;
|
||||
gi.linkentity(self);
|
||||
|
||||
monster_start_go(self);
|
||||
|
||||
if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
|
||||
{
|
||||
FoundTarget(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void monster_triggered_spawn_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
// we have a one frame delay here so we don't telefrag the guy who activated us
|
||||
self->think = monster_triggered_spawn;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
if (activator->client)
|
||||
self->enemy = activator;
|
||||
self->use = monster_use;
|
||||
}
|
||||
|
||||
void monster_triggered_start(edict_t * self)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
self->use = monster_triggered_spawn_use;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
monster_death_use
|
||||
|
||||
When a monster dies, it fires all of its targets with the current
|
||||
enemy as activator.
|
||||
================
|
||||
*/
|
||||
void monster_death_use(edict_t * self)
|
||||
{
|
||||
self->flags &= ~(FL_FLY | FL_SWIM);
|
||||
self->monsterinfo.aiflags &= AI_GOOD_GUY;
|
||||
|
||||
if (self->item)
|
||||
{
|
||||
Drop_Item(self, self->item);
|
||||
self->item = NULL;
|
||||
}
|
||||
|
||||
if (self->deathtarget)
|
||||
self->target = self->deathtarget;
|
||||
|
||||
if (!self->target)
|
||||
return;
|
||||
|
||||
G_UseTargets(self, self->enemy);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
qboolean monster_start(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
{
|
||||
self->spawnflags &= ~4;
|
||||
self->spawnflags |= 1;
|
||||
// gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
|
||||
if (!(self->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
level.total_monsters++;
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->svflags |= SVF_MONSTER;
|
||||
self->s.renderfx |= RF_FRAMELERP;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
self->air_finished = level.time + 12;
|
||||
self->use = monster_use;
|
||||
self->max_health = self->health;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
|
||||
self->s.skinnum = 0;
|
||||
self->deadflag = DEAD_NO;
|
||||
self->svflags &= ~SVF_DEADMONSTER;
|
||||
|
||||
if (!self->monsterinfo.checkattack)
|
||||
self->monsterinfo.checkattack = M_CheckAttack;
|
||||
VectorCopy(self->s.origin, self->s.old_origin);
|
||||
|
||||
if (st.item)
|
||||
{
|
||||
self->item = FindItemByClassname(st.item);
|
||||
if (!self->item)
|
||||
gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
|
||||
}
|
||||
|
||||
// randomize what frame they start on
|
||||
if (self->monsterinfo.currentmove)
|
||||
self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void monster_start_go(edict_t * self)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
if (self->health <= 0)
|
||||
return;
|
||||
|
||||
// check for target to combat_point and change to combattarget
|
||||
if (self->target)
|
||||
{
|
||||
qboolean notcombat;
|
||||
qboolean fixup;
|
||||
edict_t * target;
|
||||
|
||||
target = NULL;
|
||||
notcombat = false;
|
||||
fixup = false;
|
||||
while ((target = G_Find(target, FOFS(targetname), self->target)) != NULL)
|
||||
{
|
||||
if (strcmp(target->classname, "point_combat") == 0)
|
||||
{
|
||||
self->combattarget = self->target;
|
||||
fixup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
notcombat = true;
|
||||
}
|
||||
}
|
||||
if (notcombat && self->combattarget)
|
||||
gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
|
||||
if (fixup)
|
||||
self->target = NULL;
|
||||
}
|
||||
|
||||
// validate combattarget
|
||||
if (self->combattarget)
|
||||
{
|
||||
edict_t * target;
|
||||
|
||||
target = NULL;
|
||||
while ((target = G_Find(target, FOFS(targetname), self->combattarget)) != NULL)
|
||||
{
|
||||
if (strcmp(target->classname, "point_combat") != 0)
|
||||
{
|
||||
gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
|
||||
self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
|
||||
self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
|
||||
(int)target->s.origin[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->target)
|
||||
{
|
||||
self->goalentity = self->movetarget = G_PickTarget(self->target);
|
||||
if (!self->movetarget)
|
||||
{
|
||||
gi.dprintf("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||
self->target = NULL;
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand(self);
|
||||
}
|
||||
else if (strcmp(self->movetarget->classname, "path_corner") == 0)
|
||||
{
|
||||
VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
|
||||
self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
|
||||
self->monsterinfo.walk(self);
|
||||
self->target = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->goalentity = self->movetarget = NULL;
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand(self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand(self);
|
||||
}
|
||||
|
||||
self->think = monster_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void walkmonster_start_go(edict_t * self)
|
||||
{
|
||||
if (!(self->spawnflags & 2) && level.time < 1)
|
||||
{
|
||||
M_droptofloor(self);
|
||||
|
||||
if (self->groundentity)
|
||||
if (!M_walkmove(self, 0, 0))
|
||||
gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 20;
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go(self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
|
||||
void walkmonster_start(edict_t * self)
|
||||
{
|
||||
self->think = walkmonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
|
||||
void flymonster_start_go(edict_t * self)
|
||||
{
|
||||
if (!M_walkmove(self, 0, 0))
|
||||
gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 10;
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go(self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
|
||||
void flymonster_start(edict_t * self)
|
||||
{
|
||||
self->flags |= FL_FLY;
|
||||
self->think = flymonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
|
||||
void swimmonster_start_go(edict_t * self)
|
||||
{
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 10;
|
||||
self->viewheight = 10;
|
||||
|
||||
monster_start_go(self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
|
||||
void swimmonster_start(edict_t * self)
|
||||
{
|
||||
self->flags |= FL_SWIM;
|
||||
self->think = swimmonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,944 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// g_phys.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
|
||||
|
||||
pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
|
||||
|
||||
onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
|
||||
|
||||
doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
|
||||
bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
|
||||
corpses are SOLID_NOT and MOVETYPE_TOSS
|
||||
crates are SOLID_BBOX and MOVETYPE_TOSS
|
||||
walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
|
||||
flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
|
||||
|
||||
solid_edge items only clip against bsp models.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
============
|
||||
SV_TestEntityPosition
|
||||
|
||||
============
|
||||
*/
|
||||
edict_t * SV_TestEntityPosition(edict_t * ent)
|
||||
{
|
||||
trace_t trace;
|
||||
int mask;
|
||||
|
||||
if (ent->clipmask)
|
||||
mask = ent->clipmask;
|
||||
else
|
||||
mask = MASK_SOLID;
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask);
|
||||
|
||||
if (trace.startsolid)
|
||||
return g_edicts;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SV_CheckVelocity
|
||||
================
|
||||
*/
|
||||
void SV_CheckVelocity(edict_t * ent)
|
||||
{
|
||||
int i;
|
||||
|
||||
//
|
||||
// bound velocity
|
||||
//
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
if (ent->velocity[i] > sv_maxvelocity->value)
|
||||
ent->velocity[i] = sv_maxvelocity->value;
|
||||
else if (ent->velocity[i] < -sv_maxvelocity->value)
|
||||
ent->velocity[i] = -sv_maxvelocity->value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_RunThink
|
||||
|
||||
Runs thinking code for this frame if necessary
|
||||
=============
|
||||
*/
|
||||
qboolean SV_RunThink(edict_t * ent)
|
||||
{
|
||||
float thinktime;
|
||||
|
||||
thinktime = ent->nextthink;
|
||||
if (thinktime <= 0)
|
||||
return true;
|
||||
if (thinktime > level.time + 0.001)
|
||||
return true;
|
||||
|
||||
ent->nextthink = 0;
|
||||
if (!ent->think)
|
||||
gi.error("NULL ent->think");
|
||||
ent->think(ent);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SV_Impact
|
||||
|
||||
Two entities have touched, so run their touch functions
|
||||
==================
|
||||
*/
|
||||
void SV_Impact(edict_t * e1, trace_t * trace)
|
||||
{
|
||||
edict_t * e2;
|
||||
// cplane_t backplane;
|
||||
|
||||
e2 = trace->ent;
|
||||
|
||||
if (e1->touch && e1->solid != SOLID_NOT)
|
||||
e1->touch(e1, e2, &trace->plane, trace->surface);
|
||||
|
||||
if (e2->touch && e2->solid != SOLID_NOT)
|
||||
e2->touch(e2, e1, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ClipVelocity
|
||||
|
||||
Slide off of the impacting object
|
||||
returns the blocked flags (1 = floor, 2 = step / wall)
|
||||
==================
|
||||
*/
|
||||
#define STOP_EPSILON 0.1
|
||||
|
||||
int ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
|
||||
{
|
||||
float backoff;
|
||||
float change;
|
||||
int i, blocked;
|
||||
|
||||
blocked = 0;
|
||||
if (normal[2] > 0)
|
||||
blocked |= 1; // floor
|
||||
if (!normal[2])
|
||||
blocked |= 2; // step
|
||||
|
||||
backoff = DotProduct(in, normal) * overbounce;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
change = normal[i] * backoff;
|
||||
out[i] = in[i] - change;
|
||||
if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
SV_FlyMove
|
||||
|
||||
The basic solid body movement clip that slides along multiple planes
|
||||
Returns the clipflags if the velocity was modified (hit something solid)
|
||||
1 = floor
|
||||
2 = wall / step
|
||||
4 = dead stop
|
||||
============
|
||||
*/
|
||||
#define MAX_CLIP_PLANES 5
|
||||
int SV_FlyMove(edict_t * ent, float time, int mask)
|
||||
{
|
||||
edict_t * hit;
|
||||
int bumpcount, numbumps;
|
||||
vec3_t dir;
|
||||
float d;
|
||||
int numplanes;
|
||||
vec3_t planes[MAX_CLIP_PLANES];
|
||||
vec3_t primal_velocity, original_velocity, new_velocity;
|
||||
int i, j;
|
||||
trace_t trace;
|
||||
vec3_t end;
|
||||
float time_left;
|
||||
int blocked;
|
||||
|
||||
numbumps = 4;
|
||||
|
||||
blocked = 0;
|
||||
VectorCopy(ent->velocity, original_velocity);
|
||||
VectorCopy(ent->velocity, primal_velocity);
|
||||
numplanes = 0;
|
||||
|
||||
time_left = time;
|
||||
|
||||
ent->groundentity = NULL;
|
||||
for (bumpcount = 0; bumpcount < numbumps; bumpcount++)
|
||||
{
|
||||
for (i = 0; i < 3; i++)
|
||||
end[i] = ent->s.origin[i] + time_left * ent->velocity[i];
|
||||
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, ent, mask);
|
||||
|
||||
if (trace.allsolid)
|
||||
{ // entity is trapped in another solid
|
||||
VectorCopy(vec3_origin, ent->velocity);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (trace.fraction > 0)
|
||||
{ // actually covered some distance
|
||||
VectorCopy(trace.endpos, ent->s.origin);
|
||||
VectorCopy(ent->velocity, original_velocity);
|
||||
numplanes = 0;
|
||||
}
|
||||
|
||||
if (trace.fraction == 1)
|
||||
break; // moved the entire distance
|
||||
|
||||
hit = trace.ent;
|
||||
|
||||
if (trace.plane.normal[2] > 0.7)
|
||||
{
|
||||
blocked |= 1; // floor
|
||||
if (hit->solid == SOLID_BSP)
|
||||
{
|
||||
ent->groundentity = hit;
|
||||
ent->groundentity_linkcount = hit->linkcount;
|
||||
}
|
||||
}
|
||||
if (!trace.plane.normal[2])
|
||||
{
|
||||
blocked |= 2; // step
|
||||
}
|
||||
|
||||
//
|
||||
// run the impact function
|
||||
//
|
||||
SV_Impact(ent, &trace);
|
||||
if (!ent->inuse)
|
||||
break; // removed by the impact function
|
||||
|
||||
time_left -= time_left * trace.fraction;
|
||||
|
||||
// cliped to another plane
|
||||
if (numplanes >= MAX_CLIP_PLANES)
|
||||
{ // this shouldn't really happen
|
||||
VectorCopy(vec3_origin, ent->velocity);
|
||||
return 3;
|
||||
}
|
||||
|
||||
VectorCopy(trace.plane.normal, planes[numplanes]);
|
||||
numplanes++;
|
||||
|
||||
//
|
||||
// modify original_velocity so it parallels all of the clip planes
|
||||
//
|
||||
for (i = 0; i < numplanes; i++)
|
||||
{
|
||||
ClipVelocity(original_velocity, planes[i], new_velocity, 1);
|
||||
|
||||
for (j = 0; j < numplanes; j++)
|
||||
if ((j != i) && !VectorCompare(planes[i], planes[j]))
|
||||
{
|
||||
if (DotProduct(new_velocity, planes[j]) < 0)
|
||||
break; // not ok
|
||||
}
|
||||
if (j == numplanes)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != numplanes)
|
||||
{ // go along this plane
|
||||
VectorCopy(new_velocity, ent->velocity);
|
||||
}
|
||||
else
|
||||
{ // go along the crease
|
||||
if (numplanes != 2)
|
||||
{
|
||||
// gi.dprintf ("clip velocity, numplanes == %i\n",numplanes);
|
||||
VectorCopy(vec3_origin, ent->velocity);
|
||||
return 7;
|
||||
}
|
||||
CrossProduct(planes[0], planes[1], dir);
|
||||
d = DotProduct(dir, ent->velocity);
|
||||
VectorScale(dir, d, ent->velocity);
|
||||
}
|
||||
|
||||
//
|
||||
// if original velocity is against the original velocity, stop dead
|
||||
// to avoid tiny occilations in sloping corners
|
||||
//
|
||||
if (DotProduct(ent->velocity, primal_velocity) <= 0)
|
||||
{
|
||||
VectorCopy(vec3_origin, ent->velocity);
|
||||
return blocked;
|
||||
}
|
||||
}
|
||||
|
||||
return blocked;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
SV_AddGravity
|
||||
|
||||
============
|
||||
*/
|
||||
void SV_AddGravity(edict_t * ent)
|
||||
{
|
||||
ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
PUSHMOVE
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
============
|
||||
SV_PushEntity
|
||||
|
||||
Does not change the entities velocity at all
|
||||
============
|
||||
*/
|
||||
trace_t SV_PushEntity(edict_t * ent, vec3_t push)
|
||||
{
|
||||
trace_t trace;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
int mask;
|
||||
|
||||
VectorCopy(ent->s.origin, start);
|
||||
VectorAdd(start, push, end);
|
||||
|
||||
retry:
|
||||
if (ent->clipmask)
|
||||
mask = ent->clipmask;
|
||||
else
|
||||
mask = MASK_SOLID;
|
||||
|
||||
trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask);
|
||||
|
||||
VectorCopy(trace.endpos, ent->s.origin);
|
||||
gi.linkentity(ent);
|
||||
|
||||
if (trace.fraction != 1.0)
|
||||
{
|
||||
SV_Impact(ent, &trace);
|
||||
|
||||
// if the pushed entity went away and the pusher is still there
|
||||
if (!trace.ent->inuse && ent->inuse)
|
||||
{
|
||||
// move the pusher back and try again
|
||||
VectorCopy(start, ent->s.origin);
|
||||
gi.linkentity(ent);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->inuse)
|
||||
G_TouchTriggers(ent);
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
edict_t * ent;
|
||||
vec3_t origin;
|
||||
vec3_t angles;
|
||||
float deltayaw;
|
||||
} pushed_t;
|
||||
pushed_t pushed[MAX_EDICTS], *pushed_p;
|
||||
|
||||
edict_t * obstacle;
|
||||
|
||||
/*
|
||||
============
|
||||
SV_Push
|
||||
|
||||
Objects need to be moved back on a failed push,
|
||||
otherwise riders would continue to slide.
|
||||
============
|
||||
*/
|
||||
qboolean SV_Push(edict_t * pusher, vec3_t move, vec3_t amove)
|
||||
{
|
||||
int i, e;
|
||||
edict_t *check, *block;
|
||||
vec3_t mins, maxs;
|
||||
pushed_t * p;
|
||||
vec3_t org, org2, move2, forward, right, up;
|
||||
|
||||
// clamp the move to 1/8 units, so the position will
|
||||
// be accurate for client side prediction
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
float temp;
|
||||
temp = move[i] * 8.0;
|
||||
if (temp > 0.0)
|
||||
temp += 0.5;
|
||||
else
|
||||
temp -= 0.5;
|
||||
move[i] = 0.125 * (int)temp;
|
||||
}
|
||||
|
||||
// find the bounding box
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
mins[i] = pusher->absmin[i] + move[i];
|
||||
maxs[i] = pusher->absmax[i] + move[i];
|
||||
}
|
||||
|
||||
// we need this for pushing things later
|
||||
VectorSubtract(vec3_origin, amove, org);
|
||||
AngleVectors(org, forward, right, up);
|
||||
|
||||
// save the pusher's original position
|
||||
pushed_p->ent = pusher;
|
||||
VectorCopy(pusher->s.origin, pushed_p->origin);
|
||||
VectorCopy(pusher->s.angles, pushed_p->angles);
|
||||
if (pusher->client)
|
||||
pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];
|
||||
pushed_p++;
|
||||
|
||||
// move the pusher to it's final position
|
||||
VectorAdd(pusher->s.origin, move, pusher->s.origin);
|
||||
VectorAdd(pusher->s.angles, amove, pusher->s.angles);
|
||||
gi.linkentity(pusher);
|
||||
|
||||
// see if any solid entities are inside the final position
|
||||
check = g_edicts + 1;
|
||||
for (e = 1; e < globals.num_edicts; e++, check++)
|
||||
{
|
||||
if (!check->inuse)
|
||||
continue;
|
||||
if (check->movetype == MOVETYPE_PUSH || check->movetype == MOVETYPE_STOP || check->movetype == MOVETYPE_NONE || check->movetype == MOVETYPE_NOCLIP)
|
||||
continue;
|
||||
|
||||
if (!check->area.prev)
|
||||
continue; // not linked in anywhere
|
||||
|
||||
// if the entity is standing on the pusher, it will definitely be moved
|
||||
if (check->groundentity != pusher)
|
||||
{
|
||||
// see if the ent needs to be tested
|
||||
if (check->absmin[0] >= maxs[0] || check->absmin[1] >= maxs[1] || check->absmin[2] >= maxs[2] || check->absmax[0] <= mins[0] || check->absmax[1] <= mins[1] || check->absmax[2] <= mins[2])
|
||||
continue;
|
||||
|
||||
// see if the ent's bbox is inside the pusher's final position
|
||||
if (!SV_TestEntityPosition(check))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher))
|
||||
{
|
||||
// move this entity
|
||||
pushed_p->ent = check;
|
||||
VectorCopy(check->s.origin, pushed_p->origin);
|
||||
VectorCopy(check->s.angles, pushed_p->angles);
|
||||
pushed_p++;
|
||||
|
||||
// try moving the contacted entity
|
||||
VectorAdd(check->s.origin, move, check->s.origin);
|
||||
if (check->client)
|
||||
{ // FIXME: doesn't rotate monsters?
|
||||
check->client->ps.pmove.delta_angles[YAW] += amove[YAW];
|
||||
}
|
||||
|
||||
// figure movement due to the pusher's amove
|
||||
VectorSubtract(check->s.origin, pusher->s.origin, org);
|
||||
org2[0] = DotProduct(org, forward);
|
||||
org2[1] = -DotProduct(org, right);
|
||||
org2[2] = DotProduct(org, up);
|
||||
VectorSubtract(org2, org, move2);
|
||||
VectorAdd(check->s.origin, move2, check->s.origin);
|
||||
|
||||
// may have pushed them off an edge
|
||||
if (check->groundentity != pusher)
|
||||
check->groundentity = NULL;
|
||||
|
||||
block = SV_TestEntityPosition(check);
|
||||
if (!block)
|
||||
{ // pushed ok
|
||||
gi.linkentity(check);
|
||||
// impact?
|
||||
continue;
|
||||
}
|
||||
|
||||
// if it is ok to leave in the old position, do it
|
||||
// this is only relevent for riding entities, not pushed
|
||||
// FIXME: this doesn't acount for rotation
|
||||
VectorSubtract(check->s.origin, move, check->s.origin);
|
||||
block = SV_TestEntityPosition(check);
|
||||
if (!block)
|
||||
{
|
||||
pushed_p--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// save off the obstacle so we can call the block function
|
||||
obstacle = check;
|
||||
|
||||
// move back any entities we already moved
|
||||
// go backwards, so if the same entity was pushed
|
||||
// twice, it goes back to the original position
|
||||
for (p = pushed_p - 1; p >= pushed; p--)
|
||||
{
|
||||
VectorCopy(p->origin, p->ent->s.origin);
|
||||
VectorCopy(p->angles, p->ent->s.angles);
|
||||
if (p->ent->client)
|
||||
{
|
||||
p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;
|
||||
}
|
||||
gi.linkentity(p->ent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//FIXME: is there a better way to handle this?
|
||||
// see if anything we moved has touched a trigger
|
||||
for (p = pushed_p - 1; p >= pushed; p--)
|
||||
G_TouchTriggers(p->ent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SV_Physics_Pusher
|
||||
|
||||
Bmodel objects don't interact with each other, but
|
||||
push all box objects
|
||||
================
|
||||
*/
|
||||
void SV_Physics_Pusher(edict_t * ent)
|
||||
{
|
||||
vec3_t move, amove;
|
||||
edict_t *part, *mv;
|
||||
|
||||
// if not a team captain, so movement will be handled elsewhere
|
||||
if (ent->flags & FL_TEAMSLAVE)
|
||||
return;
|
||||
|
||||
// make sure all team slaves can move before commiting
|
||||
// any moves or calling any think functions
|
||||
// if the move is blocked, all moved objects will be backed out
|
||||
//retry:
|
||||
pushed_p = pushed;
|
||||
for (part = ent; part; part = part->teamchain)
|
||||
{
|
||||
if (part->velocity[0] || part->velocity[1] || part->velocity[2] ||
|
||||
part->avelocity[0] || part->avelocity[1] || part->avelocity[2])
|
||||
{ // object is moving
|
||||
VectorScale(part->velocity, FRAMETIME, move);
|
||||
VectorScale(part->avelocity, FRAMETIME, amove);
|
||||
|
||||
if (!SV_Push(part, move, amove))
|
||||
break; // move was blocked
|
||||
}
|
||||
}
|
||||
if (pushed_p > &pushed[MAX_EDICTS])
|
||||
gi.error(ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
|
||||
|
||||
if (part)
|
||||
{
|
||||
// the move failed, bump all nextthink times and back out moves
|
||||
for (mv = ent; mv; mv = mv->teamchain)
|
||||
{
|
||||
if (mv->nextthink > 0)
|
||||
mv->nextthink += FRAMETIME;
|
||||
}
|
||||
|
||||
// if the pusher has a "blocked" function, call it
|
||||
// otherwise, just stay in place until the obstacle is gone
|
||||
if (part->blocked)
|
||||
part->blocked(part, obstacle);
|
||||
#if 0
|
||||
// if the pushed entity went away and the pusher is still there
|
||||
if (!obstacle->inuse && part->inuse)
|
||||
goto retry;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// the move succeeded, so call all think functions
|
||||
for (part = ent; part; part = part->teamchain)
|
||||
{
|
||||
SV_RunThink(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_Physics_None
|
||||
|
||||
Non moving objects can only think
|
||||
=============
|
||||
*/
|
||||
void SV_Physics_None(edict_t * ent)
|
||||
{
|
||||
// regular thinking
|
||||
SV_RunThink(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_Physics_Noclip
|
||||
|
||||
A moving object that doesn't obey physics
|
||||
=============
|
||||
*/
|
||||
void SV_Physics_Noclip(edict_t * ent)
|
||||
{
|
||||
// regular thinking
|
||||
if (!SV_RunThink(ent))
|
||||
return;
|
||||
|
||||
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
|
||||
VectorMA(ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin);
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
TOSS / BOUNCE
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_Physics_Toss
|
||||
|
||||
Toss, bounce, and fly movement. When onground, do nothing.
|
||||
=============
|
||||
*/
|
||||
void SV_Physics_Toss(edict_t * ent)
|
||||
{
|
||||
trace_t trace;
|
||||
vec3_t move;
|
||||
float backoff;
|
||||
edict_t * slave;
|
||||
qboolean wasinwater;
|
||||
qboolean isinwater;
|
||||
vec3_t old_origin;
|
||||
|
||||
// regular thinking
|
||||
SV_RunThink(ent);
|
||||
|
||||
// if not a team captain, so movement will be handled elsewhere
|
||||
if (ent->flags & FL_TEAMSLAVE)
|
||||
return;
|
||||
|
||||
if (ent->velocity[2] > 0)
|
||||
ent->groundentity = NULL;
|
||||
|
||||
// check for the groundentity going away
|
||||
if (ent->groundentity)
|
||||
if (!ent->groundentity->inuse)
|
||||
ent->groundentity = NULL;
|
||||
|
||||
// if onground, return without moving
|
||||
if (ent->groundentity)
|
||||
return;
|
||||
|
||||
VectorCopy(ent->s.origin, old_origin);
|
||||
|
||||
SV_CheckVelocity(ent);
|
||||
|
||||
// add gravity
|
||||
if (ent->movetype != MOVETYPE_FLY && ent->movetype != MOVETYPE_FLYMISSILE)
|
||||
SV_AddGravity(ent);
|
||||
|
||||
// move angles
|
||||
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
|
||||
|
||||
// move origin
|
||||
VectorScale(ent->velocity, FRAMETIME, move);
|
||||
trace = SV_PushEntity(ent, move);
|
||||
if (!ent->inuse)
|
||||
return;
|
||||
|
||||
if (trace.fraction < 1)
|
||||
{
|
||||
if (ent->movetype == MOVETYPE_BOUNCE)
|
||||
backoff = 1.5;
|
||||
else
|
||||
backoff = 1;
|
||||
|
||||
ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff);
|
||||
|
||||
// stop if on ground
|
||||
if (trace.plane.normal[2] > 0.7)
|
||||
{
|
||||
if (ent->velocity[2] < 60 || ent->movetype != MOVETYPE_BOUNCE)
|
||||
{
|
||||
ent->groundentity = trace.ent;
|
||||
ent->groundentity_linkcount = trace.ent->linkcount;
|
||||
VectorCopy(vec3_origin, ent->velocity);
|
||||
VectorCopy(vec3_origin, ent->avelocity);
|
||||
}
|
||||
}
|
||||
|
||||
// if (ent->touch)
|
||||
// ent->touch (ent, trace.ent, &trace.plane, trace.surface);
|
||||
}
|
||||
|
||||
// check for water transition
|
||||
wasinwater = (ent->watertype & MASK_WATER);
|
||||
ent->watertype = gi.pointcontents(ent->s.origin);
|
||||
isinwater = ent->watertype & MASK_WATER;
|
||||
|
||||
if (isinwater)
|
||||
ent->waterlevel = 1;
|
||||
else
|
||||
ent->waterlevel = 0;
|
||||
|
||||
if (!wasinwater && isinwater)
|
||||
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
|
||||
else if (wasinwater && !isinwater)
|
||||
gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
|
||||
|
||||
// move teamslaves
|
||||
for (slave = ent->teamchain; slave; slave = slave->teamchain)
|
||||
{
|
||||
VectorCopy(ent->s.origin, slave->s.origin);
|
||||
gi.linkentity(slave);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
STEPPING MOVEMENT
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_Physics_Step
|
||||
|
||||
Monsters freefall when they don't have a ground entity, otherwise
|
||||
all movement is done with discrete steps.
|
||||
|
||||
This is also used for objects that have become still on the ground, but
|
||||
will fall if the floor is pulled out from under them.
|
||||
FIXME: is this true?
|
||||
=============
|
||||
*/
|
||||
|
||||
//FIXME: hacked in for E3 demo
|
||||
#define sv_stopspeed 100
|
||||
#define sv_friction 6
|
||||
#define sv_waterfriction 1
|
||||
|
||||
void SV_AddRotationalFriction(edict_t * ent)
|
||||
{
|
||||
int n;
|
||||
float adjustment;
|
||||
|
||||
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
|
||||
adjustment = FRAMETIME * sv_stopspeed * sv_friction;
|
||||
for (n = 0; n < 3; n++)
|
||||
{
|
||||
if (ent->avelocity[n] > 0)
|
||||
{
|
||||
ent->avelocity[n] -= adjustment;
|
||||
if (ent->avelocity[n] < 0)
|
||||
ent->avelocity[n] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->avelocity[n] += adjustment;
|
||||
if (ent->avelocity[n] > 0)
|
||||
ent->avelocity[n] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SV_Physics_Step(edict_t * ent)
|
||||
{
|
||||
qboolean wasonground;
|
||||
qboolean hitsound = false;
|
||||
float * vel;
|
||||
float speed, newspeed, control;
|
||||
float friction;
|
||||
edict_t * groundentity;
|
||||
int mask;
|
||||
|
||||
// airborn monsters should always check for ground
|
||||
if (!ent->groundentity)
|
||||
M_CheckGround(ent);
|
||||
|
||||
groundentity = ent->groundentity;
|
||||
|
||||
SV_CheckVelocity(ent);
|
||||
|
||||
if (groundentity)
|
||||
wasonground = true;
|
||||
else
|
||||
wasonground = false;
|
||||
|
||||
if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
|
||||
SV_AddRotationalFriction(ent);
|
||||
|
||||
// add gravity except:
|
||||
// flying monsters
|
||||
// swimming monsters who are in the water
|
||||
if (!wasonground)
|
||||
if (!(ent->flags & FL_FLY))
|
||||
if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2)))
|
||||
{
|
||||
if (ent->velocity[2] < sv_gravity->value * -0.1)
|
||||
hitsound = true;
|
||||
if (ent->waterlevel == 0)
|
||||
SV_AddGravity(ent);
|
||||
}
|
||||
|
||||
// friction for flying monsters that have been given vertical velocity
|
||||
if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
|
||||
{
|
||||
speed = fabs(ent->velocity[2]);
|
||||
control = speed < sv_stopspeed ? sv_stopspeed : speed;
|
||||
friction = sv_friction / 3;
|
||||
newspeed = speed - (FRAMETIME * control * friction);
|
||||
if (newspeed < 0)
|
||||
newspeed = 0;
|
||||
newspeed /= speed;
|
||||
ent->velocity[2] *= newspeed;
|
||||
}
|
||||
|
||||
// friction for flying monsters that have been given vertical velocity
|
||||
if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
|
||||
{
|
||||
speed = fabs(ent->velocity[2]);
|
||||
control = speed < sv_stopspeed ? sv_stopspeed : speed;
|
||||
newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel);
|
||||
if (newspeed < 0)
|
||||
newspeed = 0;
|
||||
newspeed /= speed;
|
||||
ent->velocity[2] *= newspeed;
|
||||
}
|
||||
|
||||
if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
|
||||
{
|
||||
// apply friction
|
||||
// let dead monsters who aren't completely onground slide
|
||||
if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY)))
|
||||
if (!(ent->health <= 0.0 && !M_CheckBottom(ent)))
|
||||
{
|
||||
vel = ent->velocity;
|
||||
speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]);
|
||||
if (speed)
|
||||
{
|
||||
friction = sv_friction;
|
||||
|
||||
control = speed < sv_stopspeed ? sv_stopspeed : speed;
|
||||
newspeed = speed - FRAMETIME * control * friction;
|
||||
|
||||
if (newspeed < 0)
|
||||
newspeed = 0;
|
||||
newspeed /= speed;
|
||||
|
||||
vel[0] *= newspeed;
|
||||
vel[1] *= newspeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->svflags & SVF_MONSTER)
|
||||
mask = MASK_MONSTERSOLID;
|
||||
else
|
||||
mask = MASK_SOLID;
|
||||
SV_FlyMove(ent, FRAMETIME, mask);
|
||||
|
||||
gi.linkentity(ent);
|
||||
G_TouchTriggers(ent);
|
||||
if (!ent->inuse)
|
||||
return;
|
||||
|
||||
if (ent->groundentity)
|
||||
if (!wasonground)
|
||||
if (hitsound)
|
||||
gi.sound(ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0);
|
||||
}
|
||||
|
||||
// regular thinking
|
||||
SV_RunThink(ent);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
/*
|
||||
================
|
||||
G_RunEntity
|
||||
|
||||
================
|
||||
*/
|
||||
void G_RunEntity(edict_t * ent)
|
||||
{
|
||||
if (ent->prethink)
|
||||
ent->prethink(ent);
|
||||
|
||||
switch ((int)ent->movetype)
|
||||
{
|
||||
case MOVETYPE_PUSH:
|
||||
case MOVETYPE_STOP:
|
||||
SV_Physics_Pusher(ent);
|
||||
break;
|
||||
case MOVETYPE_NONE:
|
||||
SV_Physics_None(ent);
|
||||
break;
|
||||
case MOVETYPE_NOCLIP:
|
||||
SV_Physics_Noclip(ent);
|
||||
break;
|
||||
case MOVETYPE_STEP:
|
||||
SV_Physics_Step(ent);
|
||||
break;
|
||||
case MOVETYPE_TOSS:
|
||||
case MOVETYPE_BOUNCE:
|
||||
case MOVETYPE_FLY:
|
||||
case MOVETYPE_FLYMISSILE:
|
||||
SV_Physics_Toss(ent);
|
||||
break;
|
||||
default:
|
||||
gi.error("SV_Physics: bad movetype %i", (int)ent->movetype);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
#define Function(f) \
|
||||
{ \
|
||||
#f, f \
|
||||
}
|
||||
|
||||
mmove_t mmove_reloc;
|
||||
|
||||
field_t fields[] = {
|
||||
{ "classname", FOFS(classname), F_LSTRING },
|
||||
{ "model", FOFS(model), F_LSTRING },
|
||||
{ "spawnflags", FOFS(spawnflags), F_INT },
|
||||
{ "speed", FOFS(speed), F_FLOAT },
|
||||
{ "accel", FOFS(accel), F_FLOAT },
|
||||
{ "decel", FOFS(decel), F_FLOAT },
|
||||
{ "target", FOFS(target), F_LSTRING },
|
||||
{ "targetname", FOFS(targetname), F_LSTRING },
|
||||
{ "pathtarget", FOFS(pathtarget), F_LSTRING },
|
||||
{ "deathtarget", FOFS(deathtarget), F_LSTRING },
|
||||
{ "killtarget", FOFS(killtarget), F_LSTRING },
|
||||
{ "combattarget", FOFS(combattarget), F_LSTRING },
|
||||
{ "message", FOFS(message), F_LSTRING },
|
||||
{ "team", FOFS(team), F_LSTRING },
|
||||
{ "wait", FOFS(wait), F_FLOAT },
|
||||
{ "delay", FOFS(delay), F_FLOAT },
|
||||
{ "random", FOFS(random), F_FLOAT },
|
||||
{ "move_origin", FOFS(move_origin), F_VECTOR },
|
||||
{ "move_angles", FOFS(move_angles), F_VECTOR },
|
||||
{ "style", FOFS(style), F_INT },
|
||||
{ "count", FOFS(count), F_INT },
|
||||
{ "health", FOFS(health), F_INT },
|
||||
{ "sounds", FOFS(sounds), F_INT },
|
||||
{ "light", 0, F_IGNORE },
|
||||
{ "dmg", FOFS(dmg), F_INT },
|
||||
{ "mass", FOFS(mass), F_INT },
|
||||
{ "volume", FOFS(volume), F_FLOAT },
|
||||
{ "attenuation", FOFS(attenuation), F_FLOAT },
|
||||
{ "map", FOFS(map), F_LSTRING },
|
||||
{ "origin", FOFS(s.origin), F_VECTOR },
|
||||
{ "angles", FOFS(s.angles), F_VECTOR },
|
||||
{ "angle", FOFS(s.angles), F_ANGLEHACK },
|
||||
|
||||
{ "goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN },
|
||||
{ "movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN },
|
||||
{ "enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN },
|
||||
{ "oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN },
|
||||
{ "activator", FOFS(activator), F_EDICT, FFL_NOSPAWN },
|
||||
{ "groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN },
|
||||
{ "teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN },
|
||||
{ "teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN },
|
||||
{ "owner", FOFS(owner), F_EDICT, FFL_NOSPAWN },
|
||||
{ "mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN },
|
||||
{ "mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN },
|
||||
{ "target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN },
|
||||
{ "chain", FOFS(chain), F_EDICT, FFL_NOSPAWN },
|
||||
|
||||
{ "prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "think", FOFS(think), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "use", FOFS(use), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "die", FOFS(die), F_FUNCTION, FFL_NOSPAWN },
|
||||
|
||||
{ "stand", FOFS(monsterinfo.stand), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "idle", FOFS(monsterinfo.idle), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "search", FOFS(monsterinfo.search), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "walk", FOFS(monsterinfo.walk), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "run", FOFS(monsterinfo.run), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "dodge", FOFS(monsterinfo.dodge), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "attack", FOFS(monsterinfo.attack), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "melee", FOFS(monsterinfo.melee), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "sight", FOFS(monsterinfo.sight), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "checkattack", FOFS(monsterinfo.checkattack), F_FUNCTION, FFL_NOSPAWN },
|
||||
{ "currentmove", FOFS(monsterinfo.currentmove), F_MMOVE, FFL_NOSPAWN },
|
||||
|
||||
{ "endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN },
|
||||
|
||||
// temp spawn vars -- only valid when the spawn function is called
|
||||
{ "lip", STOFS(lip), F_INT, FFL_SPAWNTEMP },
|
||||
{ "distance", STOFS(distance), F_INT, FFL_SPAWNTEMP },
|
||||
{ "height", STOFS(height), F_INT, FFL_SPAWNTEMP },
|
||||
{ "noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP },
|
||||
{ "pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP },
|
||||
|
||||
//need for item field in edict struct, FFL_SPAWNTEMP item will be skipped on saves
|
||||
{ "item", FOFS(item), F_ITEM },
|
||||
|
||||
{ "gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP },
|
||||
{ "sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP },
|
||||
{ "skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP },
|
||||
{ "minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP },
|
||||
{ "nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP },
|
||||
|
||||
{ 0, 0, 0, 0 }
|
||||
|
||||
};
|
||||
|
||||
field_t levelfields[] =
|
||||
{
|
||||
{ "changemap", LLOFS(changemap), F_LSTRING },
|
||||
|
||||
{ "sight_client", LLOFS(sight_client), F_EDICT },
|
||||
{ "sight_entity", LLOFS(sight_entity), F_EDICT },
|
||||
{ "sound_entity", LLOFS(sound_entity), F_EDICT },
|
||||
{ "sound2_entity", LLOFS(sound2_entity), F_EDICT },
|
||||
|
||||
{ NULL, 0, F_INT }
|
||||
};
|
||||
|
||||
field_t clientfields[] =
|
||||
{
|
||||
{ "pers.weapon", CLOFS(pers.weapon), F_ITEM },
|
||||
{ "pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM },
|
||||
{ "newweapon", CLOFS(newweapon), F_ITEM },
|
||||
|
||||
{ NULL, 0, F_INT }
|
||||
};
|
||||
|
||||
/*
|
||||
============
|
||||
InitGame
|
||||
|
||||
This will be called when the dll is first loaded, which
|
||||
only happens when a new game is started or a save game
|
||||
is loaded.
|
||||
============
|
||||
*/
|
||||
void InitGame(void)
|
||||
{
|
||||
gi.dprintf("==== InitGame ====\n");
|
||||
|
||||
gun_x = gi.cvar("gun_x", "0", 0);
|
||||
gun_y = gi.cvar("gun_y", "0", 0);
|
||||
gun_z = gi.cvar("gun_z", "0", 0);
|
||||
|
||||
//FIXME: sv_ prefix is wrong for these
|
||||
sv_rollspeed = gi.cvar("sv_rollspeed", "200", 0);
|
||||
sv_rollangle = gi.cvar("sv_rollangle", "2", 0);
|
||||
sv_maxvelocity = gi.cvar("sv_maxvelocity", "2000", 0);
|
||||
sv_gravity = gi.cvar("sv_gravity", "800", 0);
|
||||
|
||||
// noset vars
|
||||
dedicated = gi.cvar("dedicated", "0", CVAR_NOSET);
|
||||
|
||||
// latched vars
|
||||
sv_cheats = gi.cvar("cheats", "0", CVAR_SERVERINFO | CVAR_LATCH);
|
||||
gi.cvar("gamename", GAMEVERSION, CVAR_SERVERINFO | CVAR_LATCH);
|
||||
gi.cvar("gamedate", __DATE__, CVAR_SERVERINFO | CVAR_LATCH);
|
||||
|
||||
maxclients = gi.cvar("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
|
||||
maxspectators = gi.cvar("maxspectators", "4", CVAR_SERVERINFO);
|
||||
deathmatch = gi.cvar("deathmatch", "0", CVAR_LATCH);
|
||||
coop = gi.cvar("coop", "0", CVAR_LATCH);
|
||||
skill = gi.cvar("skill", "1", CVAR_LATCH);
|
||||
maxentities = gi.cvar("maxentities", "1024", CVAR_LATCH);
|
||||
|
||||
// change anytime vars
|
||||
dmflags = gi.cvar("dmflags", "0", CVAR_SERVERINFO);
|
||||
fraglimit = gi.cvar("fraglimit", "0", CVAR_SERVERINFO);
|
||||
timelimit = gi.cvar("timelimit", "0", CVAR_SERVERINFO);
|
||||
password = gi.cvar("password", "", CVAR_USERINFO);
|
||||
spectator_password = gi.cvar("spectator_password", "", CVAR_USERINFO);
|
||||
filterban = gi.cvar("filterban", "1", 0);
|
||||
|
||||
g_select_empty = gi.cvar("g_select_empty", "0", CVAR_ARCHIVE);
|
||||
|
||||
run_pitch = gi.cvar("run_pitch", "0.002", 0);
|
||||
run_roll = gi.cvar("run_roll", "0.005", 0);
|
||||
bob_up = gi.cvar("bob_up", "0.005", 0);
|
||||
bob_pitch = gi.cvar("bob_pitch", "0.002", 0);
|
||||
bob_roll = gi.cvar("bob_roll", "0.002", 0);
|
||||
|
||||
// flood control
|
||||
flood_msgs = gi.cvar("flood_msgs", "4", 0);
|
||||
flood_persecond = gi.cvar("flood_persecond", "4", 0);
|
||||
flood_waitdelay = gi.cvar("flood_waitdelay", "10", 0);
|
||||
|
||||
// dm map list
|
||||
sv_maplist = gi.cvar("sv_maplist", "", 0);
|
||||
|
||||
// items
|
||||
InitItems();
|
||||
|
||||
Com_sprintf(game.helpmessage1, sizeof(game.helpmessage1), "");
|
||||
Com_sprintf(game.helpmessage2, sizeof(game.helpmessage2), "");
|
||||
|
||||
// initialize all entities for this game
|
||||
game.maxentities = maxentities->value;
|
||||
g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||
globals.edicts = g_edicts;
|
||||
globals.max_edicts = game.maxentities;
|
||||
|
||||
// initialize all clients for this game
|
||||
game.maxclients = maxclients->value;
|
||||
game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||
globals.num_edicts = game.maxclients + 1;
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
void WriteField1(FILE * f, field_t * field, byte * base)
|
||||
{
|
||||
void * p;
|
||||
int len;
|
||||
int index;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_INT:
|
||||
case F_FLOAT:
|
||||
case F_ANGLEHACK:
|
||||
case F_VECTOR:
|
||||
case F_IGNORE:
|
||||
break;
|
||||
|
||||
case F_LSTRING:
|
||||
case F_GSTRING:
|
||||
if (*(char **)p)
|
||||
len = strlen(*(char **)p) + 1;
|
||||
else
|
||||
len = 0;
|
||||
*(int *)p = len;
|
||||
break;
|
||||
case F_EDICT:
|
||||
if (*(edict_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(edict_t **)p - g_edicts;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
case F_CLIENT:
|
||||
if (*(gclient_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(gclient_t **)p - game.clients;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
case F_ITEM:
|
||||
if (*(edict_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(gitem_t **)p - itemlist;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
//relative to code segment
|
||||
case F_FUNCTION:
|
||||
if (*(byte **)p == NULL)
|
||||
index = 0;
|
||||
else
|
||||
index = *(byte **)p - ((byte *)InitGame);
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
//relative to data segment
|
||||
case F_MMOVE:
|
||||
if (*(byte **)p == NULL)
|
||||
index = 0;
|
||||
else
|
||||
index = *(byte **)p - (byte *)&mmove_reloc;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
default:
|
||||
gi.error("WriteEdict: unknown field type");
|
||||
}
|
||||
}
|
||||
|
||||
void WriteField2(FILE * f, field_t * field, byte * base)
|
||||
{
|
||||
int len;
|
||||
void * p;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_LSTRING:
|
||||
if (*(char **)p)
|
||||
{
|
||||
len = strlen(*(char **)p) + 1;
|
||||
fwrite(*(char **)p, len, 1, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadField(FILE * f, field_t * field, byte * base)
|
||||
{
|
||||
void * p;
|
||||
int len;
|
||||
int index;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_INT:
|
||||
case F_FLOAT:
|
||||
case F_ANGLEHACK:
|
||||
case F_VECTOR:
|
||||
case F_IGNORE:
|
||||
break;
|
||||
|
||||
case F_LSTRING:
|
||||
len = *(int *)p;
|
||||
if (!len)
|
||||
*(char **)p = NULL;
|
||||
else
|
||||
{
|
||||
*(char **)p = gi.TagMalloc(len, TAG_LEVEL);
|
||||
fread(*(char **)p, len, 1, f);
|
||||
}
|
||||
break;
|
||||
case F_EDICT:
|
||||
index = *(int *)p;
|
||||
if (index == -1)
|
||||
*(edict_t **)p = NULL;
|
||||
else
|
||||
*(edict_t **)p = &g_edicts[index];
|
||||
break;
|
||||
case F_CLIENT:
|
||||
index = *(int *)p;
|
||||
if (index == -1)
|
||||
*(gclient_t **)p = NULL;
|
||||
else
|
||||
*(gclient_t **)p = &game.clients[index];
|
||||
break;
|
||||
case F_ITEM:
|
||||
index = *(int *)p;
|
||||
if (index == -1)
|
||||
*(gitem_t **)p = NULL;
|
||||
else
|
||||
*(gitem_t **)p = &itemlist[index];
|
||||
break;
|
||||
|
||||
//relative to code segment
|
||||
case F_FUNCTION:
|
||||
index = *(int *)p;
|
||||
if (index == 0)
|
||||
*(byte **)p = NULL;
|
||||
else
|
||||
*(byte **)p = ((byte *)InitGame) + index;
|
||||
break;
|
||||
|
||||
//relative to data segment
|
||||
case F_MMOVE:
|
||||
index = *(int *)p;
|
||||
if (index == 0)
|
||||
*(byte **)p = NULL;
|
||||
else
|
||||
*(byte **)p = (byte *)&mmove_reloc + index;
|
||||
break;
|
||||
|
||||
default:
|
||||
gi.error("ReadEdict: unknown field type");
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteClient
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteClient(FILE * f, gclient_t * client)
|
||||
{
|
||||
field_t * field;
|
||||
gclient_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = *client;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field = clientfields; field->name; field++)
|
||||
{
|
||||
WriteField1(f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite(&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field = clientfields; field->name; field++)
|
||||
{
|
||||
WriteField2(f, field, (byte *)client);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadClient
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadClient(FILE * f, gclient_t * client)
|
||||
{
|
||||
field_t * field;
|
||||
|
||||
fread(client, sizeof(*client), 1, f);
|
||||
|
||||
for (field = clientfields; field->name; field++)
|
||||
{
|
||||
ReadField(f, field, (byte *)client);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteGame
|
||||
|
||||
This will be called whenever the game goes to a new level,
|
||||
and when the user explicitly saves the game.
|
||||
|
||||
Game information include cross level data, like multi level
|
||||
triggers, help computer info, and all client states.
|
||||
|
||||
A single player death will automatically restore from the
|
||||
last save position.
|
||||
============
|
||||
*/
|
||||
void WriteGame(char * filename, qboolean autosave)
|
||||
{
|
||||
FILE * f;
|
||||
int i;
|
||||
char str[16];
|
||||
|
||||
if (!autosave)
|
||||
SaveClientData();
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
if (!f)
|
||||
gi.error("Couldn't open %s", filename);
|
||||
|
||||
memset(str, 0, sizeof(str));
|
||||
strcpy(str, __DATE__);
|
||||
fwrite(str, sizeof(str), 1, f);
|
||||
|
||||
game.autosaved = autosave;
|
||||
fwrite(&game, sizeof(game), 1, f);
|
||||
game.autosaved = false;
|
||||
|
||||
for (i = 0; i < game.maxclients; i++)
|
||||
WriteClient(f, &game.clients[i]);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void ReadGame(char * filename)
|
||||
{
|
||||
FILE * f;
|
||||
int i;
|
||||
char str[16];
|
||||
|
||||
gi.FreeTags(TAG_GAME);
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f)
|
||||
gi.error("Couldn't open %s", filename);
|
||||
|
||||
fread(str, sizeof(str), 1, f);
|
||||
if (strcmp(str, __DATE__))
|
||||
{
|
||||
fclose(f);
|
||||
gi.error("Savegame from an older version.\n");
|
||||
}
|
||||
|
||||
g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||
globals.edicts = g_edicts;
|
||||
|
||||
fread(&game, sizeof(game), 1, f);
|
||||
game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||
for (i = 0; i < game.maxclients; i++)
|
||||
ReadClient(f, &game.clients[i]);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteEdict
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteEdict(FILE * f, edict_t * ent)
|
||||
{
|
||||
field_t * field;
|
||||
edict_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = *ent;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field = fields; field->name; field++)
|
||||
{
|
||||
WriteField1(f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite(&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field = fields; field->name; field++)
|
||||
{
|
||||
WriteField2(f, field, (byte *)ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteLevelLocals
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteLevelLocals(FILE * f)
|
||||
{
|
||||
field_t * field;
|
||||
level_locals_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = level;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field = levelfields; field->name; field++)
|
||||
{
|
||||
WriteField1(f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite(&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field = levelfields; field->name; field++)
|
||||
{
|
||||
WriteField2(f, field, (byte *)&level);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadEdict
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadEdict(FILE * f, edict_t * ent)
|
||||
{
|
||||
field_t * field;
|
||||
|
||||
fread(ent, sizeof(*ent), 1, f);
|
||||
|
||||
for (field = fields; field->name; field++)
|
||||
{
|
||||
ReadField(f, field, (byte *)ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadLevelLocals
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadLevelLocals(FILE * f)
|
||||
{
|
||||
field_t * field;
|
||||
|
||||
fread(&level, sizeof(level), 1, f);
|
||||
|
||||
for (field = levelfields; field->name; field++)
|
||||
{
|
||||
ReadField(f, field, (byte *)&level);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WriteLevel
|
||||
|
||||
=================
|
||||
*/
|
||||
void WriteLevel(char * filename)
|
||||
{
|
||||
int i;
|
||||
edict_t * ent;
|
||||
FILE * f;
|
||||
void * base;
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
if (!f)
|
||||
gi.error("Couldn't open %s", filename);
|
||||
|
||||
// write out edict size for checking
|
||||
i = sizeof(edict_t);
|
||||
fwrite(&i, sizeof(i), 1, f);
|
||||
|
||||
// write out a function pointer for checking
|
||||
base = (void *)InitGame;
|
||||
fwrite(&base, sizeof(base), 1, f);
|
||||
|
||||
// write out level_locals_t
|
||||
WriteLevelLocals(f);
|
||||
|
||||
// write out all the entities
|
||||
for (i = 0; i < globals.num_edicts; i++)
|
||||
{
|
||||
ent = &g_edicts[i];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
fwrite(&i, sizeof(i), 1, f);
|
||||
WriteEdict(f, ent);
|
||||
}
|
||||
i = -1;
|
||||
fwrite(&i, sizeof(i), 1, f);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ReadLevel
|
||||
|
||||
SpawnEntities will allready have been called on the
|
||||
level the same way it was when the level was saved.
|
||||
|
||||
That is necessary to get the baselines
|
||||
set up identically.
|
||||
|
||||
The server will have cleared all of the world links before
|
||||
calling ReadLevel.
|
||||
|
||||
No clients are connected yet.
|
||||
=================
|
||||
*/
|
||||
void ReadLevel(char * filename)
|
||||
{
|
||||
int entnum;
|
||||
FILE * f;
|
||||
int i;
|
||||
void * base;
|
||||
edict_t * ent;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f)
|
||||
gi.error("Couldn't open %s", filename);
|
||||
|
||||
// free any dynamic memory allocated by loading the level
|
||||
// base state
|
||||
gi.FreeTags(TAG_LEVEL);
|
||||
|
||||
// wipe all the entities
|
||||
memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0]));
|
||||
globals.num_edicts = maxclients->value + 1;
|
||||
|
||||
// check edict size
|
||||
fread(&i, sizeof(i), 1, f);
|
||||
if (i != sizeof(edict_t))
|
||||
{
|
||||
fclose(f);
|
||||
gi.error("ReadLevel: mismatched edict size");
|
||||
}
|
||||
|
||||
// check function pointer base address
|
||||
fread(&base, sizeof(base), 1, f);
|
||||
#ifdef _WIN32
|
||||
if (base != (void *)InitGame)
|
||||
{
|
||||
fclose(f);
|
||||
gi.error("ReadLevel: function pointers have moved");
|
||||
}
|
||||
#else
|
||||
gi.dprintf("Function offsets %d\n", ((byte *)base) - ((byte *)InitGame));
|
||||
#endif
|
||||
|
||||
// load the level locals
|
||||
ReadLevelLocals(f);
|
||||
|
||||
// load all the entities
|
||||
while (1)
|
||||
{
|
||||
if (fread(&entnum, sizeof(entnum), 1, f) != 1)
|
||||
{
|
||||
fclose(f);
|
||||
gi.error("ReadLevel: failed to read entnum");
|
||||
}
|
||||
if (entnum == -1)
|
||||
break;
|
||||
if (entnum >= globals.num_edicts)
|
||||
globals.num_edicts = entnum + 1;
|
||||
|
||||
ent = &g_edicts[entnum];
|
||||
ReadEdict(f, ent);
|
||||
|
||||
// let the server rebuild world links for this ent
|
||||
memset(&ent->area, 0, sizeof(ent->area));
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
// mark all clients as unconnected
|
||||
for (i = 0; i < maxclients->value; i++)
|
||||
{
|
||||
ent = &g_edicts[i + 1];
|
||||
ent->client = game.clients + i;
|
||||
ent->client->pers.connected = false;
|
||||
}
|
||||
|
||||
// do any load time things at this point
|
||||
for (i = 0; i < globals.num_edicts; i++)
|
||||
{
|
||||
ent = &g_edicts[i];
|
||||
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
|
||||
// fire any cross-level triggers
|
||||
if (ent->classname)
|
||||
if (strcmp(ent->classname, "target_crosslevel_target") == 0)
|
||||
ent->nextthink = level.time + ent->delay;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,972 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char * name;
|
||||
void (*spawn)(edict_t * ent);
|
||||
} spawn_t;
|
||||
|
||||
void SP_item_health(edict_t * self);
|
||||
void SP_item_health_small(edict_t * self);
|
||||
void SP_item_health_large(edict_t * self);
|
||||
void SP_item_health_mega(edict_t * self);
|
||||
|
||||
void SP_info_player_start(edict_t * ent);
|
||||
void SP_info_player_deathmatch(edict_t * ent);
|
||||
void SP_info_player_coop(edict_t * ent);
|
||||
void SP_info_player_intermission(edict_t * ent);
|
||||
|
||||
void SP_func_plat(edict_t * ent);
|
||||
void SP_func_rotating(edict_t * ent);
|
||||
void SP_func_button(edict_t * ent);
|
||||
void SP_func_door(edict_t * ent);
|
||||
void SP_func_door_secret(edict_t * ent);
|
||||
void SP_func_door_rotating(edict_t * ent);
|
||||
void SP_func_water(edict_t * ent);
|
||||
void SP_func_train(edict_t * ent);
|
||||
void SP_func_conveyor(edict_t * self);
|
||||
void SP_func_wall(edict_t * self);
|
||||
void SP_func_object(edict_t * self);
|
||||
void SP_func_explosive(edict_t * self);
|
||||
void SP_func_timer(edict_t * self);
|
||||
void SP_func_areaportal(edict_t * ent);
|
||||
void SP_func_clock(edict_t * ent);
|
||||
void SP_func_killbox(edict_t * ent);
|
||||
|
||||
void SP_trigger_always(edict_t * ent);
|
||||
void SP_trigger_once(edict_t * ent);
|
||||
void SP_trigger_multiple(edict_t * ent);
|
||||
void SP_trigger_relay(edict_t * ent);
|
||||
void SP_trigger_push(edict_t * ent);
|
||||
void SP_trigger_hurt(edict_t * ent);
|
||||
void SP_trigger_key(edict_t * ent);
|
||||
void SP_trigger_counter(edict_t * ent);
|
||||
void SP_trigger_elevator(edict_t * ent);
|
||||
void SP_trigger_gravity(edict_t * ent);
|
||||
void SP_trigger_monsterjump(edict_t * ent);
|
||||
|
||||
void SP_target_temp_entity(edict_t * ent);
|
||||
void SP_target_speaker(edict_t * ent);
|
||||
void SP_target_explosion(edict_t * ent);
|
||||
void SP_target_changelevel(edict_t * ent);
|
||||
void SP_target_secret(edict_t * ent);
|
||||
void SP_target_goal(edict_t * ent);
|
||||
void SP_target_splash(edict_t * ent);
|
||||
void SP_target_spawner(edict_t * ent);
|
||||
void SP_target_blaster(edict_t * ent);
|
||||
void SP_target_crosslevel_trigger(edict_t * ent);
|
||||
void SP_target_crosslevel_target(edict_t * ent);
|
||||
void SP_target_laser(edict_t * self);
|
||||
void SP_target_help(edict_t * ent);
|
||||
void SP_target_actor(edict_t * ent);
|
||||
void SP_target_lightramp(edict_t * self);
|
||||
void SP_target_earthquake(edict_t * ent);
|
||||
void SP_target_character(edict_t * ent);
|
||||
void SP_target_string(edict_t * ent);
|
||||
|
||||
void SP_worldspawn(edict_t * ent);
|
||||
void SP_viewthing(edict_t * ent);
|
||||
|
||||
void SP_light(edict_t * self);
|
||||
void SP_light_mine1(edict_t * ent);
|
||||
void SP_light_mine2(edict_t * ent);
|
||||
void SP_info_null(edict_t * self);
|
||||
void SP_info_notnull(edict_t * self);
|
||||
void SP_path_corner(edict_t * self);
|
||||
void SP_point_combat(edict_t * self);
|
||||
|
||||
void SP_misc_explobox(edict_t * self);
|
||||
void SP_misc_banner(edict_t * self);
|
||||
void SP_misc_satellite_dish(edict_t * self);
|
||||
void SP_misc_actor(edict_t * self);
|
||||
void SP_misc_gib_arm(edict_t * self);
|
||||
void SP_misc_gib_leg(edict_t * self);
|
||||
void SP_misc_gib_head(edict_t * self);
|
||||
void SP_misc_insane(edict_t * self);
|
||||
void SP_misc_deadsoldier(edict_t * self);
|
||||
void SP_misc_viper(edict_t * self);
|
||||
void SP_misc_viper_bomb(edict_t * self);
|
||||
void SP_misc_bigviper(edict_t * self);
|
||||
void SP_misc_strogg_ship(edict_t * self);
|
||||
void SP_misc_teleporter(edict_t * self);
|
||||
void SP_misc_teleporter_dest(edict_t * self);
|
||||
void SP_misc_blackhole(edict_t * self);
|
||||
void SP_misc_eastertank(edict_t * self);
|
||||
void SP_misc_easterchick(edict_t * self);
|
||||
void SP_misc_easterchick2(edict_t * self);
|
||||
|
||||
void SP_monster_berserk(edict_t * self);
|
||||
void SP_monster_gladiator(edict_t * self);
|
||||
void SP_monster_gunner(edict_t * self);
|
||||
void SP_monster_infantry(edict_t * self);
|
||||
void SP_monster_soldier_light(edict_t * self);
|
||||
void SP_monster_soldier(edict_t * self);
|
||||
void SP_monster_soldier_ss(edict_t * self);
|
||||
void SP_monster_tank(edict_t * self);
|
||||
void SP_monster_medic(edict_t * self);
|
||||
void SP_monster_flipper(edict_t * self);
|
||||
void SP_monster_chick(edict_t * self);
|
||||
void SP_monster_parasite(edict_t * self);
|
||||
void SP_monster_flyer(edict_t * self);
|
||||
void SP_monster_brain(edict_t * self);
|
||||
void SP_monster_floater(edict_t * self);
|
||||
void SP_monster_hover(edict_t * self);
|
||||
void SP_monster_mutant(edict_t * self);
|
||||
void SP_monster_supertank(edict_t * self);
|
||||
void SP_monster_boss2(edict_t * self);
|
||||
void SP_monster_jorg(edict_t * self);
|
||||
void SP_monster_boss3_stand(edict_t * self);
|
||||
|
||||
void SP_monster_commander_body(edict_t * self);
|
||||
|
||||
void SP_turret_breach(edict_t * self);
|
||||
void SP_turret_base(edict_t * self);
|
||||
void SP_turret_driver(edict_t * self);
|
||||
|
||||
spawn_t spawns[] = {
|
||||
{ "item_health", SP_item_health },
|
||||
{ "item_health_small", SP_item_health_small },
|
||||
{ "item_health_large", SP_item_health_large },
|
||||
{ "item_health_mega", SP_item_health_mega },
|
||||
|
||||
{ "info_player_start", SP_info_player_start },
|
||||
{ "info_player_deathmatch", SP_info_player_deathmatch },
|
||||
{ "info_player_coop", SP_info_player_coop },
|
||||
{ "info_player_intermission", SP_info_player_intermission },
|
||||
|
||||
{ "func_plat", SP_func_plat },
|
||||
{ "func_button", SP_func_button },
|
||||
{ "func_door", SP_func_door },
|
||||
{ "func_door_secret", SP_func_door_secret },
|
||||
{ "func_door_rotating", SP_func_door_rotating },
|
||||
{ "func_rotating", SP_func_rotating },
|
||||
{ "func_train", SP_func_train },
|
||||
{ "func_water", SP_func_water },
|
||||
{ "func_conveyor", SP_func_conveyor },
|
||||
{ "func_areaportal", SP_func_areaportal },
|
||||
{ "func_clock", SP_func_clock },
|
||||
{ "func_wall", SP_func_wall },
|
||||
{ "func_object", SP_func_object },
|
||||
{ "func_timer", SP_func_timer },
|
||||
{ "func_explosive", SP_func_explosive },
|
||||
{ "func_killbox", SP_func_killbox },
|
||||
|
||||
{ "trigger_always", SP_trigger_always },
|
||||
{ "trigger_once", SP_trigger_once },
|
||||
{ "trigger_multiple", SP_trigger_multiple },
|
||||
{ "trigger_relay", SP_trigger_relay },
|
||||
{ "trigger_push", SP_trigger_push },
|
||||
{ "trigger_hurt", SP_trigger_hurt },
|
||||
{ "trigger_key", SP_trigger_key },
|
||||
{ "trigger_counter", SP_trigger_counter },
|
||||
{ "trigger_elevator", SP_trigger_elevator },
|
||||
{ "trigger_gravity", SP_trigger_gravity },
|
||||
{ "trigger_monsterjump", SP_trigger_monsterjump },
|
||||
|
||||
{ "target_temp_entity", SP_target_temp_entity },
|
||||
{ "target_speaker", SP_target_speaker },
|
||||
{ "target_explosion", SP_target_explosion },
|
||||
{ "target_changelevel", SP_target_changelevel },
|
||||
{ "target_secret", SP_target_secret },
|
||||
{ "target_goal", SP_target_goal },
|
||||
{ "target_splash", SP_target_splash },
|
||||
{ "target_spawner", SP_target_spawner },
|
||||
{ "target_blaster", SP_target_blaster },
|
||||
{ "target_crosslevel_trigger", SP_target_crosslevel_trigger },
|
||||
{ "target_crosslevel_target", SP_target_crosslevel_target },
|
||||
{ "target_laser", SP_target_laser },
|
||||
{ "target_help", SP_target_help },
|
||||
{ "target_actor", SP_target_actor },
|
||||
{ "target_lightramp", SP_target_lightramp },
|
||||
{ "target_earthquake", SP_target_earthquake },
|
||||
{ "target_character", SP_target_character },
|
||||
{ "target_string", SP_target_string },
|
||||
|
||||
{ "worldspawn", SP_worldspawn },
|
||||
{ "viewthing", SP_viewthing },
|
||||
|
||||
{ "light", SP_light },
|
||||
{ "light_mine1", SP_light_mine1 },
|
||||
{ "light_mine2", SP_light_mine2 },
|
||||
{ "info_null", SP_info_null },
|
||||
{ "func_group", SP_info_null },
|
||||
{ "info_notnull", SP_info_notnull },
|
||||
{ "path_corner", SP_path_corner },
|
||||
{ "point_combat", SP_point_combat },
|
||||
|
||||
{ "misc_explobox", SP_misc_explobox },
|
||||
{ "misc_banner", SP_misc_banner },
|
||||
{ "misc_satellite_dish", SP_misc_satellite_dish },
|
||||
{ "misc_actor", SP_misc_actor },
|
||||
{ "misc_gib_arm", SP_misc_gib_arm },
|
||||
{ "misc_gib_leg", SP_misc_gib_leg },
|
||||
{ "misc_gib_head", SP_misc_gib_head },
|
||||
{ "misc_insane", SP_misc_insane },
|
||||
{ "misc_deadsoldier", SP_misc_deadsoldier },
|
||||
{ "misc_viper", SP_misc_viper },
|
||||
{ "misc_viper_bomb", SP_misc_viper_bomb },
|
||||
{ "misc_bigviper", SP_misc_bigviper },
|
||||
{ "misc_strogg_ship", SP_misc_strogg_ship },
|
||||
{ "misc_teleporter", SP_misc_teleporter },
|
||||
{ "misc_teleporter_dest", SP_misc_teleporter_dest },
|
||||
{ "misc_blackhole", SP_misc_blackhole },
|
||||
{ "misc_eastertank", SP_misc_eastertank },
|
||||
{ "misc_easterchick", SP_misc_easterchick },
|
||||
{ "misc_easterchick2", SP_misc_easterchick2 },
|
||||
|
||||
{ "monster_berserk", SP_monster_berserk },
|
||||
{ "monster_gladiator", SP_monster_gladiator },
|
||||
{ "monster_gunner", SP_monster_gunner },
|
||||
{ "monster_infantry", SP_monster_infantry },
|
||||
{ "monster_soldier_light", SP_monster_soldier_light },
|
||||
{ "monster_soldier", SP_monster_soldier },
|
||||
{ "monster_soldier_ss", SP_monster_soldier_ss },
|
||||
{ "monster_tank", SP_monster_tank },
|
||||
{ "monster_tank_commander", SP_monster_tank },
|
||||
{ "monster_medic", SP_monster_medic },
|
||||
{ "monster_flipper", SP_monster_flipper },
|
||||
{ "monster_chick", SP_monster_chick },
|
||||
{ "monster_parasite", SP_monster_parasite },
|
||||
{ "monster_flyer", SP_monster_flyer },
|
||||
{ "monster_brain", SP_monster_brain },
|
||||
{ "monster_floater", SP_monster_floater },
|
||||
{ "monster_hover", SP_monster_hover },
|
||||
{ "monster_mutant", SP_monster_mutant },
|
||||
{ "monster_supertank", SP_monster_supertank },
|
||||
{ "monster_boss2", SP_monster_boss2 },
|
||||
{ "monster_boss3_stand", SP_monster_boss3_stand },
|
||||
{ "monster_jorg", SP_monster_jorg },
|
||||
|
||||
{ "monster_commander_body", SP_monster_commander_body },
|
||||
|
||||
{ "turret_breach", SP_turret_breach },
|
||||
{ "turret_base", SP_turret_base },
|
||||
{ "turret_driver", SP_turret_driver },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
===============
|
||||
ED_CallSpawn
|
||||
|
||||
Finds the spawn function for the entity and calls it
|
||||
===============
|
||||
*/
|
||||
void ED_CallSpawn(edict_t * ent)
|
||||
{
|
||||
spawn_t * s;
|
||||
gitem_t * item;
|
||||
int i;
|
||||
|
||||
if (!ent->classname)
|
||||
{
|
||||
gi.dprintf("ED_CallSpawn: NULL classname\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// check item spawn functions
|
||||
for (i = 0, item = itemlist; i < game.num_items; i++, item++)
|
||||
{
|
||||
if (!item->classname)
|
||||
continue;
|
||||
if (!strcmp(item->classname, ent->classname))
|
||||
{ // found it
|
||||
SpawnItem(ent, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check normal spawn functions
|
||||
for (s = spawns; s->name; s++)
|
||||
{
|
||||
if (!strcmp(s->name, ent->classname))
|
||||
{ // found it
|
||||
s->spawn(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gi.dprintf("%s doesn't have a spawn function\n", ent->classname);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
ED_NewString
|
||||
=============
|
||||
*/
|
||||
char * ED_NewString(char * string)
|
||||
{
|
||||
char *newb, *new_p;
|
||||
int i, l;
|
||||
|
||||
l = strlen(string) + 1;
|
||||
|
||||
newb = gi.TagMalloc(l, TAG_LEVEL);
|
||||
|
||||
new_p = newb;
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
{
|
||||
if (string[i] == '\\' && i < l - 1)
|
||||
{
|
||||
i++;
|
||||
if (string[i] == 'n')
|
||||
*new_p++ = '\n';
|
||||
else
|
||||
*new_p++ = '\\';
|
||||
}
|
||||
else
|
||||
*new_p++ = string[i];
|
||||
}
|
||||
|
||||
return newb;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ED_ParseField
|
||||
|
||||
Takes a key/value pair and sets the binary values
|
||||
in an edict
|
||||
===============
|
||||
*/
|
||||
void ED_ParseField(char * key, char * value, edict_t * ent)
|
||||
{
|
||||
field_t * f;
|
||||
byte * b;
|
||||
float v;
|
||||
vec3_t vec;
|
||||
|
||||
for (f = fields; f->name; f++)
|
||||
{
|
||||
if (!(f->flags & FFL_NOSPAWN) && !Q_stricmp(f->name, key))
|
||||
{ // found it
|
||||
if (f->flags & FFL_SPAWNTEMP)
|
||||
b = (byte *)&st;
|
||||
else
|
||||
b = (byte *)ent;
|
||||
|
||||
switch (f->type)
|
||||
{
|
||||
case F_LSTRING:
|
||||
*(char **)(b + f->ofs) = ED_NewString(value);
|
||||
break;
|
||||
case F_VECTOR:
|
||||
sscanf(value, "%f %f %f", &vec[0], &vec[1], &vec[2]);
|
||||
((float *)(b + f->ofs))[0] = vec[0];
|
||||
((float *)(b + f->ofs))[1] = vec[1];
|
||||
((float *)(b + f->ofs))[2] = vec[2];
|
||||
break;
|
||||
case F_INT:
|
||||
*(int *)(b + f->ofs) = atoi(value);
|
||||
break;
|
||||
case F_FLOAT:
|
||||
*(float *)(b + f->ofs) = atof(value);
|
||||
break;
|
||||
case F_ANGLEHACK:
|
||||
v = atof(value);
|
||||
((float *)(b + f->ofs))[0] = 0;
|
||||
((float *)(b + f->ofs))[1] = v;
|
||||
((float *)(b + f->ofs))[2] = 0;
|
||||
break;
|
||||
case F_IGNORE:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
gi.dprintf("%s is not a field\n", key);
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
ED_ParseEdict
|
||||
|
||||
Parses an edict out of the given string, returning the new position
|
||||
ed should be a properly initialized empty edict.
|
||||
====================
|
||||
*/
|
||||
char * ED_ParseEdict(char * data, edict_t * ent)
|
||||
{
|
||||
qboolean init;
|
||||
char keyname[256];
|
||||
char * com_token;
|
||||
|
||||
init = false;
|
||||
memset(&st, 0, sizeof(st));
|
||||
|
||||
// go through all the dictionary pairs
|
||||
while (1)
|
||||
{
|
||||
// parse key
|
||||
com_token = COM_Parse(&data);
|
||||
if (com_token[0] == '}')
|
||||
break;
|
||||
if (!data)
|
||||
gi.error("ED_ParseEntity: EOF without closing brace");
|
||||
|
||||
strncpy(keyname, com_token, sizeof(keyname) - 1);
|
||||
|
||||
// parse value
|
||||
com_token = COM_Parse(&data);
|
||||
if (!data)
|
||||
gi.error("ED_ParseEntity: EOF without closing brace");
|
||||
|
||||
if (com_token[0] == '}')
|
||||
gi.error("ED_ParseEntity: closing brace without data");
|
||||
|
||||
init = true;
|
||||
|
||||
// keynames with a leading underscore are used for utility comments,
|
||||
// and are immediately discarded by quake
|
||||
if (keyname[0] == '_')
|
||||
continue;
|
||||
|
||||
ED_ParseField(keyname, com_token, ent);
|
||||
}
|
||||
|
||||
if (!init)
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_FindTeams
|
||||
|
||||
Chain together all entities with a matching team field.
|
||||
|
||||
All but the first will have the FL_TEAMSLAVE flag set.
|
||||
All but the last will have the teamchain field set to the next one
|
||||
================
|
||||
*/
|
||||
void G_FindTeams(void)
|
||||
{
|
||||
edict_t *e, *e2, *chain;
|
||||
int i, j;
|
||||
int c, c2;
|
||||
|
||||
c = 0;
|
||||
c2 = 0;
|
||||
for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++)
|
||||
{
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->team)
|
||||
continue;
|
||||
if (e->flags & FL_TEAMSLAVE)
|
||||
continue;
|
||||
chain = e;
|
||||
e->teammaster = e;
|
||||
c++;
|
||||
c2++;
|
||||
for (j = i + 1, e2 = e + 1; j < globals.num_edicts; j++, e2++)
|
||||
{
|
||||
if (!e2->inuse)
|
||||
continue;
|
||||
if (!e2->team)
|
||||
continue;
|
||||
if (e2->flags & FL_TEAMSLAVE)
|
||||
continue;
|
||||
if (!strcmp(e->team, e2->team))
|
||||
{
|
||||
c2++;
|
||||
chain->teamchain = e2;
|
||||
e2->teammaster = e;
|
||||
chain = e2;
|
||||
e2->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gi.dprintf("%i teams with %i entities\n", c, c2);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SpawnEntities
|
||||
|
||||
Creates a server's entity / program execution context by
|
||||
parsing textual entity definitions out of an ent file.
|
||||
==============
|
||||
*/
|
||||
void SpawnEntities(char * mapname, char * entities, char * spawnpoint)
|
||||
{
|
||||
edict_t * ent;
|
||||
int inhibit;
|
||||
char * com_token;
|
||||
int i;
|
||||
float skill_level;
|
||||
|
||||
skill_level = floor(skill->value);
|
||||
if (skill_level < 0)
|
||||
skill_level = 0;
|
||||
if (skill_level > 3)
|
||||
skill_level = 3;
|
||||
if (skill->value != skill_level)
|
||||
gi.cvar_forceset("skill", va("%f", skill_level));
|
||||
|
||||
SaveClientData();
|
||||
|
||||
gi.FreeTags(TAG_LEVEL);
|
||||
|
||||
memset(&level, 0, sizeof(level));
|
||||
memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0]));
|
||||
|
||||
strncpy(level.mapname, mapname, sizeof(level.mapname) - 1);
|
||||
strncpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint) - 1);
|
||||
|
||||
// set client fields on player ents
|
||||
for (i = 0; i < game.maxclients; i++)
|
||||
g_edicts[i + 1].client = game.clients + i;
|
||||
|
||||
ent = NULL;
|
||||
inhibit = 0;
|
||||
|
||||
// parse ents
|
||||
while (1)
|
||||
{
|
||||
// parse the opening brace
|
||||
com_token = COM_Parse(&entities);
|
||||
if (!entities)
|
||||
break;
|
||||
if (com_token[0] != '{')
|
||||
gi.error("ED_LoadFromFile: found %s when expecting {", com_token);
|
||||
|
||||
if (!ent)
|
||||
ent = g_edicts;
|
||||
else
|
||||
ent = G_Spawn();
|
||||
entities = ED_ParseEdict(entities, ent);
|
||||
|
||||
// yet another map hack
|
||||
if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27"))
|
||||
ent->spawnflags &= ~SPAWNFLAG_NOT_HARD;
|
||||
|
||||
// remove things (except the world) from different skill levels or deathmatch
|
||||
if (ent != g_edicts)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
inhibit++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (/* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */
|
||||
((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
|
||||
((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
|
||||
(((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
inhibit++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH);
|
||||
}
|
||||
|
||||
ED_CallSpawn(ent);
|
||||
}
|
||||
|
||||
gi.dprintf("%i entities inhibited\n", inhibit);
|
||||
|
||||
#ifdef DEBUG
|
||||
i = 1;
|
||||
ent = EDICT_NUM(i);
|
||||
while (i < globals.num_edicts)
|
||||
{
|
||||
if (ent->inuse != 0 || ent->inuse != 1)
|
||||
Com_DPrintf("Invalid entity %d\n", i);
|
||||
i++, ent++;
|
||||
}
|
||||
#endif
|
||||
|
||||
G_FindTeams();
|
||||
|
||||
PlayerTrail_Init();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
#if 0
|
||||
// cursor positioning
|
||||
xl <value>
|
||||
xr <value>
|
||||
yb <value>
|
||||
yt <value>
|
||||
xv <value>
|
||||
yv <value>
|
||||
|
||||
// drawing
|
||||
statpic <name>
|
||||
pic <stat>
|
||||
num <fieldwidth> <stat>
|
||||
string <stat>
|
||||
|
||||
// control
|
||||
if <stat>
|
||||
ifeq <stat> <value>
|
||||
ifbit <stat> <value>
|
||||
endif
|
||||
|
||||
#endif
|
||||
|
||||
char * single_statusbar =
|
||||
"yb -24 "
|
||||
|
||||
// health
|
||||
"xv 0 "
|
||||
"hnum "
|
||||
"xv 50 "
|
||||
"pic 0 "
|
||||
|
||||
// ammo
|
||||
"if 2 "
|
||||
" xv 100 "
|
||||
" anum "
|
||||
" xv 150 "
|
||||
" pic 2 "
|
||||
"endif "
|
||||
|
||||
// armor
|
||||
"if 4 "
|
||||
" xv 200 "
|
||||
" rnum "
|
||||
" xv 250 "
|
||||
" pic 4 "
|
||||
"endif "
|
||||
|
||||
// selected item
|
||||
"if 6 "
|
||||
" xv 296 "
|
||||
" pic 6 "
|
||||
"endif "
|
||||
|
||||
"yb -50 "
|
||||
|
||||
// picked up item
|
||||
"if 7 "
|
||||
" xv 0 "
|
||||
" pic 7 "
|
||||
" xv 26 "
|
||||
" yb -42 "
|
||||
" stat_string 8 "
|
||||
" yb -50 "
|
||||
"endif "
|
||||
|
||||
// timer
|
||||
"if 9 "
|
||||
" xv 262 "
|
||||
" num 2 10 "
|
||||
" xv 296 "
|
||||
" pic 9 "
|
||||
"endif "
|
||||
|
||||
// help / weapon icon
|
||||
"if 11 "
|
||||
" xv 148 "
|
||||
" pic 11 "
|
||||
"endif ";
|
||||
|
||||
char * dm_statusbar =
|
||||
"yb -24 "
|
||||
|
||||
// health
|
||||
"xv 0 "
|
||||
"hnum "
|
||||
"xv 50 "
|
||||
"pic 0 "
|
||||
|
||||
// ammo
|
||||
"if 2 "
|
||||
" xv 100 "
|
||||
" anum "
|
||||
" xv 150 "
|
||||
" pic 2 "
|
||||
"endif "
|
||||
|
||||
// armor
|
||||
"if 4 "
|
||||
" xv 200 "
|
||||
" rnum "
|
||||
" xv 250 "
|
||||
" pic 4 "
|
||||
"endif "
|
||||
|
||||
// selected item
|
||||
"if 6 "
|
||||
" xv 296 "
|
||||
" pic 6 "
|
||||
"endif "
|
||||
|
||||
"yb -50 "
|
||||
|
||||
// picked up item
|
||||
"if 7 "
|
||||
" xv 0 "
|
||||
" pic 7 "
|
||||
" xv 26 "
|
||||
" yb -42 "
|
||||
" stat_string 8 "
|
||||
" yb -50 "
|
||||
"endif "
|
||||
|
||||
// timer
|
||||
"if 9 "
|
||||
" xv 246 "
|
||||
" num 2 10 "
|
||||
" xv 296 "
|
||||
" pic 9 "
|
||||
"endif "
|
||||
|
||||
// help / weapon icon
|
||||
"if 11 "
|
||||
" xv 148 "
|
||||
" pic 11 "
|
||||
"endif "
|
||||
|
||||
// frags
|
||||
"xr -50 "
|
||||
"yt 2 "
|
||||
"num 3 14 "
|
||||
|
||||
// spectator
|
||||
"if 17 "
|
||||
"xv 0 "
|
||||
"yb -58 "
|
||||
"string2 \"SPECTATOR MODE\" "
|
||||
"endif "
|
||||
|
||||
// chase camera
|
||||
"if 16 "
|
||||
"xv 0 "
|
||||
"yb -68 "
|
||||
"string \"Chasing\" "
|
||||
"xv 64 "
|
||||
"stat_string 16 "
|
||||
"endif ";
|
||||
|
||||
/*QUAKED worldspawn (0 0 0) ?
|
||||
|
||||
Only used for the world.
|
||||
"sky" environment map name
|
||||
"skyaxis" vector axis for rotating sky
|
||||
"skyrotate" speed of rotation in degrees/second
|
||||
"sounds" music cd track number
|
||||
"gravity" 800 is default gravity
|
||||
"message" text to print at user logon
|
||||
*/
|
||||
void SP_worldspawn(edict_t * ent)
|
||||
{
|
||||
ent->movetype = MOVETYPE_PUSH;
|
||||
ent->solid = SOLID_BSP;
|
||||
ent->inuse = true; // since the world doesn't use G_Spawn()
|
||||
ent->s.modelindex = 1; // world model is always index 1
|
||||
|
||||
//---------------
|
||||
|
||||
// reserve some spots for dead player bodies for coop / deathmatch
|
||||
InitBodyQue();
|
||||
|
||||
// set configstrings for items
|
||||
SetItemNames();
|
||||
|
||||
if (st.nextmap)
|
||||
strcpy(level.nextmap, st.nextmap);
|
||||
|
||||
// make some data visible to the server
|
||||
|
||||
if (ent->message && ent->message[0])
|
||||
{
|
||||
gi.configstring(CS_NAME, ent->message);
|
||||
strncpy(level.level_name, ent->message, sizeof(level.level_name));
|
||||
}
|
||||
else
|
||||
strncpy(level.level_name, level.mapname, sizeof(level.level_name));
|
||||
|
||||
if (st.sky && st.sky[0])
|
||||
gi.configstring(CS_SKY, st.sky);
|
||||
else
|
||||
gi.configstring(CS_SKY, "unit1_");
|
||||
|
||||
gi.configstring(CS_SKYROTATE, va("%f", st.skyrotate));
|
||||
|
||||
gi.configstring(CS_SKYAXIS, va("%f %f %f",
|
||||
st.skyaxis[0], st.skyaxis[1], st.skyaxis[2]));
|
||||
|
||||
gi.configstring(CS_CDTRACK, va("%i", ent->sounds));
|
||||
|
||||
gi.configstring(CS_MAXCLIENTS, va("%i", (int)(maxclients->value)));
|
||||
|
||||
// status bar program
|
||||
if (deathmatch->value)
|
||||
gi.configstring(CS_STATUSBAR, dm_statusbar);
|
||||
else
|
||||
gi.configstring(CS_STATUSBAR, single_statusbar);
|
||||
|
||||
//---------------
|
||||
|
||||
// help icon for statusbar
|
||||
gi.imageindex("i_help");
|
||||
level.pic_health = gi.imageindex("i_health");
|
||||
gi.imageindex("help");
|
||||
gi.imageindex("field_3");
|
||||
|
||||
if (!st.gravity)
|
||||
gi.cvar_set("sv_gravity", "800");
|
||||
else
|
||||
gi.cvar_set("sv_gravity", st.gravity);
|
||||
|
||||
snd_fry = gi.soundindex("player/fry.wav"); // standing in lava / slime
|
||||
|
||||
PrecacheItem(FindItem("Blaster"));
|
||||
|
||||
gi.soundindex("player/lava1.wav");
|
||||
gi.soundindex("player/lava2.wav");
|
||||
|
||||
gi.soundindex("misc/pc_up.wav");
|
||||
gi.soundindex("misc/talk1.wav");
|
||||
|
||||
gi.soundindex("misc/udeath.wav");
|
||||
|
||||
// gibs
|
||||
gi.soundindex("items/respawn1.wav");
|
||||
|
||||
// sexed sounds
|
||||
gi.soundindex("*death1.wav");
|
||||
gi.soundindex("*death2.wav");
|
||||
gi.soundindex("*death3.wav");
|
||||
gi.soundindex("*death4.wav");
|
||||
gi.soundindex("*fall1.wav");
|
||||
gi.soundindex("*fall2.wav");
|
||||
gi.soundindex("*gurp1.wav"); // drowning damage
|
||||
gi.soundindex("*gurp2.wav");
|
||||
gi.soundindex("*jump1.wav"); // player jump
|
||||
gi.soundindex("*pain25_1.wav");
|
||||
gi.soundindex("*pain25_2.wav");
|
||||
gi.soundindex("*pain50_1.wav");
|
||||
gi.soundindex("*pain50_2.wav");
|
||||
gi.soundindex("*pain75_1.wav");
|
||||
gi.soundindex("*pain75_2.wav");
|
||||
gi.soundindex("*pain100_1.wav");
|
||||
gi.soundindex("*pain100_2.wav");
|
||||
|
||||
// sexed models
|
||||
// THIS ORDER MUST MATCH THE DEFINES IN g_local.h
|
||||
// you can add more, max 15
|
||||
gi.modelindex("#w_blaster.md2");
|
||||
gi.modelindex("#w_shotgun.md2");
|
||||
gi.modelindex("#w_sshotgun.md2");
|
||||
gi.modelindex("#w_machinegun.md2");
|
||||
gi.modelindex("#w_chaingun.md2");
|
||||
gi.modelindex("#a_grenades.md2");
|
||||
gi.modelindex("#w_glauncher.md2");
|
||||
gi.modelindex("#w_rlauncher.md2");
|
||||
gi.modelindex("#w_hyperblaster.md2");
|
||||
gi.modelindex("#w_railgun.md2");
|
||||
gi.modelindex("#w_bfg.md2");
|
||||
|
||||
//-------------------
|
||||
|
||||
gi.soundindex("player/gasp1.wav"); // gasping for air
|
||||
gi.soundindex("player/gasp2.wav"); // head breaking surface, not gasping
|
||||
|
||||
gi.soundindex("player/watr_in.wav"); // feet hitting water
|
||||
gi.soundindex("player/watr_out.wav"); // feet leaving water
|
||||
|
||||
gi.soundindex("player/watr_un.wav"); // head going underwater
|
||||
|
||||
gi.soundindex("player/u_breath1.wav");
|
||||
gi.soundindex("player/u_breath2.wav");
|
||||
|
||||
gi.soundindex("items/pkup.wav"); // bonus item pickup
|
||||
gi.soundindex("world/land.wav"); // landing thud
|
||||
gi.soundindex("misc/h2ohit1.wav"); // landing splash
|
||||
|
||||
gi.soundindex("items/damage.wav");
|
||||
gi.soundindex("items/protect.wav");
|
||||
gi.soundindex("items/protect4.wav");
|
||||
gi.soundindex("weapons/noammo.wav");
|
||||
|
||||
gi.soundindex("infantry/inflies1.wav");
|
||||
|
||||
sm_meat_index = gi.modelindex("models/objects/gibs/sm_meat/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/arm/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/bone/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/bone2/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/chest/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/skull/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/head2/tris.md2");
|
||||
|
||||
//
|
||||
// Setup light animation tables. 'a' is total darkness, 'z' is doublebright.
|
||||
//
|
||||
|
||||
// 0 normal
|
||||
gi.configstring(CS_LIGHTS + 0, "m");
|
||||
|
||||
// 1 FLICKER (first variety)
|
||||
gi.configstring(CS_LIGHTS + 1, "mmnmmommommnonmmonqnmmo");
|
||||
|
||||
// 2 SLOW STRONG PULSE
|
||||
gi.configstring(CS_LIGHTS + 2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
|
||||
|
||||
// 3 CANDLE (first variety)
|
||||
gi.configstring(CS_LIGHTS + 3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
|
||||
|
||||
// 4 FAST STROBE
|
||||
gi.configstring(CS_LIGHTS + 4, "mamamamamama");
|
||||
|
||||
// 5 GENTLE PULSE 1
|
||||
gi.configstring(CS_LIGHTS + 5, "jklmnopqrstuvwxyzyxwvutsrqponmlkj");
|
||||
|
||||
// 6 FLICKER (second variety)
|
||||
gi.configstring(CS_LIGHTS + 6, "nmonqnmomnmomomno");
|
||||
|
||||
// 7 CANDLE (second variety)
|
||||
gi.configstring(CS_LIGHTS + 7, "mmmaaaabcdefgmmmmaaaammmaamm");
|
||||
|
||||
// 8 CANDLE (third variety)
|
||||
gi.configstring(CS_LIGHTS + 8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
|
||||
|
||||
// 9 SLOW STROBE (fourth variety)
|
||||
gi.configstring(CS_LIGHTS + 9, "aaaaaaaazzzzzzzz");
|
||||
|
||||
// 10 FLUORESCENT FLICKER
|
||||
gi.configstring(CS_LIGHTS + 10, "mmamammmmammamamaaamammma");
|
||||
|
||||
// 11 SLOW PULSE NOT FADE TO BLACK
|
||||
gi.configstring(CS_LIGHTS + 11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
|
||||
|
||||
// styles 32-62 are assigned by the light program for switchable lights
|
||||
|
||||
// 63 testing
|
||||
gi.configstring(CS_LIGHTS + 63, "a");
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
void Svcmd_Test_f(void)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PACKET FILTERING
|
||||
|
||||
|
||||
You can add or remove addresses from the filter list with:
|
||||
|
||||
addip <ip>
|
||||
removeip <ip>
|
||||
|
||||
The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
|
||||
|
||||
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
|
||||
|
||||
listip
|
||||
Prints the current list of filters.
|
||||
|
||||
writeip
|
||||
Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
|
||||
|
||||
filterban <0 or 1>
|
||||
|
||||
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
|
||||
|
||||
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
|
||||
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned compare;
|
||||
} ipfilter_t;
|
||||
|
||||
#define MAX_IPFILTERS 1024
|
||||
|
||||
ipfilter_t ipfilters[MAX_IPFILTERS];
|
||||
int numipfilters;
|
||||
|
||||
/*
|
||||
=================
|
||||
StringToFilter
|
||||
=================
|
||||
*/
|
||||
static qboolean StringToFilter(char * s, ipfilter_t * f)
|
||||
{
|
||||
char num[128];
|
||||
int i, j;
|
||||
byte b[4];
|
||||
byte m[4];
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
b[i] = 0;
|
||||
m[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (*s < '0' || *s > '9')
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
{
|
||||
num[j++] = *s++;
|
||||
}
|
||||
num[j] = 0;
|
||||
b[i] = atoi(num);
|
||||
if (b[i] != 0)
|
||||
m[i] = 255;
|
||||
|
||||
if (!*s)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
|
||||
f->mask = *(unsigned *)m;
|
||||
f->compare = *(unsigned *)b;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_FilterPacket
|
||||
=================
|
||||
*/
|
||||
qboolean SV_FilterPacket(char * from)
|
||||
{
|
||||
int i;
|
||||
unsigned in;
|
||||
byte m[4];
|
||||
char * p;
|
||||
|
||||
i = 0;
|
||||
p = from;
|
||||
while (*p && i < 4)
|
||||
{
|
||||
m[i] = 0;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
{
|
||||
m[i] = m[i] * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (!*p || *p == ':')
|
||||
break;
|
||||
i++, p++;
|
||||
}
|
||||
|
||||
in = *(unsigned *)m;
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if ((in & ipfilters[i].mask) == ipfilters[i].compare)
|
||||
return (int)filterban->value;
|
||||
|
||||
return (int)!filterban->value;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_AddIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_AddIP_f(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (gi.argc() < 3)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Usage: addip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if (ipfilters[i].compare == 0xffffffff)
|
||||
break; // free spot
|
||||
if (i == numipfilters)
|
||||
{
|
||||
if (numipfilters == MAX_IPFILTERS)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "IP filter list is full\n");
|
||||
return;
|
||||
}
|
||||
numipfilters++;
|
||||
}
|
||||
|
||||
if (!StringToFilter(gi.argv(2), &ipfilters[i]))
|
||||
ipfilters[i].compare = 0xffffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_RemoveIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_RemoveIP_f(void)
|
||||
{
|
||||
ipfilter_t f;
|
||||
int i, j;
|
||||
|
||||
if (gi.argc() < 3)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Usage: sv removeip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StringToFilter(gi.argv(2), &f))
|
||||
return;
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
if (ipfilters[i].mask == f.mask && ipfilters[i].compare == f.compare)
|
||||
{
|
||||
for (j = i + 1; j < numipfilters; j++)
|
||||
ipfilters[j - 1] = ipfilters[j];
|
||||
numipfilters--;
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Removed.\n");
|
||||
return;
|
||||
}
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Didn't find %s.\n", gi.argv(2));
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_ListIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_ListIP_f(void)
|
||||
{
|
||||
int i;
|
||||
byte b[4];
|
||||
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Filter list:\n");
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
{
|
||||
*(unsigned *)b = ipfilters[i].compare;
|
||||
gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_WriteIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_WriteIP_f(void)
|
||||
{
|
||||
FILE * f;
|
||||
char name[MAX_OSPATH];
|
||||
byte b[4];
|
||||
int i;
|
||||
cvar_t * game;
|
||||
|
||||
game = gi.cvar("game", "", 0);
|
||||
|
||||
if (!*game->string)
|
||||
sprintf(name, "%s/listip.cfg", GAMEVERSION);
|
||||
else
|
||||
sprintf(name, "%s/listip.cfg", game->string);
|
||||
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Writing %s.\n", name);
|
||||
|
||||
f = fopen(name, "wb");
|
||||
if (!f)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Couldn't open %s\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "set filterban %d\n", (int)filterban->value);
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
{
|
||||
*(unsigned *)b = ipfilters[i].compare;
|
||||
fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ServerCommand
|
||||
|
||||
ServerCommand will be called when an "sv" command is issued.
|
||||
The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||
of the parameters
|
||||
=================
|
||||
*/
|
||||
void ServerCommand(void)
|
||||
{
|
||||
char * cmd;
|
||||
|
||||
cmd = gi.argv(1);
|
||||
if (Q_stricmp(cmd, "test") == 0)
|
||||
Svcmd_Test_f();
|
||||
else if (Q_stricmp(cmd, "addip") == 0)
|
||||
SVCmd_AddIP_f();
|
||||
else if (Q_stricmp(cmd, "removeip") == 0)
|
||||
SVCmd_RemoveIP_f();
|
||||
else if (Q_stricmp(cmd, "listip") == 0)
|
||||
SVCmd_ListIP_f();
|
||||
else if (Q_stricmp(cmd, "writeip") == 0)
|
||||
SVCmd_WriteIP_f();
|
||||
else
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd);
|
||||
}
|
||||
@@ -0,0 +1,802 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
/*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Fire an origin based temp entity event to the clients.
|
||||
"style" type byte
|
||||
*/
|
||||
void Use_Target_Tent(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(ent->style);
|
||||
gi.WritePosition(ent->s.origin);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void SP_target_temp_entity(edict_t * ent)
|
||||
{
|
||||
ent->use = Use_Target_Tent;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
|
||||
"noise" wav file to play
|
||||
"attenuation"
|
||||
-1 = none, send to whole level
|
||||
1 = normal fighting sounds
|
||||
2 = idle sound level
|
||||
3 = ambient sound level
|
||||
"volume" 0.0 to 1.0
|
||||
|
||||
Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers.
|
||||
|
||||
Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off.
|
||||
Multiple identical looping sounds will just increase volume without any speed cost.
|
||||
*/
|
||||
void Use_Target_Speaker(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
int chan;
|
||||
|
||||
if (ent->spawnflags & 3)
|
||||
{ // looping sound toggles
|
||||
if (ent->s.sound)
|
||||
ent->s.sound = 0; // turn it off
|
||||
else
|
||||
ent->s.sound = ent->noise_index; // start it
|
||||
}
|
||||
else
|
||||
{ // normal sound
|
||||
if (ent->spawnflags & 4)
|
||||
chan = CHAN_VOICE | CHAN_RELIABLE;
|
||||
else
|
||||
chan = CHAN_VOICE;
|
||||
// use a positioned_sound, because this entity won't normally be
|
||||
// sent to any clients because it is invisible
|
||||
gi.positioned_sound(ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_speaker(edict_t * ent)
|
||||
{
|
||||
char buffer[MAX_QPATH];
|
||||
|
||||
if (!st.noise)
|
||||
{
|
||||
gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
|
||||
return;
|
||||
}
|
||||
if (!strstr(st.noise, ".wav"))
|
||||
Com_sprintf(buffer, sizeof(buffer), "%s.wav", st.noise);
|
||||
else
|
||||
strncpy(buffer, st.noise, sizeof(buffer));
|
||||
ent->noise_index = gi.soundindex(buffer);
|
||||
|
||||
if (!ent->volume)
|
||||
ent->volume = 1.0;
|
||||
|
||||
if (!ent->attenuation)
|
||||
ent->attenuation = 1.0;
|
||||
else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
|
||||
ent->attenuation = 0;
|
||||
|
||||
// check for prestarted looping sound
|
||||
if (ent->spawnflags & 1)
|
||||
ent->s.sound = ent->noise_index;
|
||||
|
||||
ent->use = Use_Target_Speaker;
|
||||
|
||||
// must link the entity so we get areas and clusters so
|
||||
// the server can determine who to send updates to
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
void Use_Target_Help(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (ent->spawnflags & 1)
|
||||
strncpy(game.helpmessage1, ent->message, sizeof(game.helpmessage2) - 1);
|
||||
else
|
||||
strncpy(game.helpmessage2, ent->message, sizeof(game.helpmessage1) - 1);
|
||||
|
||||
game.helpchanged++;
|
||||
}
|
||||
|
||||
/*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1
|
||||
When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars.
|
||||
*/
|
||||
void SP_target_help(edict_t * ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ent->message)
|
||||
{
|
||||
gi.dprintf("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
ent->use = Use_Target_Help;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
|
||||
Counts a secret found.
|
||||
These are single use targets.
|
||||
*/
|
||||
void use_target_secret(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
|
||||
level.found_secrets++;
|
||||
|
||||
G_UseTargets(ent, activator);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
void SP_target_secret(edict_t * ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
ent->use = use_target_secret;
|
||||
if (!st.noise)
|
||||
st.noise = "misc/secret.wav";
|
||||
ent->noise_index = gi.soundindex(st.noise);
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
level.total_secrets++;
|
||||
// map bug hack
|
||||
if (!Q_stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624)
|
||||
ent->message = "You have found a secret area.";
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
|
||||
Counts a goal completed.
|
||||
These are single use targets.
|
||||
*/
|
||||
void use_target_goal(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
|
||||
level.found_goals++;
|
||||
|
||||
if (level.found_goals == level.total_goals)
|
||||
gi.configstring(CS_CDTRACK, "0");
|
||||
|
||||
G_UseTargets(ent, activator);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
void SP_target_goal(edict_t * ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
ent->use = use_target_goal;
|
||||
if (!st.noise)
|
||||
st.noise = "misc/secret.wav";
|
||||
ent->noise_index = gi.soundindex(st.noise);
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
level.total_goals++;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Spawns an explosion temporary entity when used.
|
||||
|
||||
"delay" wait this long before going off
|
||||
"dmg" how much radius damage should be done, defaults to 0
|
||||
*/
|
||||
void target_explosion_explode(edict_t * self)
|
||||
{
|
||||
float save;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS);
|
||||
|
||||
T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE);
|
||||
|
||||
save = self->delay;
|
||||
self->delay = 0;
|
||||
G_UseTargets(self, self->activator);
|
||||
self->delay = save;
|
||||
}
|
||||
|
||||
void use_target_explosion(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
self->activator = activator;
|
||||
|
||||
if (!self->delay)
|
||||
{
|
||||
target_explosion_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = target_explosion_explode;
|
||||
self->nextthink = level.time + self->delay;
|
||||
}
|
||||
|
||||
void SP_target_explosion(edict_t * ent)
|
||||
{
|
||||
ent->use = use_target_explosion;
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Changes level to "map" when fired
|
||||
*/
|
||||
void use_target_changelevel(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (level.intermissiontime)
|
||||
return; // already activated
|
||||
|
||||
if (!deathmatch->value && !coop->value)
|
||||
{
|
||||
if (g_edicts[1].health <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// if noexit, do a ton of damage to other
|
||||
if (deathmatch->value && !((int)dmflags->value & DF_ALLOW_EXIT) && other != world)
|
||||
{
|
||||
T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
// if multiplayer, let everyone know who hit the exit
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (activator && activator->client)
|
||||
gi.bprintf(PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
|
||||
}
|
||||
|
||||
// if going to a new unit, clear cross triggers
|
||||
if (strstr(self->map, "*"))
|
||||
game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
|
||||
|
||||
BeginIntermission(self);
|
||||
}
|
||||
|
||||
void SP_target_changelevel(edict_t * ent)
|
||||
{
|
||||
if (!ent->map)
|
||||
{
|
||||
gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// ugly hack because *SOMEBODY* screwed up their map
|
||||
if ((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
|
||||
ent->map = "fact3$secret1";
|
||||
|
||||
ent->use = use_target_changelevel;
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Creates a particle splash effect when used.
|
||||
|
||||
Set "sounds" to one of the following:
|
||||
1) sparks
|
||||
2) blue water
|
||||
3) brown water
|
||||
4) slime
|
||||
5) lava
|
||||
6) blood
|
||||
|
||||
"count" how many pixels in the splash
|
||||
"dmg" if set, does a radius damage at this location when it splashes
|
||||
useful for lava/sparks
|
||||
*/
|
||||
|
||||
void use_target_splash(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_SPLASH);
|
||||
gi.WriteByte(self->count);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.WriteDir(self->movedir);
|
||||
gi.WriteByte(self->sounds);
|
||||
gi.multicast(self->s.origin, MULTICAST_PVS);
|
||||
|
||||
if (self->dmg)
|
||||
T_RadiusDamage(self, activator, self->dmg, NULL, self->dmg + 40, MOD_SPLASH);
|
||||
}
|
||||
|
||||
void SP_target_splash(edict_t * self)
|
||||
{
|
||||
self->use = use_target_splash;
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
|
||||
if (!self->count)
|
||||
self->count = 32;
|
||||
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Set target to the type of entity you want spawned.
|
||||
Useful for spawning monsters and gibs in the factory levels.
|
||||
|
||||
For monsters:
|
||||
Set direction to the facing you want it to have.
|
||||
|
||||
For gibs:
|
||||
Set direction if you want it moving and
|
||||
speed how fast it should be moving otherwise it
|
||||
will just be dropped
|
||||
*/
|
||||
void ED_CallSpawn(edict_t * ent);
|
||||
|
||||
void use_target_spawner(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
edict_t * ent;
|
||||
|
||||
ent = G_Spawn();
|
||||
ent->classname = self->target;
|
||||
VectorCopy(self->s.origin, ent->s.origin);
|
||||
VectorCopy(self->s.angles, ent->s.angles);
|
||||
ED_CallSpawn(ent);
|
||||
gi.unlinkentity(ent);
|
||||
KillBox(ent);
|
||||
gi.linkentity(ent);
|
||||
if (self->speed)
|
||||
VectorCopy(self->movedir, ent->velocity);
|
||||
}
|
||||
|
||||
void SP_target_spawner(edict_t * self)
|
||||
{
|
||||
self->use = use_target_spawner;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
if (self->speed)
|
||||
{
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
VectorScale(self->movedir, self->speed, self->movedir);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
|
||||
Fires a blaster bolt in the set direction when triggered.
|
||||
|
||||
dmg default is 15
|
||||
speed default is 1000
|
||||
*/
|
||||
|
||||
void use_target_blaster(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
int effect;
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
effect = 0;
|
||||
else if (self->spawnflags & 1)
|
||||
effect = EF_HYPERBLASTER;
|
||||
else
|
||||
effect = EF_BLASTER;
|
||||
|
||||
fire_blaster(self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
|
||||
gi.sound(self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void SP_target_blaster(edict_t * self)
|
||||
{
|
||||
self->use = use_target_blaster;
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
self->noise_index = gi.soundindex("weapons/laser2.wav");
|
||||
|
||||
if (!self->dmg)
|
||||
self->dmg = 15;
|
||||
if (!self->speed)
|
||||
self->speed = 1000;
|
||||
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
|
||||
Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work.
|
||||
*/
|
||||
void trigger_crosslevel_trigger_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
game.serverflags |= self->spawnflags;
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
void SP_target_crosslevel_trigger(edict_t * self)
|
||||
{
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
self->use = trigger_crosslevel_trigger_use;
|
||||
}
|
||||
|
||||
/*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
|
||||
Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be true. Delay, target and
|
||||
killtarget also work.
|
||||
|
||||
"delay" delay before using targets if the trigger has been activated (default 1)
|
||||
*/
|
||||
void target_crosslevel_target_think(edict_t * self)
|
||||
{
|
||||
if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
|
||||
{
|
||||
G_UseTargets(self, self);
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_crosslevel_target(edict_t * self)
|
||||
{
|
||||
if (!self->delay)
|
||||
self->delay = 1;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
self->think = target_crosslevel_target_think;
|
||||
self->nextthink = level.time + self->delay;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
|
||||
When triggered, fires a laser. You can either set a target
|
||||
or a direction.
|
||||
*/
|
||||
|
||||
void target_laser_think(edict_t * self)
|
||||
{
|
||||
edict_t * ignore;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
trace_t tr;
|
||||
vec3_t point;
|
||||
vec3_t last_movedir;
|
||||
int count;
|
||||
|
||||
if (self->spawnflags & 0x80000000)
|
||||
count = 8;
|
||||
else
|
||||
count = 4;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorCopy(self->movedir, last_movedir);
|
||||
VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point);
|
||||
VectorSubtract(point, self->s.origin, self->movedir);
|
||||
VectorNormalize(self->movedir);
|
||||
if (!VectorCompare(self->movedir, last_movedir))
|
||||
self->spawnflags |= 0x80000000;
|
||||
}
|
||||
|
||||
ignore = self;
|
||||
VectorCopy(self->s.origin, start);
|
||||
VectorMA(start, 2048, self->movedir, end);
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
|
||||
|
||||
if (!tr.ent)
|
||||
break;
|
||||
|
||||
// hurt it if we can
|
||||
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
|
||||
T_Damage(tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
|
||||
|
||||
// if we hit something that's not a monster or player or is immune to lasers, we're done
|
||||
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||
{
|
||||
if (self->spawnflags & 0x80000000)
|
||||
{
|
||||
self->spawnflags &= ~0x80000000;
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_LASER_SPARKS);
|
||||
gi.WriteByte(count);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(tr.plane.normal);
|
||||
gi.WriteByte(self->s.skinnum);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ignore = tr.ent;
|
||||
VectorCopy(tr.endpos, start);
|
||||
}
|
||||
|
||||
VectorCopy(tr.endpos, self->s.old_origin);
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void target_laser_on(edict_t * self)
|
||||
{
|
||||
if (!self->activator)
|
||||
self->activator = self;
|
||||
self->spawnflags |= 0x80000001;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
target_laser_think(self);
|
||||
}
|
||||
|
||||
void target_laser_off(edict_t * self)
|
||||
{
|
||||
self->spawnflags &= ~1;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
void target_laser_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
self->activator = activator;
|
||||
if (self->spawnflags & 1)
|
||||
target_laser_off(self);
|
||||
else
|
||||
target_laser_on(self);
|
||||
}
|
||||
|
||||
void target_laser_start(edict_t * self)
|
||||
{
|
||||
edict_t * ent;
|
||||
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT;
|
||||
self->s.modelindex = 1; // must be non-zero
|
||||
|
||||
// set the beam diameter
|
||||
if (self->spawnflags & 64)
|
||||
self->s.frame = 16;
|
||||
else
|
||||
self->s.frame = 4;
|
||||
|
||||
// set the color
|
||||
if (self->spawnflags & 2)
|
||||
self->s.skinnum = 0xf2f2f0f0;
|
||||
else if (self->spawnflags & 4)
|
||||
self->s.skinnum = 0xd0d1d2d3;
|
||||
else if (self->spawnflags & 8)
|
||||
self->s.skinnum = 0xf3f3f1f1;
|
||||
else if (self->spawnflags & 16)
|
||||
self->s.skinnum = 0xdcdddedf;
|
||||
else if (self->spawnflags & 32)
|
||||
self->s.skinnum = 0xe0e1e2e3;
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (self->target)
|
||||
{
|
||||
ent = G_Find(NULL, FOFS(targetname), self->target);
|
||||
if (!ent)
|
||||
gi.dprintf("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
|
||||
self->enemy = ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
}
|
||||
}
|
||||
self->use = target_laser_use;
|
||||
self->think = target_laser_think;
|
||||
|
||||
if (!self->dmg)
|
||||
self->dmg = 1;
|
||||
|
||||
VectorSet(self->mins, -8, -8, -8);
|
||||
VectorSet(self->maxs, 8, 8, 8);
|
||||
gi.linkentity(self);
|
||||
|
||||
if (self->spawnflags & 1)
|
||||
target_laser_on(self);
|
||||
else
|
||||
target_laser_off(self);
|
||||
}
|
||||
|
||||
void SP_target_laser(edict_t * self)
|
||||
{
|
||||
// let everything else get spawned before we start firing
|
||||
self->think = target_laser_start;
|
||||
self->nextthink = level.time + 1;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
|
||||
speed How many seconds the ramping will take
|
||||
message two letters; starting lightlevel and ending lightlevel
|
||||
*/
|
||||
|
||||
void target_lightramp_think(edict_t * self)
|
||||
{
|
||||
char style[2];
|
||||
|
||||
style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
|
||||
style[1] = 0;
|
||||
gi.configstring(CS_LIGHTS + self->enemy->style, style);
|
||||
|
||||
if ((level.time - self->timestamp) < self->speed)
|
||||
{
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
else if (self->spawnflags & 1)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = self->movedir[0];
|
||||
self->movedir[0] = self->movedir[1];
|
||||
self->movedir[1] = temp;
|
||||
self->movedir[2] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void target_lightramp_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (!self->enemy)
|
||||
{
|
||||
edict_t * e;
|
||||
|
||||
// check all the targets
|
||||
e = NULL;
|
||||
while (1)
|
||||
{
|
||||
e = G_Find(e, FOFS(targetname), self->target);
|
||||
if (!e)
|
||||
break;
|
||||
if (strcmp(e->classname, "light") != 0)
|
||||
{
|
||||
gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
|
||||
gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->timestamp = level.time;
|
||||
target_lightramp_think(self);
|
||||
}
|
||||
|
||||
void SP_target_lightramp(edict_t * self)
|
||||
{
|
||||
if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1])
|
||||
{
|
||||
gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->use = target_lightramp_use;
|
||||
self->think = target_lightramp_think;
|
||||
|
||||
self->movedir[0] = self->message[0] - 'a';
|
||||
self->movedir[1] = self->message[1] - 'a';
|
||||
self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
When triggered, this initiates a level-wide earthquake.
|
||||
All players and monsters are affected.
|
||||
"speed" severity of the quake (default:200)
|
||||
"count" duration of the quake (default:5)
|
||||
*/
|
||||
|
||||
void target_earthquake_think(edict_t * self)
|
||||
{
|
||||
int i;
|
||||
edict_t * e;
|
||||
|
||||
if (self->last_move_time < level.time)
|
||||
{
|
||||
gi.positioned_sound(self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
|
||||
self->last_move_time = level.time + 0.5;
|
||||
}
|
||||
|
||||
for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++)
|
||||
{
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client)
|
||||
continue;
|
||||
if (!e->groundentity)
|
||||
continue;
|
||||
|
||||
e->groundentity = NULL;
|
||||
e->velocity[0] += crandom() * 150;
|
||||
e->velocity[1] += crandom() * 150;
|
||||
e->velocity[2] = self->speed * (100.0 / e->mass);
|
||||
}
|
||||
|
||||
if (level.time < self->timestamp)
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void target_earthquake_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
self->timestamp = level.time + self->count;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->activator = activator;
|
||||
self->last_move_time = 0;
|
||||
}
|
||||
|
||||
void SP_target_earthquake(edict_t * self)
|
||||
{
|
||||
if (!self->targetname)
|
||||
gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
if (!self->count)
|
||||
self->count = 5;
|
||||
|
||||
if (!self->speed)
|
||||
self->speed = 200;
|
||||
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->think = target_earthquake_think;
|
||||
self->use = target_earthquake_use;
|
||||
|
||||
self->noise_index = gi.soundindex("world/quake.wav");
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
void InitTrigger(edict_t * self)
|
||||
{
|
||||
if (!VectorCompare(self->s.angles, vec3_origin))
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
gi.setmodel(self, self->model);
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
// the wait time has passed, so set back up for another activation
|
||||
void multi_wait(edict_t * ent)
|
||||
{
|
||||
ent->nextthink = 0;
|
||||
}
|
||||
|
||||
// the trigger was just activated
|
||||
// ent->activator should be set to the activator so it can be held through a delay
|
||||
// so wait for the delay time before firing
|
||||
void multi_trigger(edict_t * ent)
|
||||
{
|
||||
if (ent->nextthink)
|
||||
return; // already been triggered
|
||||
|
||||
G_UseTargets(ent, ent->activator);
|
||||
|
||||
if (ent->wait > 0)
|
||||
{
|
||||
ent->think = multi_wait;
|
||||
ent->nextthink = level.time + ent->wait;
|
||||
}
|
||||
else
|
||||
{ // we can't just remove (self) here, because this is a touch function
|
||||
// called while looping through area links...
|
||||
ent->touch = NULL;
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
ent->think = G_FreeEdict;
|
||||
}
|
||||
}
|
||||
|
||||
void Use_Multi(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
ent->activator = activator;
|
||||
multi_trigger(ent);
|
||||
}
|
||||
|
||||
void Touch_Multi(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
if (other->client)
|
||||
{
|
||||
if (self->spawnflags & 2)
|
||||
return;
|
||||
}
|
||||
else if (other->svflags & SVF_MONSTER)
|
||||
{
|
||||
if (!(self->spawnflags & 1))
|
||||
return;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (!VectorCompare(self->movedir, vec3_origin))
|
||||
{
|
||||
vec3_t forward;
|
||||
|
||||
AngleVectors(other->s.angles, forward, NULL, NULL);
|
||||
if (_DotProduct(forward, self->movedir) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
self->activator = other;
|
||||
multi_trigger(self);
|
||||
}
|
||||
|
||||
/*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
|
||||
Variable sized repeatable trigger. Must be targeted at one or more entities.
|
||||
If "delay" is set, the trigger waits some time after activating before firing.
|
||||
"wait" : Seconds between triggerings. (.2 default)
|
||||
sounds
|
||||
1) secret
|
||||
2) beep beep
|
||||
3) large switch
|
||||
4)
|
||||
set "message" to text string
|
||||
*/
|
||||
void trigger_enable(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->use = Use_Multi;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void SP_trigger_multiple(edict_t * ent)
|
||||
{
|
||||
if (ent->sounds == 1)
|
||||
ent->noise_index = gi.soundindex("misc/secret.wav");
|
||||
else if (ent->sounds == 2)
|
||||
ent->noise_index = gi.soundindex("misc/talk.wav");
|
||||
else if (ent->sounds == 3)
|
||||
ent->noise_index = gi.soundindex("misc/trigger1.wav");
|
||||
|
||||
if (!ent->wait)
|
||||
ent->wait = 0.2;
|
||||
ent->touch = Touch_Multi;
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->svflags |= SVF_NOCLIENT;
|
||||
|
||||
if (ent->spawnflags & 4)
|
||||
{
|
||||
ent->solid = SOLID_NOT;
|
||||
ent->use = trigger_enable;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->solid = SOLID_TRIGGER;
|
||||
ent->use = Use_Multi;
|
||||
}
|
||||
|
||||
if (!VectorCompare(ent->s.angles, vec3_origin))
|
||||
G_SetMovedir(ent->s.angles, ent->movedir);
|
||||
|
||||
gi.setmodel(ent, ent->model);
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
/*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
|
||||
Triggers once, then removes itself.
|
||||
You must set the key "target" to the name of another object in the level that has a matching "targetname".
|
||||
|
||||
If TRIGGERED, this trigger must be triggered before it is live.
|
||||
|
||||
sounds
|
||||
1) secret
|
||||
2) beep beep
|
||||
3) large switch
|
||||
4)
|
||||
|
||||
"message" string to be displayed when triggered
|
||||
*/
|
||||
|
||||
void SP_trigger_once(edict_t * ent)
|
||||
{
|
||||
// make old maps work because I messed up on flag assignments here
|
||||
// triggered was on bit 1 when it should have been on bit 4
|
||||
if (ent->spawnflags & 1)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
VectorMA(ent->mins, 0.5, ent->size, v);
|
||||
ent->spawnflags &= ~1;
|
||||
ent->spawnflags |= 4;
|
||||
gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
|
||||
}
|
||||
|
||||
ent->wait = -1;
|
||||
SP_trigger_multiple(ent);
|
||||
}
|
||||
|
||||
/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
This fixed size trigger cannot be touched, it can only be fired by other events.
|
||||
*/
|
||||
void trigger_relay_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
G_UseTargets(self, activator);
|
||||
}
|
||||
|
||||
void SP_trigger_relay(edict_t * self)
|
||||
{
|
||||
self->use = trigger_relay_use;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_key
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
A relay trigger that only fires it's targets if player has the proper key.
|
||||
Use "item" to specify the required key, for example "key_data_cd"
|
||||
*/
|
||||
void trigger_key_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!self->item)
|
||||
return;
|
||||
if (!activator->client)
|
||||
return;
|
||||
|
||||
index = ITEM_INDEX(self->item);
|
||||
if (!activator->client->pers.inventory[index])
|
||||
{
|
||||
if (level.time < self->touch_debounce_time)
|
||||
return;
|
||||
self->touch_debounce_time = level.time + 5.0;
|
||||
gi.centerprintf(activator, "You need the %s", self->item->pickup_name);
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keytry.wav"), 1, ATTN_NORM, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keyuse.wav"), 1, ATTN_NORM, 0);
|
||||
if (coop->value)
|
||||
{
|
||||
int player;
|
||||
edict_t * ent;
|
||||
|
||||
if (strcmp(self->item->classname, "key_power_cube") == 0)
|
||||
{
|
||||
int cube;
|
||||
|
||||
for (cube = 0; cube < 8; cube++)
|
||||
if (activator->client->pers.power_cubes & (1 << cube))
|
||||
break;
|
||||
for (player = 1; player <= game.maxclients; player++)
|
||||
{
|
||||
ent = &g_edicts[player];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (!ent->client)
|
||||
continue;
|
||||
if (ent->client->pers.power_cubes & (1 << cube))
|
||||
{
|
||||
ent->client->pers.inventory[index]--;
|
||||
ent->client->pers.power_cubes &= ~(1 << cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (player = 1; player <= game.maxclients; player++)
|
||||
{
|
||||
ent = &g_edicts[player];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (!ent->client)
|
||||
continue;
|
||||
ent->client->pers.inventory[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
activator->client->pers.inventory[index]--;
|
||||
}
|
||||
|
||||
G_UseTargets(self, activator);
|
||||
|
||||
self->use = NULL;
|
||||
}
|
||||
|
||||
void SP_trigger_key(edict_t * self)
|
||||
{
|
||||
if (!st.item)
|
||||
{
|
||||
gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
self->item = FindItemByClassname(st.item);
|
||||
|
||||
if (!self->item)
|
||||
{
|
||||
gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
|
||||
gi.soundindex("misc/keytry.wav");
|
||||
gi.soundindex("misc/keyuse.wav");
|
||||
|
||||
self->use = trigger_key_use;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_counter
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_counter (.5 .5 .5) ? nomessage
|
||||
Acts as an intermediary for an action that takes multiple inputs.
|
||||
|
||||
If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
|
||||
|
||||
After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
|
||||
*/
|
||||
|
||||
void trigger_counter_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (self->count == 0)
|
||||
return;
|
||||
|
||||
self->count--;
|
||||
|
||||
if (self->count)
|
||||
{
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
gi.centerprintf(activator, "%i more to go...", self->count);
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
gi.centerprintf(activator, "Sequence completed!");
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
self->activator = activator;
|
||||
multi_trigger(self);
|
||||
}
|
||||
|
||||
void SP_trigger_counter(edict_t * self)
|
||||
{
|
||||
self->wait = -1;
|
||||
if (!self->count)
|
||||
self->count = 2;
|
||||
|
||||
self->use = trigger_counter_use;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_always
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
This trigger will always fire. It is activated by the world.
|
||||
*/
|
||||
void SP_trigger_always(edict_t * ent)
|
||||
{
|
||||
// we must have some delay to make sure our use targets are present
|
||||
if (ent->delay < 0.2)
|
||||
ent->delay = 0.2;
|
||||
G_UseTargets(ent, ent);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_push
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#define PUSH_ONCE 1
|
||||
|
||||
static int windsound;
|
||||
|
||||
void trigger_push_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
if (strcmp(other->classname, "grenade") == 0)
|
||||
{
|
||||
VectorScale(self->movedir, self->speed * 10, other->velocity);
|
||||
}
|
||||
else if (other->health > 0)
|
||||
{
|
||||
VectorScale(self->movedir, self->speed * 10, other->velocity);
|
||||
|
||||
if (other->client)
|
||||
{
|
||||
// don't take falling damage immediately from this
|
||||
VectorCopy(other->velocity, other->client->oldvelocity);
|
||||
if (other->fly_sound_debounce_time < level.time)
|
||||
{
|
||||
other->fly_sound_debounce_time = level.time + 1.5;
|
||||
gi.sound(other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self->spawnflags & PUSH_ONCE)
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
|
||||
Pushes the player
|
||||
"speed" defaults to 1000
|
||||
*/
|
||||
void SP_trigger_push(edict_t * self)
|
||||
{
|
||||
InitTrigger(self);
|
||||
windsound = gi.soundindex("misc/windfly.wav");
|
||||
self->touch = trigger_push_touch;
|
||||
if (!self->speed)
|
||||
self->speed = 1000;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_hurt
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
|
||||
Any entity that touches this will be hurt.
|
||||
|
||||
It does dmg points of damage each server frame
|
||||
|
||||
SILENT supresses playing the sound
|
||||
SLOW changes the damage rate to once per second
|
||||
NO_PROTECTION *nothing* stops the damage
|
||||
|
||||
"dmg" default 5 (whole numbers only)
|
||||
|
||||
*/
|
||||
void hurt_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
if (self->solid == SOLID_NOT)
|
||||
self->solid = SOLID_TRIGGER;
|
||||
else
|
||||
self->solid = SOLID_NOT;
|
||||
gi.linkentity(self);
|
||||
|
||||
if (!(self->spawnflags & 2))
|
||||
self->use = NULL;
|
||||
}
|
||||
|
||||
void hurt_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
int dflags;
|
||||
|
||||
if (!other->takedamage)
|
||||
return;
|
||||
|
||||
if (self->timestamp > level.time)
|
||||
return;
|
||||
|
||||
if (self->spawnflags & 16)
|
||||
self->timestamp = level.time + 1;
|
||||
else
|
||||
self->timestamp = level.time + FRAMETIME;
|
||||
|
||||
if (!(self->spawnflags & 4))
|
||||
{
|
||||
if ((level.framenum % 10) == 0)
|
||||
gi.sound(other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
if (self->spawnflags & 8)
|
||||
dflags = DAMAGE_NO_PROTECTION;
|
||||
else
|
||||
dflags = 0;
|
||||
T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
|
||||
}
|
||||
|
||||
void SP_trigger_hurt(edict_t * self)
|
||||
{
|
||||
InitTrigger(self);
|
||||
|
||||
self->noise_index = gi.soundindex("world/electro.wav");
|
||||
self->touch = hurt_touch;
|
||||
|
||||
if (!self->dmg)
|
||||
self->dmg = 5;
|
||||
|
||||
if (self->spawnflags & 1)
|
||||
self->solid = SOLID_NOT;
|
||||
else
|
||||
self->solid = SOLID_TRIGGER;
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
self->use = hurt_use;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_gravity
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_gravity (.5 .5 .5) ?
|
||||
Changes the touching entites gravity to
|
||||
the value of "gravity". 1.0 is standard
|
||||
gravity for the level.
|
||||
*/
|
||||
|
||||
void trigger_gravity_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
other->gravity = self->gravity;
|
||||
}
|
||||
|
||||
void SP_trigger_gravity(edict_t * self)
|
||||
{
|
||||
if (st.gravity == 0)
|
||||
{
|
||||
gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
InitTrigger(self);
|
||||
self->gravity = atoi(st.gravity);
|
||||
self->touch = trigger_gravity_touch;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
trigger_monsterjump
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*QUAKED trigger_monsterjump (.5 .5 .5) ?
|
||||
Walking monsters that touch this will jump in the direction of the trigger's angle
|
||||
"speed" default to 200, the speed thrown forward
|
||||
"height" default to 200, the speed thrown upwards
|
||||
*/
|
||||
|
||||
void trigger_monsterjump_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
if (other->flags & (FL_FLY | FL_SWIM))
|
||||
return;
|
||||
if (other->svflags & SVF_DEADMONSTER)
|
||||
return;
|
||||
if (!(other->svflags & SVF_MONSTER))
|
||||
return;
|
||||
|
||||
// set XY even if not on ground, so the jump will clear lips
|
||||
other->velocity[0] = self->movedir[0] * self->speed;
|
||||
other->velocity[1] = self->movedir[1] * self->speed;
|
||||
|
||||
if (!other->groundentity)
|
||||
return;
|
||||
|
||||
other->groundentity = NULL;
|
||||
other->velocity[2] = self->movedir[2];
|
||||
}
|
||||
|
||||
void SP_trigger_monsterjump(edict_t * self)
|
||||
{
|
||||
if (!self->speed)
|
||||
self->speed = 200;
|
||||
if (!st.height)
|
||||
st.height = 200;
|
||||
if (self->s.angles[YAW] == 0)
|
||||
self->s.angles[YAW] = 360;
|
||||
InitTrigger(self);
|
||||
self->touch = trigger_monsterjump_touch;
|
||||
self->movedir[2] = st.height;
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// g_turret.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
void AnglesNormalize(vec3_t vec)
|
||||
{
|
||||
while (vec[0] > 360)
|
||||
vec[0] -= 360;
|
||||
while (vec[0] < 0)
|
||||
vec[0] += 360;
|
||||
while (vec[1] > 360)
|
||||
vec[1] -= 360;
|
||||
while (vec[1] < 0)
|
||||
vec[1] += 360;
|
||||
}
|
||||
|
||||
float SnapToEights(float x)
|
||||
{
|
||||
x *= 8.0;
|
||||
if (x > 0.0)
|
||||
x += 0.5;
|
||||
else
|
||||
x -= 0.5;
|
||||
return 0.125 * (int)x;
|
||||
}
|
||||
|
||||
void turret_blocked(edict_t * self, edict_t * other)
|
||||
{
|
||||
edict_t * attacker;
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
if (self->teammaster->owner)
|
||||
attacker = self->teammaster->owner;
|
||||
else
|
||||
attacker = self->teammaster;
|
||||
T_Damage(other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED turret_breach (0 0 0) ?
|
||||
This portion of the turret can change both pitch and yaw.
|
||||
The model should be made with a flat pitch.
|
||||
It (and the associated base) need to be oriented towards 0.
|
||||
Use "angle" to set the starting angle.
|
||||
|
||||
"speed" default 50
|
||||
"dmg" default 10
|
||||
"angle" point this forward
|
||||
"target" point this at an info_notnull at the muzzle tip
|
||||
"minpitch" min acceptable pitch angle : default -30
|
||||
"maxpitch" max acceptable pitch angle : default 30
|
||||
"minyaw" min acceptable yaw angle : default 0
|
||||
"maxyaw" max acceptable yaw angle : default 360
|
||||
*/
|
||||
|
||||
void turret_breach_fire(edict_t * self)
|
||||
{
|
||||
vec3_t f, r, u;
|
||||
vec3_t start;
|
||||
int damage;
|
||||
int speed;
|
||||
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
VectorMA(self->s.origin, self->move_origin[0], f, start);
|
||||
VectorMA(start, self->move_origin[1], r, start);
|
||||
VectorMA(start, self->move_origin[2], u, start);
|
||||
|
||||
damage = 100 + random() * 50;
|
||||
speed = 550 + 50 * skill->value;
|
||||
fire_rocket(self->teammaster->owner, start, f, damage, speed, 150, damage);
|
||||
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void turret_breach_think(edict_t * self)
|
||||
{
|
||||
edict_t * ent;
|
||||
vec3_t current_angles;
|
||||
vec3_t delta;
|
||||
|
||||
VectorCopy(self->s.angles, current_angles);
|
||||
AnglesNormalize(current_angles);
|
||||
|
||||
AnglesNormalize(self->move_angles);
|
||||
if (self->move_angles[PITCH] > 180)
|
||||
self->move_angles[PITCH] -= 360;
|
||||
|
||||
// clamp angles to mins & maxs
|
||||
if (self->move_angles[PITCH] > self->pos1[PITCH])
|
||||
self->move_angles[PITCH] = self->pos1[PITCH];
|
||||
else if (self->move_angles[PITCH] < self->pos2[PITCH])
|
||||
self->move_angles[PITCH] = self->pos2[PITCH];
|
||||
|
||||
if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
|
||||
{
|
||||
float dmin, dmax;
|
||||
|
||||
dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
|
||||
if (dmin < -180)
|
||||
dmin += 360;
|
||||
else if (dmin > 180)
|
||||
dmin -= 360;
|
||||
dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
|
||||
if (dmax < -180)
|
||||
dmax += 360;
|
||||
else if (dmax > 180)
|
||||
dmax -= 360;
|
||||
if (fabs(dmin) < fabs(dmax))
|
||||
self->move_angles[YAW] = self->pos1[YAW];
|
||||
else
|
||||
self->move_angles[YAW] = self->pos2[YAW];
|
||||
}
|
||||
|
||||
VectorSubtract(self->move_angles, current_angles, delta);
|
||||
if (delta[0] < -180)
|
||||
delta[0] += 360;
|
||||
else if (delta[0] > 180)
|
||||
delta[0] -= 360;
|
||||
if (delta[1] < -180)
|
||||
delta[1] += 360;
|
||||
else if (delta[1] > 180)
|
||||
delta[1] -= 360;
|
||||
delta[2] = 0;
|
||||
|
||||
if (delta[0] > self->speed * FRAMETIME)
|
||||
delta[0] = self->speed * FRAMETIME;
|
||||
if (delta[0] < -1 * self->speed * FRAMETIME)
|
||||
delta[0] = -1 * self->speed * FRAMETIME;
|
||||
if (delta[1] > self->speed * FRAMETIME)
|
||||
delta[1] = self->speed * FRAMETIME;
|
||||
if (delta[1] < -1 * self->speed * FRAMETIME)
|
||||
delta[1] = -1 * self->speed * FRAMETIME;
|
||||
|
||||
VectorScale(delta, 1.0 / FRAMETIME, self->avelocity);
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
for (ent = self->teammaster; ent; ent = ent->teamchain)
|
||||
ent->avelocity[1] = self->avelocity[1];
|
||||
|
||||
// if we have adriver, adjust his velocities
|
||||
if (self->owner)
|
||||
{
|
||||
float angle;
|
||||
float target_z;
|
||||
float diff;
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
|
||||
// angular is easy, just copy ours
|
||||
self->owner->avelocity[0] = self->avelocity[0];
|
||||
self->owner->avelocity[1] = self->avelocity[1];
|
||||
|
||||
// x & y
|
||||
angle = self->s.angles[1] + self->owner->move_origin[1];
|
||||
angle *= (M_PI * 2 / 360);
|
||||
target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
|
||||
target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
|
||||
target[2] = self->owner->s.origin[2];
|
||||
|
||||
VectorSubtract(target, self->owner->s.origin, dir);
|
||||
self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
|
||||
self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
|
||||
|
||||
// z
|
||||
angle = self->s.angles[PITCH] * (M_PI * 2 / 360);
|
||||
target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
|
||||
|
||||
diff = target_z - self->owner->s.origin[2];
|
||||
self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
|
||||
|
||||
if (self->spawnflags & 65536)
|
||||
{
|
||||
turret_breach_fire(self);
|
||||
self->spawnflags &= ~65536;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void turret_breach_finish_init(edict_t * self)
|
||||
{
|
||||
// get and save info for muzzle location
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
else
|
||||
{
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
VectorSubtract(self->target_ent->s.origin, self->s.origin, self->move_origin);
|
||||
G_FreeEdict(self->target_ent);
|
||||
}
|
||||
|
||||
self->teammaster->dmg = self->dmg;
|
||||
self->think = turret_breach_think;
|
||||
self->think(self);
|
||||
}
|
||||
|
||||
void SP_turret_breach(edict_t * self)
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.setmodel(self, self->model);
|
||||
|
||||
if (!self->speed)
|
||||
self->speed = 50;
|
||||
if (!self->dmg)
|
||||
self->dmg = 10;
|
||||
|
||||
if (!st.minpitch)
|
||||
st.minpitch = -30;
|
||||
if (!st.maxpitch)
|
||||
st.maxpitch = 30;
|
||||
if (!st.maxyaw)
|
||||
st.maxyaw = 360;
|
||||
|
||||
self->pos1[PITCH] = -1 * st.minpitch;
|
||||
self->pos1[YAW] = st.minyaw;
|
||||
self->pos2[PITCH] = -1 * st.maxpitch;
|
||||
self->pos2[YAW] = st.maxyaw;
|
||||
|
||||
self->ideal_yaw = self->s.angles[YAW];
|
||||
self->move_angles[YAW] = self->ideal_yaw;
|
||||
|
||||
self->blocked = turret_blocked;
|
||||
|
||||
self->think = turret_breach_finish_init;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*QUAKED turret_base (0 0 0) ?
|
||||
This portion of the turret changes yaw only.
|
||||
MUST be teamed with a turret_breach.
|
||||
*/
|
||||
|
||||
void SP_turret_base(edict_t * self)
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.setmodel(self, self->model);
|
||||
self->blocked = turret_blocked;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
|
||||
Must NOT be on the team with the rest of the turret parts.
|
||||
Instead it must target the turret_breach.
|
||||
*/
|
||||
|
||||
void infantry_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage);
|
||||
void infantry_stand(edict_t * self);
|
||||
void monster_use(edict_t * self, edict_t * other, edict_t * activator);
|
||||
|
||||
void turret_driver_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
edict_t * ent;
|
||||
|
||||
// level the gun
|
||||
self->target_ent->move_angles[0] = 0;
|
||||
|
||||
// remove the driver from the end of them team chain
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
|
||||
;
|
||||
ent->teamchain = NULL;
|
||||
self->teammaster = NULL;
|
||||
self->flags &= ~FL_TEAMSLAVE;
|
||||
|
||||
self->target_ent->owner = NULL;
|
||||
self->target_ent->teammaster->owner = NULL;
|
||||
|
||||
infantry_die(self, inflictor, attacker, damage);
|
||||
}
|
||||
|
||||
qboolean FindTarget(edict_t * self);
|
||||
|
||||
void turret_driver_think(edict_t * self)
|
||||
{
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
float reaction_time;
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
|
||||
self->enemy = NULL;
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
return;
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// let the turret know where we want it to aim
|
||||
VectorCopy(self->enemy->s.origin, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, self->target_ent->s.origin, dir);
|
||||
vectoangles(dir, self->target_ent->move_angles);
|
||||
|
||||
// decide if we should shoot
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return;
|
||||
|
||||
reaction_time = (3 - skill->value) * 1.0;
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
return;
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
|
||||
//FIXME how do we really want to pass this along?
|
||||
self->target_ent->spawnflags |= 65536;
|
||||
}
|
||||
|
||||
void turret_driver_link(edict_t * self)
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t * ent;
|
||||
|
||||
self->think = turret_driver_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
VectorCopy(self->target_ent->s.angles, self->s.angles);
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = VectorLength(vec);
|
||||
|
||||
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
|
||||
vectoangles(vec, vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
// add the driver to the end of them team chain
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
;
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
void SP_turret_driver(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 100;
|
||||
self->gib_health = 0;
|
||||
self->mass = 200;
|
||||
self->viewheight = 24;
|
||||
|
||||
self->die = turret_driver_die;
|
||||
self->monsterinfo.stand = infantry_stand;
|
||||
|
||||
self->flags |= FL_NO_KNOCKBACK;
|
||||
|
||||
level.total_monsters++;
|
||||
|
||||
self->svflags |= SVF_MONSTER;
|
||||
self->s.renderfx |= RF_FRAMELERP;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
self->use = monster_use;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
VectorCopy(self->s.origin, self->s.old_origin);
|
||||
self->monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED;
|
||||
|
||||
if (st.item)
|
||||
{
|
||||
self->item = FindItemByClassname(st.item);
|
||||
if (!self->item)
|
||||
gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
|
||||
}
|
||||
|
||||
self->think = turret_driver_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// g_utils.c -- misc utility functions for game module
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
|
||||
{
|
||||
result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
|
||||
result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
|
||||
result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
G_Find
|
||||
|
||||
Searches all active entities for the next one that holds
|
||||
the matching string at fieldofs (use the FOFS() macro) in the structure.
|
||||
|
||||
Searches beginning at the edict after from, or the beginning if NULL
|
||||
NULL will be returned if the end of the list is reached.
|
||||
|
||||
=============
|
||||
*/
|
||||
edict_t * G_Find(edict_t * from, int fieldofs, char * match)
|
||||
{
|
||||
char * s;
|
||||
|
||||
if (!from)
|
||||
from = g_edicts;
|
||||
else
|
||||
from++;
|
||||
|
||||
for (; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
continue;
|
||||
s = *(char **)((byte *)from + fieldofs);
|
||||
if (!s)
|
||||
continue;
|
||||
if (!Q_stricmp(s, match))
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
findradius
|
||||
|
||||
Returns entities that have origins within a spherical area
|
||||
|
||||
findradius (origin, radius)
|
||||
=================
|
||||
*/
|
||||
edict_t * findradius(edict_t * from, vec3_t org, float rad)
|
||||
{
|
||||
vec3_t eorg;
|
||||
int j;
|
||||
|
||||
if (!from)
|
||||
from = g_edicts;
|
||||
else
|
||||
from++;
|
||||
for (; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
continue;
|
||||
if (from->solid == SOLID_NOT)
|
||||
continue;
|
||||
for (j = 0; j < 3; j++)
|
||||
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j]) * 0.5);
|
||||
if (VectorLength(eorg) > rad)
|
||||
continue;
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
G_PickTarget
|
||||
|
||||
Searches all active entities for the next one that holds
|
||||
the matching string at fieldofs (use the FOFS() macro) in the structure.
|
||||
|
||||
Searches beginning at the edict after from, or the beginning if NULL
|
||||
NULL will be returned if the end of the list is reached.
|
||||
|
||||
=============
|
||||
*/
|
||||
#define MAXCHOICES 8
|
||||
|
||||
edict_t * G_PickTarget(char * targetname)
|
||||
{
|
||||
edict_t * ent = NULL;
|
||||
int num_choices = 0;
|
||||
edict_t * choice[MAXCHOICES];
|
||||
|
||||
if (!targetname)
|
||||
{
|
||||
gi.dprintf("G_PickTarget called with NULL targetname\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
ent = G_Find(ent, FOFS(targetname), targetname);
|
||||
if (!ent)
|
||||
break;
|
||||
choice[num_choices++] = ent;
|
||||
if (num_choices == MAXCHOICES)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!num_choices)
|
||||
{
|
||||
gi.dprintf("G_PickTarget: target %s not found\n", targetname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return choice[rand() % num_choices];
|
||||
}
|
||||
|
||||
void Think_Delay(edict_t * ent)
|
||||
{
|
||||
G_UseTargets(ent, ent->activator);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
==============================
|
||||
G_UseTargets
|
||||
|
||||
the global "activator" should be set to the entity that initiated the firing.
|
||||
|
||||
If self.delay is set, a DelayedUse entity will be created that will actually
|
||||
do the SUB_UseTargets after that many seconds have passed.
|
||||
|
||||
Centerprints any self.message to the activator.
|
||||
|
||||
Search for (string)targetname in all entities that
|
||||
match (string)self.target and call their .use function
|
||||
|
||||
==============================
|
||||
*/
|
||||
void G_UseTargets(edict_t * ent, edict_t * activator)
|
||||
{
|
||||
edict_t * t;
|
||||
|
||||
//
|
||||
// check for a delay
|
||||
//
|
||||
if (ent->delay)
|
||||
{
|
||||
// create a temp object to fire at a later time
|
||||
t = G_Spawn();
|
||||
t->classname = "DelayedUse";
|
||||
t->nextthink = level.time + ent->delay;
|
||||
t->think = Think_Delay;
|
||||
t->activator = activator;
|
||||
if (!activator)
|
||||
gi.dprintf("Think_Delay with no activator\n");
|
||||
t->message = ent->message;
|
||||
t->target = ent->target;
|
||||
t->killtarget = ent->killtarget;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// print the message
|
||||
//
|
||||
if ((ent->message) && !(activator->svflags & SVF_MONSTER))
|
||||
{
|
||||
gi.centerprintf(activator, "%s", ent->message);
|
||||
if (ent->noise_index)
|
||||
gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// kill killtargets
|
||||
//
|
||||
if (ent->killtarget)
|
||||
{
|
||||
t = NULL;
|
||||
while ((t = G_Find(t, FOFS(targetname), ent->killtarget)))
|
||||
{
|
||||
G_FreeEdict(t);
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.dprintf("entity was removed while using killtargets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// fire targets
|
||||
//
|
||||
if (ent->target)
|
||||
{
|
||||
t = NULL;
|
||||
while ((t = G_Find(t, FOFS(targetname), ent->target)))
|
||||
{
|
||||
// doors fire area portals in a specific way
|
||||
if (!Q_stricmp(t->classname, "func_areaportal") &&
|
||||
(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
|
||||
continue;
|
||||
|
||||
if (t == ent)
|
||||
{
|
||||
gi.dprintf("WARNING: Entity used itself.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->use)
|
||||
t->use(t, ent, activator);
|
||||
}
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.dprintf("entity was removed while using targets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
TempVector
|
||||
|
||||
This is just a convenience function
|
||||
for making temporary vectors for function calls
|
||||
=============
|
||||
*/
|
||||
float * tv(float x, float y, float z)
|
||||
{
|
||||
static int index;
|
||||
static vec3_t vecs[8];
|
||||
float * v;
|
||||
|
||||
// use an array so that multiple tempvectors won't collide
|
||||
// for a while
|
||||
v = vecs[index];
|
||||
index = (index + 1) & 7;
|
||||
|
||||
v[0] = x;
|
||||
v[1] = y;
|
||||
v[2] = z;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
VectorToString
|
||||
|
||||
This is just a convenience function
|
||||
for printing vectors
|
||||
=============
|
||||
*/
|
||||
char * vtos(vec3_t v)
|
||||
{
|
||||
static int index;
|
||||
static char str[8][32];
|
||||
char * s;
|
||||
|
||||
// use an array so that multiple vtos won't collide
|
||||
s = str[index];
|
||||
index = (index + 1) & 7;
|
||||
|
||||
Com_sprintf(s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
vec3_t VEC_UP = { 0, -1, 0 };
|
||||
vec3_t MOVEDIR_UP = { 0, 0, 1 };
|
||||
vec3_t VEC_DOWN = { 0, -2, 0 };
|
||||
vec3_t MOVEDIR_DOWN = { 0, 0, -1 };
|
||||
|
||||
void G_SetMovedir(vec3_t angles, vec3_t movedir)
|
||||
{
|
||||
if (VectorCompare(angles, VEC_UP))
|
||||
{
|
||||
VectorCopy(MOVEDIR_UP, movedir);
|
||||
}
|
||||
else if (VectorCompare(angles, VEC_DOWN))
|
||||
{
|
||||
VectorCopy(MOVEDIR_DOWN, movedir);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(angles, movedir, NULL, NULL);
|
||||
}
|
||||
|
||||
VectorClear(angles);
|
||||
}
|
||||
|
||||
float vectoyaw(vec3_t vec)
|
||||
{
|
||||
float yaw;
|
||||
|
||||
if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
|
||||
{
|
||||
yaw = 0;
|
||||
if (vec[YAW] > 0)
|
||||
yaw = 90;
|
||||
else if (vec[YAW] < 0)
|
||||
yaw = -90;
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = (int)(atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
|
||||
if (yaw < 0)
|
||||
yaw += 360;
|
||||
}
|
||||
|
||||
return yaw;
|
||||
}
|
||||
|
||||
void vectoangles(vec3_t value1, vec3_t angles)
|
||||
{
|
||||
float forward;
|
||||
float yaw, pitch;
|
||||
|
||||
if (value1[1] == 0 && value1[0] == 0)
|
||||
{
|
||||
yaw = 0;
|
||||
if (value1[2] > 0)
|
||||
pitch = 90;
|
||||
else
|
||||
pitch = 270;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value1[0])
|
||||
yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI);
|
||||
else if (value1[1] > 0)
|
||||
yaw = 90;
|
||||
else
|
||||
yaw = -90;
|
||||
if (yaw < 0)
|
||||
yaw += 360;
|
||||
|
||||
forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
|
||||
pitch = (int)(atan2(value1[2], forward) * 180 / M_PI);
|
||||
if (pitch < 0)
|
||||
pitch += 360;
|
||||
}
|
||||
|
||||
angles[PITCH] = -pitch;
|
||||
angles[YAW] = yaw;
|
||||
angles[ROLL] = 0;
|
||||
}
|
||||
|
||||
char * G_CopyString(char * in)
|
||||
{
|
||||
char * out;
|
||||
|
||||
out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL);
|
||||
strcpy(out, in);
|
||||
return out;
|
||||
}
|
||||
|
||||
void G_InitEdict(edict_t * e)
|
||||
{
|
||||
e->inuse = true;
|
||||
e->classname = "noclass";
|
||||
e->gravity = 1.0;
|
||||
e->s.number = e - g_edicts;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_Spawn
|
||||
|
||||
Either finds a free edict, or allocates a new one.
|
||||
Try to avoid reusing an entity that was recently freed, because it
|
||||
can cause the client to think the entity morphed into something else
|
||||
instead of being removed and recreated, which can cause interpolated
|
||||
angles and bad trails.
|
||||
=================
|
||||
*/
|
||||
edict_t * G_Spawn(void)
|
||||
{
|
||||
int i;
|
||||
edict_t * e;
|
||||
|
||||
e = &g_edicts[(int)maxclients->value + 1];
|
||||
for (i = maxclients->value + 1; i < globals.num_edicts; i++, e++)
|
||||
{
|
||||
// the first couple seconds of server time can involve a lot of
|
||||
// freeing and allocating, so relax the replacement policy
|
||||
if (!e->inuse && (e->freetime < 2 || level.time - e->freetime > 0.5))
|
||||
{
|
||||
G_InitEdict(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == game.maxentities)
|
||||
gi.error("ED_Alloc: no free edicts");
|
||||
|
||||
globals.num_edicts++;
|
||||
G_InitEdict(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_FreeEdict
|
||||
|
||||
Marks the edict as free
|
||||
=================
|
||||
*/
|
||||
void G_FreeEdict(edict_t * ed)
|
||||
{
|
||||
gi.unlinkentity(ed); // unlink from world
|
||||
|
||||
if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
|
||||
{
|
||||
// gi.dprintf("tried to free special edict\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(ed, 0, sizeof(*ed));
|
||||
ed->classname = "freed";
|
||||
ed->freetime = level.time;
|
||||
ed->inuse = false;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
G_TouchTriggers
|
||||
|
||||
============
|
||||
*/
|
||||
void G_TouchTriggers(edict_t * ent)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hit;
|
||||
|
||||
// dead things don't activate triggers!
|
||||
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
|
||||
return;
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_TRIGGERS);
|
||||
|
||||
// be careful, it is possible to have an entity in this
|
||||
// list removed before we get to it (killtriggered)
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
if (!hit->inuse)
|
||||
continue;
|
||||
if (!hit->touch)
|
||||
continue;
|
||||
hit->touch(hit, ent, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
G_TouchSolids
|
||||
|
||||
Call after linking a new trigger in during gameplay
|
||||
to force all entities it covers to immediately touch it
|
||||
============
|
||||
*/
|
||||
void G_TouchSolids(edict_t * ent)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hit;
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID);
|
||||
|
||||
// be careful, it is possible to have an entity in this
|
||||
// list removed before we get to it (killtriggered)
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
if (!hit->inuse)
|
||||
continue;
|
||||
if (ent->touch)
|
||||
ent->touch(hit, ent, NULL, NULL);
|
||||
if (!ent->inuse)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
Kill box
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
KillBox
|
||||
|
||||
Kills all entities that would touch the proposed new positioning
|
||||
of ent. Ent should be unlinked before calling this!
|
||||
=================
|
||||
*/
|
||||
qboolean KillBox(edict_t * ent)
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
|
||||
if (!tr.ent)
|
||||
break;
|
||||
|
||||
// nail it
|
||||
T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||
|
||||
// if we didn't kill it, fail
|
||||
if (tr.ent->solid)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // all clear
|
||||
}
|
||||
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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 "g_local.h"
|
||||
|
||||
/*
|
||||
=================
|
||||
check_dodge
|
||||
|
||||
This is a support routine used when a client is firing
|
||||
a non-instant attack weapon. It checks to see if a
|
||||
monster's dodge function should be called.
|
||||
=================
|
||||
*/
|
||||
static void check_dodge(edict_t * self, vec3_t start, vec3_t dir, int speed)
|
||||
{
|
||||
vec3_t end;
|
||||
vec3_t v;
|
||||
trace_t tr;
|
||||
float eta;
|
||||
|
||||
// easy mode only ducks one quarter the time
|
||||
if (skill->value == 0)
|
||||
{
|
||||
if (random() > 0.25)
|
||||
return;
|
||||
}
|
||||
VectorMA(start, 8192, dir, end);
|
||||
tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
|
||||
if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self))
|
||||
{
|
||||
VectorSubtract(tr.endpos, start, v);
|
||||
eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
|
||||
tr.ent->monsterinfo.dodge(tr.ent, self, eta);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_hit
|
||||
|
||||
Used for all impact (hit/punch/slash) attacks
|
||||
=================
|
||||
*/
|
||||
qboolean fire_hit(edict_t * self, vec3_t aim, int damage, int kick)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t forward, right, up;
|
||||
vec3_t v;
|
||||
vec3_t point;
|
||||
float range;
|
||||
vec3_t dir;
|
||||
|
||||
//see if enemy is in range
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
range = VectorLength(dir);
|
||||
if (range > aim[0])
|
||||
return false;
|
||||
|
||||
if (aim[1] > self->mins[0] && aim[1] < self->maxs[0])
|
||||
{
|
||||
// the hit is straight on so back the range up to the edge of their bbox
|
||||
range -= self->enemy->maxs[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is a side hit so adjust the "right" value out to the edge of their bbox
|
||||
if (aim[1] < 0)
|
||||
aim[1] = self->enemy->mins[0];
|
||||
else
|
||||
aim[1] = self->enemy->maxs[0];
|
||||
}
|
||||
|
||||
VectorMA(self->s.origin, range, dir, point);
|
||||
|
||||
tr = gi.trace(self->s.origin, NULL, NULL, point, self, MASK_SHOT);
|
||||
if (tr.fraction < 1)
|
||||
{
|
||||
if (!tr.ent->takedamage)
|
||||
return false;
|
||||
// if it will hit any client/monster then hit the one we wanted to hit
|
||||
if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
|
||||
tr.ent = self->enemy;
|
||||
}
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, up);
|
||||
VectorMA(self->s.origin, range, forward, point);
|
||||
VectorMA(point, aim[1], right, point);
|
||||
VectorMA(point, aim[2], up, point);
|
||||
VectorSubtract(point, self->enemy->s.origin, dir);
|
||||
|
||||
// do the damage
|
||||
T_Damage(tr.ent, self, self, dir, point, vec3_origin, damage, kick / 2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
|
||||
|
||||
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||
return false;
|
||||
|
||||
// do our special form of knockback here
|
||||
VectorMA(self->enemy->absmin, 0.5, self->enemy->size, v);
|
||||
VectorSubtract(v, point, v);
|
||||
VectorNormalize(v);
|
||||
VectorMA(self->enemy->velocity, kick, v, self->enemy->velocity);
|
||||
if (self->enemy->velocity[2] > 0)
|
||||
self->enemy->groundentity = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_lead
|
||||
|
||||
This is an internal support routine used for bullet/pellet based weapons.
|
||||
=================
|
||||
*/
|
||||
static void fire_lead(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right, up;
|
||||
vec3_t end;
|
||||
float r;
|
||||
float u;
|
||||
vec3_t water_start;
|
||||
qboolean water = false;
|
||||
int content_mask = MASK_SHOT | MASK_WATER;
|
||||
|
||||
tr = gi.trace(self->s.origin, NULL, NULL, start, self, MASK_SHOT);
|
||||
if (!(tr.fraction < 1.0))
|
||||
{
|
||||
vectoangles(aimdir, dir);
|
||||
AngleVectors(dir, forward, right, up);
|
||||
|
||||
r = crandom() * hspread;
|
||||
u = crandom() * vspread;
|
||||
VectorMA(start, 8192, forward, end);
|
||||
VectorMA(end, r, right, end);
|
||||
VectorMA(end, u, up, end);
|
||||
|
||||
if (gi.pointcontents(start) & MASK_WATER)
|
||||
{
|
||||
water = true;
|
||||
VectorCopy(start, water_start);
|
||||
content_mask &= ~MASK_WATER;
|
||||
}
|
||||
|
||||
tr = gi.trace(start, NULL, NULL, end, self, content_mask);
|
||||
|
||||
// see if we hit water
|
||||
if (tr.contents & MASK_WATER)
|
||||
{
|
||||
int color;
|
||||
|
||||
water = true;
|
||||
VectorCopy(tr.endpos, water_start);
|
||||
|
||||
if (!VectorCompare(start, tr.endpos))
|
||||
{
|
||||
if (tr.contents & CONTENTS_WATER)
|
||||
{
|
||||
if (strcmp(tr.surface->name, "*brwater") == 0)
|
||||
color = SPLASH_BROWN_WATER;
|
||||
else
|
||||
color = SPLASH_BLUE_WATER;
|
||||
}
|
||||
else if (tr.contents & CONTENTS_SLIME)
|
||||
color = SPLASH_SLIME;
|
||||
else if (tr.contents & CONTENTS_LAVA)
|
||||
color = SPLASH_LAVA;
|
||||
else
|
||||
color = SPLASH_UNKNOWN;
|
||||
|
||||
if (color != SPLASH_UNKNOWN)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_SPLASH);
|
||||
gi.WriteByte(8);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(tr.plane.normal);
|
||||
gi.WriteByte(color);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
// change bullet's course when it enters water
|
||||
VectorSubtract(end, start, dir);
|
||||
vectoangles(dir, dir);
|
||||
AngleVectors(dir, forward, right, up);
|
||||
r = crandom() * hspread * 2;
|
||||
u = crandom() * vspread * 2;
|
||||
VectorMA(water_start, 8192, forward, end);
|
||||
VectorMA(end, r, right, end);
|
||||
VectorMA(end, u, up, end);
|
||||
}
|
||||
|
||||
// re-trace ignoring water this time
|
||||
tr = gi.trace(water_start, NULL, NULL, end, self, MASK_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
// send gun puff / flash
|
||||
if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
||||
{
|
||||
if (tr.fraction < 1.0)
|
||||
{
|
||||
if (tr.ent->takedamage)
|
||||
{
|
||||
T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strncmp(tr.surface->name, "sky", 3) != 0)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(te_impact);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(tr.plane.normal);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS);
|
||||
|
||||
if (self->client)
|
||||
PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if went through water, determine where the end and make a bubble trail
|
||||
if (water)
|
||||
{
|
||||
vec3_t pos;
|
||||
|
||||
VectorSubtract(tr.endpos, water_start, dir);
|
||||
VectorNormalize(dir);
|
||||
VectorMA(tr.endpos, -2, dir, pos);
|
||||
if (gi.pointcontents(pos) & MASK_WATER)
|
||||
VectorCopy(pos, tr.endpos);
|
||||
else
|
||||
tr = gi.trace(pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
|
||||
|
||||
VectorAdd(water_start, tr.endpos, pos);
|
||||
VectorScale(pos, 0.5, pos);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BUBBLETRAIL);
|
||||
gi.WritePosition(water_start);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.multicast(pos, MULTICAST_PVS);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_bullet
|
||||
|
||||
Fires a single round. Used for machinegun and chaingun. Would be fine for
|
||||
pistols, rifles, etc....
|
||||
=================
|
||||
*/
|
||||
void fire_bullet(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod)
|
||||
{
|
||||
fire_lead(self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_shotgun
|
||||
|
||||
Shoots shotgun pellets. Used by shotgun and super shotgun.
|
||||
=================
|
||||
*/
|
||||
void fire_shotgun(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
fire_lead(self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_blaster
|
||||
|
||||
Fires a single blaster bolt. Used by the blaster and hyper blaster.
|
||||
=================
|
||||
*/
|
||||
void blaster_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
int mod;
|
||||
|
||||
if (other == self->owner)
|
||||
return;
|
||||
|
||||
if (surf && (surf->flags & SURF_SKY))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->owner->client)
|
||||
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
if (self->spawnflags & 1)
|
||||
mod = MOD_HYPERBLASTER;
|
||||
else
|
||||
mod = MOD_BLASTER;
|
||||
T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BLASTER);
|
||||
gi.WritePosition(self->s.origin);
|
||||
if (!plane)
|
||||
gi.WriteDir(vec3_origin);
|
||||
else
|
||||
gi.WriteDir(plane->normal);
|
||||
gi.multicast(self->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
void fire_blaster(edict_t * self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper)
|
||||
{
|
||||
edict_t * bolt;
|
||||
trace_t tr;
|
||||
|
||||
VectorNormalize(dir);
|
||||
|
||||
bolt = G_Spawn();
|
||||
bolt->svflags = SVF_DEADMONSTER;
|
||||
// yes, I know it looks weird that projectiles are deadmonsters
|
||||
// what this means is that when prediction is used against the object
|
||||
// (blaster/hyperblaster shots), the player won't be solid clipped against
|
||||
// the object. Right now trying to run into a firing hyperblaster
|
||||
// is very jerky since you are predicted 'against' the shots.
|
||||
VectorCopy(start, bolt->s.origin);
|
||||
VectorCopy(start, bolt->s.old_origin);
|
||||
vectoangles(dir, bolt->s.angles);
|
||||
VectorScale(dir, speed, bolt->velocity);
|
||||
bolt->movetype = MOVETYPE_FLYMISSILE;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->solid = SOLID_BBOX;
|
||||
bolt->s.effects |= effect;
|
||||
VectorClear(bolt->mins);
|
||||
VectorClear(bolt->maxs);
|
||||
bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2");
|
||||
bolt->s.sound = gi.soundindex("misc/lasfly.wav");
|
||||
bolt->owner = self;
|
||||
bolt->touch = blaster_touch;
|
||||
bolt->nextthink = level.time + 2;
|
||||
bolt->think = G_FreeEdict;
|
||||
bolt->dmg = damage;
|
||||
bolt->classname = "bolt";
|
||||
if (hyper)
|
||||
bolt->spawnflags = 1;
|
||||
gi.linkentity(bolt);
|
||||
|
||||
if (self->client)
|
||||
check_dodge(self, bolt->s.origin, dir, speed);
|
||||
|
||||
tr = gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
|
||||
if (tr.fraction < 1.0)
|
||||
{
|
||||
VectorMA(bolt->s.origin, -10, dir, bolt->s.origin);
|
||||
bolt->touch(bolt, tr.ent, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_grenade
|
||||
=================
|
||||
*/
|
||||
static void Grenade_Explode(edict_t * ent)
|
||||
{
|
||||
vec3_t origin;
|
||||
int mod;
|
||||
|
||||
if (ent->owner->client)
|
||||
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||
|
||||
//FIXME: if we are onground then raise our Z just a bit since we are a point?
|
||||
if (ent->enemy)
|
||||
{
|
||||
float points;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
VectorAdd(ent->enemy->mins, ent->enemy->maxs, v);
|
||||
VectorMA(ent->enemy->s.origin, 0.5, v, v);
|
||||
VectorSubtract(ent->s.origin, v, v);
|
||||
points = ent->dmg - 0.5 * VectorLength(v);
|
||||
VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir);
|
||||
if (ent->spawnflags & 1)
|
||||
mod = MOD_HANDGRENADE;
|
||||
else
|
||||
mod = MOD_GRENADE;
|
||||
T_Damage(ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||
}
|
||||
|
||||
if (ent->spawnflags & 2)
|
||||
mod = MOD_HELD_GRENADE;
|
||||
else if (ent->spawnflags & 1)
|
||||
mod = MOD_HG_SPLASH;
|
||||
else
|
||||
mod = MOD_G_SPLASH;
|
||||
T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
|
||||
|
||||
VectorMA(ent->s.origin, -0.02, ent->velocity, origin);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
if (ent->waterlevel)
|
||||
{
|
||||
if (ent->groundentity)
|
||||
gi.WriteByte(TE_GRENADE_EXPLOSION_WATER);
|
||||
else
|
||||
gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ent->groundentity)
|
||||
gi.WriteByte(TE_GRENADE_EXPLOSION);
|
||||
else
|
||||
gi.WriteByte(TE_ROCKET_EXPLOSION);
|
||||
}
|
||||
gi.WritePosition(origin);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PHS);
|
||||
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
static void Grenade_Touch(edict_t * ent, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
if (other == ent->owner)
|
||||
return;
|
||||
|
||||
if (surf && (surf->flags & SURF_SKY))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!other->takedamage)
|
||||
{
|
||||
if (ent->spawnflags & 1)
|
||||
{
|
||||
if (random() > 0.5)
|
||||
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ent->enemy = other;
|
||||
Grenade_Explode(ent);
|
||||
}
|
||||
|
||||
void fire_grenade(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||
{
|
||||
edict_t * grenade;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right, up;
|
||||
|
||||
vectoangles(aimdir, dir);
|
||||
AngleVectors(dir, forward, right, up);
|
||||
|
||||
grenade = G_Spawn();
|
||||
VectorCopy(start, grenade->s.origin);
|
||||
VectorScale(aimdir, speed, grenade->velocity);
|
||||
VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||
VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||
VectorSet(grenade->avelocity, 300, 300, 300);
|
||||
grenade->movetype = MOVETYPE_BOUNCE;
|
||||
grenade->clipmask = MASK_SHOT;
|
||||
grenade->solid = SOLID_BBOX;
|
||||
grenade->s.effects |= EF_GRENADE;
|
||||
VectorClear(grenade->mins);
|
||||
VectorClear(grenade->maxs);
|
||||
grenade->s.modelindex = gi.modelindex("models/objects/grenade/tris.md2");
|
||||
grenade->owner = self;
|
||||
grenade->touch = Grenade_Touch;
|
||||
grenade->nextthink = level.time + timer;
|
||||
grenade->think = Grenade_Explode;
|
||||
grenade->dmg = damage;
|
||||
grenade->dmg_radius = damage_radius;
|
||||
grenade->classname = "grenade";
|
||||
|
||||
gi.linkentity(grenade);
|
||||
}
|
||||
|
||||
void fire_grenade2(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
|
||||
{
|
||||
edict_t * grenade;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right, up;
|
||||
|
||||
vectoangles(aimdir, dir);
|
||||
AngleVectors(dir, forward, right, up);
|
||||
|
||||
grenade = G_Spawn();
|
||||
VectorCopy(start, grenade->s.origin);
|
||||
VectorScale(aimdir, speed, grenade->velocity);
|
||||
VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||
VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||
VectorSet(grenade->avelocity, 300, 300, 300);
|
||||
grenade->movetype = MOVETYPE_BOUNCE;
|
||||
grenade->clipmask = MASK_SHOT;
|
||||
grenade->solid = SOLID_BBOX;
|
||||
grenade->s.effects |= EF_GRENADE;
|
||||
VectorClear(grenade->mins);
|
||||
VectorClear(grenade->maxs);
|
||||
grenade->s.modelindex = gi.modelindex("models/objects/grenade2/tris.md2");
|
||||
grenade->owner = self;
|
||||
grenade->touch = Grenade_Touch;
|
||||
grenade->nextthink = level.time + timer;
|
||||
grenade->think = Grenade_Explode;
|
||||
grenade->dmg = damage;
|
||||
grenade->dmg_radius = damage_radius;
|
||||
grenade->classname = "hgrenade";
|
||||
if (held)
|
||||
grenade->spawnflags = 3;
|
||||
else
|
||||
grenade->spawnflags = 1;
|
||||
grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
|
||||
|
||||
if (timer <= 0.0)
|
||||
Grenade_Explode(grenade);
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
|
||||
gi.linkentity(grenade);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_rocket
|
||||
=================
|
||||
*/
|
||||
void rocket_touch(edict_t * ent, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
vec3_t origin;
|
||||
int n;
|
||||
|
||||
if (other == ent->owner)
|
||||
return;
|
||||
|
||||
if (surf && (surf->flags & SURF_SKY))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->owner->client)
|
||||
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||
|
||||
// calculate position for the explosion entity
|
||||
VectorMA(ent->s.origin, -0.02, ent->velocity, origin);
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
T_Damage(other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't throw any debris in net games
|
||||
if (!deathmatch->value && !coop->value)
|
||||
{
|
||||
if ((surf) && !(surf->flags & (SURF_WARP | SURF_TRANS33 | SURF_TRANS66 | SURF_FLOWING)))
|
||||
{
|
||||
n = rand() % 5;
|
||||
while (n--)
|
||||
ThrowDebris(ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
if (ent->waterlevel)
|
||||
gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
|
||||
else
|
||||
gi.WriteByte(TE_ROCKET_EXPLOSION);
|
||||
gi.WritePosition(origin);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PHS);
|
||||
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
void fire_rocket(edict_t * self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
||||
{
|
||||
edict_t * rocket;
|
||||
|
||||
rocket = G_Spawn();
|
||||
VectorCopy(start, rocket->s.origin);
|
||||
VectorCopy(dir, rocket->movedir);
|
||||
vectoangles(dir, rocket->s.angles);
|
||||
VectorScale(dir, speed, rocket->velocity);
|
||||
rocket->movetype = MOVETYPE_FLYMISSILE;
|
||||
rocket->clipmask = MASK_SHOT;
|
||||
rocket->solid = SOLID_BBOX;
|
||||
rocket->s.effects |= EF_ROCKET;
|
||||
VectorClear(rocket->mins);
|
||||
VectorClear(rocket->maxs);
|
||||
rocket->s.modelindex = gi.modelindex("models/objects/rocket/tris.md2");
|
||||
rocket->owner = self;
|
||||
rocket->touch = rocket_touch;
|
||||
rocket->nextthink = level.time + 8000 / speed;
|
||||
rocket->think = G_FreeEdict;
|
||||
rocket->dmg = damage;
|
||||
rocket->radius_dmg = radius_damage;
|
||||
rocket->dmg_radius = damage_radius;
|
||||
rocket->s.sound = gi.soundindex("weapons/rockfly.wav");
|
||||
rocket->classname = "rocket";
|
||||
|
||||
if (self->client)
|
||||
check_dodge(self, rocket->s.origin, dir, speed);
|
||||
|
||||
gi.linkentity(rocket);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_rail
|
||||
=================
|
||||
*/
|
||||
void fire_rail(edict_t * self, vec3_t start, vec3_t aimdir, int damage, int kick)
|
||||
{
|
||||
vec3_t from;
|
||||
vec3_t end;
|
||||
trace_t tr;
|
||||
edict_t * ignore;
|
||||
int mask;
|
||||
qboolean water;
|
||||
|
||||
VectorMA(start, 8192, aimdir, end);
|
||||
VectorCopy(start, from);
|
||||
ignore = self;
|
||||
water = false;
|
||||
mask = MASK_SHOT | CONTENTS_SLIME | CONTENTS_LAVA;
|
||||
while (ignore)
|
||||
{
|
||||
tr = gi.trace(from, NULL, NULL, end, ignore, mask);
|
||||
|
||||
if (tr.contents & (CONTENTS_SLIME | CONTENTS_LAVA))
|
||||
{
|
||||
mask &= ~(CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
water = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
|
||||
ignore = tr.ent;
|
||||
else
|
||||
ignore = NULL;
|
||||
|
||||
if ((tr.ent != self) && (tr.ent->takedamage))
|
||||
T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);
|
||||
}
|
||||
|
||||
VectorCopy(tr.endpos, from);
|
||||
}
|
||||
|
||||
// send gun puff / flash
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_RAILTRAIL);
|
||||
gi.WritePosition(start);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS);
|
||||
// gi.multicast (start, MULTICAST_PHS);
|
||||
if (water)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_RAILTRAIL);
|
||||
gi.WritePosition(start);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.multicast(tr.endpos, MULTICAST_PHS);
|
||||
}
|
||||
|
||||
if (self->client)
|
||||
PlayerNoise(self, tr.endpos, PNOISE_IMPACT);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_bfg
|
||||
=================
|
||||
*/
|
||||
void bfg_explode(edict_t * self)
|
||||
{
|
||||
edict_t * ent;
|
||||
float points;
|
||||
vec3_t v;
|
||||
float dist;
|
||||
|
||||
if (self->s.frame == 0)
|
||||
{
|
||||
// the BFG effect
|
||||
ent = NULL;
|
||||
while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL)
|
||||
{
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
if (ent == self->owner)
|
||||
continue;
|
||||
if (!CanDamage(ent, self))
|
||||
continue;
|
||||
if (!CanDamage(ent, self->owner))
|
||||
continue;
|
||||
|
||||
VectorAdd(ent->mins, ent->maxs, v);
|
||||
VectorMA(ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract(self->s.origin, v, v);
|
||||
dist = VectorLength(v);
|
||||
points = self->radius_dmg * (1.0 - sqrt(dist / self->dmg_radius));
|
||||
if (ent == self->owner)
|
||||
points = points * 0.5;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BFG_EXPLOSION);
|
||||
gi.WritePosition(ent->s.origin);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PHS);
|
||||
T_Damage(ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT);
|
||||
}
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->s.frame++;
|
||||
if (self->s.frame == 5)
|
||||
self->think = G_FreeEdict;
|
||||
}
|
||||
|
||||
void bfg_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
if (other == self->owner)
|
||||
return;
|
||||
|
||||
if (surf && (surf->flags & SURF_SKY))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->owner->client)
|
||||
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||
|
||||
// core explosion - prevents firing it into the wall/floor
|
||||
if (other->takedamage)
|
||||
T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST);
|
||||
T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST);
|
||||
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
|
||||
self->solid = SOLID_NOT;
|
||||
self->touch = NULL;
|
||||
VectorMA(self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin);
|
||||
VectorClear(self->velocity);
|
||||
self->s.modelindex = gi.modelindex("sprites/s_bfg3.sp2");
|
||||
self->s.frame = 0;
|
||||
self->s.sound = 0;
|
||||
self->s.effects &= ~EF_ANIM_ALLFAST;
|
||||
self->think = bfg_explode;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->enemy = other;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BFG_BIGEXPLOSION);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.multicast(self->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void bfg_think(edict_t * self)
|
||||
{
|
||||
edict_t * ent;
|
||||
edict_t * ignore;
|
||||
vec3_t point;
|
||||
vec3_t dir;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
int dmg;
|
||||
trace_t tr;
|
||||
|
||||
if (deathmatch->value)
|
||||
dmg = 5;
|
||||
else
|
||||
dmg = 10;
|
||||
|
||||
ent = NULL;
|
||||
while ((ent = findradius(ent, self->s.origin, 256)) != NULL)
|
||||
{
|
||||
if (ent == self)
|
||||
continue;
|
||||
|
||||
if (ent == self->owner)
|
||||
continue;
|
||||
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))
|
||||
continue;
|
||||
|
||||
VectorMA(ent->absmin, 0.5, ent->size, point);
|
||||
|
||||
VectorSubtract(point, self->s.origin, dir);
|
||||
VectorNormalize(dir);
|
||||
|
||||
ignore = self;
|
||||
VectorCopy(self->s.origin, start);
|
||||
VectorMA(start, 2048, dir, end);
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
|
||||
|
||||
if (!tr.ent)
|
||||
break;
|
||||
|
||||
// hurt it if we can
|
||||
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
|
||||
T_Damage(tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);
|
||||
|
||||
// if we hit something that's not a monster or player we're done
|
||||
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_LASER_SPARKS);
|
||||
gi.WriteByte(4);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(tr.plane.normal);
|
||||
gi.WriteByte(self->s.skinnum);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS);
|
||||
break;
|
||||
}
|
||||
|
||||
ignore = tr.ent;
|
||||
VectorCopy(tr.endpos, start);
|
||||
}
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BFG_LASER);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.multicast(self->s.origin, MULTICAST_PHS);
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void fire_bfg(edict_t * self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
|
||||
{
|
||||
edict_t * bfg;
|
||||
|
||||
bfg = G_Spawn();
|
||||
VectorCopy(start, bfg->s.origin);
|
||||
VectorCopy(dir, bfg->movedir);
|
||||
vectoangles(dir, bfg->s.angles);
|
||||
VectorScale(dir, speed, bfg->velocity);
|
||||
bfg->movetype = MOVETYPE_FLYMISSILE;
|
||||
bfg->clipmask = MASK_SHOT;
|
||||
bfg->solid = SOLID_BBOX;
|
||||
bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
|
||||
VectorClear(bfg->mins);
|
||||
VectorClear(bfg->maxs);
|
||||
bfg->s.modelindex = gi.modelindex("sprites/s_bfg1.sp2");
|
||||
bfg->owner = self;
|
||||
bfg->touch = bfg_touch;
|
||||
bfg->nextthink = level.time + 8000 / speed;
|
||||
bfg->think = G_FreeEdict;
|
||||
bfg->radius_dmg = damage;
|
||||
bfg->dmg_radius = damage_radius;
|
||||
bfg->classname = "bfg blast";
|
||||
bfg->s.sound = gi.soundindex("weapons/bfg__l1a.wav");
|
||||
|
||||
bfg->think = bfg_think;
|
||||
bfg->nextthink = level.time + FRAMETIME;
|
||||
bfg->teammaster = bfg;
|
||||
bfg->teamchain = NULL;
|
||||
|
||||
if (self->client)
|
||||
check_dodge(self, bfg->s.origin, dir, speed);
|
||||
|
||||
gi.linkentity(bfg);
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
|
||||
// game.h -- game dll information visible to server
|
||||
|
||||
#define GAME_API_VERSION 3
|
||||
|
||||
// edict->svflags
|
||||
|
||||
#define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects
|
||||
#define SVF_DEADMONSTER 0x00000002 // treat as CONTENTS_DEADMONSTER for collision
|
||||
#define SVF_MONSTER 0x00000004 // treat as CONTENTS_MONSTER for collision
|
||||
|
||||
// edict->solid values
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SOLID_NOT, // no interaction with other objects
|
||||
SOLID_TRIGGER, // only touch when inside, after moving
|
||||
SOLID_BBOX, // touch on edge
|
||||
SOLID_BSP // bsp clip, touch on edge
|
||||
} solid_t;
|
||||
|
||||
//===============================================================
|
||||
|
||||
// link_t is only used for entity area links now
|
||||
typedef struct link_s
|
||||
{
|
||||
struct link_s *prev, *next;
|
||||
} link_t;
|
||||
|
||||
#define MAX_ENT_CLUSTERS 16
|
||||
|
||||
typedef struct edict_s edict_t;
|
||||
typedef struct gclient_s gclient_t;
|
||||
|
||||
#ifndef GAME_INCLUDE
|
||||
|
||||
struct gclient_s
|
||||
{
|
||||
player_state_t ps; // communicated by server to clients
|
||||
int ping;
|
||||
// the game dll can add anything it wants after
|
||||
// this point in the structure
|
||||
};
|
||||
|
||||
struct edict_s
|
||||
{
|
||||
entity_state_t s;
|
||||
struct gclient_s * client;
|
||||
qboolean inuse;
|
||||
int linkcount;
|
||||
|
||||
// FIXME: move these fields to a server private sv_entity_t
|
||||
link_t area; // linked to a division node or leaf
|
||||
|
||||
int num_clusters; // if -1, use headnode instead
|
||||
int clusternums[MAX_ENT_CLUSTERS];
|
||||
int headnode; // unused if num_clusters != -1
|
||||
int areanum, areanum2;
|
||||
|
||||
//================================
|
||||
|
||||
int svflags; // SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc
|
||||
vec3_t mins, maxs;
|
||||
vec3_t absmin, absmax, size;
|
||||
solid_t solid;
|
||||
int clipmask;
|
||||
edict_t * owner;
|
||||
|
||||
// the game dll can add anything it wants after
|
||||
// this point in the structure
|
||||
};
|
||||
|
||||
#endif // GAME_INCLUDE
|
||||
|
||||
//===============================================================
|
||||
|
||||
//
|
||||
// functions provided by the main engine
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
// special messages
|
||||
void (*bprintf)(int printlevel, char * fmt, ...);
|
||||
void (*dprintf)(char * fmt, ...);
|
||||
void (*cprintf)(edict_t * ent, int printlevel, char * fmt, ...);
|
||||
void (*centerprintf)(edict_t * ent, char * fmt, ...);
|
||||
void (*sound)(edict_t * ent, int channel, int soundindex, float volume, float attenuation, float timeofs);
|
||||
void (*positioned_sound)(vec3_t origin, edict_t * ent, int channel, int soundinedex, float volume, float attenuation, float timeofs);
|
||||
|
||||
// config strings hold all the index strings, the lightstyles,
|
||||
// and misc data like the sky definition and cdtrack.
|
||||
// All of the current configstrings are sent to clients when
|
||||
// they connect, and changes are sent to all connected clients.
|
||||
void (*configstring)(int num, char * string);
|
||||
|
||||
void (*error)(char * fmt, ...);
|
||||
|
||||
// the *index functions create configstrings and some internal server state
|
||||
int (*modelindex)(char * name);
|
||||
int (*soundindex)(char * name);
|
||||
int (*imageindex)(char * name);
|
||||
void (*setmodel)(edict_t * ent, char * name);
|
||||
|
||||
// collision detection
|
||||
trace_t (*trace)(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t * passent, int contentmask);
|
||||
int (*pointcontents)(vec3_t point);
|
||||
qboolean (*inPVS)(vec3_t p1, vec3_t p2);
|
||||
qboolean (*inPHS)(vec3_t p1, vec3_t p2);
|
||||
void (*SetAreaPortalState)(int portalnum, qboolean open);
|
||||
qboolean (*AreasConnected)(int area1, int area2);
|
||||
|
||||
// an entity will never be sent to a client or used for collision
|
||||
// if it is not passed to linkentity. If the size, position, or
|
||||
// solidity changes, it must be relinked.
|
||||
void (*linkentity)(edict_t * ent);
|
||||
void (*unlinkentity)(edict_t * ent); // call before removing an interactive edict
|
||||
int (*BoxEdicts)(vec3_t mins, vec3_t maxs, edict_t ** list, int maxcount, int areatype);
|
||||
void (*Pmove)(pmove_t * pmove); // player movement code common with client prediction
|
||||
|
||||
// network messaging
|
||||
void (*multicast)(vec3_t origin, multicast_t to);
|
||||
void (*unicast)(edict_t * ent, qboolean reliable);
|
||||
void (*WriteChar)(int c);
|
||||
void (*WriteByte)(int c);
|
||||
void (*WriteShort)(int c);
|
||||
void (*WriteLong)(int c);
|
||||
void (*WriteFloat)(float f);
|
||||
void (*WriteString)(char * s);
|
||||
void (*WritePosition)(vec3_t pos); // some fractional bits
|
||||
void (*WriteDir)(vec3_t pos); // single byte encoded, very coarse
|
||||
void (*WriteAngle)(float f);
|
||||
|
||||
// managed memory allocation
|
||||
void * (*TagMalloc)(int size, int tag);
|
||||
void (*TagFree)(void * block);
|
||||
void (*FreeTags)(int tag);
|
||||
|
||||
// console variable interaction
|
||||
cvar_t * (*cvar)(const char * var_name, const char * value, int flags);
|
||||
cvar_t * (*cvar_set)(const char * var_name, const char * value);
|
||||
cvar_t * (*cvar_forceset)(const char * var_name, const char * value);
|
||||
|
||||
// ClientCommand and ServerCommand parameter access
|
||||
int (*argc)(void);
|
||||
char * (*argv)(int n);
|
||||
char * (*args)(void); // concatenation of all argv >= 1
|
||||
|
||||
// add commands to the server console as if they were typed in
|
||||
// for map changing, etc
|
||||
void (*AddCommandString)(char * text);
|
||||
|
||||
void (*DebugGraph)(float value, int color);
|
||||
} game_import_t;
|
||||
|
||||
//
|
||||
// functions exported by the game subsystem
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
int apiversion;
|
||||
|
||||
// the init function will only be called when a game starts,
|
||||
// not each time a level is loaded. Persistent data for clients
|
||||
// and the server can be allocated in init
|
||||
void (*Init)(void);
|
||||
void (*Shutdown)(void);
|
||||
|
||||
// each new level entered will cause a call to SpawnEntities
|
||||
void (*SpawnEntities)(char * mapname, char * entstring, char * spawnpoint);
|
||||
|
||||
// Read/Write Game is for storing persistant cross level information
|
||||
// about the world state and the clients.
|
||||
// WriteGame is called every time a level is exited.
|
||||
// ReadGame is called on a loadgame.
|
||||
void (*WriteGame)(char * filename, qboolean autosave);
|
||||
void (*ReadGame)(char * filename);
|
||||
|
||||
// ReadLevel is called after the default map information has been
|
||||
// loaded with SpawnEntities
|
||||
void (*WriteLevel)(char * filename);
|
||||
void (*ReadLevel)(char * filename);
|
||||
|
||||
qboolean (*ClientConnect)(edict_t * ent, char * userinfo);
|
||||
void (*ClientBegin)(edict_t * ent);
|
||||
void (*ClientUserinfoChanged)(edict_t * ent, char * userinfo);
|
||||
void (*ClientDisconnect)(edict_t * ent);
|
||||
void (*ClientCommand)(edict_t * ent);
|
||||
void (*ClientThink)(edict_t * ent, usercmd_t * cmd);
|
||||
|
||||
void (*RunFrame)(void);
|
||||
|
||||
// ServerCommand will be called when an "sv <command>" command is issued on the
|
||||
// server console.
|
||||
// The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||
// of the parameters
|
||||
void (*ServerCommand)(void);
|
||||
|
||||
//
|
||||
// global variables shared between game and server
|
||||
//
|
||||
|
||||
// The edict array is allocated in the game dll so it
|
||||
// can vary in size from one game to another.
|
||||
//
|
||||
// The size will be fixed when ge->Init() is called
|
||||
struct edict_s * edicts;
|
||||
int edict_size;
|
||||
int num_edicts; // current number, <= max_edicts
|
||||
int max_edicts;
|
||||
} game_export_t;
|
||||
|
||||
game_export_t * GetGameAPI(game_import_t * import);
|
||||
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// g_actor.c
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_actor.h"
|
||||
|
||||
#define MAX_ACTOR_NAMES 8
|
||||
char * actor_names[MAX_ACTOR_NAMES] =
|
||||
{
|
||||
"Hellrot",
|
||||
"Tokay",
|
||||
"Killme",
|
||||
"Disruptor",
|
||||
"Adrianator",
|
||||
"Rambear",
|
||||
"Titus",
|
||||
"Bitterman"
|
||||
};
|
||||
|
||||
mframe_t actor_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_stand = { FRAME_stand101, FRAME_stand140, actor_frames_stand, NULL };
|
||||
|
||||
void actor_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &actor_move_stand;
|
||||
|
||||
// randomize on startup
|
||||
if (level.time < 1.0)
|
||||
self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
|
||||
}
|
||||
|
||||
mframe_t actor_frames_walk[] =
|
||||
{
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 10, NULL,
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 10, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_walk = { FRAME_walk01, FRAME_walk08, actor_frames_walk, NULL };
|
||||
|
||||
void actor_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &actor_move_walk;
|
||||
}
|
||||
|
||||
mframe_t actor_frames_run[] =
|
||||
{
|
||||
ai_run, 4, NULL,
|
||||
ai_run, 15, NULL,
|
||||
ai_run, 15, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 20, NULL,
|
||||
ai_run, 15, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 17, NULL,
|
||||
ai_run, 12, NULL,
|
||||
ai_run, -2, NULL,
|
||||
ai_run, -2, NULL,
|
||||
ai_run, -1, NULL
|
||||
};
|
||||
mmove_t actor_move_run = { FRAME_run02, FRAME_run07, actor_frames_run, NULL };
|
||||
|
||||
void actor_run(edict_t * self)
|
||||
{
|
||||
if ((level.time < self->pain_debounce_time) && (!self->enemy))
|
||||
{
|
||||
if (self->movetarget)
|
||||
actor_walk(self);
|
||||
else
|
||||
actor_stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
actor_stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.currentmove = &actor_move_run;
|
||||
}
|
||||
|
||||
mframe_t actor_frames_pain1[] =
|
||||
{
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 1, NULL
|
||||
};
|
||||
mmove_t actor_move_pain1 = { FRAME_pain101, FRAME_pain103, actor_frames_pain1, actor_run };
|
||||
|
||||
mframe_t actor_frames_pain2[] =
|
||||
{
|
||||
ai_move, -4, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_pain2 = { FRAME_pain201, FRAME_pain203, actor_frames_pain2, actor_run };
|
||||
|
||||
mframe_t actor_frames_pain3[] =
|
||||
{
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_pain3 = { FRAME_pain301, FRAME_pain303, actor_frames_pain3, actor_run };
|
||||
|
||||
mframe_t actor_frames_flipoff[] =
|
||||
{
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_flipoff = { FRAME_flip01, FRAME_flip14, actor_frames_flipoff, actor_run };
|
||||
|
||||
mframe_t actor_frames_taunt[] =
|
||||
{
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL,
|
||||
ai_turn, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_taunt = { FRAME_taunt01, FRAME_taunt17, actor_frames_taunt, actor_run };
|
||||
|
||||
char * messages[] =
|
||||
{
|
||||
"Watch it",
|
||||
"#$@*&",
|
||||
"Idiot",
|
||||
"Check your targets"
|
||||
};
|
||||
|
||||
void actor_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if ((other->client) && (random() < 0.4))
|
||||
{
|
||||
vec3_t v;
|
||||
char * name;
|
||||
|
||||
VectorSubtract(other->s.origin, self->s.origin, v);
|
||||
self->ideal_yaw = vectoyaw(v);
|
||||
if (random() < 0.5)
|
||||
self->monsterinfo.currentmove = &actor_move_flipoff;
|
||||
else
|
||||
self->monsterinfo.currentmove = &actor_move_taunt;
|
||||
name = actor_names[(self - g_edicts) % MAX_ACTOR_NAMES];
|
||||
gi.cprintf(other, PRINT_CHAT, "%s: %s!\n", name, messages[rand() % 3]);
|
||||
return;
|
||||
}
|
||||
|
||||
n = rand() % 3;
|
||||
if (n == 0)
|
||||
self->monsterinfo.currentmove = &actor_move_pain1;
|
||||
else if (n == 1)
|
||||
self->monsterinfo.currentmove = &actor_move_pain2;
|
||||
else
|
||||
self->monsterinfo.currentmove = &actor_move_pain3;
|
||||
}
|
||||
|
||||
void actorMachineGun(edict_t * self)
|
||||
{
|
||||
vec3_t start, target;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_ACTOR_MACHINEGUN_1], forward, right, start);
|
||||
if (self->enemy)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(self->enemy->absmin, target);
|
||||
target[2] += (self->enemy->size[2] / 2);
|
||||
}
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(self->s.angles, forward, NULL, NULL);
|
||||
}
|
||||
monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_ACTOR_MACHINEGUN_1);
|
||||
}
|
||||
|
||||
void actor_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t actor_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -13, NULL,
|
||||
ai_move, 14, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 1, NULL
|
||||
};
|
||||
mmove_t actor_move_death1 = { FRAME_death101, FRAME_death107, actor_frames_death1, actor_dead };
|
||||
|
||||
mframe_t actor_frames_death2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 7, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -9, NULL,
|
||||
ai_move, -13, NULL,
|
||||
ai_move, -13, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t actor_move_death2 = { FRAME_death201, FRAME_death213, actor_frames_death2, actor_dead };
|
||||
|
||||
void actor_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= -80)
|
||||
{
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_gib, 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
// gi.sound (self, CHAN_VOICE, actor.sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
n = rand() % 2;
|
||||
if (n == 0)
|
||||
self->monsterinfo.currentmove = &actor_move_death1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &actor_move_death2;
|
||||
}
|
||||
|
||||
void actor_fire(edict_t * self)
|
||||
{
|
||||
actorMachineGun(self);
|
||||
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
mframe_t actor_frames_attack[] =
|
||||
{
|
||||
ai_charge, -2, actor_fire,
|
||||
ai_charge, -2, NULL,
|
||||
ai_charge, 3, NULL,
|
||||
ai_charge, 2, NULL
|
||||
};
|
||||
mmove_t actor_move_attack = { FRAME_attak01, FRAME_attak04, actor_frames_attack, actor_run };
|
||||
|
||||
void actor_attack(edict_t * self)
|
||||
{
|
||||
int n;
|
||||
|
||||
self->monsterinfo.currentmove = &actor_move_attack;
|
||||
n = (rand() & 15) + 3 + 7;
|
||||
self->monsterinfo.pausetime = level.time + n * FRAMETIME;
|
||||
}
|
||||
|
||||
void actor_use(edict_t * self, edict_t * other, edict_t * activator)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
self->goalentity = self->movetarget = G_PickTarget(self->target);
|
||||
if ((!self->movetarget) || (strcmp(self->movetarget->classname, "target_actor") != 0))
|
||||
{
|
||||
gi.dprintf("%s has bad target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||
self->target = NULL;
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand(self);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorSubtract(self->goalentity->s.origin, self->s.origin, v);
|
||||
self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
|
||||
self->monsterinfo.walk(self);
|
||||
self->target = NULL;
|
||||
}
|
||||
|
||||
/*QUAKED misc_actor (1 .5 0) (-16 -16 -24) (16 16 32)
|
||||
*/
|
||||
|
||||
void SP_misc_actor(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->targetname)
|
||||
{
|
||||
gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("players/male/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
if (!self->health)
|
||||
self->health = 100;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = actor_pain;
|
||||
self->die = actor_die;
|
||||
|
||||
self->monsterinfo.stand = actor_stand;
|
||||
self->monsterinfo.walk = actor_walk;
|
||||
self->monsterinfo.run = actor_run;
|
||||
self->monsterinfo.attack = actor_attack;
|
||||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = NULL;
|
||||
|
||||
self->monsterinfo.aiflags |= AI_GOOD_GUY;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &actor_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
|
||||
// actors always start in a dormant state, they *must* be used to get going
|
||||
self->use = actor_use;
|
||||
}
|
||||
|
||||
/*QUAKED target_actor (.5 .3 0) (-8 -8 -8) (8 8 8) JUMP SHOOT ATTACK x HOLD BRUTAL
|
||||
JUMP jump in set direction upon reaching this target
|
||||
SHOOT take a single shot at the pathtarget
|
||||
ATTACK attack pathtarget until it or actor is dead
|
||||
|
||||
"target" next target_actor
|
||||
"pathtarget" target of any action to be taken at this point
|
||||
"wait" amount of time actor should pause at this point
|
||||
"message" actor will "say" this to the player
|
||||
|
||||
for JUMP only:
|
||||
"speed" speed thrown forward (default 200)
|
||||
"height" speed thrown upwards (default 200)
|
||||
*/
|
||||
|
||||
void target_actor_touch(edict_t * self, edict_t * other, cplane_t * plane, csurface_t * surf)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
if (other->movetarget != self)
|
||||
return;
|
||||
|
||||
if (other->enemy)
|
||||
return;
|
||||
|
||||
other->goalentity = other->movetarget = NULL;
|
||||
|
||||
if (self->message)
|
||||
{
|
||||
int n;
|
||||
edict_t * ent;
|
||||
|
||||
for (n = 1; n <= game.maxclients; n++)
|
||||
{
|
||||
ent = &g_edicts[n];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
gi.cprintf(ent, PRINT_CHAT, "%s: %s\n", actor_names[(other - g_edicts) % MAX_ACTOR_NAMES], self->message);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags & 1) //jump
|
||||
{
|
||||
other->velocity[0] = self->movedir[0] * self->speed;
|
||||
other->velocity[1] = self->movedir[1] * self->speed;
|
||||
|
||||
if (other->groundentity)
|
||||
{
|
||||
other->groundentity = NULL;
|
||||
other->velocity[2] = self->movedir[2];
|
||||
gi.sound(other, CHAN_VOICE, gi.soundindex("player/male/jump1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2) //shoot
|
||||
{
|
||||
}
|
||||
else if (self->spawnflags & 4) //attack
|
||||
{
|
||||
other->enemy = G_PickTarget(self->pathtarget);
|
||||
if (other->enemy)
|
||||
{
|
||||
other->goalentity = other->enemy;
|
||||
if (self->spawnflags & 32)
|
||||
other->monsterinfo.aiflags |= AI_BRUTAL;
|
||||
if (self->spawnflags & 16)
|
||||
{
|
||||
other->monsterinfo.aiflags |= AI_STAND_GROUND;
|
||||
actor_stand(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
actor_run(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(self->spawnflags & 6) && (self->pathtarget))
|
||||
{
|
||||
char * savetarget;
|
||||
|
||||
savetarget = self->target;
|
||||
self->target = self->pathtarget;
|
||||
G_UseTargets(self, other);
|
||||
self->target = savetarget;
|
||||
}
|
||||
|
||||
other->movetarget = G_PickTarget(self->target);
|
||||
|
||||
if (!other->goalentity)
|
||||
other->goalentity = other->movetarget;
|
||||
|
||||
if (!other->movetarget && !other->enemy)
|
||||
{
|
||||
other->monsterinfo.pausetime = level.time + 100000000;
|
||||
other->monsterinfo.stand(other);
|
||||
}
|
||||
else if (other->movetarget == other->goalentity)
|
||||
{
|
||||
VectorSubtract(other->movetarget->s.origin, other->s.origin, v);
|
||||
other->ideal_yaw = vectoyaw(v);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_actor(edict_t * self)
|
||||
{
|
||||
if (!self->targetname)
|
||||
gi.dprintf("%s with no targetname at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->touch = target_actor_touch;
|
||||
VectorSet(self->mins, -8, -8, -8);
|
||||
VectorSet(self->maxs, 8, 8, 8);
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
if (self->spawnflags & 1)
|
||||
{
|
||||
if (!self->speed)
|
||||
self->speed = 200;
|
||||
if (!st.height)
|
||||
st.height = 200;
|
||||
if (self->s.angles[YAW] == 0)
|
||||
self->s.angles[YAW] = 360;
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
self->movedir[2] = st.height;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/player_y
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_attak01 0
|
||||
#define FRAME_attak02 1
|
||||
#define FRAME_attak03 2
|
||||
#define FRAME_attak04 3
|
||||
#define FRAME_death101 4
|
||||
#define FRAME_death102 5
|
||||
#define FRAME_death103 6
|
||||
#define FRAME_death104 7
|
||||
#define FRAME_death105 8
|
||||
#define FRAME_death106 9
|
||||
#define FRAME_death107 10
|
||||
#define FRAME_death201 11
|
||||
#define FRAME_death202 12
|
||||
#define FRAME_death203 13
|
||||
#define FRAME_death204 14
|
||||
#define FRAME_death205 15
|
||||
#define FRAME_death206 16
|
||||
#define FRAME_death207 17
|
||||
#define FRAME_death208 18
|
||||
#define FRAME_death209 19
|
||||
#define FRAME_death210 20
|
||||
#define FRAME_death211 21
|
||||
#define FRAME_death212 22
|
||||
#define FRAME_death213 23
|
||||
#define FRAME_death301 24
|
||||
#define FRAME_death302 25
|
||||
#define FRAME_death303 26
|
||||
#define FRAME_death304 27
|
||||
#define FRAME_death305 28
|
||||
#define FRAME_death306 29
|
||||
#define FRAME_death307 30
|
||||
#define FRAME_death308 31
|
||||
#define FRAME_death309 32
|
||||
#define FRAME_death310 33
|
||||
#define FRAME_death311 34
|
||||
#define FRAME_death312 35
|
||||
#define FRAME_death313 36
|
||||
#define FRAME_death314 37
|
||||
#define FRAME_death315 38
|
||||
#define FRAME_flip01 39
|
||||
#define FRAME_flip02 40
|
||||
#define FRAME_flip03 41
|
||||
#define FRAME_flip04 42
|
||||
#define FRAME_flip05 43
|
||||
#define FRAME_flip06 44
|
||||
#define FRAME_flip07 45
|
||||
#define FRAME_flip08 46
|
||||
#define FRAME_flip09 47
|
||||
#define FRAME_flip10 48
|
||||
#define FRAME_flip11 49
|
||||
#define FRAME_flip12 50
|
||||
#define FRAME_flip13 51
|
||||
#define FRAME_flip14 52
|
||||
#define FRAME_grenad01 53
|
||||
#define FRAME_grenad02 54
|
||||
#define FRAME_grenad03 55
|
||||
#define FRAME_grenad04 56
|
||||
#define FRAME_grenad05 57
|
||||
#define FRAME_grenad06 58
|
||||
#define FRAME_grenad07 59
|
||||
#define FRAME_grenad08 60
|
||||
#define FRAME_grenad09 61
|
||||
#define FRAME_grenad10 62
|
||||
#define FRAME_grenad11 63
|
||||
#define FRAME_grenad12 64
|
||||
#define FRAME_grenad13 65
|
||||
#define FRAME_grenad14 66
|
||||
#define FRAME_grenad15 67
|
||||
#define FRAME_jump01 68
|
||||
#define FRAME_jump02 69
|
||||
#define FRAME_jump03 70
|
||||
#define FRAME_jump04 71
|
||||
#define FRAME_jump05 72
|
||||
#define FRAME_jump06 73
|
||||
#define FRAME_pain101 74
|
||||
#define FRAME_pain102 75
|
||||
#define FRAME_pain103 76
|
||||
#define FRAME_pain201 77
|
||||
#define FRAME_pain202 78
|
||||
#define FRAME_pain203 79
|
||||
#define FRAME_pain301 80
|
||||
#define FRAME_pain302 81
|
||||
#define FRAME_pain303 82
|
||||
#define FRAME_push01 83
|
||||
#define FRAME_push02 84
|
||||
#define FRAME_push03 85
|
||||
#define FRAME_push04 86
|
||||
#define FRAME_push05 87
|
||||
#define FRAME_push06 88
|
||||
#define FRAME_push07 89
|
||||
#define FRAME_push08 90
|
||||
#define FRAME_push09 91
|
||||
#define FRAME_run01 92
|
||||
#define FRAME_run02 93
|
||||
#define FRAME_run03 94
|
||||
#define FRAME_run04 95
|
||||
#define FRAME_run05 96
|
||||
#define FRAME_run06 97
|
||||
#define FRAME_run07 98
|
||||
#define FRAME_run08 99
|
||||
#define FRAME_run09 100
|
||||
#define FRAME_run10 101
|
||||
#define FRAME_run11 102
|
||||
#define FRAME_run12 103
|
||||
#define FRAME_runs01 104
|
||||
#define FRAME_runs02 105
|
||||
#define FRAME_runs03 106
|
||||
#define FRAME_runs04 107
|
||||
#define FRAME_runs05 108
|
||||
#define FRAME_runs06 109
|
||||
#define FRAME_runs07 110
|
||||
#define FRAME_runs08 111
|
||||
#define FRAME_runs09 112
|
||||
#define FRAME_runs10 113
|
||||
#define FRAME_runs11 114
|
||||
#define FRAME_runs12 115
|
||||
#define FRAME_salute01 116
|
||||
#define FRAME_salute02 117
|
||||
#define FRAME_salute03 118
|
||||
#define FRAME_salute04 119
|
||||
#define FRAME_salute05 120
|
||||
#define FRAME_salute06 121
|
||||
#define FRAME_salute07 122
|
||||
#define FRAME_salute08 123
|
||||
#define FRAME_salute09 124
|
||||
#define FRAME_salute10 125
|
||||
#define FRAME_salute11 126
|
||||
#define FRAME_salute12 127
|
||||
#define FRAME_stand101 128
|
||||
#define FRAME_stand102 129
|
||||
#define FRAME_stand103 130
|
||||
#define FRAME_stand104 131
|
||||
#define FRAME_stand105 132
|
||||
#define FRAME_stand106 133
|
||||
#define FRAME_stand107 134
|
||||
#define FRAME_stand108 135
|
||||
#define FRAME_stand109 136
|
||||
#define FRAME_stand110 137
|
||||
#define FRAME_stand111 138
|
||||
#define FRAME_stand112 139
|
||||
#define FRAME_stand113 140
|
||||
#define FRAME_stand114 141
|
||||
#define FRAME_stand115 142
|
||||
#define FRAME_stand116 143
|
||||
#define FRAME_stand117 144
|
||||
#define FRAME_stand118 145
|
||||
#define FRAME_stand119 146
|
||||
#define FRAME_stand120 147
|
||||
#define FRAME_stand121 148
|
||||
#define FRAME_stand122 149
|
||||
#define FRAME_stand123 150
|
||||
#define FRAME_stand124 151
|
||||
#define FRAME_stand125 152
|
||||
#define FRAME_stand126 153
|
||||
#define FRAME_stand127 154
|
||||
#define FRAME_stand128 155
|
||||
#define FRAME_stand129 156
|
||||
#define FRAME_stand130 157
|
||||
#define FRAME_stand131 158
|
||||
#define FRAME_stand132 159
|
||||
#define FRAME_stand133 160
|
||||
#define FRAME_stand134 161
|
||||
#define FRAME_stand135 162
|
||||
#define FRAME_stand136 163
|
||||
#define FRAME_stand137 164
|
||||
#define FRAME_stand138 165
|
||||
#define FRAME_stand139 166
|
||||
#define FRAME_stand140 167
|
||||
#define FRAME_stand201 168
|
||||
#define FRAME_stand202 169
|
||||
#define FRAME_stand203 170
|
||||
#define FRAME_stand204 171
|
||||
#define FRAME_stand205 172
|
||||
#define FRAME_stand206 173
|
||||
#define FRAME_stand207 174
|
||||
#define FRAME_stand208 175
|
||||
#define FRAME_stand209 176
|
||||
#define FRAME_stand210 177
|
||||
#define FRAME_stand211 178
|
||||
#define FRAME_stand212 179
|
||||
#define FRAME_stand213 180
|
||||
#define FRAME_stand214 181
|
||||
#define FRAME_stand215 182
|
||||
#define FRAME_stand216 183
|
||||
#define FRAME_stand217 184
|
||||
#define FRAME_stand218 185
|
||||
#define FRAME_stand219 186
|
||||
#define FRAME_stand220 187
|
||||
#define FRAME_stand221 188
|
||||
#define FRAME_stand222 189
|
||||
#define FRAME_stand223 190
|
||||
#define FRAME_swim01 191
|
||||
#define FRAME_swim02 192
|
||||
#define FRAME_swim03 193
|
||||
#define FRAME_swim04 194
|
||||
#define FRAME_swim05 195
|
||||
#define FRAME_swim06 196
|
||||
#define FRAME_swim07 197
|
||||
#define FRAME_swim08 198
|
||||
#define FRAME_swim09 199
|
||||
#define FRAME_swim10 200
|
||||
#define FRAME_swim11 201
|
||||
#define FRAME_swim12 202
|
||||
#define FRAME_sw_atk01 203
|
||||
#define FRAME_sw_atk02 204
|
||||
#define FRAME_sw_atk03 205
|
||||
#define FRAME_sw_atk04 206
|
||||
#define FRAME_sw_atk05 207
|
||||
#define FRAME_sw_atk06 208
|
||||
#define FRAME_sw_pan01 209
|
||||
#define FRAME_sw_pan02 210
|
||||
#define FRAME_sw_pan03 211
|
||||
#define FRAME_sw_pan04 212
|
||||
#define FRAME_sw_pan05 213
|
||||
#define FRAME_sw_std01 214
|
||||
#define FRAME_sw_std02 215
|
||||
#define FRAME_sw_std03 216
|
||||
#define FRAME_sw_std04 217
|
||||
#define FRAME_sw_std05 218
|
||||
#define FRAME_sw_std06 219
|
||||
#define FRAME_sw_std07 220
|
||||
#define FRAME_sw_std08 221
|
||||
#define FRAME_sw_std09 222
|
||||
#define FRAME_sw_std10 223
|
||||
#define FRAME_sw_std11 224
|
||||
#define FRAME_sw_std12 225
|
||||
#define FRAME_sw_std13 226
|
||||
#define FRAME_sw_std14 227
|
||||
#define FRAME_sw_std15 228
|
||||
#define FRAME_sw_std16 229
|
||||
#define FRAME_sw_std17 230
|
||||
#define FRAME_sw_std18 231
|
||||
#define FRAME_sw_std19 232
|
||||
#define FRAME_sw_std20 233
|
||||
#define FRAME_taunt01 234
|
||||
#define FRAME_taunt02 235
|
||||
#define FRAME_taunt03 236
|
||||
#define FRAME_taunt04 237
|
||||
#define FRAME_taunt05 238
|
||||
#define FRAME_taunt06 239
|
||||
#define FRAME_taunt07 240
|
||||
#define FRAME_taunt08 241
|
||||
#define FRAME_taunt09 242
|
||||
#define FRAME_taunt10 243
|
||||
#define FRAME_taunt11 244
|
||||
#define FRAME_taunt12 245
|
||||
#define FRAME_taunt13 246
|
||||
#define FRAME_taunt14 247
|
||||
#define FRAME_taunt15 248
|
||||
#define FRAME_taunt16 249
|
||||
#define FRAME_taunt17 250
|
||||
#define FRAME_walk01 251
|
||||
#define FRAME_walk02 252
|
||||
#define FRAME_walk03 253
|
||||
#define FRAME_walk04 254
|
||||
#define FRAME_walk05 255
|
||||
#define FRAME_walk06 256
|
||||
#define FRAME_walk07 257
|
||||
#define FRAME_walk08 258
|
||||
#define FRAME_walk09 259
|
||||
#define FRAME_walk10 260
|
||||
#define FRAME_walk11 261
|
||||
#define FRAME_wave01 262
|
||||
#define FRAME_wave02 263
|
||||
#define FRAME_wave03 264
|
||||
#define FRAME_wave04 265
|
||||
#define FRAME_wave05 266
|
||||
#define FRAME_wave06 267
|
||||
#define FRAME_wave07 268
|
||||
#define FRAME_wave08 269
|
||||
#define FRAME_wave09 270
|
||||
#define FRAME_wave10 271
|
||||
#define FRAME_wave11 272
|
||||
#define FRAME_wave12 273
|
||||
#define FRAME_wave13 274
|
||||
#define FRAME_wave14 275
|
||||
#define FRAME_wave15 276
|
||||
#define FRAME_wave16 277
|
||||
#define FRAME_wave17 278
|
||||
#define FRAME_wave18 279
|
||||
#define FRAME_wave19 280
|
||||
#define FRAME_wave20 281
|
||||
#define FRAME_wave21 282
|
||||
#define FRAME_bl_atk01 283
|
||||
#define FRAME_bl_atk02 284
|
||||
#define FRAME_bl_atk03 285
|
||||
#define FRAME_bl_atk04 286
|
||||
#define FRAME_bl_atk05 287
|
||||
#define FRAME_bl_atk06 288
|
||||
#define FRAME_bl_flp01 289
|
||||
#define FRAME_bl_flp02 290
|
||||
#define FRAME_bl_flp13 291
|
||||
#define FRAME_bl_flp14 292
|
||||
#define FRAME_bl_flp15 293
|
||||
#define FRAME_bl_jmp01 294
|
||||
#define FRAME_bl_jmp02 295
|
||||
#define FRAME_bl_jmp03 296
|
||||
#define FRAME_bl_jmp04 297
|
||||
#define FRAME_bl_jmp05 298
|
||||
#define FRAME_bl_jmp06 299
|
||||
#define FRAME_bl_pn101 300
|
||||
#define FRAME_bl_pn102 301
|
||||
#define FRAME_bl_pn103 302
|
||||
#define FRAME_bl_pn201 303
|
||||
#define FRAME_bl_pn202 304
|
||||
#define FRAME_bl_pn203 305
|
||||
#define FRAME_bl_pn301 306
|
||||
#define FRAME_bl_pn302 307
|
||||
#define FRAME_bl_pn303 308
|
||||
#define FRAME_bl_psh08 309
|
||||
#define FRAME_bl_psh09 310
|
||||
#define FRAME_bl_run01 311
|
||||
#define FRAME_bl_run02 312
|
||||
#define FRAME_bl_run03 313
|
||||
#define FRAME_bl_run04 314
|
||||
#define FRAME_bl_run05 315
|
||||
#define FRAME_bl_run06 316
|
||||
#define FRAME_bl_run07 317
|
||||
#define FRAME_bl_run08 318
|
||||
#define FRAME_bl_run09 319
|
||||
#define FRAME_bl_run10 320
|
||||
#define FRAME_bl_run11 321
|
||||
#define FRAME_bl_run12 322
|
||||
#define FRAME_bl_rns03 323
|
||||
#define FRAME_bl_rns04 324
|
||||
#define FRAME_bl_rns05 325
|
||||
#define FRAME_bl_rns06 326
|
||||
#define FRAME_bl_rns07 327
|
||||
#define FRAME_bl_rns08 328
|
||||
#define FRAME_bl_rns09 329
|
||||
#define FRAME_bl_sal10 330
|
||||
#define FRAME_bl_sal11 331
|
||||
#define FRAME_bl_sal12 332
|
||||
#define FRAME_bl_std01 333
|
||||
#define FRAME_bl_std02 334
|
||||
#define FRAME_bl_std03 335
|
||||
#define FRAME_bl_std04 336
|
||||
#define FRAME_bl_std05 337
|
||||
#define FRAME_bl_std06 338
|
||||
#define FRAME_bl_std07 339
|
||||
#define FRAME_bl_std08 340
|
||||
#define FRAME_bl_std09 341
|
||||
#define FRAME_bl_std10 342
|
||||
#define FRAME_bl_std11 343
|
||||
#define FRAME_bl_std12 344
|
||||
#define FRAME_bl_std13 345
|
||||
#define FRAME_bl_std14 346
|
||||
#define FRAME_bl_std15 347
|
||||
#define FRAME_bl_std16 348
|
||||
#define FRAME_bl_std17 349
|
||||
#define FRAME_bl_std18 350
|
||||
#define FRAME_bl_std19 351
|
||||
#define FRAME_bl_std20 352
|
||||
#define FRAME_bl_std21 353
|
||||
#define FRAME_bl_std22 354
|
||||
#define FRAME_bl_std23 355
|
||||
#define FRAME_bl_std24 356
|
||||
#define FRAME_bl_std25 357
|
||||
#define FRAME_bl_std26 358
|
||||
#define FRAME_bl_std27 359
|
||||
#define FRAME_bl_std28 360
|
||||
#define FRAME_bl_std29 361
|
||||
#define FRAME_bl_std30 362
|
||||
#define FRAME_bl_std31 363
|
||||
#define FRAME_bl_std32 364
|
||||
#define FRAME_bl_std33 365
|
||||
#define FRAME_bl_std34 366
|
||||
#define FRAME_bl_std35 367
|
||||
#define FRAME_bl_std36 368
|
||||
#define FRAME_bl_std37 369
|
||||
#define FRAME_bl_std38 370
|
||||
#define FRAME_bl_std39 371
|
||||
#define FRAME_bl_std40 372
|
||||
#define FRAME_bl_swm01 373
|
||||
#define FRAME_bl_swm02 374
|
||||
#define FRAME_bl_swm03 375
|
||||
#define FRAME_bl_swm04 376
|
||||
#define FRAME_bl_swm05 377
|
||||
#define FRAME_bl_swm06 378
|
||||
#define FRAME_bl_swm07 379
|
||||
#define FRAME_bl_swm08 380
|
||||
#define FRAME_bl_swm09 381
|
||||
#define FRAME_bl_swm10 382
|
||||
#define FRAME_bl_swm11 383
|
||||
#define FRAME_bl_swm12 384
|
||||
#define FRAME_bl_swk01 385
|
||||
#define FRAME_bl_swk02 386
|
||||
#define FRAME_bl_swk03 387
|
||||
#define FRAME_bl_swk04 388
|
||||
#define FRAME_bl_swk05 389
|
||||
#define FRAME_bl_swk06 390
|
||||
#define FRAME_bl_swp01 391
|
||||
#define FRAME_bl_swp02 392
|
||||
#define FRAME_bl_swp03 393
|
||||
#define FRAME_bl_swp04 394
|
||||
#define FRAME_bl_swp05 395
|
||||
#define FRAME_bl_sws01 396
|
||||
#define FRAME_bl_sws02 397
|
||||
#define FRAME_bl_sws03 398
|
||||
#define FRAME_bl_sws04 399
|
||||
#define FRAME_bl_sws05 400
|
||||
#define FRAME_bl_sws06 401
|
||||
#define FRAME_bl_sws07 402
|
||||
#define FRAME_bl_sws08 403
|
||||
#define FRAME_bl_sws09 404
|
||||
#define FRAME_bl_sws10 405
|
||||
#define FRAME_bl_sws11 406
|
||||
#define FRAME_bl_sws12 407
|
||||
#define FRAME_bl_sws13 408
|
||||
#define FRAME_bl_sws14 409
|
||||
#define FRAME_bl_tau14 410
|
||||
#define FRAME_bl_tau15 411
|
||||
#define FRAME_bl_tau16 412
|
||||
#define FRAME_bl_tau17 413
|
||||
#define FRAME_bl_wlk01 414
|
||||
#define FRAME_bl_wlk02 415
|
||||
#define FRAME_bl_wlk03 416
|
||||
#define FRAME_bl_wlk04 417
|
||||
#define FRAME_bl_wlk05 418
|
||||
#define FRAME_bl_wlk06 419
|
||||
#define FRAME_bl_wlk07 420
|
||||
#define FRAME_bl_wlk08 421
|
||||
#define FRAME_bl_wlk09 422
|
||||
#define FRAME_bl_wlk10 423
|
||||
#define FRAME_bl_wlk11 424
|
||||
#define FRAME_bl_wav19 425
|
||||
#define FRAME_bl_wav20 426
|
||||
#define FRAME_bl_wav21 427
|
||||
#define FRAME_cr_atk01 428
|
||||
#define FRAME_cr_atk02 429
|
||||
#define FRAME_cr_atk03 430
|
||||
#define FRAME_cr_atk04 431
|
||||
#define FRAME_cr_atk05 432
|
||||
#define FRAME_cr_atk06 433
|
||||
#define FRAME_cr_atk07 434
|
||||
#define FRAME_cr_atk08 435
|
||||
#define FRAME_cr_pan01 436
|
||||
#define FRAME_cr_pan02 437
|
||||
#define FRAME_cr_pan03 438
|
||||
#define FRAME_cr_pan04 439
|
||||
#define FRAME_cr_std01 440
|
||||
#define FRAME_cr_std02 441
|
||||
#define FRAME_cr_std03 442
|
||||
#define FRAME_cr_std04 443
|
||||
#define FRAME_cr_std05 444
|
||||
#define FRAME_cr_std06 445
|
||||
#define FRAME_cr_std07 446
|
||||
#define FRAME_cr_std08 447
|
||||
#define FRAME_cr_wlk01 448
|
||||
#define FRAME_cr_wlk02 449
|
||||
#define FRAME_cr_wlk03 450
|
||||
#define FRAME_cr_wlk04 451
|
||||
#define FRAME_cr_wlk05 452
|
||||
#define FRAME_cr_wlk06 453
|
||||
#define FRAME_cr_wlk07 454
|
||||
#define FRAME_crbl_a01 455
|
||||
#define FRAME_crbl_a02 456
|
||||
#define FRAME_crbl_a03 457
|
||||
#define FRAME_crbl_a04 458
|
||||
#define FRAME_crbl_a05 459
|
||||
#define FRAME_crbl_a06 460
|
||||
#define FRAME_crbl_a07 461
|
||||
#define FRAME_crbl_p01 462
|
||||
#define FRAME_crbl_p02 463
|
||||
#define FRAME_crbl_p03 464
|
||||
#define FRAME_crbl_p04 465
|
||||
#define FRAME_crbl_s01 466
|
||||
#define FRAME_crbl_s02 467
|
||||
#define FRAME_crbl_s03 468
|
||||
#define FRAME_crbl_s04 469
|
||||
#define FRAME_crbl_s05 470
|
||||
#define FRAME_crbl_s06 471
|
||||
#define FRAME_crbl_s07 472
|
||||
#define FRAME_crbl_s08 473
|
||||
#define FRAME_crbl_w01 474
|
||||
#define FRAME_crbl_w02 475
|
||||
#define FRAME_crbl_w03 476
|
||||
#define FRAME_crbl_w04 477
|
||||
#define FRAME_crbl_w05 478
|
||||
#define FRAME_crbl_w06 479
|
||||
#define FRAME_crbl_w07 480
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
BERSERK
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_berserk.h"
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_die;
|
||||
static int sound_idle;
|
||||
static int sound_punch;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
|
||||
void berserk_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void berserk_search(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void berserk_fidget(edict_t * self);
|
||||
mframe_t berserk_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, berserk_fidget,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_stand = { FRAME_stand1, FRAME_stand5, berserk_frames_stand, NULL };
|
||||
|
||||
void berserk_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &berserk_move_stand;
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_stand_fidget[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_stand_fidget = { FRAME_standb1, FRAME_standb20, berserk_frames_stand_fidget, berserk_stand };
|
||||
|
||||
void berserk_fidget(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
if (random() > 0.15)
|
||||
return;
|
||||
|
||||
self->monsterinfo.currentmove = &berserk_move_stand_fidget;
|
||||
gi.sound(self, CHAN_WEAPON, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_walk[] =
|
||||
{
|
||||
ai_walk, 9.1, NULL,
|
||||
ai_walk, 6.3, NULL,
|
||||
ai_walk, 4.9, NULL,
|
||||
ai_walk, 6.7, NULL,
|
||||
ai_walk, 6.0, NULL,
|
||||
ai_walk, 8.2, NULL,
|
||||
ai_walk, 7.2, NULL,
|
||||
ai_walk, 6.1, NULL,
|
||||
ai_walk, 4.9, NULL,
|
||||
ai_walk, 4.7, NULL,
|
||||
ai_walk, 4.7, NULL,
|
||||
ai_walk, 4.8, NULL
|
||||
};
|
||||
mmove_t berserk_move_walk = { FRAME_walkc1, FRAME_walkc11, berserk_frames_walk, NULL };
|
||||
|
||||
void berserk_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &berserk_move_walk;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*****************************
|
||||
SKIPPED THIS FOR NOW!
|
||||
*****************************
|
||||
|
||||
Running -> Arm raised in air
|
||||
|
||||
void() berserk_runb1 =[ $r_att1 , berserk_runb2 ] {ai_run(21);};
|
||||
void() berserk_runb2 =[ $r_att2 , berserk_runb3 ] {ai_run(11);};
|
||||
void() berserk_runb3 =[ $r_att3 , berserk_runb4 ] {ai_run(21);};
|
||||
void() berserk_runb4 =[ $r_att4 , berserk_runb5 ] {ai_run(25);};
|
||||
void() berserk_runb5 =[ $r_att5 , berserk_runb6 ] {ai_run(18);};
|
||||
void() berserk_runb6 =[ $r_att6 , berserk_runb7 ] {ai_run(19);};
|
||||
// running with arm in air : start loop
|
||||
void() berserk_runb7 =[ $r_att7 , berserk_runb8 ] {ai_run(21);};
|
||||
void() berserk_runb8 =[ $r_att8 , berserk_runb9 ] {ai_run(11);};
|
||||
void() berserk_runb9 =[ $r_att9 , berserk_runb10 ] {ai_run(21);};
|
||||
void() berserk_runb10 =[ $r_att10 , berserk_runb11 ] {ai_run(25);};
|
||||
void() berserk_runb11 =[ $r_att11 , berserk_runb12 ] {ai_run(18);};
|
||||
void() berserk_runb12 =[ $r_att12 , berserk_runb7 ] {ai_run(19);};
|
||||
// running with arm in air : end loop
|
||||
*/
|
||||
|
||||
mframe_t berserk_frames_run1[] =
|
||||
{
|
||||
ai_run, 21, NULL,
|
||||
ai_run, 11, NULL,
|
||||
ai_run, 21, NULL,
|
||||
ai_run, 25, NULL,
|
||||
ai_run, 18, NULL,
|
||||
ai_run, 19, NULL
|
||||
};
|
||||
mmove_t berserk_move_run1 = { FRAME_run1, FRAME_run6, berserk_frames_run1, NULL };
|
||||
|
||||
void berserk_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &berserk_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &berserk_move_run1;
|
||||
}
|
||||
|
||||
void berserk_attack_spike(edict_t * self)
|
||||
{
|
||||
static vec3_t aim = { MELEE_DISTANCE, 0, -24 };
|
||||
fire_hit(self, aim, (15 + (rand() % 6)), 400); // Faster attack -- upwards and backwards
|
||||
}
|
||||
|
||||
void berserk_swing(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_spike[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, berserk_swing,
|
||||
ai_charge, 0, berserk_attack_spike,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_attack_spike = { FRAME_att_c1, FRAME_att_c8, berserk_frames_attack_spike, berserk_run };
|
||||
|
||||
void berserk_attack_club(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->mins[0], -4);
|
||||
fire_hit(self, aim, (5 + (rand() % 6)), 400); // Slower attack
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_club[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, berserk_swing,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, berserk_attack_club,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_attack_club = { FRAME_att_c9, FRAME_att_c20, berserk_frames_attack_club, berserk_run };
|
||||
|
||||
void berserk_strike(edict_t * self)
|
||||
{
|
||||
//FIXME play impact sound
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_attack_strike[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, berserk_swing,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, berserk_strike,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 9.7, NULL,
|
||||
ai_move, 13.6, NULL
|
||||
};
|
||||
|
||||
mmove_t berserk_move_attack_strike = { FRAME_att_c21, FRAME_att_c34, berserk_frames_attack_strike, berserk_run };
|
||||
|
||||
void berserk_melee(edict_t * self)
|
||||
{
|
||||
if ((rand() % 2) == 0)
|
||||
self->monsterinfo.currentmove = &berserk_move_attack_spike;
|
||||
else
|
||||
self->monsterinfo.currentmove = &berserk_move_attack_club;
|
||||
}
|
||||
|
||||
/*
|
||||
void() berserk_atke1 =[ $r_attb1, berserk_atke2 ] {ai_run(9);};
|
||||
void() berserk_atke2 =[ $r_attb2, berserk_atke3 ] {ai_run(6);};
|
||||
void() berserk_atke3 =[ $r_attb3, berserk_atke4 ] {ai_run(18.4);};
|
||||
void() berserk_atke4 =[ $r_attb4, berserk_atke5 ] {ai_run(25);};
|
||||
void() berserk_atke5 =[ $r_attb5, berserk_atke6 ] {ai_run(14);};
|
||||
void() berserk_atke6 =[ $r_attb6, berserk_atke7 ] {ai_run(20);};
|
||||
void() berserk_atke7 =[ $r_attb7, berserk_atke8 ] {ai_run(8.5);};
|
||||
void() berserk_atke8 =[ $r_attb8, berserk_atke9 ] {ai_run(3);};
|
||||
void() berserk_atke9 =[ $r_attb9, berserk_atke10 ] {ai_run(17.5);};
|
||||
void() berserk_atke10 =[ $r_attb10, berserk_atke11 ] {ai_run(17);};
|
||||
void() berserk_atke11 =[ $r_attb11, berserk_atke12 ] {ai_run(9);};
|
||||
void() berserk_atke12 =[ $r_attb12, berserk_atke13 ] {ai_run(25);};
|
||||
void() berserk_atke13 =[ $r_attb13, berserk_atke14 ] {ai_run(3.7);};
|
||||
void() berserk_atke14 =[ $r_attb14, berserk_atke15 ] {ai_run(2.6);};
|
||||
void() berserk_atke15 =[ $r_attb15, berserk_atke16 ] {ai_run(19);};
|
||||
void() berserk_atke16 =[ $r_attb16, berserk_atke17 ] {ai_run(25);};
|
||||
void() berserk_atke17 =[ $r_attb17, berserk_atke18 ] {ai_run(19.6);};
|
||||
void() berserk_atke18 =[ $r_attb18, berserk_run1 ] {ai_run(7.8);};
|
||||
*/
|
||||
|
||||
mframe_t berserk_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_pain1 = { FRAME_painc1, FRAME_painc4, berserk_frames_pain1, berserk_run };
|
||||
|
||||
mframe_t berserk_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_pain2 = { FRAME_painb1, FRAME_painb20, berserk_frames_pain2, berserk_run };
|
||||
|
||||
void berserk_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if ((damage < 20) || (random() < 0.5))
|
||||
self->monsterinfo.currentmove = &berserk_move_pain1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &berserk_move_pain2;
|
||||
}
|
||||
|
||||
void berserk_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t berserk_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
|
||||
};
|
||||
mmove_t berserk_move_death1 = { FRAME_death1, FRAME_death13, berserk_frames_death1, berserk_dead };
|
||||
|
||||
mframe_t berserk_frames_death2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t berserk_move_death2 = { FRAME_deathc1, FRAME_deathc8, berserk_frames_death2, berserk_dead };
|
||||
|
||||
void berserk_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
if (damage >= 50)
|
||||
self->monsterinfo.currentmove = &berserk_move_death1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &berserk_move_death2;
|
||||
}
|
||||
|
||||
/*QUAKED monster_berserk (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_berserk(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// pre-caches
|
||||
sound_pain = gi.soundindex("berserk/berpain2.wav");
|
||||
sound_die = gi.soundindex("berserk/berdeth2.wav");
|
||||
sound_idle = gi.soundindex("berserk/beridle1.wav");
|
||||
sound_punch = gi.soundindex("berserk/attack.wav");
|
||||
sound_search = gi.soundindex("berserk/bersrch1.wav");
|
||||
sound_sight = gi.soundindex("berserk/sight.wav");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/berserk/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->health = 240;
|
||||
self->gib_health = -60;
|
||||
self->mass = 250;
|
||||
|
||||
self->pain = berserk_pain;
|
||||
self->die = berserk_die;
|
||||
|
||||
self->monsterinfo.stand = berserk_stand;
|
||||
self->monsterinfo.walk = berserk_walk;
|
||||
self->monsterinfo.run = berserk_run;
|
||||
self->monsterinfo.dodge = NULL;
|
||||
self->monsterinfo.attack = NULL;
|
||||
self->monsterinfo.melee = berserk_melee;
|
||||
self->monsterinfo.sight = berserk_sight;
|
||||
self->monsterinfo.search = berserk_search;
|
||||
|
||||
self->monsterinfo.currentmove = &berserk_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/berserk
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_stand1 0
|
||||
#define FRAME_stand2 1
|
||||
#define FRAME_stand3 2
|
||||
#define FRAME_stand4 3
|
||||
#define FRAME_stand5 4
|
||||
#define FRAME_standb1 5
|
||||
#define FRAME_standb2 6
|
||||
#define FRAME_standb3 7
|
||||
#define FRAME_standb4 8
|
||||
#define FRAME_standb5 9
|
||||
#define FRAME_standb6 10
|
||||
#define FRAME_standb7 11
|
||||
#define FRAME_standb8 12
|
||||
#define FRAME_standb9 13
|
||||
#define FRAME_standb10 14
|
||||
#define FRAME_standb11 15
|
||||
#define FRAME_standb12 16
|
||||
#define FRAME_standb13 17
|
||||
#define FRAME_standb14 18
|
||||
#define FRAME_standb15 19
|
||||
#define FRAME_standb16 20
|
||||
#define FRAME_standb17 21
|
||||
#define FRAME_standb18 22
|
||||
#define FRAME_standb19 23
|
||||
#define FRAME_standb20 24
|
||||
#define FRAME_walkc1 25
|
||||
#define FRAME_walkc2 26
|
||||
#define FRAME_walkc3 27
|
||||
#define FRAME_walkc4 28
|
||||
#define FRAME_walkc5 29
|
||||
#define FRAME_walkc6 30
|
||||
#define FRAME_walkc7 31
|
||||
#define FRAME_walkc8 32
|
||||
#define FRAME_walkc9 33
|
||||
#define FRAME_walkc10 34
|
||||
#define FRAME_walkc11 35
|
||||
#define FRAME_run1 36
|
||||
#define FRAME_run2 37
|
||||
#define FRAME_run3 38
|
||||
#define FRAME_run4 39
|
||||
#define FRAME_run5 40
|
||||
#define FRAME_run6 41
|
||||
#define FRAME_att_a1 42
|
||||
#define FRAME_att_a2 43
|
||||
#define FRAME_att_a3 44
|
||||
#define FRAME_att_a4 45
|
||||
#define FRAME_att_a5 46
|
||||
#define FRAME_att_a6 47
|
||||
#define FRAME_att_a7 48
|
||||
#define FRAME_att_a8 49
|
||||
#define FRAME_att_a9 50
|
||||
#define FRAME_att_a10 51
|
||||
#define FRAME_att_a11 52
|
||||
#define FRAME_att_a12 53
|
||||
#define FRAME_att_a13 54
|
||||
#define FRAME_att_b1 55
|
||||
#define FRAME_att_b2 56
|
||||
#define FRAME_att_b3 57
|
||||
#define FRAME_att_b4 58
|
||||
#define FRAME_att_b5 59
|
||||
#define FRAME_att_b6 60
|
||||
#define FRAME_att_b7 61
|
||||
#define FRAME_att_b8 62
|
||||
#define FRAME_att_b9 63
|
||||
#define FRAME_att_b10 64
|
||||
#define FRAME_att_b11 65
|
||||
#define FRAME_att_b12 66
|
||||
#define FRAME_att_b13 67
|
||||
#define FRAME_att_b14 68
|
||||
#define FRAME_att_b15 69
|
||||
#define FRAME_att_b16 70
|
||||
#define FRAME_att_b17 71
|
||||
#define FRAME_att_b18 72
|
||||
#define FRAME_att_b19 73
|
||||
#define FRAME_att_b20 74
|
||||
#define FRAME_att_b21 75
|
||||
#define FRAME_att_c1 76
|
||||
#define FRAME_att_c2 77
|
||||
#define FRAME_att_c3 78
|
||||
#define FRAME_att_c4 79
|
||||
#define FRAME_att_c5 80
|
||||
#define FRAME_att_c6 81
|
||||
#define FRAME_att_c7 82
|
||||
#define FRAME_att_c8 83
|
||||
#define FRAME_att_c9 84
|
||||
#define FRAME_att_c10 85
|
||||
#define FRAME_att_c11 86
|
||||
#define FRAME_att_c12 87
|
||||
#define FRAME_att_c13 88
|
||||
#define FRAME_att_c14 89
|
||||
#define FRAME_att_c15 90
|
||||
#define FRAME_att_c16 91
|
||||
#define FRAME_att_c17 92
|
||||
#define FRAME_att_c18 93
|
||||
#define FRAME_att_c19 94
|
||||
#define FRAME_att_c20 95
|
||||
#define FRAME_att_c21 96
|
||||
#define FRAME_att_c22 97
|
||||
#define FRAME_att_c23 98
|
||||
#define FRAME_att_c24 99
|
||||
#define FRAME_att_c25 100
|
||||
#define FRAME_att_c26 101
|
||||
#define FRAME_att_c27 102
|
||||
#define FRAME_att_c28 103
|
||||
#define FRAME_att_c29 104
|
||||
#define FRAME_att_c30 105
|
||||
#define FRAME_att_c31 106
|
||||
#define FRAME_att_c32 107
|
||||
#define FRAME_att_c33 108
|
||||
#define FRAME_att_c34 109
|
||||
#define FRAME_r_att1 110
|
||||
#define FRAME_r_att2 111
|
||||
#define FRAME_r_att3 112
|
||||
#define FRAME_r_att4 113
|
||||
#define FRAME_r_att5 114
|
||||
#define FRAME_r_att6 115
|
||||
#define FRAME_r_att7 116
|
||||
#define FRAME_r_att8 117
|
||||
#define FRAME_r_att9 118
|
||||
#define FRAME_r_att10 119
|
||||
#define FRAME_r_att11 120
|
||||
#define FRAME_r_att12 121
|
||||
#define FRAME_r_att13 122
|
||||
#define FRAME_r_att14 123
|
||||
#define FRAME_r_att15 124
|
||||
#define FRAME_r_att16 125
|
||||
#define FRAME_r_att17 126
|
||||
#define FRAME_r_att18 127
|
||||
#define FRAME_r_attb1 128
|
||||
#define FRAME_r_attb2 129
|
||||
#define FRAME_r_attb3 130
|
||||
#define FRAME_r_attb4 131
|
||||
#define FRAME_r_attb5 132
|
||||
#define FRAME_r_attb6 133
|
||||
#define FRAME_r_attb7 134
|
||||
#define FRAME_r_attb8 135
|
||||
#define FRAME_r_attb9 136
|
||||
#define FRAME_r_attb10 137
|
||||
#define FRAME_r_attb11 138
|
||||
#define FRAME_r_attb12 139
|
||||
#define FRAME_r_attb13 140
|
||||
#define FRAME_r_attb14 141
|
||||
#define FRAME_r_attb15 142
|
||||
#define FRAME_r_attb16 143
|
||||
#define FRAME_r_attb17 144
|
||||
#define FRAME_r_attb18 145
|
||||
#define FRAME_slam1 146
|
||||
#define FRAME_slam2 147
|
||||
#define FRAME_slam3 148
|
||||
#define FRAME_slam4 149
|
||||
#define FRAME_slam5 150
|
||||
#define FRAME_slam6 151
|
||||
#define FRAME_slam7 152
|
||||
#define FRAME_slam8 153
|
||||
#define FRAME_slam9 154
|
||||
#define FRAME_slam10 155
|
||||
#define FRAME_slam11 156
|
||||
#define FRAME_slam12 157
|
||||
#define FRAME_slam13 158
|
||||
#define FRAME_slam14 159
|
||||
#define FRAME_slam15 160
|
||||
#define FRAME_slam16 161
|
||||
#define FRAME_slam17 162
|
||||
#define FRAME_slam18 163
|
||||
#define FRAME_slam19 164
|
||||
#define FRAME_slam20 165
|
||||
#define FRAME_slam21 166
|
||||
#define FRAME_slam22 167
|
||||
#define FRAME_slam23 168
|
||||
#define FRAME_duck1 169
|
||||
#define FRAME_duck2 170
|
||||
#define FRAME_duck3 171
|
||||
#define FRAME_duck4 172
|
||||
#define FRAME_duck5 173
|
||||
#define FRAME_duck6 174
|
||||
#define FRAME_duck7 175
|
||||
#define FRAME_duck8 176
|
||||
#define FRAME_duck9 177
|
||||
#define FRAME_duck10 178
|
||||
#define FRAME_fall1 179
|
||||
#define FRAME_fall2 180
|
||||
#define FRAME_fall3 181
|
||||
#define FRAME_fall4 182
|
||||
#define FRAME_fall5 183
|
||||
#define FRAME_fall6 184
|
||||
#define FRAME_fall7 185
|
||||
#define FRAME_fall8 186
|
||||
#define FRAME_fall9 187
|
||||
#define FRAME_fall10 188
|
||||
#define FRAME_fall11 189
|
||||
#define FRAME_fall12 190
|
||||
#define FRAME_fall13 191
|
||||
#define FRAME_fall14 192
|
||||
#define FRAME_fall15 193
|
||||
#define FRAME_fall16 194
|
||||
#define FRAME_fall17 195
|
||||
#define FRAME_fall18 196
|
||||
#define FRAME_fall19 197
|
||||
#define FRAME_fall20 198
|
||||
#define FRAME_painc1 199
|
||||
#define FRAME_painc2 200
|
||||
#define FRAME_painc3 201
|
||||
#define FRAME_painc4 202
|
||||
#define FRAME_painb1 203
|
||||
#define FRAME_painb2 204
|
||||
#define FRAME_painb3 205
|
||||
#define FRAME_painb4 206
|
||||
#define FRAME_painb5 207
|
||||
#define FRAME_painb6 208
|
||||
#define FRAME_painb7 209
|
||||
#define FRAME_painb8 210
|
||||
#define FRAME_painb9 211
|
||||
#define FRAME_painb10 212
|
||||
#define FRAME_painb11 213
|
||||
#define FRAME_painb12 214
|
||||
#define FRAME_painb13 215
|
||||
#define FRAME_painb14 216
|
||||
#define FRAME_painb15 217
|
||||
#define FRAME_painb16 218
|
||||
#define FRAME_painb17 219
|
||||
#define FRAME_painb18 220
|
||||
#define FRAME_painb19 221
|
||||
#define FRAME_painb20 222
|
||||
#define FRAME_death1 223
|
||||
#define FRAME_death2 224
|
||||
#define FRAME_death3 225
|
||||
#define FRAME_death4 226
|
||||
#define FRAME_death5 227
|
||||
#define FRAME_death6 228
|
||||
#define FRAME_death7 229
|
||||
#define FRAME_death8 230
|
||||
#define FRAME_death9 231
|
||||
#define FRAME_death10 232
|
||||
#define FRAME_death11 233
|
||||
#define FRAME_death12 234
|
||||
#define FRAME_death13 235
|
||||
#define FRAME_deathc1 236
|
||||
#define FRAME_deathc2 237
|
||||
#define FRAME_deathc3 238
|
||||
#define FRAME_deathc4 239
|
||||
#define FRAME_deathc5 240
|
||||
#define FRAME_deathc6 241
|
||||
#define FRAME_deathc7 242
|
||||
#define FRAME_deathc8 243
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
boss2
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss2.h"
|
||||
|
||||
void BossExplode(edict_t * self);
|
||||
|
||||
qboolean infront(edict_t * self, edict_t * other);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_death;
|
||||
static int sound_search1;
|
||||
|
||||
void boss2_search(edict_t * self)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void boss2_run(edict_t * self);
|
||||
void boss2_stand(edict_t * self);
|
||||
void boss2_dead(edict_t * self);
|
||||
void boss2_attack(edict_t * self);
|
||||
void boss2_attack_mg(edict_t * self);
|
||||
void boss2_reattack_mg(edict_t * self);
|
||||
void boss2_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point);
|
||||
|
||||
void Boss2Rocket(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
|
||||
//1
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_1], forward, right, start);
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_1);
|
||||
|
||||
//2
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_2], forward, right, start);
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_2);
|
||||
|
||||
//3
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_3], forward, right, start);
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_3);
|
||||
|
||||
//4
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_4], forward, right, start);
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_4);
|
||||
}
|
||||
|
||||
void boss2_firebullet_right(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_R1], forward, right, start);
|
||||
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_R1);
|
||||
}
|
||||
|
||||
void boss2_firebullet_left(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_L1], forward, right, start);
|
||||
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_L1);
|
||||
}
|
||||
|
||||
void Boss2MachineGun(edict_t * self)
|
||||
{
|
||||
/* vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
int flash_number;
|
||||
|
||||
AngleVectors (self->s.angles, forward, right, NULL);
|
||||
|
||||
flash_number = MZ2_BOSS2_MACHINEGUN_1 + (self->s.frame - FRAME_attack10);
|
||||
G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
VectorCopy (self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract (vec, start, dir);
|
||||
VectorNormalize (dir);
|
||||
monster_fire_bullet (self, start, dir, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||||
*/
|
||||
boss2_firebullet_left(self);
|
||||
boss2_firebullet_right(self);
|
||||
}
|
||||
|
||||
mframe_t boss2_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t boss2_move_stand = { FRAME_stand30, FRAME_stand50, boss2_frames_stand, NULL };
|
||||
|
||||
mframe_t boss2_frames_fidget[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t boss2_move_fidget = { FRAME_stand1, FRAME_stand30, boss2_frames_fidget, NULL };
|
||||
|
||||
mframe_t boss2_frames_walk[] =
|
||||
{
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL
|
||||
};
|
||||
mmove_t boss2_move_walk = { FRAME_walk1, FRAME_walk20, boss2_frames_walk, NULL };
|
||||
|
||||
mframe_t boss2_frames_run[] =
|
||||
{
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL
|
||||
};
|
||||
mmove_t boss2_move_run = { FRAME_walk1, FRAME_walk20, boss2_frames_run, NULL };
|
||||
|
||||
mframe_t boss2_frames_attack_pre_mg[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, boss2_attack_mg
|
||||
};
|
||||
mmove_t boss2_move_attack_pre_mg = { FRAME_attack1, FRAME_attack9, boss2_frames_attack_pre_mg, NULL };
|
||||
|
||||
// Loop this
|
||||
mframe_t boss2_frames_attack_mg[] =
|
||||
{
|
||||
ai_charge, 1, Boss2MachineGun,
|
||||
ai_charge, 1, Boss2MachineGun,
|
||||
ai_charge, 1, Boss2MachineGun,
|
||||
ai_charge, 1, Boss2MachineGun,
|
||||
ai_charge, 1, Boss2MachineGun,
|
||||
ai_charge, 1, boss2_reattack_mg
|
||||
};
|
||||
mmove_t boss2_move_attack_mg = { FRAME_attack10, FRAME_attack15, boss2_frames_attack_mg, NULL };
|
||||
|
||||
mframe_t boss2_frames_attack_post_mg[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL
|
||||
};
|
||||
mmove_t boss2_move_attack_post_mg = { FRAME_attack16, FRAME_attack19, boss2_frames_attack_post_mg, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_attack_rocket[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_move, -20, Boss2Rocket,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL
|
||||
};
|
||||
mmove_t boss2_move_attack_rocket = { FRAME_attack20, FRAME_attack40, boss2_frames_attack_rocket, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_pain_heavy[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t boss2_move_pain_heavy = { FRAME_pain2, FRAME_pain19, boss2_frames_pain_heavy, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_pain_light[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t boss2_move_pain_light = { FRAME_pain20, FRAME_pain23, boss2_frames_pain_light, boss2_run };
|
||||
|
||||
mframe_t boss2_frames_death[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, BossExplode
|
||||
};
|
||||
mmove_t boss2_move_death = { FRAME_death2, FRAME_death50, boss2_frames_death, boss2_dead };
|
||||
|
||||
void boss2_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &boss2_move_stand;
|
||||
}
|
||||
|
||||
void boss2_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &boss2_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &boss2_move_run;
|
||||
}
|
||||
|
||||
void boss2_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &boss2_move_walk;
|
||||
}
|
||||
|
||||
void boss2_attack(edict_t * self)
|
||||
{
|
||||
vec3_t vec;
|
||||
float range;
|
||||
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, vec);
|
||||
range = VectorLength(vec);
|
||||
|
||||
if (range <= 125)
|
||||
{
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_pre_mg;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (random() <= 0.6)
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_pre_mg;
|
||||
else
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_rocket;
|
||||
}
|
||||
}
|
||||
|
||||
void boss2_attack_mg(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_mg;
|
||||
}
|
||||
|
||||
void boss2_reattack_mg(edict_t * self)
|
||||
{
|
||||
if (infront(self, self->enemy))
|
||||
if (random() <= 0.7)
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_mg;
|
||||
else
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_post_mg;
|
||||
else
|
||||
self->monsterinfo.currentmove = &boss2_move_attack_post_mg;
|
||||
}
|
||||
|
||||
void boss2_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
// American wanted these at no attenuation
|
||||
if (damage < 10)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &boss2_move_pain_light;
|
||||
}
|
||||
else if (damage < 30)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &boss2_move_pain_light;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &boss2_move_pain_heavy;
|
||||
}
|
||||
}
|
||||
|
||||
void boss2_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -56, -56, 0);
|
||||
VectorSet(self->maxs, 56, 56, 80);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void boss2_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_NO;
|
||||
self->count = 0;
|
||||
self->monsterinfo.currentmove = &boss2_move_death;
|
||||
#if 0
|
||||
int n;
|
||||
|
||||
self->s.sound = 0;
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n= 0; n < 2; n++)
|
||||
ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n= 0; n < 4; n++)
|
||||
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.currentmove = &boss2_move_death;
|
||||
#endif
|
||||
}
|
||||
|
||||
qboolean Boss2_CheckAttack(edict_t * self)
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
qboolean enemy_infront;
|
||||
int enemy_range;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
VectorCopy(self->s.origin, spot1);
|
||||
spot1[2] += self->viewheight;
|
||||
VectorCopy(self->enemy->s.origin, spot2);
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy)
|
||||
return false;
|
||||
}
|
||||
|
||||
enemy_infront = infront(self, self->enemy);
|
||||
enemy_range = range(self, self->enemy);
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, temp);
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range == RANGE_FAR)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8;
|
||||
}
|
||||
else if (enemy_range == RANGE_NEAR)
|
||||
{
|
||||
chance = 0.8;
|
||||
}
|
||||
else if (enemy_range == RANGE_MID)
|
||||
{
|
||||
chance = 0.8;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (random() < chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + 2 * random();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (random() < 0.3)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*QUAKED monster_boss2 (1 .5 0) (-56 -56 0) (56 56 80) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_boss2(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("bosshovr/bhvpain1.wav");
|
||||
sound_pain2 = gi.soundindex("bosshovr/bhvpain2.wav");
|
||||
sound_pain3 = gi.soundindex("bosshovr/bhvpain3.wav");
|
||||
sound_death = gi.soundindex("bosshovr/bhvdeth1.wav");
|
||||
sound_search1 = gi.soundindex("bosshovr/bhvunqv1.wav");
|
||||
|
||||
self->s.sound = gi.soundindex("bosshovr/bhvengn1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss2/tris.md2");
|
||||
VectorSet(self->mins, -56, -56, 0);
|
||||
VectorSet(self->maxs, 56, 56, 80);
|
||||
|
||||
self->health = 2000;
|
||||
self->gib_health = -200;
|
||||
self->mass = 1000;
|
||||
|
||||
self->flags |= FL_IMMUNE_LASER;
|
||||
|
||||
self->pain = boss2_pain;
|
||||
self->die = boss2_die;
|
||||
|
||||
self->monsterinfo.stand = boss2_stand;
|
||||
self->monsterinfo.walk = boss2_walk;
|
||||
self->monsterinfo.run = boss2_run;
|
||||
self->monsterinfo.attack = boss2_attack;
|
||||
self->monsterinfo.search = boss2_search;
|
||||
self->monsterinfo.checkattack = Boss2_CheckAttack;
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &boss2_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/boss2
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_stand30 0
|
||||
#define FRAME_stand31 1
|
||||
#define FRAME_stand32 2
|
||||
#define FRAME_stand33 3
|
||||
#define FRAME_stand34 4
|
||||
#define FRAME_stand35 5
|
||||
#define FRAME_stand36 6
|
||||
#define FRAME_stand37 7
|
||||
#define FRAME_stand38 8
|
||||
#define FRAME_stand39 9
|
||||
#define FRAME_stand40 10
|
||||
#define FRAME_stand41 11
|
||||
#define FRAME_stand42 12
|
||||
#define FRAME_stand43 13
|
||||
#define FRAME_stand44 14
|
||||
#define FRAME_stand45 15
|
||||
#define FRAME_stand46 16
|
||||
#define FRAME_stand47 17
|
||||
#define FRAME_stand48 18
|
||||
#define FRAME_stand49 19
|
||||
#define FRAME_stand50 20
|
||||
#define FRAME_stand1 21
|
||||
#define FRAME_stand2 22
|
||||
#define FRAME_stand3 23
|
||||
#define FRAME_stand4 24
|
||||
#define FRAME_stand5 25
|
||||
#define FRAME_stand6 26
|
||||
#define FRAME_stand7 27
|
||||
#define FRAME_stand8 28
|
||||
#define FRAME_stand9 29
|
||||
#define FRAME_stand10 30
|
||||
#define FRAME_stand11 31
|
||||
#define FRAME_stand12 32
|
||||
#define FRAME_stand13 33
|
||||
#define FRAME_stand14 34
|
||||
#define FRAME_stand15 35
|
||||
#define FRAME_stand16 36
|
||||
#define FRAME_stand17 37
|
||||
#define FRAME_stand18 38
|
||||
#define FRAME_stand19 39
|
||||
#define FRAME_stand20 40
|
||||
#define FRAME_stand21 41
|
||||
#define FRAME_stand22 42
|
||||
#define FRAME_stand23 43
|
||||
#define FRAME_stand24 44
|
||||
#define FRAME_stand25 45
|
||||
#define FRAME_stand26 46
|
||||
#define FRAME_stand27 47
|
||||
#define FRAME_stand28 48
|
||||
#define FRAME_stand29 49
|
||||
#define FRAME_walk1 50
|
||||
#define FRAME_walk2 51
|
||||
#define FRAME_walk3 52
|
||||
#define FRAME_walk4 53
|
||||
#define FRAME_walk5 54
|
||||
#define FRAME_walk6 55
|
||||
#define FRAME_walk7 56
|
||||
#define FRAME_walk8 57
|
||||
#define FRAME_walk9 58
|
||||
#define FRAME_walk10 59
|
||||
#define FRAME_walk11 60
|
||||
#define FRAME_walk12 61
|
||||
#define FRAME_walk13 62
|
||||
#define FRAME_walk14 63
|
||||
#define FRAME_walk15 64
|
||||
#define FRAME_walk16 65
|
||||
#define FRAME_walk17 66
|
||||
#define FRAME_walk18 67
|
||||
#define FRAME_walk19 68
|
||||
#define FRAME_walk20 69
|
||||
#define FRAME_attack1 70
|
||||
#define FRAME_attack2 71
|
||||
#define FRAME_attack3 72
|
||||
#define FRAME_attack4 73
|
||||
#define FRAME_attack5 74
|
||||
#define FRAME_attack6 75
|
||||
#define FRAME_attack7 76
|
||||
#define FRAME_attack8 77
|
||||
#define FRAME_attack9 78
|
||||
#define FRAME_attack10 79
|
||||
#define FRAME_attack11 80
|
||||
#define FRAME_attack12 81
|
||||
#define FRAME_attack13 82
|
||||
#define FRAME_attack14 83
|
||||
#define FRAME_attack15 84
|
||||
#define FRAME_attack16 85
|
||||
#define FRAME_attack17 86
|
||||
#define FRAME_attack18 87
|
||||
#define FRAME_attack19 88
|
||||
#define FRAME_attack20 89
|
||||
#define FRAME_attack21 90
|
||||
#define FRAME_attack22 91
|
||||
#define FRAME_attack23 92
|
||||
#define FRAME_attack24 93
|
||||
#define FRAME_attack25 94
|
||||
#define FRAME_attack26 95
|
||||
#define FRAME_attack27 96
|
||||
#define FRAME_attack28 97
|
||||
#define FRAME_attack29 98
|
||||
#define FRAME_attack30 99
|
||||
#define FRAME_attack31 100
|
||||
#define FRAME_attack32 101
|
||||
#define FRAME_attack33 102
|
||||
#define FRAME_attack34 103
|
||||
#define FRAME_attack35 104
|
||||
#define FRAME_attack36 105
|
||||
#define FRAME_attack37 106
|
||||
#define FRAME_attack38 107
|
||||
#define FRAME_attack39 108
|
||||
#define FRAME_attack40 109
|
||||
#define FRAME_pain2 110
|
||||
#define FRAME_pain3 111
|
||||
#define FRAME_pain4 112
|
||||
#define FRAME_pain5 113
|
||||
#define FRAME_pain6 114
|
||||
#define FRAME_pain7 115
|
||||
#define FRAME_pain8 116
|
||||
#define FRAME_pain9 117
|
||||
#define FRAME_pain10 118
|
||||
#define FRAME_pain11 119
|
||||
#define FRAME_pain12 120
|
||||
#define FRAME_pain13 121
|
||||
#define FRAME_pain14 122
|
||||
#define FRAME_pain15 123
|
||||
#define FRAME_pain16 124
|
||||
#define FRAME_pain17 125
|
||||
#define FRAME_pain18 126
|
||||
#define FRAME_pain19 127
|
||||
#define FRAME_pain20 128
|
||||
#define FRAME_pain21 129
|
||||
#define FRAME_pain22 130
|
||||
#define FRAME_pain23 131
|
||||
#define FRAME_death2 132
|
||||
#define FRAME_death3 133
|
||||
#define FRAME_death4 134
|
||||
#define FRAME_death5 135
|
||||
#define FRAME_death6 136
|
||||
#define FRAME_death7 137
|
||||
#define FRAME_death8 138
|
||||
#define FRAME_death9 139
|
||||
#define FRAME_death10 140
|
||||
#define FRAME_death11 141
|
||||
#define FRAME_death12 142
|
||||
#define FRAME_death13 143
|
||||
#define FRAME_death14 144
|
||||
#define FRAME_death15 145
|
||||
#define FRAME_death16 146
|
||||
#define FRAME_death17 147
|
||||
#define FRAME_death18 148
|
||||
#define FRAME_death19 149
|
||||
#define FRAME_death20 150
|
||||
#define FRAME_death21 151
|
||||
#define FRAME_death22 152
|
||||
#define FRAME_death23 153
|
||||
#define FRAME_death24 154
|
||||
#define FRAME_death25 155
|
||||
#define FRAME_death26 156
|
||||
#define FRAME_death27 157
|
||||
#define FRAME_death28 158
|
||||
#define FRAME_death29 159
|
||||
#define FRAME_death30 160
|
||||
#define FRAME_death31 161
|
||||
#define FRAME_death32 162
|
||||
#define FRAME_death33 163
|
||||
#define FRAME_death34 164
|
||||
#define FRAME_death35 165
|
||||
#define FRAME_death36 166
|
||||
#define FRAME_death37 167
|
||||
#define FRAME_death38 168
|
||||
#define FRAME_death39 169
|
||||
#define FRAME_death40 170
|
||||
#define FRAME_death41 171
|
||||
#define FRAME_death42 172
|
||||
#define FRAME_death43 173
|
||||
#define FRAME_death44 174
|
||||
#define FRAME_death45 175
|
||||
#define FRAME_death46 176
|
||||
#define FRAME_death47 177
|
||||
#define FRAME_death48 178
|
||||
#define FRAME_death49 179
|
||||
#define FRAME_death50 180
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
boss3
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss32.h"
|
||||
|
||||
void Use_Boss3(edict_t * ent, edict_t * other, edict_t * activator)
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_BOSSTPORT);
|
||||
gi.WritePosition(ent->s.origin);
|
||||
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
void Think_Boss3Stand(edict_t * ent)
|
||||
{
|
||||
if (ent->s.frame == FRAME_stand260)
|
||||
ent->s.frame = FRAME_stand201;
|
||||
else
|
||||
ent->s.frame++;
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
/*QUAKED monster_boss3_stand (1 .5 0) (-32 -32 0) (32 32 90)
|
||||
|
||||
Just stands and cycles in one place until targeted, then teleports away.
|
||||
*/
|
||||
void SP_monster_boss3_stand(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->model = "models/monsters/boss3/rider/tris.md2";
|
||||
self->s.modelindex = gi.modelindex(self->model);
|
||||
self->s.frame = FRAME_stand201;
|
||||
|
||||
gi.soundindex("misc/bigtele.wav");
|
||||
|
||||
VectorSet(self->mins, -32, -32, 0);
|
||||
VectorSet(self->maxs, 32, 32, 90);
|
||||
|
||||
self->use = Use_Boss3;
|
||||
self->think = Think_Boss3Stand;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
jorg
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss31.h"
|
||||
|
||||
extern SP_monster_makron(edict_t * self);
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_idle;
|
||||
static int sound_death;
|
||||
static int sound_search1;
|
||||
static int sound_search2;
|
||||
static int sound_search3;
|
||||
static int sound_attack1;
|
||||
static int sound_attack2;
|
||||
static int sound_firegun;
|
||||
static int sound_step_left;
|
||||
static int sound_step_right;
|
||||
static int sound_death_hit;
|
||||
|
||||
void BossExplode(edict_t * self);
|
||||
void MakronToss(edict_t * self);
|
||||
|
||||
void jorg_search(edict_t * self)
|
||||
{
|
||||
float r;
|
||||
|
||||
r = random();
|
||||
|
||||
if (r <= 0.3)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0);
|
||||
else if (r <= 0.6)
|
||||
gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_dead(edict_t * self);
|
||||
void jorgBFG(edict_t * self);
|
||||
void jorgMachineGun(edict_t * self);
|
||||
void jorg_firebullet(edict_t * self);
|
||||
void jorg_reattack1(edict_t * self);
|
||||
void jorg_attack1(edict_t * self);
|
||||
void jorg_idle(edict_t * self);
|
||||
void jorg_step_left(edict_t * self);
|
||||
void jorg_step_right(edict_t * self);
|
||||
void jorg_death_hit(edict_t * self);
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t jorg_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, jorg_idle,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 10
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 20
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 30
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 19, NULL,
|
||||
ai_stand, 11, jorg_step_left,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 6, NULL,
|
||||
ai_stand, 9, jorg_step_right,
|
||||
ai_stand, 0, NULL, // 40
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -2, NULL,
|
||||
ai_stand, -17, jorg_step_left,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -12, NULL, // 50
|
||||
ai_stand, -14, jorg_step_right // 51
|
||||
};
|
||||
mmove_t jorg_move_stand = { FRAME_stand01, FRAME_stand51, jorg_frames_stand, NULL };
|
||||
|
||||
void jorg_idle(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_death_hit(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_death_hit, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_step_left(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_step_right(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void jorg_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &jorg_move_stand;
|
||||
}
|
||||
|
||||
mframe_t jorg_frames_run[] =
|
||||
{
|
||||
ai_run, 17, jorg_step_left,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 12, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 33, jorg_step_right,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 9, NULL
|
||||
};
|
||||
mmove_t jorg_move_run = { FRAME_walk06, FRAME_walk19, jorg_frames_run, NULL };
|
||||
|
||||
//
|
||||
// walk
|
||||
//
|
||||
|
||||
mframe_t jorg_frames_start_walk[] =
|
||||
{
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, 15, NULL
|
||||
};
|
||||
mmove_t jorg_move_start_walk = { FRAME_walk01, FRAME_walk05, jorg_frames_start_walk, NULL };
|
||||
|
||||
mframe_t jorg_frames_walk[] =
|
||||
{
|
||||
ai_walk, 17, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 12, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 10, NULL,
|
||||
ai_walk, 33, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, 9, NULL
|
||||
};
|
||||
mmove_t jorg_move_walk = { FRAME_walk06, FRAME_walk19, jorg_frames_walk, NULL };
|
||||
|
||||
mframe_t jorg_frames_end_walk[] =
|
||||
{
|
||||
ai_walk, 11, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, -8, NULL
|
||||
};
|
||||
mmove_t jorg_move_end_walk = { FRAME_walk20, FRAME_walk25, jorg_frames_end_walk, NULL };
|
||||
|
||||
void jorg_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &jorg_move_walk;
|
||||
}
|
||||
|
||||
void jorg_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &jorg_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &jorg_move_run;
|
||||
}
|
||||
|
||||
mframe_t jorg_frames_pain3[] =
|
||||
{
|
||||
ai_move, -28, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -3, jorg_step_left,
|
||||
ai_move, -9, NULL,
|
||||
ai_move, 0, jorg_step_right,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -11, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 10, NULL,
|
||||
ai_move, 11, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 10, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 10, NULL,
|
||||
ai_move, 7, jorg_step_left,
|
||||
ai_move, 17, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, jorg_step_right
|
||||
};
|
||||
mmove_t jorg_move_pain3 = { FRAME_pain301, FRAME_pain325, jorg_frames_pain3, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t jorg_move_pain2 = { FRAME_pain201, FRAME_pain203, jorg_frames_pain2, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t jorg_move_pain1 = { FRAME_pain101, FRAME_pain103, jorg_frames_pain1, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 10
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 20
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 30
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 40
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, MakronToss,
|
||||
ai_move, 0, BossExplode // 50
|
||||
};
|
||||
mmove_t jorg_move_death = { FRAME_death01, FRAME_death50, jorg_frames_death1, jorg_dead };
|
||||
|
||||
mframe_t jorg_frames_attack2[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, jorgBFG,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t jorg_move_attack2 = { FRAME_attak201, FRAME_attak213, jorg_frames_attack2, jorg_run };
|
||||
|
||||
mframe_t jorg_frames_start_attack1[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t jorg_move_start_attack1 = { FRAME_attak101, FRAME_attak108, jorg_frames_start_attack1, jorg_attack1 };
|
||||
|
||||
mframe_t jorg_frames_attack1[] =
|
||||
{
|
||||
ai_charge, 0, jorg_firebullet,
|
||||
ai_charge, 0, jorg_firebullet,
|
||||
ai_charge, 0, jorg_firebullet,
|
||||
ai_charge, 0, jorg_firebullet,
|
||||
ai_charge, 0, jorg_firebullet,
|
||||
ai_charge, 0, jorg_firebullet
|
||||
};
|
||||
mmove_t jorg_move_attack1 = { FRAME_attak109, FRAME_attak114, jorg_frames_attack1, jorg_reattack1 };
|
||||
|
||||
mframe_t jorg_frames_end_attack1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t jorg_move_end_attack1 = { FRAME_attak115, FRAME_attak118, jorg_frames_end_attack1, jorg_run };
|
||||
|
||||
void jorg_reattack1(edict_t * self)
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
if (random() < 0.9)
|
||||
self->monsterinfo.currentmove = &jorg_move_attack1;
|
||||
else
|
||||
{
|
||||
self->s.sound = 0;
|
||||
self->monsterinfo.currentmove = &jorg_move_end_attack1;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->s.sound = 0;
|
||||
self->monsterinfo.currentmove = &jorg_move_end_attack1;
|
||||
}
|
||||
}
|
||||
|
||||
void jorg_attack1(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &jorg_move_attack1;
|
||||
}
|
||||
|
||||
void jorg_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
self->s.sound = 0;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// Lessen the chance of him going into his pain frames if he takes little damage
|
||||
if (damage <= 40)
|
||||
if (random() <= 0.6)
|
||||
return;
|
||||
|
||||
/*
|
||||
If he's entering his attack1 or using attack1, lessen the chance of him
|
||||
going into pain
|
||||
*/
|
||||
|
||||
if ((self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak108))
|
||||
if (random() <= 0.005)
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_attak109) && (self->s.frame <= FRAME_attak114))
|
||||
if (random() <= 0.00005)
|
||||
return;
|
||||
|
||||
if ((self->s.frame >= FRAME_attak201) && (self->s.frame <= FRAME_attak208))
|
||||
if (random() <= 0.005)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 50)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &jorg_move_pain1;
|
||||
}
|
||||
else if (damage <= 100)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &jorg_move_pain2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (random() <= 0.3)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &jorg_move_pain3;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void jorgBFG(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_BFG_1], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
gi.sound(self, CHAN_VOICE, sound_attack2, 1, ATTN_NORM, 0);
|
||||
/*void monster_fire_bfg (edict_t *self,
|
||||
vec3_t start,
|
||||
vec3_t aimdir,
|
||||
int damage,
|
||||
int speed,
|
||||
int kick,
|
||||
float damage_radius,
|
||||
int flashtype)*/
|
||||
monster_fire_bfg(self, start, dir, 50, 300, 100, 200, MZ2_JORG_BFG_1);
|
||||
}
|
||||
|
||||
void jorg_firebullet_right(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_MACHINEGUN_R1], forward, right, start);
|
||||
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_R1);
|
||||
}
|
||||
|
||||
void jorg_firebullet_left(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right, target;
|
||||
vec3_t start;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_MACHINEGUN_L1], forward, right, start);
|
||||
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
|
||||
monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_L1);
|
||||
}
|
||||
|
||||
void jorg_firebullet(edict_t * self)
|
||||
{
|
||||
jorg_firebullet_left(self);
|
||||
jorg_firebullet_right(self);
|
||||
};
|
||||
|
||||
void jorg_attack(edict_t * self)
|
||||
{
|
||||
vec3_t vec;
|
||||
float range;
|
||||
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, vec);
|
||||
range = VectorLength(vec);
|
||||
|
||||
if (random() <= 0.75)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_attack1, 1, ATTN_NORM, 0);
|
||||
self->s.sound = gi.soundindex("boss3/w_loop.wav");
|
||||
self->monsterinfo.currentmove = &jorg_move_start_attack1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_attack2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &jorg_move_attack2;
|
||||
}
|
||||
}
|
||||
|
||||
void jorg_dead(edict_t * self)
|
||||
{
|
||||
#if 0
|
||||
edict_t *tempent;
|
||||
/*
|
||||
VectorSet (self->mins, -16, -16, -24);
|
||||
VectorSet (self->maxs, 16, 16, -8);
|
||||
*/
|
||||
|
||||
// Jorg is on modelindex2. Do not clear him.
|
||||
VectorSet (self->mins, -60, -60, 0);
|
||||
VectorSet (self->maxs, 60, 60, 72);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity (self);
|
||||
|
||||
tempent = G_Spawn();
|
||||
VectorCopy (self->s.origin, tempent->s.origin);
|
||||
VectorCopy (self->s.angles, tempent->s.angles);
|
||||
tempent->killtarget = self->killtarget;
|
||||
tempent->target = self->target;
|
||||
tempent->activator = self->enemy;
|
||||
self->killtarget = 0;
|
||||
self->target = 0;
|
||||
SP_monster_makron (tempent);
|
||||
#endif
|
||||
}
|
||||
|
||||
void jorg_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_NO;
|
||||
self->s.sound = 0;
|
||||
self->count = 0;
|
||||
self->monsterinfo.currentmove = &jorg_move_death;
|
||||
}
|
||||
|
||||
qboolean Jorg_CheckAttack(edict_t * self)
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
qboolean enemy_infront;
|
||||
int enemy_range;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
VectorCopy(self->s.origin, spot1);
|
||||
spot1[2] += self->viewheight;
|
||||
VectorCopy(self->enemy->s.origin, spot2);
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy)
|
||||
return false;
|
||||
}
|
||||
|
||||
enemy_infront = infront(self, self->enemy);
|
||||
enemy_range = range(self, self->enemy);
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, temp);
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range == RANGE_FAR)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8;
|
||||
}
|
||||
else if (enemy_range == RANGE_NEAR)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else if (enemy_range == RANGE_MID)
|
||||
{
|
||||
chance = 0.2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (random() < chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + 2 * random();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (random() < 0.3)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MakronPrecache(void);
|
||||
|
||||
/*QUAKED monster_jorg (1 .5 0) (-80 -80 0) (90 90 140) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_jorg(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("boss3/bs3pain1.wav");
|
||||
sound_pain2 = gi.soundindex("boss3/bs3pain2.wav");
|
||||
sound_pain3 = gi.soundindex("boss3/bs3pain3.wav");
|
||||
sound_death = gi.soundindex("boss3/bs3deth1.wav");
|
||||
sound_attack1 = gi.soundindex("boss3/bs3atck1.wav");
|
||||
sound_attack2 = gi.soundindex("boss3/bs3atck2.wav");
|
||||
sound_search1 = gi.soundindex("boss3/bs3srch1.wav");
|
||||
sound_search2 = gi.soundindex("boss3/bs3srch2.wav");
|
||||
sound_search3 = gi.soundindex("boss3/bs3srch3.wav");
|
||||
sound_idle = gi.soundindex("boss3/bs3idle1.wav");
|
||||
sound_step_left = gi.soundindex("boss3/step1.wav");
|
||||
sound_step_right = gi.soundindex("boss3/step2.wav");
|
||||
sound_firegun = gi.soundindex("boss3/xfire.wav");
|
||||
sound_death_hit = gi.soundindex("boss3/d_hit.wav");
|
||||
|
||||
MakronPrecache();
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
self->s.modelindex2 = gi.modelindex("models/monsters/boss3/jorg/tris.md2");
|
||||
VectorSet(self->mins, -80, -80, 0);
|
||||
VectorSet(self->maxs, 80, 80, 140);
|
||||
|
||||
self->health = 3000;
|
||||
self->gib_health = -2000;
|
||||
self->mass = 1000;
|
||||
|
||||
self->pain = jorg_pain;
|
||||
self->die = jorg_die;
|
||||
self->monsterinfo.stand = jorg_stand;
|
||||
self->monsterinfo.walk = jorg_walk;
|
||||
self->monsterinfo.run = jorg_run;
|
||||
self->monsterinfo.dodge = NULL;
|
||||
self->monsterinfo.attack = jorg_attack;
|
||||
self->monsterinfo.search = jorg_search;
|
||||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = NULL;
|
||||
self->monsterinfo.checkattack = Jorg_CheckAttack;
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &jorg_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/boss3/jorg
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_attak101 0
|
||||
#define FRAME_attak102 1
|
||||
#define FRAME_attak103 2
|
||||
#define FRAME_attak104 3
|
||||
#define FRAME_attak105 4
|
||||
#define FRAME_attak106 5
|
||||
#define FRAME_attak107 6
|
||||
#define FRAME_attak108 7
|
||||
#define FRAME_attak109 8
|
||||
#define FRAME_attak110 9
|
||||
#define FRAME_attak111 10
|
||||
#define FRAME_attak112 11
|
||||
#define FRAME_attak113 12
|
||||
#define FRAME_attak114 13
|
||||
#define FRAME_attak115 14
|
||||
#define FRAME_attak116 15
|
||||
#define FRAME_attak117 16
|
||||
#define FRAME_attak118 17
|
||||
#define FRAME_attak201 18
|
||||
#define FRAME_attak202 19
|
||||
#define FRAME_attak203 20
|
||||
#define FRAME_attak204 21
|
||||
#define FRAME_attak205 22
|
||||
#define FRAME_attak206 23
|
||||
#define FRAME_attak207 24
|
||||
#define FRAME_attak208 25
|
||||
#define FRAME_attak209 26
|
||||
#define FRAME_attak210 27
|
||||
#define FRAME_attak211 28
|
||||
#define FRAME_attak212 29
|
||||
#define FRAME_attak213 30
|
||||
#define FRAME_death01 31
|
||||
#define FRAME_death02 32
|
||||
#define FRAME_death03 33
|
||||
#define FRAME_death04 34
|
||||
#define FRAME_death05 35
|
||||
#define FRAME_death06 36
|
||||
#define FRAME_death07 37
|
||||
#define FRAME_death08 38
|
||||
#define FRAME_death09 39
|
||||
#define FRAME_death10 40
|
||||
#define FRAME_death11 41
|
||||
#define FRAME_death12 42
|
||||
#define FRAME_death13 43
|
||||
#define FRAME_death14 44
|
||||
#define FRAME_death15 45
|
||||
#define FRAME_death16 46
|
||||
#define FRAME_death17 47
|
||||
#define FRAME_death18 48
|
||||
#define FRAME_death19 49
|
||||
#define FRAME_death20 50
|
||||
#define FRAME_death21 51
|
||||
#define FRAME_death22 52
|
||||
#define FRAME_death23 53
|
||||
#define FRAME_death24 54
|
||||
#define FRAME_death25 55
|
||||
#define FRAME_death26 56
|
||||
#define FRAME_death27 57
|
||||
#define FRAME_death28 58
|
||||
#define FRAME_death29 59
|
||||
#define FRAME_death30 60
|
||||
#define FRAME_death31 61
|
||||
#define FRAME_death32 62
|
||||
#define FRAME_death33 63
|
||||
#define FRAME_death34 64
|
||||
#define FRAME_death35 65
|
||||
#define FRAME_death36 66
|
||||
#define FRAME_death37 67
|
||||
#define FRAME_death38 68
|
||||
#define FRAME_death39 69
|
||||
#define FRAME_death40 70
|
||||
#define FRAME_death41 71
|
||||
#define FRAME_death42 72
|
||||
#define FRAME_death43 73
|
||||
#define FRAME_death44 74
|
||||
#define FRAME_death45 75
|
||||
#define FRAME_death46 76
|
||||
#define FRAME_death47 77
|
||||
#define FRAME_death48 78
|
||||
#define FRAME_death49 79
|
||||
#define FRAME_death50 80
|
||||
#define FRAME_pain101 81
|
||||
#define FRAME_pain102 82
|
||||
#define FRAME_pain103 83
|
||||
#define FRAME_pain201 84
|
||||
#define FRAME_pain202 85
|
||||
#define FRAME_pain203 86
|
||||
#define FRAME_pain301 87
|
||||
#define FRAME_pain302 88
|
||||
#define FRAME_pain303 89
|
||||
#define FRAME_pain304 90
|
||||
#define FRAME_pain305 91
|
||||
#define FRAME_pain306 92
|
||||
#define FRAME_pain307 93
|
||||
#define FRAME_pain308 94
|
||||
#define FRAME_pain309 95
|
||||
#define FRAME_pain310 96
|
||||
#define FRAME_pain311 97
|
||||
#define FRAME_pain312 98
|
||||
#define FRAME_pain313 99
|
||||
#define FRAME_pain314 100
|
||||
#define FRAME_pain315 101
|
||||
#define FRAME_pain316 102
|
||||
#define FRAME_pain317 103
|
||||
#define FRAME_pain318 104
|
||||
#define FRAME_pain319 105
|
||||
#define FRAME_pain320 106
|
||||
#define FRAME_pain321 107
|
||||
#define FRAME_pain322 108
|
||||
#define FRAME_pain323 109
|
||||
#define FRAME_pain324 110
|
||||
#define FRAME_pain325 111
|
||||
#define FRAME_stand01 112
|
||||
#define FRAME_stand02 113
|
||||
#define FRAME_stand03 114
|
||||
#define FRAME_stand04 115
|
||||
#define FRAME_stand05 116
|
||||
#define FRAME_stand06 117
|
||||
#define FRAME_stand07 118
|
||||
#define FRAME_stand08 119
|
||||
#define FRAME_stand09 120
|
||||
#define FRAME_stand10 121
|
||||
#define FRAME_stand11 122
|
||||
#define FRAME_stand12 123
|
||||
#define FRAME_stand13 124
|
||||
#define FRAME_stand14 125
|
||||
#define FRAME_stand15 126
|
||||
#define FRAME_stand16 127
|
||||
#define FRAME_stand17 128
|
||||
#define FRAME_stand18 129
|
||||
#define FRAME_stand19 130
|
||||
#define FRAME_stand20 131
|
||||
#define FRAME_stand21 132
|
||||
#define FRAME_stand22 133
|
||||
#define FRAME_stand23 134
|
||||
#define FRAME_stand24 135
|
||||
#define FRAME_stand25 136
|
||||
#define FRAME_stand26 137
|
||||
#define FRAME_stand27 138
|
||||
#define FRAME_stand28 139
|
||||
#define FRAME_stand29 140
|
||||
#define FRAME_stand30 141
|
||||
#define FRAME_stand31 142
|
||||
#define FRAME_stand32 143
|
||||
#define FRAME_stand33 144
|
||||
#define FRAME_stand34 145
|
||||
#define FRAME_stand35 146
|
||||
#define FRAME_stand36 147
|
||||
#define FRAME_stand37 148
|
||||
#define FRAME_stand38 149
|
||||
#define FRAME_stand39 150
|
||||
#define FRAME_stand40 151
|
||||
#define FRAME_stand41 152
|
||||
#define FRAME_stand42 153
|
||||
#define FRAME_stand43 154
|
||||
#define FRAME_stand44 155
|
||||
#define FRAME_stand45 156
|
||||
#define FRAME_stand46 157
|
||||
#define FRAME_stand47 158
|
||||
#define FRAME_stand48 159
|
||||
#define FRAME_stand49 160
|
||||
#define FRAME_stand50 161
|
||||
#define FRAME_stand51 162
|
||||
#define FRAME_walk01 163
|
||||
#define FRAME_walk02 164
|
||||
#define FRAME_walk03 165
|
||||
#define FRAME_walk04 166
|
||||
#define FRAME_walk05 167
|
||||
#define FRAME_walk06 168
|
||||
#define FRAME_walk07 169
|
||||
#define FRAME_walk08 170
|
||||
#define FRAME_walk09 171
|
||||
#define FRAME_walk10 172
|
||||
#define FRAME_walk11 173
|
||||
#define FRAME_walk12 174
|
||||
#define FRAME_walk13 175
|
||||
#define FRAME_walk14 176
|
||||
#define FRAME_walk15 177
|
||||
#define FRAME_walk16 178
|
||||
#define FRAME_walk17 179
|
||||
#define FRAME_walk18 180
|
||||
#define FRAME_walk19 181
|
||||
#define FRAME_walk20 182
|
||||
#define FRAME_walk21 183
|
||||
#define FRAME_walk22 184
|
||||
#define FRAME_walk23 185
|
||||
#define FRAME_walk24 186
|
||||
#define FRAME_walk25 187
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,901 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
Makron -- Final Boss
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_boss32.h"
|
||||
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
|
||||
void MakronRailgun(edict_t * self);
|
||||
void MakronSaveloc(edict_t * self);
|
||||
void MakronHyperblaster(edict_t * self);
|
||||
void makron_step_left(edict_t * self);
|
||||
void makron_step_right(edict_t * self);
|
||||
void makronBFG(edict_t * self);
|
||||
void makron_dead(edict_t * self);
|
||||
|
||||
static int sound_pain4;
|
||||
static int sound_pain5;
|
||||
static int sound_pain6;
|
||||
static int sound_death;
|
||||
static int sound_step_left;
|
||||
static int sound_step_right;
|
||||
static int sound_attack_bfg;
|
||||
static int sound_brainsplorch;
|
||||
static int sound_prerailgun;
|
||||
static int sound_popup;
|
||||
static int sound_taunt1;
|
||||
static int sound_taunt2;
|
||||
static int sound_taunt3;
|
||||
static int sound_hit;
|
||||
|
||||
void makron_taunt(edict_t * self)
|
||||
{
|
||||
float r;
|
||||
|
||||
r = random();
|
||||
if (r <= 0.3)
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt1, 1, ATTN_NONE, 0);
|
||||
else if (r <= 0.6)
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt2, 1, ATTN_NONE, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_AUTO, sound_taunt3, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
//
|
||||
// stand
|
||||
//
|
||||
|
||||
mframe_t makron_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 10
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 20
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 30
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 40
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL, // 50
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL // 60
|
||||
};
|
||||
mmove_t makron_move_stand = { FRAME_stand201, FRAME_stand260, makron_frames_stand, NULL };
|
||||
|
||||
void makron_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &makron_move_stand;
|
||||
}
|
||||
|
||||
mframe_t makron_frames_run[] =
|
||||
{
|
||||
ai_run, 3, makron_step_left,
|
||||
ai_run, 12, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, makron_step_right,
|
||||
ai_run, 6, NULL,
|
||||
ai_run, 12, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 6, NULL,
|
||||
ai_run, 12, NULL
|
||||
};
|
||||
mmove_t makron_move_run = { FRAME_walk204, FRAME_walk213, makron_frames_run, NULL };
|
||||
|
||||
void makron_hit(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_AUTO, sound_hit, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void makron_popup(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_popup, 1, ATTN_NONE, 0);
|
||||
}
|
||||
|
||||
void makron_step_left(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_step_right(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_brainsplorch(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_brainsplorch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void makron_prerailgun(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_prerailgun, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_walk[] =
|
||||
{
|
||||
ai_walk, 3, makron_step_left,
|
||||
ai_walk, 12, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 8, makron_step_right,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 12, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 12, NULL
|
||||
};
|
||||
mmove_t makron_move_walk = { FRAME_walk204, FRAME_walk213, makron_frames_run, NULL };
|
||||
|
||||
void makron_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &makron_move_walk;
|
||||
}
|
||||
|
||||
void makron_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &makron_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &makron_move_run;
|
||||
}
|
||||
|
||||
mframe_t makron_frames_pain6[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 10
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, makron_popup,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 20
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, makron_taunt,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_pain6 = { FRAME_pain601, FRAME_pain627, makron_frames_pain6, makron_run };
|
||||
|
||||
mframe_t makron_frames_pain5[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_pain5 = { FRAME_pain501, FRAME_pain504, makron_frames_pain5, makron_run };
|
||||
|
||||
mframe_t makron_frames_pain4[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_pain4 = { FRAME_pain401, FRAME_pain404, makron_frames_pain4, makron_run };
|
||||
|
||||
mframe_t makron_frames_death2[] =
|
||||
{
|
||||
ai_move, -15, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, -12, NULL,
|
||||
ai_move, 0, makron_step_left,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 10
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 11, NULL,
|
||||
ai_move, 12, NULL,
|
||||
ai_move, 11, makron_step_right,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 20
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 30
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, 7, NULL,
|
||||
ai_move, 6, makron_step_left,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 2, NULL, // 40
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 50
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -6, makron_step_right,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -4, makron_step_left,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 60
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, -3, makron_step_right,
|
||||
ai_move, -8, NULL,
|
||||
ai_move, -3, makron_step_left,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -4, makron_step_right, // 70
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, 0, makron_step_left,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // 80
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL, // 90
|
||||
ai_move, 27, makron_hit,
|
||||
ai_move, 26, NULL,
|
||||
ai_move, 0, makron_brainsplorch,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL // 95
|
||||
};
|
||||
mmove_t makron_move_death2 = { FRAME_death201, FRAME_death295, makron_frames_death2, makron_dead };
|
||||
|
||||
mframe_t makron_frames_death3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_death3 = { FRAME_death301, FRAME_death320, makron_frames_death3, NULL };
|
||||
|
||||
mframe_t makron_frames_sight[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_sight = { FRAME_active01, FRAME_active13, makron_frames_sight, makron_run };
|
||||
|
||||
void makronBFG(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_MAKRON_BFG], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
gi.sound(self, CHAN_VOICE, sound_attack_bfg, 1, ATTN_NORM, 0);
|
||||
monster_fire_bfg(self, start, dir, 50, 300, 100, 300, MZ2_MAKRON_BFG);
|
||||
}
|
||||
|
||||
mframe_t makron_frames_attack3[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, makronBFG, // FIXME: BFG Attack here
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_attack3 = { FRAME_attak301, FRAME_attak308, makron_frames_attack3, makron_run };
|
||||
|
||||
mframe_t makron_frames_attack4[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, MakronHyperblaster, // fire
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_attack4 = { FRAME_attak401, FRAME_attak426, makron_frames_attack4, makron_run };
|
||||
|
||||
mframe_t makron_frames_attack5[] =
|
||||
{
|
||||
ai_charge, 0, makron_prerailgun,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, MakronSaveloc,
|
||||
ai_move, 0, MakronRailgun, // Fire railgun
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t makron_move_attack5 = { FRAME_attak501, FRAME_attak516, makron_frames_attack5, makron_run };
|
||||
|
||||
void MakronSaveloc(edict_t * self)
|
||||
{
|
||||
VectorCopy(self->enemy->s.origin, self->pos1); //save for aiming the shot
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
};
|
||||
|
||||
// FIXME: He's not firing from the proper Z
|
||||
void MakronRailgun(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_MAKRON_RAILGUN_1], forward, right, start);
|
||||
|
||||
// calc direction to where we targted
|
||||
VectorSubtract(self->pos1, start, dir);
|
||||
VectorNormalize(dir);
|
||||
|
||||
monster_fire_railgun(self, start, dir, 50, 100, MZ2_MAKRON_RAILGUN_1);
|
||||
}
|
||||
|
||||
// FIXME: This is all wrong. He's not firing at the proper angles.
|
||||
void MakronHyperblaster(edict_t * self)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
int flash_number;
|
||||
|
||||
flash_number = MZ2_MAKRON_BLASTER_1 + (self->s.frame - FRAME_attak405);
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, vec);
|
||||
vectoangles(vec, vec);
|
||||
dir[0] = vec[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
dir[0] = 0;
|
||||
}
|
||||
if (self->s.frame <= FRAME_attak413)
|
||||
dir[1] = self->s.angles[1] - 10 * (self->s.frame - FRAME_attak413);
|
||||
else
|
||||
dir[1] = self->s.angles[1] + 10 * (self->s.frame - FRAME_attak421);
|
||||
dir[2] = 0;
|
||||
|
||||
AngleVectors(dir, forward, NULL, NULL);
|
||||
|
||||
monster_fire_blaster(self, start, forward, 15, 1000, MZ2_MAKRON_BLASTER_1, EF_BLASTER);
|
||||
}
|
||||
|
||||
void makron_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
// Lessen the chance of him going into his pain frames
|
||||
if (damage <= 25)
|
||||
if (random() < 0.2)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 40)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain4, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &makron_move_pain4;
|
||||
}
|
||||
else if (damage <= 110)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain5, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &makron_move_pain5;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (damage <= 150)
|
||||
if (random() <= 0.45)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &makron_move_pain6;
|
||||
}
|
||||
else if (random() <= 0.35)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0);
|
||||
self->monsterinfo.currentmove = &makron_move_pain6;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void makron_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
self->monsterinfo.currentmove = &makron_move_sight;
|
||||
};
|
||||
|
||||
void makron_attack(edict_t * self)
|
||||
{
|
||||
vec3_t vec;
|
||||
float range;
|
||||
float r;
|
||||
|
||||
r = random();
|
||||
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, vec);
|
||||
range = VectorLength(vec);
|
||||
|
||||
if (r <= 0.3)
|
||||
self->monsterinfo.currentmove = &makron_move_attack3;
|
||||
else if (r <= 0.6)
|
||||
self->monsterinfo.currentmove = &makron_move_attack4;
|
||||
else
|
||||
self->monsterinfo.currentmove = &makron_move_attack5;
|
||||
}
|
||||
|
||||
/*
|
||||
---
|
||||
Makron Torso. This needs to be spawned in
|
||||
---
|
||||
*/
|
||||
|
||||
void makron_torso_think(edict_t * self)
|
||||
{
|
||||
if (++self->s.frame < 365)
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
else
|
||||
{
|
||||
self->s.frame = 346;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
}
|
||||
|
||||
void makron_torso(edict_t * ent)
|
||||
{
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->solid = SOLID_NOT;
|
||||
VectorSet(ent->mins, -8, -8, 0);
|
||||
VectorSet(ent->maxs, 8, 8, 8);
|
||||
ent->s.frame = 346;
|
||||
ent->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
ent->think = makron_torso_think;
|
||||
ent->nextthink = level.time + 2 * FRAMETIME;
|
||||
ent->s.sound = gi.soundindex("makron/spine.wav");
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
//
|
||||
// death
|
||||
//
|
||||
|
||||
void makron_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -60, -60, 0);
|
||||
VectorSet(self->maxs, 60, 60, 72);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void makron_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
edict_t * tempent;
|
||||
|
||||
int n;
|
||||
|
||||
self->s.sound = 0;
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 1 /*4*/; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC);
|
||||
ThrowHead(self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
tempent = G_Spawn();
|
||||
VectorCopy(self->s.origin, tempent->s.origin);
|
||||
VectorCopy(self->s.angles, tempent->s.angles);
|
||||
tempent->s.origin[1] -= 84;
|
||||
makron_torso(tempent);
|
||||
|
||||
self->monsterinfo.currentmove = &makron_move_death2;
|
||||
}
|
||||
|
||||
qboolean Makron_CheckAttack(edict_t * self)
|
||||
{
|
||||
vec3_t spot1, spot2;
|
||||
vec3_t temp;
|
||||
float chance;
|
||||
trace_t tr;
|
||||
qboolean enemy_infront;
|
||||
int enemy_range;
|
||||
float enemy_yaw;
|
||||
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
// see if any entities are in the way of the shot
|
||||
VectorCopy(self->s.origin, spot1);
|
||||
spot1[2] += self->viewheight;
|
||||
VectorCopy(self->enemy->s.origin, spot2);
|
||||
spot2[2] += self->enemy->viewheight;
|
||||
|
||||
tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA);
|
||||
|
||||
// do we have a clear shot?
|
||||
if (tr.ent != self->enemy)
|
||||
return false;
|
||||
}
|
||||
|
||||
enemy_infront = infront(self, self->enemy);
|
||||
enemy_range = range(self, self->enemy);
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, temp);
|
||||
enemy_yaw = vectoyaw(temp);
|
||||
|
||||
self->ideal_yaw = enemy_yaw;
|
||||
|
||||
// melee attack
|
||||
if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
if (self->monsterinfo.melee)
|
||||
self->monsterinfo.attack_state = AS_MELEE;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// missile attack
|
||||
if (!self->monsterinfo.attack)
|
||||
return false;
|
||||
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
return false;
|
||||
|
||||
if (enemy_range == RANGE_FAR)
|
||||
return false;
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else if (enemy_range == RANGE_MELEE)
|
||||
{
|
||||
chance = 0.8;
|
||||
}
|
||||
else if (enemy_range == RANGE_NEAR)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else if (enemy_range == RANGE_MID)
|
||||
{
|
||||
chance = 0.2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (random() < chance)
|
||||
{
|
||||
self->monsterinfo.attack_state = AS_MISSILE;
|
||||
self->monsterinfo.attack_finished = level.time + 2 * random();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self->flags & FL_FLY)
|
||||
{
|
||||
if (random() < 0.3)
|
||||
self->monsterinfo.attack_state = AS_SLIDING;
|
||||
else
|
||||
self->monsterinfo.attack_state = AS_STRAIGHT;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// monster_makron
|
||||
//
|
||||
|
||||
void MakronPrecache(void)
|
||||
{
|
||||
sound_pain4 = gi.soundindex("makron/pain3.wav");
|
||||
sound_pain5 = gi.soundindex("makron/pain2.wav");
|
||||
sound_pain6 = gi.soundindex("makron/pain1.wav");
|
||||
sound_death = gi.soundindex("makron/death.wav");
|
||||
sound_step_left = gi.soundindex("makron/step1.wav");
|
||||
sound_step_right = gi.soundindex("makron/step2.wav");
|
||||
sound_attack_bfg = gi.soundindex("makron/bfg_fire.wav");
|
||||
sound_brainsplorch = gi.soundindex("makron/brain1.wav");
|
||||
sound_prerailgun = gi.soundindex("makron/rail_up.wav");
|
||||
sound_popup = gi.soundindex("makron/popup.wav");
|
||||
sound_taunt1 = gi.soundindex("makron/voice4.wav");
|
||||
sound_taunt2 = gi.soundindex("makron/voice3.wav");
|
||||
sound_taunt3 = gi.soundindex("makron/voice.wav");
|
||||
sound_hit = gi.soundindex("makron/bhit.wav");
|
||||
|
||||
gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
}
|
||||
|
||||
/*QUAKED monster_makron (1 .5 0) (-30 -30 0) (30 30 90) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_makron(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
MakronPrecache();
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2");
|
||||
VectorSet(self->mins, -30, -30, 0);
|
||||
VectorSet(self->maxs, 30, 30, 90);
|
||||
|
||||
self->health = 3000;
|
||||
self->gib_health = -2000;
|
||||
self->mass = 500;
|
||||
|
||||
self->pain = makron_pain;
|
||||
self->die = makron_die;
|
||||
self->monsterinfo.stand = makron_stand;
|
||||
self->monsterinfo.walk = makron_walk;
|
||||
self->monsterinfo.run = makron_run;
|
||||
self->monsterinfo.dodge = NULL;
|
||||
self->monsterinfo.attack = makron_attack;
|
||||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = makron_sight;
|
||||
self->monsterinfo.checkattack = Makron_CheckAttack;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
// self->monsterinfo.currentmove = &makron_move_stand;
|
||||
self->monsterinfo.currentmove = &makron_move_sight;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakronSpawn
|
||||
|
||||
=================
|
||||
*/
|
||||
void MakronSpawn(edict_t * self)
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t * player;
|
||||
|
||||
SP_monster_makron(self);
|
||||
|
||||
// jump at player
|
||||
player = level.sight_client;
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
VectorSubtract(player->s.origin, self->s.origin, vec);
|
||||
self->s.angles[YAW] = vectoyaw(vec);
|
||||
VectorNormalize(vec);
|
||||
VectorMA(vec3_origin, 400, vec, self->velocity);
|
||||
self->velocity[2] = 200;
|
||||
self->groundentity = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
MakronToss
|
||||
|
||||
Jorg is just about dead, so set up to launch Makron out
|
||||
=================
|
||||
*/
|
||||
void MakronToss(edict_t * self)
|
||||
{
|
||||
edict_t * ent;
|
||||
|
||||
ent = G_Spawn();
|
||||
ent->nextthink = level.time + 0.8;
|
||||
ent->think = MakronSpawn;
|
||||
ent->target = self->target;
|
||||
VectorCopy(self->s.origin, ent->s.origin);
|
||||
}
|
||||
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/boss3/rider
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_attak101 0
|
||||
#define FRAME_attak102 1
|
||||
#define FRAME_attak103 2
|
||||
#define FRAME_attak104 3
|
||||
#define FRAME_attak105 4
|
||||
#define FRAME_attak106 5
|
||||
#define FRAME_attak107 6
|
||||
#define FRAME_attak108 7
|
||||
#define FRAME_attak109 8
|
||||
#define FRAME_attak110 9
|
||||
#define FRAME_attak111 10
|
||||
#define FRAME_attak112 11
|
||||
#define FRAME_attak113 12
|
||||
#define FRAME_attak114 13
|
||||
#define FRAME_attak115 14
|
||||
#define FRAME_attak116 15
|
||||
#define FRAME_attak117 16
|
||||
#define FRAME_attak118 17
|
||||
#define FRAME_attak201 18
|
||||
#define FRAME_attak202 19
|
||||
#define FRAME_attak203 20
|
||||
#define FRAME_attak204 21
|
||||
#define FRAME_attak205 22
|
||||
#define FRAME_attak206 23
|
||||
#define FRAME_attak207 24
|
||||
#define FRAME_attak208 25
|
||||
#define FRAME_attak209 26
|
||||
#define FRAME_attak210 27
|
||||
#define FRAME_attak211 28
|
||||
#define FRAME_attak212 29
|
||||
#define FRAME_attak213 30
|
||||
#define FRAME_death01 31
|
||||
#define FRAME_death02 32
|
||||
#define FRAME_death03 33
|
||||
#define FRAME_death04 34
|
||||
#define FRAME_death05 35
|
||||
#define FRAME_death06 36
|
||||
#define FRAME_death07 37
|
||||
#define FRAME_death08 38
|
||||
#define FRAME_death09 39
|
||||
#define FRAME_death10 40
|
||||
#define FRAME_death11 41
|
||||
#define FRAME_death12 42
|
||||
#define FRAME_death13 43
|
||||
#define FRAME_death14 44
|
||||
#define FRAME_death15 45
|
||||
#define FRAME_death16 46
|
||||
#define FRAME_death17 47
|
||||
#define FRAME_death18 48
|
||||
#define FRAME_death19 49
|
||||
#define FRAME_death20 50
|
||||
#define FRAME_death21 51
|
||||
#define FRAME_death22 52
|
||||
#define FRAME_death23 53
|
||||
#define FRAME_death24 54
|
||||
#define FRAME_death25 55
|
||||
#define FRAME_death26 56
|
||||
#define FRAME_death27 57
|
||||
#define FRAME_death28 58
|
||||
#define FRAME_death29 59
|
||||
#define FRAME_death30 60
|
||||
#define FRAME_death31 61
|
||||
#define FRAME_death32 62
|
||||
#define FRAME_death33 63
|
||||
#define FRAME_death34 64
|
||||
#define FRAME_death35 65
|
||||
#define FRAME_death36 66
|
||||
#define FRAME_death37 67
|
||||
#define FRAME_death38 68
|
||||
#define FRAME_death39 69
|
||||
#define FRAME_death40 70
|
||||
#define FRAME_death41 71
|
||||
#define FRAME_death42 72
|
||||
#define FRAME_death43 73
|
||||
#define FRAME_death44 74
|
||||
#define FRAME_death45 75
|
||||
#define FRAME_death46 76
|
||||
#define FRAME_death47 77
|
||||
#define FRAME_death48 78
|
||||
#define FRAME_death49 79
|
||||
#define FRAME_death50 80
|
||||
#define FRAME_pain101 81
|
||||
#define FRAME_pain102 82
|
||||
#define FRAME_pain103 83
|
||||
#define FRAME_pain201 84
|
||||
#define FRAME_pain202 85
|
||||
#define FRAME_pain203 86
|
||||
#define FRAME_pain301 87
|
||||
#define FRAME_pain302 88
|
||||
#define FRAME_pain303 89
|
||||
#define FRAME_pain304 90
|
||||
#define FRAME_pain305 91
|
||||
#define FRAME_pain306 92
|
||||
#define FRAME_pain307 93
|
||||
#define FRAME_pain308 94
|
||||
#define FRAME_pain309 95
|
||||
#define FRAME_pain310 96
|
||||
#define FRAME_pain311 97
|
||||
#define FRAME_pain312 98
|
||||
#define FRAME_pain313 99
|
||||
#define FRAME_pain314 100
|
||||
#define FRAME_pain315 101
|
||||
#define FRAME_pain316 102
|
||||
#define FRAME_pain317 103
|
||||
#define FRAME_pain318 104
|
||||
#define FRAME_pain319 105
|
||||
#define FRAME_pain320 106
|
||||
#define FRAME_pain321 107
|
||||
#define FRAME_pain322 108
|
||||
#define FRAME_pain323 109
|
||||
#define FRAME_pain324 110
|
||||
#define FRAME_pain325 111
|
||||
#define FRAME_stand01 112
|
||||
#define FRAME_stand02 113
|
||||
#define FRAME_stand03 114
|
||||
#define FRAME_stand04 115
|
||||
#define FRAME_stand05 116
|
||||
#define FRAME_stand06 117
|
||||
#define FRAME_stand07 118
|
||||
#define FRAME_stand08 119
|
||||
#define FRAME_stand09 120
|
||||
#define FRAME_stand10 121
|
||||
#define FRAME_stand11 122
|
||||
#define FRAME_stand12 123
|
||||
#define FRAME_stand13 124
|
||||
#define FRAME_stand14 125
|
||||
#define FRAME_stand15 126
|
||||
#define FRAME_stand16 127
|
||||
#define FRAME_stand17 128
|
||||
#define FRAME_stand18 129
|
||||
#define FRAME_stand19 130
|
||||
#define FRAME_stand20 131
|
||||
#define FRAME_stand21 132
|
||||
#define FRAME_stand22 133
|
||||
#define FRAME_stand23 134
|
||||
#define FRAME_stand24 135
|
||||
#define FRAME_stand25 136
|
||||
#define FRAME_stand26 137
|
||||
#define FRAME_stand27 138
|
||||
#define FRAME_stand28 139
|
||||
#define FRAME_stand29 140
|
||||
#define FRAME_stand30 141
|
||||
#define FRAME_stand31 142
|
||||
#define FRAME_stand32 143
|
||||
#define FRAME_stand33 144
|
||||
#define FRAME_stand34 145
|
||||
#define FRAME_stand35 146
|
||||
#define FRAME_stand36 147
|
||||
#define FRAME_stand37 148
|
||||
#define FRAME_stand38 149
|
||||
#define FRAME_stand39 150
|
||||
#define FRAME_stand40 151
|
||||
#define FRAME_stand41 152
|
||||
#define FRAME_stand42 153
|
||||
#define FRAME_stand43 154
|
||||
#define FRAME_stand44 155
|
||||
#define FRAME_stand45 156
|
||||
#define FRAME_stand46 157
|
||||
#define FRAME_stand47 158
|
||||
#define FRAME_stand48 159
|
||||
#define FRAME_stand49 160
|
||||
#define FRAME_stand50 161
|
||||
#define FRAME_stand51 162
|
||||
#define FRAME_walk01 163
|
||||
#define FRAME_walk02 164
|
||||
#define FRAME_walk03 165
|
||||
#define FRAME_walk04 166
|
||||
#define FRAME_walk05 167
|
||||
#define FRAME_walk06 168
|
||||
#define FRAME_walk07 169
|
||||
#define FRAME_walk08 170
|
||||
#define FRAME_walk09 171
|
||||
#define FRAME_walk10 172
|
||||
#define FRAME_walk11 173
|
||||
#define FRAME_walk12 174
|
||||
#define FRAME_walk13 175
|
||||
#define FRAME_walk14 176
|
||||
#define FRAME_walk15 177
|
||||
#define FRAME_walk16 178
|
||||
#define FRAME_walk17 179
|
||||
#define FRAME_walk18 180
|
||||
#define FRAME_walk19 181
|
||||
#define FRAME_walk20 182
|
||||
#define FRAME_walk21 183
|
||||
#define FRAME_walk22 184
|
||||
#define FRAME_walk23 185
|
||||
#define FRAME_walk24 186
|
||||
#define FRAME_walk25 187
|
||||
#define FRAME_active01 188
|
||||
#define FRAME_active02 189
|
||||
#define FRAME_active03 190
|
||||
#define FRAME_active04 191
|
||||
#define FRAME_active05 192
|
||||
#define FRAME_active06 193
|
||||
#define FRAME_active07 194
|
||||
#define FRAME_active08 195
|
||||
#define FRAME_active09 196
|
||||
#define FRAME_active10 197
|
||||
#define FRAME_active11 198
|
||||
#define FRAME_active12 199
|
||||
#define FRAME_active13 200
|
||||
#define FRAME_attak301 201
|
||||
#define FRAME_attak302 202
|
||||
#define FRAME_attak303 203
|
||||
#define FRAME_attak304 204
|
||||
#define FRAME_attak305 205
|
||||
#define FRAME_attak306 206
|
||||
#define FRAME_attak307 207
|
||||
#define FRAME_attak308 208
|
||||
#define FRAME_attak401 209
|
||||
#define FRAME_attak402 210
|
||||
#define FRAME_attak403 211
|
||||
#define FRAME_attak404 212
|
||||
#define FRAME_attak405 213
|
||||
#define FRAME_attak406 214
|
||||
#define FRAME_attak407 215
|
||||
#define FRAME_attak408 216
|
||||
#define FRAME_attak409 217
|
||||
#define FRAME_attak410 218
|
||||
#define FRAME_attak411 219
|
||||
#define FRAME_attak412 220
|
||||
#define FRAME_attak413 221
|
||||
#define FRAME_attak414 222
|
||||
#define FRAME_attak415 223
|
||||
#define FRAME_attak416 224
|
||||
#define FRAME_attak417 225
|
||||
#define FRAME_attak418 226
|
||||
#define FRAME_attak419 227
|
||||
#define FRAME_attak420 228
|
||||
#define FRAME_attak421 229
|
||||
#define FRAME_attak422 230
|
||||
#define FRAME_attak423 231
|
||||
#define FRAME_attak424 232
|
||||
#define FRAME_attak425 233
|
||||
#define FRAME_attak426 234
|
||||
#define FRAME_attak501 235
|
||||
#define FRAME_attak502 236
|
||||
#define FRAME_attak503 237
|
||||
#define FRAME_attak504 238
|
||||
#define FRAME_attak505 239
|
||||
#define FRAME_attak506 240
|
||||
#define FRAME_attak507 241
|
||||
#define FRAME_attak508 242
|
||||
#define FRAME_attak509 243
|
||||
#define FRAME_attak510 244
|
||||
#define FRAME_attak511 245
|
||||
#define FRAME_attak512 246
|
||||
#define FRAME_attak513 247
|
||||
#define FRAME_attak514 248
|
||||
#define FRAME_attak515 249
|
||||
#define FRAME_attak516 250
|
||||
#define FRAME_death201 251
|
||||
#define FRAME_death202 252
|
||||
#define FRAME_death203 253
|
||||
#define FRAME_death204 254
|
||||
#define FRAME_death205 255
|
||||
#define FRAME_death206 256
|
||||
#define FRAME_death207 257
|
||||
#define FRAME_death208 258
|
||||
#define FRAME_death209 259
|
||||
#define FRAME_death210 260
|
||||
#define FRAME_death211 261
|
||||
#define FRAME_death212 262
|
||||
#define FRAME_death213 263
|
||||
#define FRAME_death214 264
|
||||
#define FRAME_death215 265
|
||||
#define FRAME_death216 266
|
||||
#define FRAME_death217 267
|
||||
#define FRAME_death218 268
|
||||
#define FRAME_death219 269
|
||||
#define FRAME_death220 270
|
||||
#define FRAME_death221 271
|
||||
#define FRAME_death222 272
|
||||
#define FRAME_death223 273
|
||||
#define FRAME_death224 274
|
||||
#define FRAME_death225 275
|
||||
#define FRAME_death226 276
|
||||
#define FRAME_death227 277
|
||||
#define FRAME_death228 278
|
||||
#define FRAME_death229 279
|
||||
#define FRAME_death230 280
|
||||
#define FRAME_death231 281
|
||||
#define FRAME_death232 282
|
||||
#define FRAME_death233 283
|
||||
#define FRAME_death234 284
|
||||
#define FRAME_death235 285
|
||||
#define FRAME_death236 286
|
||||
#define FRAME_death237 287
|
||||
#define FRAME_death238 288
|
||||
#define FRAME_death239 289
|
||||
#define FRAME_death240 290
|
||||
#define FRAME_death241 291
|
||||
#define FRAME_death242 292
|
||||
#define FRAME_death243 293
|
||||
#define FRAME_death244 294
|
||||
#define FRAME_death245 295
|
||||
#define FRAME_death246 296
|
||||
#define FRAME_death247 297
|
||||
#define FRAME_death248 298
|
||||
#define FRAME_death249 299
|
||||
#define FRAME_death250 300
|
||||
#define FRAME_death251 301
|
||||
#define FRAME_death252 302
|
||||
#define FRAME_death253 303
|
||||
#define FRAME_death254 304
|
||||
#define FRAME_death255 305
|
||||
#define FRAME_death256 306
|
||||
#define FRAME_death257 307
|
||||
#define FRAME_death258 308
|
||||
#define FRAME_death259 309
|
||||
#define FRAME_death260 310
|
||||
#define FRAME_death261 311
|
||||
#define FRAME_death262 312
|
||||
#define FRAME_death263 313
|
||||
#define FRAME_death264 314
|
||||
#define FRAME_death265 315
|
||||
#define FRAME_death266 316
|
||||
#define FRAME_death267 317
|
||||
#define FRAME_death268 318
|
||||
#define FRAME_death269 319
|
||||
#define FRAME_death270 320
|
||||
#define FRAME_death271 321
|
||||
#define FRAME_death272 322
|
||||
#define FRAME_death273 323
|
||||
#define FRAME_death274 324
|
||||
#define FRAME_death275 325
|
||||
#define FRAME_death276 326
|
||||
#define FRAME_death277 327
|
||||
#define FRAME_death278 328
|
||||
#define FRAME_death279 329
|
||||
#define FRAME_death280 330
|
||||
#define FRAME_death281 331
|
||||
#define FRAME_death282 332
|
||||
#define FRAME_death283 333
|
||||
#define FRAME_death284 334
|
||||
#define FRAME_death285 335
|
||||
#define FRAME_death286 336
|
||||
#define FRAME_death287 337
|
||||
#define FRAME_death288 338
|
||||
#define FRAME_death289 339
|
||||
#define FRAME_death290 340
|
||||
#define FRAME_death291 341
|
||||
#define FRAME_death292 342
|
||||
#define FRAME_death293 343
|
||||
#define FRAME_death294 344
|
||||
#define FRAME_death295 345
|
||||
#define FRAME_death301 346
|
||||
#define FRAME_death302 347
|
||||
#define FRAME_death303 348
|
||||
#define FRAME_death304 349
|
||||
#define FRAME_death305 350
|
||||
#define FRAME_death306 351
|
||||
#define FRAME_death307 352
|
||||
#define FRAME_death308 353
|
||||
#define FRAME_death309 354
|
||||
#define FRAME_death310 355
|
||||
#define FRAME_death311 356
|
||||
#define FRAME_death312 357
|
||||
#define FRAME_death313 358
|
||||
#define FRAME_death314 359
|
||||
#define FRAME_death315 360
|
||||
#define FRAME_death316 361
|
||||
#define FRAME_death317 362
|
||||
#define FRAME_death318 363
|
||||
#define FRAME_death319 364
|
||||
#define FRAME_death320 365
|
||||
#define FRAME_jump01 366
|
||||
#define FRAME_jump02 367
|
||||
#define FRAME_jump03 368
|
||||
#define FRAME_jump04 369
|
||||
#define FRAME_jump05 370
|
||||
#define FRAME_jump06 371
|
||||
#define FRAME_jump07 372
|
||||
#define FRAME_jump08 373
|
||||
#define FRAME_jump09 374
|
||||
#define FRAME_jump10 375
|
||||
#define FRAME_jump11 376
|
||||
#define FRAME_jump12 377
|
||||
#define FRAME_jump13 378
|
||||
#define FRAME_pain401 379
|
||||
#define FRAME_pain402 380
|
||||
#define FRAME_pain403 381
|
||||
#define FRAME_pain404 382
|
||||
#define FRAME_pain501 383
|
||||
#define FRAME_pain502 384
|
||||
#define FRAME_pain503 385
|
||||
#define FRAME_pain504 386
|
||||
#define FRAME_pain601 387
|
||||
#define FRAME_pain602 388
|
||||
#define FRAME_pain603 389
|
||||
#define FRAME_pain604 390
|
||||
#define FRAME_pain605 391
|
||||
#define FRAME_pain606 392
|
||||
#define FRAME_pain607 393
|
||||
#define FRAME_pain608 394
|
||||
#define FRAME_pain609 395
|
||||
#define FRAME_pain610 396
|
||||
#define FRAME_pain611 397
|
||||
#define FRAME_pain612 398
|
||||
#define FRAME_pain613 399
|
||||
#define FRAME_pain614 400
|
||||
#define FRAME_pain615 401
|
||||
#define FRAME_pain616 402
|
||||
#define FRAME_pain617 403
|
||||
#define FRAME_pain618 404
|
||||
#define FRAME_pain619 405
|
||||
#define FRAME_pain620 406
|
||||
#define FRAME_pain621 407
|
||||
#define FRAME_pain622 408
|
||||
#define FRAME_pain623 409
|
||||
#define FRAME_pain624 410
|
||||
#define FRAME_pain625 411
|
||||
#define FRAME_pain626 412
|
||||
#define FRAME_pain627 413
|
||||
#define FRAME_stand201 414
|
||||
#define FRAME_stand202 415
|
||||
#define FRAME_stand203 416
|
||||
#define FRAME_stand204 417
|
||||
#define FRAME_stand205 418
|
||||
#define FRAME_stand206 419
|
||||
#define FRAME_stand207 420
|
||||
#define FRAME_stand208 421
|
||||
#define FRAME_stand209 422
|
||||
#define FRAME_stand210 423
|
||||
#define FRAME_stand211 424
|
||||
#define FRAME_stand212 425
|
||||
#define FRAME_stand213 426
|
||||
#define FRAME_stand214 427
|
||||
#define FRAME_stand215 428
|
||||
#define FRAME_stand216 429
|
||||
#define FRAME_stand217 430
|
||||
#define FRAME_stand218 431
|
||||
#define FRAME_stand219 432
|
||||
#define FRAME_stand220 433
|
||||
#define FRAME_stand221 434
|
||||
#define FRAME_stand222 435
|
||||
#define FRAME_stand223 436
|
||||
#define FRAME_stand224 437
|
||||
#define FRAME_stand225 438
|
||||
#define FRAME_stand226 439
|
||||
#define FRAME_stand227 440
|
||||
#define FRAME_stand228 441
|
||||
#define FRAME_stand229 442
|
||||
#define FRAME_stand230 443
|
||||
#define FRAME_stand231 444
|
||||
#define FRAME_stand232 445
|
||||
#define FRAME_stand233 446
|
||||
#define FRAME_stand234 447
|
||||
#define FRAME_stand235 448
|
||||
#define FRAME_stand236 449
|
||||
#define FRAME_stand237 450
|
||||
#define FRAME_stand238 451
|
||||
#define FRAME_stand239 452
|
||||
#define FRAME_stand240 453
|
||||
#define FRAME_stand241 454
|
||||
#define FRAME_stand242 455
|
||||
#define FRAME_stand243 456
|
||||
#define FRAME_stand244 457
|
||||
#define FRAME_stand245 458
|
||||
#define FRAME_stand246 459
|
||||
#define FRAME_stand247 460
|
||||
#define FRAME_stand248 461
|
||||
#define FRAME_stand249 462
|
||||
#define FRAME_stand250 463
|
||||
#define FRAME_stand251 464
|
||||
#define FRAME_stand252 465
|
||||
#define FRAME_stand253 466
|
||||
#define FRAME_stand254 467
|
||||
#define FRAME_stand255 468
|
||||
#define FRAME_stand256 469
|
||||
#define FRAME_stand257 470
|
||||
#define FRAME_stand258 471
|
||||
#define FRAME_stand259 472
|
||||
#define FRAME_stand260 473
|
||||
#define FRAME_walk201 474
|
||||
#define FRAME_walk202 475
|
||||
#define FRAME_walk203 476
|
||||
#define FRAME_walk204 477
|
||||
#define FRAME_walk205 478
|
||||
#define FRAME_walk206 479
|
||||
#define FRAME_walk207 480
|
||||
#define FRAME_walk208 481
|
||||
#define FRAME_walk209 482
|
||||
#define FRAME_walk210 483
|
||||
#define FRAME_walk211 484
|
||||
#define FRAME_walk212 485
|
||||
#define FRAME_walk213 486
|
||||
#define FRAME_walk214 487
|
||||
#define FRAME_walk215 488
|
||||
#define FRAME_walk216 489
|
||||
#define FRAME_walk217 490
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,661 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
brain
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_brain.h"
|
||||
|
||||
static int sound_chest_open;
|
||||
static int sound_tentacles_extend;
|
||||
static int sound_tentacles_retract;
|
||||
static int sound_death;
|
||||
static int sound_idle1;
|
||||
static int sound_idle2;
|
||||
static int sound_idle3;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_melee1;
|
||||
static int sound_melee2;
|
||||
static int sound_melee3;
|
||||
|
||||
void brain_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_search(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_run(edict_t * self);
|
||||
void brain_dead(edict_t * self);
|
||||
|
||||
//
|
||||
// STAND
|
||||
//
|
||||
|
||||
mframe_t brain_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t brain_move_stand = { FRAME_stand01, FRAME_stand30, brain_frames_stand, NULL };
|
||||
|
||||
void brain_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &brain_move_stand;
|
||||
}
|
||||
|
||||
//
|
||||
// IDLE
|
||||
//
|
||||
|
||||
mframe_t brain_frames_idle[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t brain_move_idle = { FRAME_stand31, FRAME_stand60, brain_frames_idle, brain_stand };
|
||||
|
||||
void brain_idle(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_AUTO, sound_idle3, 1, ATTN_IDLE, 0);
|
||||
self->monsterinfo.currentmove = &brain_move_idle;
|
||||
}
|
||||
|
||||
//
|
||||
// WALK
|
||||
//
|
||||
mframe_t brain_frames_walk1[] =
|
||||
{
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, -4, NULL,
|
||||
ai_walk, -1, NULL,
|
||||
ai_walk, 2, NULL
|
||||
};
|
||||
mmove_t brain_move_walk1 = { FRAME_walk101, FRAME_walk111, brain_frames_walk1, NULL };
|
||||
|
||||
// walk2 is FUBAR, do not use
|
||||
#if 0
|
||||
void brain_walk2_cycle (edict_t *self)
|
||||
{
|
||||
if (random() > 0.1)
|
||||
self->monsterinfo.nextframe = FRAME_walk220;
|
||||
}
|
||||
|
||||
mframe_t brain_frames_walk2 [] =
|
||||
{
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, -2, NULL,
|
||||
ai_walk, -4, NULL,
|
||||
ai_walk, -3, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 12, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, -3, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
|
||||
ai_walk, -2, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 10, NULL, // Cycle Start
|
||||
|
||||
ai_walk, -1, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, -3, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, -3, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
|
||||
ai_walk, 4, brain_walk2_cycle,
|
||||
ai_walk, -1, NULL,
|
||||
ai_walk, -1, NULL,
|
||||
ai_walk, -8, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, -1, NULL,
|
||||
ai_walk, -5, NULL
|
||||
};
|
||||
mmove_t brain_move_walk2 = {FRAME_walk201, FRAME_walk240, brain_frames_walk2, NULL};
|
||||
#endif
|
||||
|
||||
void brain_walk(edict_t * self)
|
||||
{
|
||||
// if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &brain_move_walk1;
|
||||
// else
|
||||
// self->monsterinfo.currentmove = &brain_move_walk2;
|
||||
}
|
||||
|
||||
mframe_t brain_frames_defense[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t brain_move_defense = { FRAME_defens01, FRAME_defens08, brain_frames_defense, NULL };
|
||||
|
||||
mframe_t brain_frames_pain3[] =
|
||||
{
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -4, NULL
|
||||
};
|
||||
mmove_t brain_move_pain3 = { FRAME_pain301, FRAME_pain306, brain_frames_pain3, brain_run };
|
||||
|
||||
mframe_t brain_frames_pain2[] =
|
||||
{
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -2, NULL
|
||||
};
|
||||
mmove_t brain_move_pain2 = { FRAME_pain201, FRAME_pain208, brain_frames_pain2, brain_run };
|
||||
|
||||
mframe_t brain_frames_pain1[] =
|
||||
{
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 7, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, -1, NULL
|
||||
};
|
||||
mmove_t brain_move_pain1 = { FRAME_pain101, FRAME_pain121, brain_frames_pain1, brain_run };
|
||||
|
||||
//
|
||||
// DUCK
|
||||
//
|
||||
|
||||
void brain_duck_down(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
return;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
self->maxs[2] -= 32;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void brain_duck_hold(edict_t * self)
|
||||
{
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
void brain_duck_up(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_DUCKED;
|
||||
self->maxs[2] += 32;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t brain_frames_duck[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, brain_duck_down,
|
||||
ai_move, 17, brain_duck_hold,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -1, brain_duck_up,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -6, NULL
|
||||
};
|
||||
mmove_t brain_move_duck = { FRAME_duck01, FRAME_duck08, brain_frames_duck, brain_run };
|
||||
|
||||
void brain_dodge(edict_t * self, edict_t * attacker, float eta)
|
||||
{
|
||||
if (random() > 0.25)
|
||||
return;
|
||||
|
||||
if (!self->enemy)
|
||||
self->enemy = attacker;
|
||||
|
||||
self->monsterinfo.pausetime = level.time + eta + 0.5;
|
||||
self->monsterinfo.currentmove = &brain_move_duck;
|
||||
}
|
||||
|
||||
mframe_t brain_frames_death2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 9, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t brain_move_death2 = { FRAME_death201, FRAME_death205, brain_frames_death2, brain_dead };
|
||||
|
||||
mframe_t brain_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 9, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t brain_move_death1 = { FRAME_death101, FRAME_death118, brain_frames_death1, brain_dead };
|
||||
|
||||
//
|
||||
// MELEE
|
||||
//
|
||||
|
||||
void brain_swing_right(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_melee1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_hit_right(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->maxs[0], 8);
|
||||
if (fire_hit(self, aim, (15 + (rand() % 5)), 40))
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_swing_left(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_melee2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_hit_left(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->mins[0], 8);
|
||||
if (fire_hit(self, aim, (15 + (rand() % 5)), 40))
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t brain_frames_attack1[] =
|
||||
{
|
||||
ai_charge, 8, NULL,
|
||||
ai_charge, 3, NULL,
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -3, brain_swing_right,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -5, NULL,
|
||||
ai_charge, -7, brain_hit_right,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 6, brain_swing_left,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 2, brain_hit_left,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 6, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 2, NULL,
|
||||
ai_charge, -11, NULL
|
||||
};
|
||||
mmove_t brain_move_attack1 = { FRAME_attak101, FRAME_attak118, brain_frames_attack1, brain_run };
|
||||
|
||||
void brain_chest_open(edict_t * self)
|
||||
{
|
||||
self->spawnflags &= ~65536;
|
||||
self->monsterinfo.power_armor_type = POWER_ARMOR_NONE;
|
||||
gi.sound(self, CHAN_BODY, sound_chest_open, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_tentacle_attack(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, 0, 8);
|
||||
if (fire_hit(self, aim, (10 + (rand() % 5)), -600) && skill->value > 0)
|
||||
self->spawnflags |= 65536;
|
||||
gi.sound(self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void brain_chest_closed(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
|
||||
if (self->spawnflags & 65536)
|
||||
{
|
||||
self->spawnflags &= ~65536;
|
||||
self->monsterinfo.currentmove = &brain_move_attack1;
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t brain_frames_attack2[] =
|
||||
{
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, -4, NULL,
|
||||
ai_charge, -4, NULL,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 0, brain_chest_open,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 13, brain_tentacle_attack,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 2, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -9, brain_chest_closed,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 4, NULL,
|
||||
ai_charge, 3, NULL,
|
||||
ai_charge, 2, NULL,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, -6, NULL
|
||||
};
|
||||
mmove_t brain_move_attack2 = { FRAME_attak201, FRAME_attak217, brain_frames_attack2, brain_run };
|
||||
|
||||
void brain_melee(edict_t * self)
|
||||
{
|
||||
if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &brain_move_attack1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &brain_move_attack2;
|
||||
}
|
||||
|
||||
//
|
||||
// RUN
|
||||
//
|
||||
|
||||
mframe_t brain_frames_run[] =
|
||||
{
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 2, NULL,
|
||||
ai_run, 3, NULL,
|
||||
ai_run, 3, NULL,
|
||||
ai_run, 1, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, -4, NULL,
|
||||
ai_run, -1, NULL,
|
||||
ai_run, 2, NULL
|
||||
};
|
||||
mmove_t brain_move_run = { FRAME_walk101, FRAME_walk111, brain_frames_run, NULL };
|
||||
|
||||
void brain_run(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &brain_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &brain_move_run;
|
||||
}
|
||||
|
||||
void brain_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
float r;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
r = random();
|
||||
if (r < 0.33)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &brain_move_pain1;
|
||||
}
|
||||
else if (r < 0.66)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &brain_move_pain2;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &brain_move_pain3;
|
||||
}
|
||||
}
|
||||
|
||||
void brain_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void brain_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
self->s.effects = 0;
|
||||
self->monsterinfo.power_armor_type = POWER_ARMOR_NONE;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &brain_move_death1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &brain_move_death2;
|
||||
}
|
||||
|
||||
/*QUAKED monster_brain (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_brain(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_chest_open = gi.soundindex("brain/brnatck1.wav");
|
||||
sound_tentacles_extend = gi.soundindex("brain/brnatck2.wav");
|
||||
sound_tentacles_retract = gi.soundindex("brain/brnatck3.wav");
|
||||
sound_death = gi.soundindex("brain/brndeth1.wav");
|
||||
sound_idle1 = gi.soundindex("brain/brnidle1.wav");
|
||||
sound_idle2 = gi.soundindex("brain/brnidle2.wav");
|
||||
sound_idle3 = gi.soundindex("brain/brnlens1.wav");
|
||||
sound_pain1 = gi.soundindex("brain/brnpain1.wav");
|
||||
sound_pain2 = gi.soundindex("brain/brnpain2.wav");
|
||||
sound_sight = gi.soundindex("brain/brnsght1.wav");
|
||||
sound_search = gi.soundindex("brain/brnsrch1.wav");
|
||||
sound_melee1 = gi.soundindex("brain/melee1.wav");
|
||||
sound_melee2 = gi.soundindex("brain/melee2.wav");
|
||||
sound_melee3 = gi.soundindex("brain/melee3.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/brain/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 300;
|
||||
self->gib_health = -150;
|
||||
self->mass = 400;
|
||||
|
||||
self->pain = brain_pain;
|
||||
self->die = brain_die;
|
||||
|
||||
self->monsterinfo.stand = brain_stand;
|
||||
self->monsterinfo.walk = brain_walk;
|
||||
self->monsterinfo.run = brain_run;
|
||||
self->monsterinfo.dodge = brain_dodge;
|
||||
// self->monsterinfo.attack = brain_attack;
|
||||
self->monsterinfo.melee = brain_melee;
|
||||
self->monsterinfo.sight = brain_sight;
|
||||
self->monsterinfo.search = brain_search;
|
||||
self->monsterinfo.idle = brain_idle;
|
||||
|
||||
self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN;
|
||||
self->monsterinfo.power_armor_power = 100;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &brain_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/brain
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_walk101 0
|
||||
#define FRAME_walk102 1
|
||||
#define FRAME_walk103 2
|
||||
#define FRAME_walk104 3
|
||||
#define FRAME_walk105 4
|
||||
#define FRAME_walk106 5
|
||||
#define FRAME_walk107 6
|
||||
#define FRAME_walk108 7
|
||||
#define FRAME_walk109 8
|
||||
#define FRAME_walk110 9
|
||||
#define FRAME_walk111 10
|
||||
#define FRAME_walk112 11
|
||||
#define FRAME_walk113 12
|
||||
#define FRAME_walk201 13
|
||||
#define FRAME_walk202 14
|
||||
#define FRAME_walk203 15
|
||||
#define FRAME_walk204 16
|
||||
#define FRAME_walk205 17
|
||||
#define FRAME_walk206 18
|
||||
#define FRAME_walk207 19
|
||||
#define FRAME_walk208 20
|
||||
#define FRAME_walk209 21
|
||||
#define FRAME_walk210 22
|
||||
#define FRAME_walk211 23
|
||||
#define FRAME_walk212 24
|
||||
#define FRAME_walk213 25
|
||||
#define FRAME_walk214 26
|
||||
#define FRAME_walk215 27
|
||||
#define FRAME_walk216 28
|
||||
#define FRAME_walk217 29
|
||||
#define FRAME_walk218 30
|
||||
#define FRAME_walk219 31
|
||||
#define FRAME_walk220 32
|
||||
#define FRAME_walk221 33
|
||||
#define FRAME_walk222 34
|
||||
#define FRAME_walk223 35
|
||||
#define FRAME_walk224 36
|
||||
#define FRAME_walk225 37
|
||||
#define FRAME_walk226 38
|
||||
#define FRAME_walk227 39
|
||||
#define FRAME_walk228 40
|
||||
#define FRAME_walk229 41
|
||||
#define FRAME_walk230 42
|
||||
#define FRAME_walk231 43
|
||||
#define FRAME_walk232 44
|
||||
#define FRAME_walk233 45
|
||||
#define FRAME_walk234 46
|
||||
#define FRAME_walk235 47
|
||||
#define FRAME_walk236 48
|
||||
#define FRAME_walk237 49
|
||||
#define FRAME_walk238 50
|
||||
#define FRAME_walk239 51
|
||||
#define FRAME_walk240 52
|
||||
#define FRAME_attak101 53
|
||||
#define FRAME_attak102 54
|
||||
#define FRAME_attak103 55
|
||||
#define FRAME_attak104 56
|
||||
#define FRAME_attak105 57
|
||||
#define FRAME_attak106 58
|
||||
#define FRAME_attak107 59
|
||||
#define FRAME_attak108 60
|
||||
#define FRAME_attak109 61
|
||||
#define FRAME_attak110 62
|
||||
#define FRAME_attak111 63
|
||||
#define FRAME_attak112 64
|
||||
#define FRAME_attak113 65
|
||||
#define FRAME_attak114 66
|
||||
#define FRAME_attak115 67
|
||||
#define FRAME_attak116 68
|
||||
#define FRAME_attak117 69
|
||||
#define FRAME_attak118 70
|
||||
#define FRAME_attak201 71
|
||||
#define FRAME_attak202 72
|
||||
#define FRAME_attak203 73
|
||||
#define FRAME_attak204 74
|
||||
#define FRAME_attak205 75
|
||||
#define FRAME_attak206 76
|
||||
#define FRAME_attak207 77
|
||||
#define FRAME_attak208 78
|
||||
#define FRAME_attak209 79
|
||||
#define FRAME_attak210 80
|
||||
#define FRAME_attak211 81
|
||||
#define FRAME_attak212 82
|
||||
#define FRAME_attak213 83
|
||||
#define FRAME_attak214 84
|
||||
#define FRAME_attak215 85
|
||||
#define FRAME_attak216 86
|
||||
#define FRAME_attak217 87
|
||||
#define FRAME_pain101 88
|
||||
#define FRAME_pain102 89
|
||||
#define FRAME_pain103 90
|
||||
#define FRAME_pain104 91
|
||||
#define FRAME_pain105 92
|
||||
#define FRAME_pain106 93
|
||||
#define FRAME_pain107 94
|
||||
#define FRAME_pain108 95
|
||||
#define FRAME_pain109 96
|
||||
#define FRAME_pain110 97
|
||||
#define FRAME_pain111 98
|
||||
#define FRAME_pain112 99
|
||||
#define FRAME_pain113 100
|
||||
#define FRAME_pain114 101
|
||||
#define FRAME_pain115 102
|
||||
#define FRAME_pain116 103
|
||||
#define FRAME_pain117 104
|
||||
#define FRAME_pain118 105
|
||||
#define FRAME_pain119 106
|
||||
#define FRAME_pain120 107
|
||||
#define FRAME_pain121 108
|
||||
#define FRAME_pain201 109
|
||||
#define FRAME_pain202 110
|
||||
#define FRAME_pain203 111
|
||||
#define FRAME_pain204 112
|
||||
#define FRAME_pain205 113
|
||||
#define FRAME_pain206 114
|
||||
#define FRAME_pain207 115
|
||||
#define FRAME_pain208 116
|
||||
#define FRAME_pain301 117
|
||||
#define FRAME_pain302 118
|
||||
#define FRAME_pain303 119
|
||||
#define FRAME_pain304 120
|
||||
#define FRAME_pain305 121
|
||||
#define FRAME_pain306 122
|
||||
#define FRAME_death101 123
|
||||
#define FRAME_death102 124
|
||||
#define FRAME_death103 125
|
||||
#define FRAME_death104 126
|
||||
#define FRAME_death105 127
|
||||
#define FRAME_death106 128
|
||||
#define FRAME_death107 129
|
||||
#define FRAME_death108 130
|
||||
#define FRAME_death109 131
|
||||
#define FRAME_death110 132
|
||||
#define FRAME_death111 133
|
||||
#define FRAME_death112 134
|
||||
#define FRAME_death113 135
|
||||
#define FRAME_death114 136
|
||||
#define FRAME_death115 137
|
||||
#define FRAME_death116 138
|
||||
#define FRAME_death117 139
|
||||
#define FRAME_death118 140
|
||||
#define FRAME_death201 141
|
||||
#define FRAME_death202 142
|
||||
#define FRAME_death203 143
|
||||
#define FRAME_death204 144
|
||||
#define FRAME_death205 145
|
||||
#define FRAME_duck01 146
|
||||
#define FRAME_duck02 147
|
||||
#define FRAME_duck03 148
|
||||
#define FRAME_duck04 149
|
||||
#define FRAME_duck05 150
|
||||
#define FRAME_duck06 151
|
||||
#define FRAME_duck07 152
|
||||
#define FRAME_duck08 153
|
||||
#define FRAME_defens01 154
|
||||
#define FRAME_defens02 155
|
||||
#define FRAME_defens03 156
|
||||
#define FRAME_defens04 157
|
||||
#define FRAME_defens05 158
|
||||
#define FRAME_defens06 159
|
||||
#define FRAME_defens07 160
|
||||
#define FRAME_defens08 161
|
||||
#define FRAME_stand01 162
|
||||
#define FRAME_stand02 163
|
||||
#define FRAME_stand03 164
|
||||
#define FRAME_stand04 165
|
||||
#define FRAME_stand05 166
|
||||
#define FRAME_stand06 167
|
||||
#define FRAME_stand07 168
|
||||
#define FRAME_stand08 169
|
||||
#define FRAME_stand09 170
|
||||
#define FRAME_stand10 171
|
||||
#define FRAME_stand11 172
|
||||
#define FRAME_stand12 173
|
||||
#define FRAME_stand13 174
|
||||
#define FRAME_stand14 175
|
||||
#define FRAME_stand15 176
|
||||
#define FRAME_stand16 177
|
||||
#define FRAME_stand17 178
|
||||
#define FRAME_stand18 179
|
||||
#define FRAME_stand19 180
|
||||
#define FRAME_stand20 181
|
||||
#define FRAME_stand21 182
|
||||
#define FRAME_stand22 183
|
||||
#define FRAME_stand23 184
|
||||
#define FRAME_stand24 185
|
||||
#define FRAME_stand25 186
|
||||
#define FRAME_stand26 187
|
||||
#define FRAME_stand27 188
|
||||
#define FRAME_stand28 189
|
||||
#define FRAME_stand29 190
|
||||
#define FRAME_stand30 191
|
||||
#define FRAME_stand31 192
|
||||
#define FRAME_stand32 193
|
||||
#define FRAME_stand33 194
|
||||
#define FRAME_stand34 195
|
||||
#define FRAME_stand35 196
|
||||
#define FRAME_stand36 197
|
||||
#define FRAME_stand37 198
|
||||
#define FRAME_stand38 199
|
||||
#define FRAME_stand39 200
|
||||
#define FRAME_stand40 201
|
||||
#define FRAME_stand41 202
|
||||
#define FRAME_stand42 203
|
||||
#define FRAME_stand43 204
|
||||
#define FRAME_stand44 205
|
||||
#define FRAME_stand45 206
|
||||
#define FRAME_stand46 207
|
||||
#define FRAME_stand47 208
|
||||
#define FRAME_stand48 209
|
||||
#define FRAME_stand49 210
|
||||
#define FRAME_stand50 211
|
||||
#define FRAME_stand51 212
|
||||
#define FRAME_stand52 213
|
||||
#define FRAME_stand53 214
|
||||
#define FRAME_stand54 215
|
||||
#define FRAME_stand55 216
|
||||
#define FRAME_stand56 217
|
||||
#define FRAME_stand57 218
|
||||
#define FRAME_stand58 219
|
||||
#define FRAME_stand59 220
|
||||
#define FRAME_stand60 221
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
chick
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_chick.h"
|
||||
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
|
||||
void chick_stand(edict_t * self);
|
||||
void chick_run(edict_t * self);
|
||||
void chick_reslash(edict_t * self);
|
||||
void chick_rerocket(edict_t * self);
|
||||
void chick_attack1(edict_t * self);
|
||||
|
||||
static int sound_missile_prelaunch;
|
||||
static int sound_missile_launch;
|
||||
static int sound_melee_swing;
|
||||
static int sound_melee_hit;
|
||||
static int sound_missile_reload;
|
||||
static int sound_death1;
|
||||
static int sound_death2;
|
||||
static int sound_fall_down;
|
||||
static int sound_idle1;
|
||||
static int sound_idle2;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_pain3;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
|
||||
void ChickMoan(edict_t * self)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
gi.sound(self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_idle2, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_fidget[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, ChickMoan,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t chick_move_fidget = { FRAME_stand201, FRAME_stand230, chick_frames_fidget, chick_stand };
|
||||
|
||||
void chick_fidget(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
if (random() <= 0.3)
|
||||
self->monsterinfo.currentmove = &chick_move_fidget;
|
||||
}
|
||||
|
||||
mframe_t chick_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, chick_fidget,
|
||||
|
||||
};
|
||||
mmove_t chick_move_stand = { FRAME_stand101, FRAME_stand130, chick_frames_stand, NULL };
|
||||
|
||||
void chick_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_stand;
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_run[] =
|
||||
{
|
||||
ai_run, 1, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, -1, NULL,
|
||||
ai_run, -1, NULL,
|
||||
ai_run, 0, NULL,
|
||||
ai_run, 1, NULL,
|
||||
ai_run, 3, NULL,
|
||||
ai_run, 6, NULL,
|
||||
ai_run, 3, NULL
|
||||
};
|
||||
mmove_t chick_move_start_run = { FRAME_walk01, FRAME_walk10, chick_frames_start_run, chick_run };
|
||||
|
||||
mframe_t chick_frames_run[] =
|
||||
{
|
||||
ai_run, 6, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 5, NULL,
|
||||
ai_run, 7, NULL,
|
||||
ai_run, 4, NULL,
|
||||
ai_run, 11, NULL,
|
||||
ai_run, 5, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 7, NULL
|
||||
|
||||
};
|
||||
|
||||
mmove_t chick_move_run = { FRAME_walk11, FRAME_walk20, chick_frames_run, NULL };
|
||||
|
||||
mframe_t chick_frames_walk[] =
|
||||
{
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 13, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 11, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 9, NULL,
|
||||
ai_walk, 7, NULL
|
||||
};
|
||||
|
||||
mmove_t chick_move_walk = { FRAME_walk11, FRAME_walk20, chick_frames_walk, NULL };
|
||||
|
||||
void chick_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_walk;
|
||||
}
|
||||
|
||||
void chick_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_stand;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.currentmove == &chick_move_walk ||
|
||||
self->monsterinfo.currentmove == &chick_move_start_run)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_run;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_start_run;
|
||||
}
|
||||
}
|
||||
|
||||
mframe_t chick_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t chick_move_pain1 = { FRAME_pain101, FRAME_pain105, chick_frames_pain1, chick_run };
|
||||
|
||||
mframe_t chick_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t chick_move_pain2 = { FRAME_pain201, FRAME_pain205, chick_frames_pain2, chick_run };
|
||||
|
||||
mframe_t chick_frames_pain3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 11, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, 7, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -8, NULL,
|
||||
ai_move, 2, NULL
|
||||
};
|
||||
mmove_t chick_move_pain3 = { FRAME_pain301, FRAME_pain321, chick_frames_pain3, chick_run };
|
||||
|
||||
void chick_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
float r;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
r = random();
|
||||
if (r < 0.33)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else if (r < 0.66)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0);
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 10)
|
||||
self->monsterinfo.currentmove = &chick_move_pain1;
|
||||
else if (damage <= 25)
|
||||
self->monsterinfo.currentmove = &chick_move_pain2;
|
||||
else
|
||||
self->monsterinfo.currentmove = &chick_move_pain3;
|
||||
}
|
||||
|
||||
void chick_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, 0);
|
||||
VectorSet(self->maxs, 16, 16, 16);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_death2[] =
|
||||
{
|
||||
ai_move, -6, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 10, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 15, NULL,
|
||||
ai_move, 14, NULL,
|
||||
ai_move, 1, NULL
|
||||
};
|
||||
mmove_t chick_move_death2 = { FRAME_death201, FRAME_death223, chick_frames_death2, chick_dead };
|
||||
|
||||
mframe_t chick_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 11, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
|
||||
};
|
||||
mmove_t chick_move_death1 = { FRAME_death101, FRAME_death112, chick_frames_death1, chick_dead };
|
||||
|
||||
void chick_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
n = rand() % 2;
|
||||
if (n == 0)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_death1;
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_death2;
|
||||
gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void chick_duck_down(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
return;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
self->maxs[2] -= 32;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.pausetime = level.time + 1;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void chick_duck_hold(edict_t * self)
|
||||
{
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
void chick_duck_up(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_DUCKED;
|
||||
self->maxs[2] += 32;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_duck[] =
|
||||
{
|
||||
ai_move, 0, chick_duck_down,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 4, chick_duck_hold,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -5, chick_duck_up,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL
|
||||
};
|
||||
mmove_t chick_move_duck = { FRAME_duck01, FRAME_duck07, chick_frames_duck, chick_run };
|
||||
|
||||
void chick_dodge(edict_t * self, edict_t * attacker, float eta)
|
||||
{
|
||||
if (random() > 0.25)
|
||||
return;
|
||||
|
||||
if (!self->enemy)
|
||||
self->enemy = attacker;
|
||||
|
||||
self->monsterinfo.currentmove = &chick_move_duck;
|
||||
}
|
||||
|
||||
void ChickSlash(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->mins[0], 10);
|
||||
gi.sound(self, CHAN_WEAPON, sound_melee_swing, 1, ATTN_NORM, 0);
|
||||
fire_hit(self, aim, (10 + (rand() % 6)), 100);
|
||||
}
|
||||
|
||||
void ChickRocket(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_CHICK_ROCKET_1], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
|
||||
void Chick_PreAttack1(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_missile_prelaunch, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void ChickReload(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_missile_reload, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_attack1[] =
|
||||
{
|
||||
ai_charge, 0, Chick_PreAttack1,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 4, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 3, NULL,
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, 7, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, chick_attack1
|
||||
};
|
||||
mmove_t chick_move_start_attack1 = { FRAME_attak101, FRAME_attak113, chick_frames_start_attack1, NULL };
|
||||
|
||||
mframe_t chick_frames_attack1[] =
|
||||
{
|
||||
ai_charge, 19, ChickRocket,
|
||||
ai_charge, -6, NULL,
|
||||
ai_charge, -5, NULL,
|
||||
ai_charge, -2, NULL,
|
||||
ai_charge, -7, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 10, ChickReload,
|
||||
ai_charge, 4, NULL,
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, 6, NULL,
|
||||
ai_charge, 6, NULL,
|
||||
ai_charge, 4, NULL,
|
||||
ai_charge, 3, chick_rerocket
|
||||
|
||||
};
|
||||
mmove_t chick_move_attack1 = { FRAME_attak114, FRAME_attak127, chick_frames_attack1, NULL };
|
||||
|
||||
mframe_t chick_frames_end_attack1[] =
|
||||
{
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -6, NULL,
|
||||
ai_charge, -4, NULL,
|
||||
ai_charge, -2, NULL
|
||||
};
|
||||
mmove_t chick_move_end_attack1 = { FRAME_attak128, FRAME_attak132, chick_frames_end_attack1, chick_run };
|
||||
|
||||
void chick_rerocket(edict_t * self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
if (range(self, self->enemy) > RANGE_MELEE)
|
||||
if (visible(self, self->enemy))
|
||||
if (random() <= 0.6)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_attack1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
self->monsterinfo.currentmove = &chick_move_end_attack1;
|
||||
}
|
||||
|
||||
void chick_attack1(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_attack1;
|
||||
}
|
||||
|
||||
mframe_t chick_frames_slash[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 7, ChickSlash,
|
||||
ai_charge, -7, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, -2, chick_reslash
|
||||
};
|
||||
mmove_t chick_move_slash = { FRAME_attak204, FRAME_attak212, chick_frames_slash, NULL };
|
||||
|
||||
mframe_t chick_frames_end_slash[] =
|
||||
{
|
||||
ai_charge, -6, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, -6, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t chick_move_end_slash = { FRAME_attak213, FRAME_attak216, chick_frames_end_slash, chick_run };
|
||||
|
||||
void chick_reslash(edict_t * self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
{
|
||||
if (range(self, self->enemy) == RANGE_MELEE)
|
||||
if (random() <= 0.9)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_slash;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_end_slash;
|
||||
return;
|
||||
}
|
||||
}
|
||||
self->monsterinfo.currentmove = &chick_move_end_slash;
|
||||
}
|
||||
|
||||
void chick_slash(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_slash;
|
||||
}
|
||||
|
||||
mframe_t chick_frames_start_slash[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 8, NULL,
|
||||
ai_charge, 3, NULL
|
||||
};
|
||||
mmove_t chick_move_start_slash = { FRAME_attak201, FRAME_attak203, chick_frames_start_slash, chick_slash };
|
||||
|
||||
void chick_melee(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_start_slash;
|
||||
}
|
||||
|
||||
void chick_attack(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &chick_move_start_attack1;
|
||||
}
|
||||
|
||||
void chick_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
/*QUAKED monster_chick (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_chick(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_missile_prelaunch = gi.soundindex("chick/chkatck1.wav");
|
||||
sound_missile_launch = gi.soundindex("chick/chkatck2.wav");
|
||||
sound_melee_swing = gi.soundindex("chick/chkatck3.wav");
|
||||
sound_melee_hit = gi.soundindex("chick/chkatck4.wav");
|
||||
sound_missile_reload = gi.soundindex("chick/chkatck5.wav");
|
||||
sound_death1 = gi.soundindex("chick/chkdeth1.wav");
|
||||
sound_death2 = gi.soundindex("chick/chkdeth2.wav");
|
||||
sound_fall_down = gi.soundindex("chick/chkfall1.wav");
|
||||
sound_idle1 = gi.soundindex("chick/chkidle1.wav");
|
||||
sound_idle2 = gi.soundindex("chick/chkidle2.wav");
|
||||
sound_pain1 = gi.soundindex("chick/chkpain1.wav");
|
||||
sound_pain2 = gi.soundindex("chick/chkpain2.wav");
|
||||
sound_pain3 = gi.soundindex("chick/chkpain3.wav");
|
||||
sound_sight = gi.soundindex("chick/chksght1.wav");
|
||||
sound_search = gi.soundindex("chick/chksrch1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, 0);
|
||||
VectorSet(self->maxs, 16, 16, 56);
|
||||
|
||||
self->health = 175;
|
||||
self->gib_health = -70;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = chick_pain;
|
||||
self->die = chick_die;
|
||||
|
||||
self->monsterinfo.stand = chick_stand;
|
||||
self->monsterinfo.walk = chick_walk;
|
||||
self->monsterinfo.run = chick_run;
|
||||
self->monsterinfo.dodge = chick_dodge;
|
||||
self->monsterinfo.attack = chick_attack;
|
||||
self->monsterinfo.melee = chick_melee;
|
||||
self->monsterinfo.sight = chick_sight;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &chick_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/bitch
|
||||
|
||||
// This file generated by qdata - Do NOT Modify
|
||||
|
||||
#define FRAME_attak101 0
|
||||
#define FRAME_attak102 1
|
||||
#define FRAME_attak103 2
|
||||
#define FRAME_attak104 3
|
||||
#define FRAME_attak105 4
|
||||
#define FRAME_attak106 5
|
||||
#define FRAME_attak107 6
|
||||
#define FRAME_attak108 7
|
||||
#define FRAME_attak109 8
|
||||
#define FRAME_attak110 9
|
||||
#define FRAME_attak111 10
|
||||
#define FRAME_attak112 11
|
||||
#define FRAME_attak113 12
|
||||
#define FRAME_attak114 13
|
||||
#define FRAME_attak115 14
|
||||
#define FRAME_attak116 15
|
||||
#define FRAME_attak117 16
|
||||
#define FRAME_attak118 17
|
||||
#define FRAME_attak119 18
|
||||
#define FRAME_attak120 19
|
||||
#define FRAME_attak121 20
|
||||
#define FRAME_attak122 21
|
||||
#define FRAME_attak123 22
|
||||
#define FRAME_attak124 23
|
||||
#define FRAME_attak125 24
|
||||
#define FRAME_attak126 25
|
||||
#define FRAME_attak127 26
|
||||
#define FRAME_attak128 27
|
||||
#define FRAME_attak129 28
|
||||
#define FRAME_attak130 29
|
||||
#define FRAME_attak131 30
|
||||
#define FRAME_attak132 31
|
||||
#define FRAME_attak201 32
|
||||
#define FRAME_attak202 33
|
||||
#define FRAME_attak203 34
|
||||
#define FRAME_attak204 35
|
||||
#define FRAME_attak205 36
|
||||
#define FRAME_attak206 37
|
||||
#define FRAME_attak207 38
|
||||
#define FRAME_attak208 39
|
||||
#define FRAME_attak209 40
|
||||
#define FRAME_attak210 41
|
||||
#define FRAME_attak211 42
|
||||
#define FRAME_attak212 43
|
||||
#define FRAME_attak213 44
|
||||
#define FRAME_attak214 45
|
||||
#define FRAME_attak215 46
|
||||
#define FRAME_attak216 47
|
||||
#define FRAME_death101 48
|
||||
#define FRAME_death102 49
|
||||
#define FRAME_death103 50
|
||||
#define FRAME_death104 51
|
||||
#define FRAME_death105 52
|
||||
#define FRAME_death106 53
|
||||
#define FRAME_death107 54
|
||||
#define FRAME_death108 55
|
||||
#define FRAME_death109 56
|
||||
#define FRAME_death110 57
|
||||
#define FRAME_death111 58
|
||||
#define FRAME_death112 59
|
||||
#define FRAME_death201 60
|
||||
#define FRAME_death202 61
|
||||
#define FRAME_death203 62
|
||||
#define FRAME_death204 63
|
||||
#define FRAME_death205 64
|
||||
#define FRAME_death206 65
|
||||
#define FRAME_death207 66
|
||||
#define FRAME_death208 67
|
||||
#define FRAME_death209 68
|
||||
#define FRAME_death210 69
|
||||
#define FRAME_death211 70
|
||||
#define FRAME_death212 71
|
||||
#define FRAME_death213 72
|
||||
#define FRAME_death214 73
|
||||
#define FRAME_death215 74
|
||||
#define FRAME_death216 75
|
||||
#define FRAME_death217 76
|
||||
#define FRAME_death218 77
|
||||
#define FRAME_death219 78
|
||||
#define FRAME_death220 79
|
||||
#define FRAME_death221 80
|
||||
#define FRAME_death222 81
|
||||
#define FRAME_death223 82
|
||||
#define FRAME_duck01 83
|
||||
#define FRAME_duck02 84
|
||||
#define FRAME_duck03 85
|
||||
#define FRAME_duck04 86
|
||||
#define FRAME_duck05 87
|
||||
#define FRAME_duck06 88
|
||||
#define FRAME_duck07 89
|
||||
#define FRAME_pain101 90
|
||||
#define FRAME_pain102 91
|
||||
#define FRAME_pain103 92
|
||||
#define FRAME_pain104 93
|
||||
#define FRAME_pain105 94
|
||||
#define FRAME_pain201 95
|
||||
#define FRAME_pain202 96
|
||||
#define FRAME_pain203 97
|
||||
#define FRAME_pain204 98
|
||||
#define FRAME_pain205 99
|
||||
#define FRAME_pain301 100
|
||||
#define FRAME_pain302 101
|
||||
#define FRAME_pain303 102
|
||||
#define FRAME_pain304 103
|
||||
#define FRAME_pain305 104
|
||||
#define FRAME_pain306 105
|
||||
#define FRAME_pain307 106
|
||||
#define FRAME_pain308 107
|
||||
#define FRAME_pain309 108
|
||||
#define FRAME_pain310 109
|
||||
#define FRAME_pain311 110
|
||||
#define FRAME_pain312 111
|
||||
#define FRAME_pain313 112
|
||||
#define FRAME_pain314 113
|
||||
#define FRAME_pain315 114
|
||||
#define FRAME_pain316 115
|
||||
#define FRAME_pain317 116
|
||||
#define FRAME_pain318 117
|
||||
#define FRAME_pain319 118
|
||||
#define FRAME_pain320 119
|
||||
#define FRAME_pain321 120
|
||||
#define FRAME_stand101 121
|
||||
#define FRAME_stand102 122
|
||||
#define FRAME_stand103 123
|
||||
#define FRAME_stand104 124
|
||||
#define FRAME_stand105 125
|
||||
#define FRAME_stand106 126
|
||||
#define FRAME_stand107 127
|
||||
#define FRAME_stand108 128
|
||||
#define FRAME_stand109 129
|
||||
#define FRAME_stand110 130
|
||||
#define FRAME_stand111 131
|
||||
#define FRAME_stand112 132
|
||||
#define FRAME_stand113 133
|
||||
#define FRAME_stand114 134
|
||||
#define FRAME_stand115 135
|
||||
#define FRAME_stand116 136
|
||||
#define FRAME_stand117 137
|
||||
#define FRAME_stand118 138
|
||||
#define FRAME_stand119 139
|
||||
#define FRAME_stand120 140
|
||||
#define FRAME_stand121 141
|
||||
#define FRAME_stand122 142
|
||||
#define FRAME_stand123 143
|
||||
#define FRAME_stand124 144
|
||||
#define FRAME_stand125 145
|
||||
#define FRAME_stand126 146
|
||||
#define FRAME_stand127 147
|
||||
#define FRAME_stand128 148
|
||||
#define FRAME_stand129 149
|
||||
#define FRAME_stand130 150
|
||||
#define FRAME_stand201 151
|
||||
#define FRAME_stand202 152
|
||||
#define FRAME_stand203 153
|
||||
#define FRAME_stand204 154
|
||||
#define FRAME_stand205 155
|
||||
#define FRAME_stand206 156
|
||||
#define FRAME_stand207 157
|
||||
#define FRAME_stand208 158
|
||||
#define FRAME_stand209 159
|
||||
#define FRAME_stand210 160
|
||||
#define FRAME_stand211 161
|
||||
#define FRAME_stand212 162
|
||||
#define FRAME_stand213 163
|
||||
#define FRAME_stand214 164
|
||||
#define FRAME_stand215 165
|
||||
#define FRAME_stand216 166
|
||||
#define FRAME_stand217 167
|
||||
#define FRAME_stand218 168
|
||||
#define FRAME_stand219 169
|
||||
#define FRAME_stand220 170
|
||||
#define FRAME_stand221 171
|
||||
#define FRAME_stand222 172
|
||||
#define FRAME_stand223 173
|
||||
#define FRAME_stand224 174
|
||||
#define FRAME_stand225 175
|
||||
#define FRAME_stand226 176
|
||||
#define FRAME_stand227 177
|
||||
#define FRAME_stand228 178
|
||||
#define FRAME_stand229 179
|
||||
#define FRAME_stand230 180
|
||||
#define FRAME_walk01 181
|
||||
#define FRAME_walk02 182
|
||||
#define FRAME_walk03 183
|
||||
#define FRAME_walk04 184
|
||||
#define FRAME_walk05 185
|
||||
#define FRAME_walk06 186
|
||||
#define FRAME_walk07 187
|
||||
#define FRAME_walk08 188
|
||||
#define FRAME_walk09 189
|
||||
#define FRAME_walk10 190
|
||||
#define FRAME_walk11 191
|
||||
#define FRAME_walk12 192
|
||||
#define FRAME_walk13 193
|
||||
#define FRAME_walk14 194
|
||||
#define FRAME_walk15 195
|
||||
#define FRAME_walk16 196
|
||||
#define FRAME_walk17 197
|
||||
#define FRAME_walk18 198
|
||||
#define FRAME_walk19 199
|
||||
#define FRAME_walk20 200
|
||||
#define FRAME_walk21 201
|
||||
#define FRAME_walk22 202
|
||||
#define FRAME_walk23 203
|
||||
#define FRAME_walk24 204
|
||||
#define FRAME_walk25 205
|
||||
#define FRAME_walk26 206
|
||||
#define FRAME_walk27 207
|
||||
#define FRAME_recln201 208
|
||||
#define FRAME_recln202 209
|
||||
#define FRAME_recln203 210
|
||||
#define FRAME_recln204 211
|
||||
#define FRAME_recln205 212
|
||||
#define FRAME_recln206 213
|
||||
#define FRAME_recln207 214
|
||||
#define FRAME_recln208 215
|
||||
#define FRAME_recln209 216
|
||||
#define FRAME_recln210 217
|
||||
#define FRAME_recln211 218
|
||||
#define FRAME_recln212 219
|
||||
#define FRAME_recln213 220
|
||||
#define FRAME_recln214 221
|
||||
#define FRAME_recln215 222
|
||||
#define FRAME_recln216 223
|
||||
#define FRAME_recln217 224
|
||||
#define FRAME_recln218 225
|
||||
#define FRAME_recln219 226
|
||||
#define FRAME_recln220 227
|
||||
#define FRAME_recln221 228
|
||||
#define FRAME_recln222 229
|
||||
#define FRAME_recln223 230
|
||||
#define FRAME_recln224 231
|
||||
#define FRAME_recln225 232
|
||||
#define FRAME_recln226 233
|
||||
#define FRAME_recln227 234
|
||||
#define FRAME_recln228 235
|
||||
#define FRAME_recln229 236
|
||||
#define FRAME_recln230 237
|
||||
#define FRAME_recln231 238
|
||||
#define FRAME_recln232 239
|
||||
#define FRAME_recln233 240
|
||||
#define FRAME_recln234 241
|
||||
#define FRAME_recln235 242
|
||||
#define FRAME_recln236 243
|
||||
#define FRAME_recln237 244
|
||||
#define FRAME_recln238 245
|
||||
#define FRAME_recln239 246
|
||||
#define FRAME_recln240 247
|
||||
#define FRAME_recln101 248
|
||||
#define FRAME_recln102 249
|
||||
#define FRAME_recln103 250
|
||||
#define FRAME_recln104 251
|
||||
#define FRAME_recln105 252
|
||||
#define FRAME_recln106 253
|
||||
#define FRAME_recln107 254
|
||||
#define FRAME_recln108 255
|
||||
#define FRAME_recln109 256
|
||||
#define FRAME_recln110 257
|
||||
#define FRAME_recln111 258
|
||||
#define FRAME_recln112 259
|
||||
#define FRAME_recln113 260
|
||||
#define FRAME_recln114 261
|
||||
#define FRAME_recln115 262
|
||||
#define FRAME_recln116 263
|
||||
#define FRAME_recln117 264
|
||||
#define FRAME_recln118 265
|
||||
#define FRAME_recln119 266
|
||||
#define FRAME_recln120 267
|
||||
#define FRAME_recln121 268
|
||||
#define FRAME_recln122 269
|
||||
#define FRAME_recln123 270
|
||||
#define FRAME_recln124 271
|
||||
#define FRAME_recln125 272
|
||||
#define FRAME_recln126 273
|
||||
#define FRAME_recln127 274
|
||||
#define FRAME_recln128 275
|
||||
#define FRAME_recln129 276
|
||||
#define FRAME_recln130 277
|
||||
#define FRAME_recln131 278
|
||||
#define FRAME_recln132 279
|
||||
#define FRAME_recln133 280
|
||||
#define FRAME_recln134 281
|
||||
#define FRAME_recln135 282
|
||||
#define FRAME_recln136 283
|
||||
#define FRAME_recln137 284
|
||||
#define FRAME_recln138 285
|
||||
#define FRAME_recln139 286
|
||||
#define FRAME_recln140 287
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// m_flash.c
|
||||
|
||||
#include "q_shared.h"
|
||||
|
||||
// this file is included in both the game dll and quake2,
|
||||
// the game needs it to source shot locations, the client
|
||||
// needs it to position muzzle flashes
|
||||
vec3_t monster_flash_offset[] =
|
||||
{
|
||||
// flash 0 is not used
|
||||
0.0, 0.0, 0.0,
|
||||
|
||||
// MZ2_TANK_BLASTER_1 1
|
||||
20.7, -18.5, 28.7,
|
||||
// MZ2_TANK_BLASTER_2 2
|
||||
16.6, -21.5, 30.1,
|
||||
// MZ2_TANK_BLASTER_3 3
|
||||
11.8, -23.9, 32.1,
|
||||
// MZ2_TANK_MACHINEGUN_1 4
|
||||
22.9, -0.7, 25.3,
|
||||
// MZ2_TANK_MACHINEGUN_2 5
|
||||
22.2, 6.2, 22.3,
|
||||
// MZ2_TANK_MACHINEGUN_3 6
|
||||
19.4, 13.1, 18.6,
|
||||
// MZ2_TANK_MACHINEGUN_4 7
|
||||
19.4, 18.8, 18.6,
|
||||
// MZ2_TANK_MACHINEGUN_5 8
|
||||
17.9, 25.0, 18.6,
|
||||
// MZ2_TANK_MACHINEGUN_6 9
|
||||
14.1, 30.5, 20.6,
|
||||
// MZ2_TANK_MACHINEGUN_7 10
|
||||
9.3, 35.3, 22.1,
|
||||
// MZ2_TANK_MACHINEGUN_8 11
|
||||
4.7, 38.4, 22.1,
|
||||
// MZ2_TANK_MACHINEGUN_9 12
|
||||
-1.1, 40.4, 24.1,
|
||||
// MZ2_TANK_MACHINEGUN_10 13
|
||||
-6.5, 41.2, 24.1,
|
||||
// MZ2_TANK_MACHINEGUN_11 14
|
||||
3.2, 40.1, 24.7,
|
||||
// MZ2_TANK_MACHINEGUN_12 15
|
||||
11.7, 36.7, 26.0,
|
||||
// MZ2_TANK_MACHINEGUN_13 16
|
||||
18.9, 31.3, 26.0,
|
||||
// MZ2_TANK_MACHINEGUN_14 17
|
||||
24.4, 24.4, 26.4,
|
||||
// MZ2_TANK_MACHINEGUN_15 18
|
||||
27.1, 17.1, 27.2,
|
||||
// MZ2_TANK_MACHINEGUN_16 19
|
||||
28.5, 9.1, 28.0,
|
||||
// MZ2_TANK_MACHINEGUN_17 20
|
||||
27.1, 2.2, 28.0,
|
||||
// MZ2_TANK_MACHINEGUN_18 21
|
||||
24.9, -2.8, 28.0,
|
||||
// MZ2_TANK_MACHINEGUN_19 22
|
||||
21.6, -7.0, 26.4,
|
||||
// MZ2_TANK_ROCKET_1 23
|
||||
6.2, 29.1, 49.1,
|
||||
// MZ2_TANK_ROCKET_2 24
|
||||
6.9, 23.8, 49.1,
|
||||
// MZ2_TANK_ROCKET_3 25
|
||||
8.3, 17.8, 49.5,
|
||||
|
||||
// MZ2_INFANTRY_MACHINEGUN_1 26
|
||||
26.6, 7.1, 13.1,
|
||||
// MZ2_INFANTRY_MACHINEGUN_2 27
|
||||
18.2, 7.5, 15.4,
|
||||
// MZ2_INFANTRY_MACHINEGUN_3 28
|
||||
17.2, 10.3, 17.9,
|
||||
// MZ2_INFANTRY_MACHINEGUN_4 29
|
||||
17.0, 12.8, 20.1,
|
||||
// MZ2_INFANTRY_MACHINEGUN_5 30
|
||||
15.1, 14.1, 21.8,
|
||||
// MZ2_INFANTRY_MACHINEGUN_6 31
|
||||
11.8, 17.2, 23.1,
|
||||
// MZ2_INFANTRY_MACHINEGUN_7 32
|
||||
11.4, 20.2, 21.0,
|
||||
// MZ2_INFANTRY_MACHINEGUN_8 33
|
||||
9.0, 23.0, 18.9,
|
||||
// MZ2_INFANTRY_MACHINEGUN_9 34
|
||||
13.9, 18.6, 17.7,
|
||||
// MZ2_INFANTRY_MACHINEGUN_10 35
|
||||
15.4, 15.6, 15.8,
|
||||
// MZ2_INFANTRY_MACHINEGUN_11 36
|
||||
10.2, 15.2, 25.1,
|
||||
// MZ2_INFANTRY_MACHINEGUN_12 37
|
||||
-1.9, 15.1, 28.2,
|
||||
// MZ2_INFANTRY_MACHINEGUN_13 38
|
||||
-12.4, 13.0, 20.2,
|
||||
|
||||
// MZ2_SOLDIER_BLASTER_1 39
|
||||
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_2 40
|
||||
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_1 41
|
||||
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_2 42
|
||||
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_1 43
|
||||
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_2 44
|
||||
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||
|
||||
// MZ2_GUNNER_MACHINEGUN_1 45
|
||||
30.1 * 1.15, 3.9 * 1.15, 19.6 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_2 46
|
||||
29.1 * 1.15, 2.5 * 1.15, 20.7 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_3 47
|
||||
28.2 * 1.15, 2.5 * 1.15, 22.2 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_4 48
|
||||
28.2 * 1.15, 3.6 * 1.15, 22.0 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_5 49
|
||||
26.9 * 1.15, 2.0 * 1.15, 23.4 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_6 50
|
||||
26.5 * 1.15, 0.6 * 1.15, 20.8 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_7 51
|
||||
26.9 * 1.15, 0.5 * 1.15, 21.5 * 1.15,
|
||||
// MZ2_GUNNER_MACHINEGUN_8 52
|
||||
29.0 * 1.15, 2.4 * 1.15, 19.5 * 1.15,
|
||||
// MZ2_GUNNER_GRENADE_1 53
|
||||
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||
// MZ2_GUNNER_GRENADE_2 54
|
||||
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||
// MZ2_GUNNER_GRENADE_3 55
|
||||
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||
// MZ2_GUNNER_GRENADE_4 56
|
||||
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||
|
||||
// MZ2_CHICK_ROCKET_1 57
|
||||
// -24.8, -9.0, 39.0,
|
||||
24.8, -9.0, 39.0, // PGM - this was incorrect in Q2
|
||||
|
||||
// MZ2_FLYER_BLASTER_1 58
|
||||
12.1, 13.4, -14.5,
|
||||
// MZ2_FLYER_BLASTER_2 59
|
||||
12.1, -7.4, -14.5,
|
||||
|
||||
// MZ2_MEDIC_BLASTER_1 60
|
||||
12.1, 5.4, 16.5,
|
||||
|
||||
// MZ2_GLADIATOR_RAILGUN_1 61
|
||||
30.0, 18.0, 28.0,
|
||||
|
||||
// MZ2_HOVER_BLASTER_1 62
|
||||
32.5, -0.8, 10.0,
|
||||
|
||||
// MZ2_ACTOR_MACHINEGUN_1 63
|
||||
18.4, 7.4, 9.6,
|
||||
|
||||
// MZ2_SUPERTANK_MACHINEGUN_1 64
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_MACHINEGUN_2 65
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_MACHINEGUN_3 66
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_MACHINEGUN_4 67
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_MACHINEGUN_5 68
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_MACHINEGUN_6 69
|
||||
30.0, 30.0, 88.5,
|
||||
// MZ2_SUPERTANK_ROCKET_1 70
|
||||
16.0, -22.5, 91.2,
|
||||
// MZ2_SUPERTANK_ROCKET_2 71
|
||||
16.0, -33.4, 86.7,
|
||||
// MZ2_SUPERTANK_ROCKET_3 72
|
||||
16.0, -42.8, 83.3,
|
||||
|
||||
// --- Start Xian Stuff ---
|
||||
// MZ2_BOSS2_MACHINEGUN_L1 73
|
||||
32, -40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_L2 74
|
||||
32, -40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_L3 75
|
||||
32, -40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_L4 76
|
||||
32, -40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_L5 77
|
||||
32, -40, 70,
|
||||
// --- End Xian Stuff
|
||||
|
||||
// MZ2_BOSS2_ROCKET_1 78
|
||||
22.0, 16.0, 10.0,
|
||||
// MZ2_BOSS2_ROCKET_2 79
|
||||
22.0, 8.0, 10.0,
|
||||
// MZ2_BOSS2_ROCKET_3 80
|
||||
22.0, -8.0, 10.0,
|
||||
// MZ2_BOSS2_ROCKET_4 81
|
||||
22.0, -16.0, 10.0,
|
||||
|
||||
// MZ2_FLOAT_BLASTER_1 82
|
||||
32.5, -0.8, 10,
|
||||
|
||||
// MZ2_SOLDIER_BLASTER_3 83
|
||||
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_3 84
|
||||
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_3 85
|
||||
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_4 86
|
||||
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_4 87
|
||||
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_4 88
|
||||
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_5 89
|
||||
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_5 90
|
||||
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_5 91
|
||||
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_6 92
|
||||
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_6 93
|
||||
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_6 94
|
||||
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_7 95
|
||||
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_7 96
|
||||
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_7 97
|
||||
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||
// MZ2_SOLDIER_BLASTER_8 98
|
||||
// 34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||
31.5 * 1.2, 9.6 * 1.2, 10.1 * 1.2,
|
||||
// MZ2_SOLDIER_SHOTGUN_8 99
|
||||
34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||
// MZ2_SOLDIER_MACHINEGUN_8 100
|
||||
34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||
|
||||
// --- Xian shit below ---
|
||||
// MZ2_MAKRON_BFG 101
|
||||
17, -19.5, 62.9,
|
||||
// MZ2_MAKRON_BLASTER_1 102
|
||||
-3.6, -24.1, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_2 103
|
||||
-1.6, -19.3, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_3 104
|
||||
-0.1, -14.4, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_4 105
|
||||
2.0, -7.6, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_5 106
|
||||
3.4, 1.3, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_6 107
|
||||
3.7, 11.1, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_7 108
|
||||
-0.3, 22.3, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_8 109
|
||||
-6, 33, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_9 110
|
||||
-9.3, 36.4, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_10 111
|
||||
-7, 35, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_11 112
|
||||
-2.1, 29, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_12 113
|
||||
3.9, 17.3, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_13 114
|
||||
6.1, 5.8, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_14 115
|
||||
5.9, -4.4, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_15 116
|
||||
4.2, -14.1, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_16 117
|
||||
2.4, -18.8, 59.5,
|
||||
// MZ2_MAKRON_BLASTER_17 118
|
||||
-1.8, -25.5, 59.5,
|
||||
// MZ2_MAKRON_RAILGUN_1 119
|
||||
-17.3, 7.8, 72.4,
|
||||
|
||||
// MZ2_JORG_MACHINEGUN_L1 120
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_L2 121
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_L3 122
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_L4 123
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_L5 124
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_L6 125
|
||||
78.5, -47.1, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R1 126
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R2 127
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R3 128
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R4 129
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R5 130
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_MACHINEGUN_R6 131
|
||||
78.5, 46.7, 96,
|
||||
// MZ2_JORG_BFG_1 132
|
||||
6.3, -9, 111.2,
|
||||
|
||||
// MZ2_BOSS2_MACHINEGUN_R1 73
|
||||
32, 40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_R2 74
|
||||
32, 40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_R3 75
|
||||
32, 40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_R4 76
|
||||
32, 40, 70,
|
||||
// MZ2_BOSS2_MACHINEGUN_R5 77
|
||||
32, 40, 70,
|
||||
|
||||
// --- End Xian Shit ---
|
||||
|
||||
// ROGUE
|
||||
// note that the above really ends at 137
|
||||
// carrier machineguns
|
||||
// MZ2_CARRIER_MACHINEGUN_L1
|
||||
56, -32, 32,
|
||||
// MZ2_CARRIER_MACHINEGUN_R1
|
||||
56, 32, 32,
|
||||
// MZ2_CARRIER_GRENADE
|
||||
42, 24, 50,
|
||||
// MZ2_TURRET_MACHINEGUN 141
|
||||
16, 0, 0,
|
||||
// MZ2_TURRET_ROCKET 142
|
||||
16, 0, 0,
|
||||
// MZ2_TURRET_BLASTER 143
|
||||
16, 0, 0,
|
||||
// MZ2_STALKER_BLASTER 144
|
||||
24, 0, 6,
|
||||
// MZ2_DAEDALUS_BLASTER 145
|
||||
32.5, -0.8, 10.0,
|
||||
// MZ2_MEDIC_BLASTER_2 146
|
||||
12.1, 5.4, 16.5,
|
||||
// MZ2_CARRIER_RAILGUN 147
|
||||
32, 0, 6,
|
||||
// MZ2_WIDOW_DISRUPTOR 148
|
||||
57.72, 14.50, 88.81,
|
||||
// MZ2_WIDOW_BLASTER 149
|
||||
56, 32, 32,
|
||||
// MZ2_WIDOW_RAIL 150
|
||||
62, -20, 84,
|
||||
// MZ2_WIDOW_PLASMABEAM 151 // PMM - not used!
|
||||
32, 0, 6,
|
||||
// MZ2_CARRIER_MACHINEGUN_L2 152
|
||||
61, -32, 12,
|
||||
// MZ2_CARRIER_MACHINEGUN_R2 153
|
||||
61, 32, 12,
|
||||
// MZ2_WIDOW_RAIL_LEFT 154
|
||||
17, -62, 91,
|
||||
// MZ2_WIDOW_RAIL_RIGHT 155
|
||||
68, 12, 86,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP1 156 pmm - the sweeps need to be in sequential order
|
||||
47.5, 56, 89,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP2 157
|
||||
54, 52, 91,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP3 158
|
||||
58, 40, 91,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP4 159
|
||||
68, 30, 88,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP5 160
|
||||
74, 20, 88,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP6 161
|
||||
73, 11, 87,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP7 162
|
||||
73, 3, 87,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP8 163
|
||||
70, -12, 87,
|
||||
// MZ2_WIDOW_BLASTER_SWEEP9 164
|
||||
67, -20, 90,
|
||||
// MZ2_WIDOW_BLASTER_100 165
|
||||
-20, 76, 90,
|
||||
// MZ2_WIDOW_BLASTER_90 166
|
||||
-8, 74, 90,
|
||||
// MZ2_WIDOW_BLASTER_80 167
|
||||
0, 72, 90,
|
||||
// MZ2_WIDOW_BLASTER_70 168 d06
|
||||
10, 71, 89,
|
||||
// MZ2_WIDOW_BLASTER_60 169 d07
|
||||
23, 70, 87,
|
||||
// MZ2_WIDOW_BLASTER_50 170 d08
|
||||
32, 64, 85,
|
||||
// MZ2_WIDOW_BLASTER_40 171
|
||||
40, 58, 84,
|
||||
// MZ2_WIDOW_BLASTER_30 172 d10
|
||||
48, 50, 83,
|
||||
// MZ2_WIDOW_BLASTER_20 173
|
||||
54, 42, 82,
|
||||
// MZ2_WIDOW_BLASTER_10 174 d12
|
||||
56, 34, 82,
|
||||
// MZ2_WIDOW_BLASTER_0 175
|
||||
58, 26, 82,
|
||||
// MZ2_WIDOW_BLASTER_10L 176 d14
|
||||
60, 16, 82,
|
||||
// MZ2_WIDOW_BLASTER_20L 177
|
||||
59, 6, 81,
|
||||
// MZ2_WIDOW_BLASTER_30L 178 d16
|
||||
58, -2, 80,
|
||||
// MZ2_WIDOW_BLASTER_40L 179
|
||||
57, -10, 79,
|
||||
// MZ2_WIDOW_BLASTER_50L 180 d18
|
||||
54, -18, 78,
|
||||
// MZ2_WIDOW_BLASTER_60L 181
|
||||
42, -32, 80,
|
||||
// MZ2_WIDOW_BLASTER_70L 182 d20
|
||||
36, -40, 78,
|
||||
// MZ2_WIDOW_RUN_1 183
|
||||
68.4, 10.88, 82.08,
|
||||
// MZ2_WIDOW_RUN_2 184
|
||||
68.51, 8.64, 85.14,
|
||||
// MZ2_WIDOW_RUN_3 185
|
||||
68.66, 6.38, 88.78,
|
||||
// MZ2_WIDOW_RUN_4 186
|
||||
68.73, 5.1, 84.47,
|
||||
// MZ2_WIDOW_RUN_5 187
|
||||
68.82, 4.79, 80.52,
|
||||
// MZ2_WIDOW_RUN_6 188
|
||||
68.77, 6.11, 85.37,
|
||||
// MZ2_WIDOW_RUN_7 189
|
||||
68.67, 7.99, 90.24,
|
||||
// MZ2_WIDOW_RUN_8 190
|
||||
68.55, 9.54, 87.36,
|
||||
// MZ2_CARRIER_ROCKET_1 191
|
||||
0, 0, -5,
|
||||
// MZ2_CARRIER_ROCKET_2 192
|
||||
0, 0, -5,
|
||||
// MZ2_CARRIER_ROCKET_3 193
|
||||
0, 0, -5,
|
||||
// MZ2_CARRIER_ROCKET_4 194
|
||||
0, 0, -5,
|
||||
// MZ2_WIDOW2_BEAMER_1 195
|
||||
// 72.13, -17.63, 93.77,
|
||||
69.00, -17.63, 93.77,
|
||||
// MZ2_WIDOW2_BEAMER_2 196
|
||||
// 71.46, -17.08, 89.82,
|
||||
69.00, -17.08, 89.82,
|
||||
// MZ2_WIDOW2_BEAMER_3 197
|
||||
// 71.47, -18.40, 90.70,
|
||||
69.00, -18.40, 90.70,
|
||||
// MZ2_WIDOW2_BEAMER_4 198
|
||||
// 71.96, -18.34, 94.32,
|
||||
69.00, -18.34, 94.32,
|
||||
// MZ2_WIDOW2_BEAMER_5 199
|
||||
// 72.25, -18.30, 97.98,
|
||||
69.00, -18.30, 97.98,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_1 200
|
||||
45.04, -59.02, 92.24,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_2 201
|
||||
50.68, -54.70, 91.96,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_3 202
|
||||
56.57, -47.72, 91.65,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_4 203
|
||||
61.75, -38.75, 91.38,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_5 204
|
||||
65.55, -28.76, 91.24,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_6 205
|
||||
67.79, -18.90, 91.22,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_7 206
|
||||
68.60, -9.52, 91.23,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_8 207
|
||||
68.08, 0.18, 91.32,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_9 208
|
||||
66.14, 9.79, 91.44,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_10 209
|
||||
62.77, 18.91, 91.65,
|
||||
// MZ2_WIDOW2_BEAM_SWEEP_11 210
|
||||
58.29, 27.11, 92.00,
|
||||
|
||||
// end of table
|
||||
0.0, 0.0, 0.0
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
FLIPPER
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_flipper.h"
|
||||
|
||||
static int sound_chomp;
|
||||
static int sound_attack;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_death;
|
||||
static int sound_idle;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
void flipper_stand(edict_t * self);
|
||||
|
||||
mframe_t flipper_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
|
||||
mmove_t flipper_move_stand = { FRAME_flphor01, FRAME_flphor01, flipper_frames_stand, NULL };
|
||||
|
||||
void flipper_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_stand;
|
||||
}
|
||||
|
||||
#define FLIPPER_RUN_SPEED 24
|
||||
|
||||
mframe_t flipper_frames_run[] =
|
||||
{
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL, // 6
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL, // 10
|
||||
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL, // 20
|
||||
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL,
|
||||
ai_run, FLIPPER_RUN_SPEED, NULL // 29
|
||||
};
|
||||
mmove_t flipper_move_run_loop = { FRAME_flpver06, FRAME_flpver29, flipper_frames_run, NULL };
|
||||
|
||||
void flipper_run_loop(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_run_loop;
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_run_start[] =
|
||||
{
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL
|
||||
};
|
||||
mmove_t flipper_move_run_start = { FRAME_flpver01, FRAME_flpver06, flipper_frames_run_start, flipper_run_loop };
|
||||
|
||||
void flipper_run(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_run_start;
|
||||
}
|
||||
|
||||
/* Standard Swimming */
|
||||
mframe_t flipper_frames_walk[] =
|
||||
{
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL
|
||||
};
|
||||
mmove_t flipper_move_walk = { FRAME_flphor01, FRAME_flphor24, flipper_frames_walk, NULL };
|
||||
|
||||
void flipper_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_walk;
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_start_run[] =
|
||||
{
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 8, flipper_run
|
||||
};
|
||||
mmove_t flipper_move_start_run = { FRAME_flphor01, FRAME_flphor05, flipper_frames_start_run, NULL };
|
||||
|
||||
void flipper_start_run(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_start_run;
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flipper_move_pain2 = { FRAME_flppn101, FRAME_flppn105, flipper_frames_pain2, flipper_run };
|
||||
|
||||
mframe_t flipper_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flipper_move_pain1 = { FRAME_flppn201, FRAME_flppn205, flipper_frames_pain1, flipper_run };
|
||||
|
||||
void flipper_bite(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, 0, 0);
|
||||
fire_hit(self, aim, 5, 0);
|
||||
}
|
||||
|
||||
void flipper_preattack(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_chomp, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_attack[] =
|
||||
{
|
||||
ai_charge, 0, flipper_preattack,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, flipper_bite,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, flipper_bite,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t flipper_move_attack = { FRAME_flpbit01, FRAME_flpbit20, flipper_frames_attack, flipper_run };
|
||||
|
||||
void flipper_melee(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flipper_move_attack;
|
||||
}
|
||||
|
||||
void flipper_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
n = (rand() + 1) % 2;
|
||||
if (n == 0)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &flipper_move_pain1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &flipper_move_pain2;
|
||||
}
|
||||
}
|
||||
|
||||
void flipper_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t flipper_frames_death[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flipper_move_death = { FRAME_flpdth01, FRAME_flpdth56, flipper_frames_death, flipper_dead };
|
||||
|
||||
void flipper_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void flipper_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.currentmove = &flipper_move_death;
|
||||
}
|
||||
|
||||
/*QUAKED monster_flipper (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_flipper(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("flipper/flppain1.wav");
|
||||
sound_pain2 = gi.soundindex("flipper/flppain2.wav");
|
||||
sound_death = gi.soundindex("flipper/flpdeth1.wav");
|
||||
sound_chomp = gi.soundindex("flipper/flpatck1.wav");
|
||||
sound_attack = gi.soundindex("flipper/flpatck2.wav");
|
||||
sound_idle = gi.soundindex("flipper/flpidle1.wav");
|
||||
sound_search = gi.soundindex("flipper/flpsrch1.wav");
|
||||
sound_sight = gi.soundindex("flipper/flpsght1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/flipper/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, 0);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 50;
|
||||
self->gib_health = -30;
|
||||
self->mass = 100;
|
||||
|
||||
self->pain = flipper_pain;
|
||||
self->die = flipper_die;
|
||||
|
||||
self->monsterinfo.stand = flipper_stand;
|
||||
self->monsterinfo.walk = flipper_walk;
|
||||
self->monsterinfo.run = flipper_start_run;
|
||||
self->monsterinfo.melee = flipper_melee;
|
||||
self->monsterinfo.sight = flipper_sight;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &flipper_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
swimmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/flipper
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_flpbit01 0
|
||||
#define FRAME_flpbit02 1
|
||||
#define FRAME_flpbit03 2
|
||||
#define FRAME_flpbit04 3
|
||||
#define FRAME_flpbit05 4
|
||||
#define FRAME_flpbit06 5
|
||||
#define FRAME_flpbit07 6
|
||||
#define FRAME_flpbit08 7
|
||||
#define FRAME_flpbit09 8
|
||||
#define FRAME_flpbit10 9
|
||||
#define FRAME_flpbit11 10
|
||||
#define FRAME_flpbit12 11
|
||||
#define FRAME_flpbit13 12
|
||||
#define FRAME_flpbit14 13
|
||||
#define FRAME_flpbit15 14
|
||||
#define FRAME_flpbit16 15
|
||||
#define FRAME_flpbit17 16
|
||||
#define FRAME_flpbit18 17
|
||||
#define FRAME_flpbit19 18
|
||||
#define FRAME_flpbit20 19
|
||||
#define FRAME_flptal01 20
|
||||
#define FRAME_flptal02 21
|
||||
#define FRAME_flptal03 22
|
||||
#define FRAME_flptal04 23
|
||||
#define FRAME_flptal05 24
|
||||
#define FRAME_flptal06 25
|
||||
#define FRAME_flptal07 26
|
||||
#define FRAME_flptal08 27
|
||||
#define FRAME_flptal09 28
|
||||
#define FRAME_flptal10 29
|
||||
#define FRAME_flptal11 30
|
||||
#define FRAME_flptal12 31
|
||||
#define FRAME_flptal13 32
|
||||
#define FRAME_flptal14 33
|
||||
#define FRAME_flptal15 34
|
||||
#define FRAME_flptal16 35
|
||||
#define FRAME_flptal17 36
|
||||
#define FRAME_flptal18 37
|
||||
#define FRAME_flptal19 38
|
||||
#define FRAME_flptal20 39
|
||||
#define FRAME_flptal21 40
|
||||
#define FRAME_flphor01 41
|
||||
#define FRAME_flphor02 42
|
||||
#define FRAME_flphor03 43
|
||||
#define FRAME_flphor04 44
|
||||
#define FRAME_flphor05 45
|
||||
#define FRAME_flphor06 46
|
||||
#define FRAME_flphor07 47
|
||||
#define FRAME_flphor08 48
|
||||
#define FRAME_flphor09 49
|
||||
#define FRAME_flphor10 50
|
||||
#define FRAME_flphor11 51
|
||||
#define FRAME_flphor12 52
|
||||
#define FRAME_flphor13 53
|
||||
#define FRAME_flphor14 54
|
||||
#define FRAME_flphor15 55
|
||||
#define FRAME_flphor16 56
|
||||
#define FRAME_flphor17 57
|
||||
#define FRAME_flphor18 58
|
||||
#define FRAME_flphor19 59
|
||||
#define FRAME_flphor20 60
|
||||
#define FRAME_flphor21 61
|
||||
#define FRAME_flphor22 62
|
||||
#define FRAME_flphor23 63
|
||||
#define FRAME_flphor24 64
|
||||
#define FRAME_flpver01 65
|
||||
#define FRAME_flpver02 66
|
||||
#define FRAME_flpver03 67
|
||||
#define FRAME_flpver04 68
|
||||
#define FRAME_flpver05 69
|
||||
#define FRAME_flpver06 70
|
||||
#define FRAME_flpver07 71
|
||||
#define FRAME_flpver08 72
|
||||
#define FRAME_flpver09 73
|
||||
#define FRAME_flpver10 74
|
||||
#define FRAME_flpver11 75
|
||||
#define FRAME_flpver12 76
|
||||
#define FRAME_flpver13 77
|
||||
#define FRAME_flpver14 78
|
||||
#define FRAME_flpver15 79
|
||||
#define FRAME_flpver16 80
|
||||
#define FRAME_flpver17 81
|
||||
#define FRAME_flpver18 82
|
||||
#define FRAME_flpver19 83
|
||||
#define FRAME_flpver20 84
|
||||
#define FRAME_flpver21 85
|
||||
#define FRAME_flpver22 86
|
||||
#define FRAME_flpver23 87
|
||||
#define FRAME_flpver24 88
|
||||
#define FRAME_flpver25 89
|
||||
#define FRAME_flpver26 90
|
||||
#define FRAME_flpver27 91
|
||||
#define FRAME_flpver28 92
|
||||
#define FRAME_flpver29 93
|
||||
#define FRAME_flppn101 94
|
||||
#define FRAME_flppn102 95
|
||||
#define FRAME_flppn103 96
|
||||
#define FRAME_flppn104 97
|
||||
#define FRAME_flppn105 98
|
||||
#define FRAME_flppn201 99
|
||||
#define FRAME_flppn202 100
|
||||
#define FRAME_flppn203 101
|
||||
#define FRAME_flppn204 102
|
||||
#define FRAME_flppn205 103
|
||||
#define FRAME_flpdth01 104
|
||||
#define FRAME_flpdth02 105
|
||||
#define FRAME_flpdth03 106
|
||||
#define FRAME_flpdth04 107
|
||||
#define FRAME_flpdth05 108
|
||||
#define FRAME_flpdth06 109
|
||||
#define FRAME_flpdth07 110
|
||||
#define FRAME_flpdth08 111
|
||||
#define FRAME_flpdth09 112
|
||||
#define FRAME_flpdth10 113
|
||||
#define FRAME_flpdth11 114
|
||||
#define FRAME_flpdth12 115
|
||||
#define FRAME_flpdth13 116
|
||||
#define FRAME_flpdth14 117
|
||||
#define FRAME_flpdth15 118
|
||||
#define FRAME_flpdth16 119
|
||||
#define FRAME_flpdth17 120
|
||||
#define FRAME_flpdth18 121
|
||||
#define FRAME_flpdth19 122
|
||||
#define FRAME_flpdth20 123
|
||||
#define FRAME_flpdth21 124
|
||||
#define FRAME_flpdth22 125
|
||||
#define FRAME_flpdth23 126
|
||||
#define FRAME_flpdth24 127
|
||||
#define FRAME_flpdth25 128
|
||||
#define FRAME_flpdth26 129
|
||||
#define FRAME_flpdth27 130
|
||||
#define FRAME_flpdth28 131
|
||||
#define FRAME_flpdth29 132
|
||||
#define FRAME_flpdth30 133
|
||||
#define FRAME_flpdth31 134
|
||||
#define FRAME_flpdth32 135
|
||||
#define FRAME_flpdth33 136
|
||||
#define FRAME_flpdth34 137
|
||||
#define FRAME_flpdth35 138
|
||||
#define FRAME_flpdth36 139
|
||||
#define FRAME_flpdth37 140
|
||||
#define FRAME_flpdth38 141
|
||||
#define FRAME_flpdth39 142
|
||||
#define FRAME_flpdth40 143
|
||||
#define FRAME_flpdth41 144
|
||||
#define FRAME_flpdth42 145
|
||||
#define FRAME_flpdth43 146
|
||||
#define FRAME_flpdth44 147
|
||||
#define FRAME_flpdth45 148
|
||||
#define FRAME_flpdth46 149
|
||||
#define FRAME_flpdth47 150
|
||||
#define FRAME_flpdth48 151
|
||||
#define FRAME_flpdth49 152
|
||||
#define FRAME_flpdth50 153
|
||||
#define FRAME_flpdth51 154
|
||||
#define FRAME_flpdth52 155
|
||||
#define FRAME_flpdth53 156
|
||||
#define FRAME_flpdth54 157
|
||||
#define FRAME_flpdth55 158
|
||||
#define FRAME_flpdth56 159
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
floater
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_float.h"
|
||||
|
||||
static int sound_attack2;
|
||||
static int sound_attack3;
|
||||
static int sound_death1;
|
||||
static int sound_idle;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_sight;
|
||||
|
||||
void floater_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void floater_idle(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
//void floater_stand1 (edict_t *self);
|
||||
void floater_dead(edict_t * self);
|
||||
void floater_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point);
|
||||
void floater_run(edict_t * self);
|
||||
void floater_wham(edict_t * self);
|
||||
void floater_zap(edict_t * self);
|
||||
|
||||
void floater_fire_blaster(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
int effect;
|
||||
|
||||
if ((self->s.frame == FRAME_attak104) || (self->s.frame == FRAME_attak107))
|
||||
effect = EF_HYPERBLASTER;
|
||||
else
|
||||
effect = 0;
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_FLOAT_BLASTER_1], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, end);
|
||||
end[2] += self->enemy->viewheight;
|
||||
VectorSubtract(end, start, dir);
|
||||
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, MZ2_FLOAT_BLASTER_1, effect);
|
||||
}
|
||||
|
||||
mframe_t floater_frames_stand1[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_stand1 = { FRAME_stand101, FRAME_stand152, floater_frames_stand1, NULL };
|
||||
|
||||
mframe_t floater_frames_stand2[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_stand2 = { FRAME_stand201, FRAME_stand252, floater_frames_stand2, NULL };
|
||||
|
||||
void floater_stand(edict_t * self)
|
||||
{
|
||||
if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &floater_move_stand1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &floater_move_stand2;
|
||||
}
|
||||
|
||||
mframe_t floater_frames_activate[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_activate = { FRAME_actvat01, FRAME_actvat31, floater_frames_activate, NULL };
|
||||
|
||||
mframe_t floater_frames_attack1[] =
|
||||
{
|
||||
ai_charge, 0, NULL, // Blaster attack
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, floater_fire_blaster, // BOOM (0, -25.8, 32.5) -- LOOP Starts
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, floater_fire_blaster,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL // -- LOOP Ends
|
||||
};
|
||||
mmove_t floater_move_attack1 = { FRAME_attak101, FRAME_attak114, floater_frames_attack1, floater_run };
|
||||
|
||||
mframe_t floater_frames_attack2[] =
|
||||
{
|
||||
ai_charge, 0, NULL, // Claws
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, floater_wham, // WHAM (0, -45, 29.6) -- LOOP Starts
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL, // -- LOOP Ends
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_attack2 = { FRAME_attak201, FRAME_attak225, floater_frames_attack2, floater_run };
|
||||
|
||||
mframe_t floater_frames_attack3[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, floater_zap, // -- LOOP Starts
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL, // -- LOOP Ends
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_attack3 = { FRAME_attak301, FRAME_attak334, floater_frames_attack3, floater_run };
|
||||
|
||||
mframe_t floater_frames_death[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_death = { FRAME_death01, FRAME_death13, floater_frames_death, floater_dead };
|
||||
|
||||
mframe_t floater_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_pain1 = { FRAME_pain101, FRAME_pain107, floater_frames_pain1, floater_run };
|
||||
|
||||
mframe_t floater_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_pain2 = { FRAME_pain201, FRAME_pain208, floater_frames_pain2, floater_run };
|
||||
|
||||
mframe_t floater_frames_pain3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t floater_move_pain3 = { FRAME_pain301, FRAME_pain312, floater_frames_pain3, floater_run };
|
||||
|
||||
mframe_t floater_frames_walk[] =
|
||||
{
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL
|
||||
};
|
||||
mmove_t floater_move_walk = { FRAME_stand101, FRAME_stand152, floater_frames_walk, NULL };
|
||||
|
||||
mframe_t floater_frames_run[] =
|
||||
{
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 13, NULL
|
||||
};
|
||||
mmove_t floater_move_run = { FRAME_stand101, FRAME_stand152, floater_frames_run, NULL };
|
||||
|
||||
void floater_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &floater_move_stand1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &floater_move_run;
|
||||
}
|
||||
|
||||
void floater_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &floater_move_walk;
|
||||
}
|
||||
|
||||
void floater_wham(edict_t * self)
|
||||
{
|
||||
static vec3_t aim = { MELEE_DISTANCE, 0, 0 };
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack3, 1, ATTN_NORM, 0);
|
||||
fire_hit(self, aim, 5 + rand() % 6, -50);
|
||||
}
|
||||
|
||||
void floater_zap(edict_t * self)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t origin;
|
||||
vec3_t dir;
|
||||
vec3_t offset;
|
||||
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
//FIXME use a flash and replace these two lines with the commented one
|
||||
VectorSet(offset, 18.5, -0.9, 10);
|
||||
G_ProjectSource(self->s.origin, offset, forward, right, origin);
|
||||
// G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, origin);
|
||||
|
||||
gi.sound(self, CHAN_WEAPON, sound_attack2, 1, ATTN_NORM, 0);
|
||||
|
||||
//FIXME use the flash, Luke
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_SPLASH);
|
||||
gi.WriteByte(32);
|
||||
gi.WritePosition(origin);
|
||||
gi.WriteDir(dir);
|
||||
gi.WriteByte(1); //sparks
|
||||
gi.multicast(origin, MULTICAST_PVS);
|
||||
|
||||
T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, 5 + rand() % 6, -10, DAMAGE_ENERGY, MOD_UNKNOWN);
|
||||
}
|
||||
|
||||
void floater_attack(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &floater_move_attack1;
|
||||
}
|
||||
|
||||
void floater_melee(edict_t * self)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
self->monsterinfo.currentmove = &floater_move_attack3;
|
||||
else
|
||||
self->monsterinfo.currentmove = &floater_move_attack2;
|
||||
}
|
||||
|
||||
void floater_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
n = (rand() + 1) % 3;
|
||||
if (n == 0)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &floater_move_pain1;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &floater_move_pain2;
|
||||
}
|
||||
}
|
||||
|
||||
void floater_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void floater_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
BecomeExplosion1(self);
|
||||
}
|
||||
|
||||
/*QUAKED monster_floater (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_floater(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_attack2 = gi.soundindex("floater/fltatck2.wav");
|
||||
sound_attack3 = gi.soundindex("floater/fltatck3.wav");
|
||||
sound_death1 = gi.soundindex("floater/fltdeth1.wav");
|
||||
sound_idle = gi.soundindex("floater/fltidle1.wav");
|
||||
sound_pain1 = gi.soundindex("floater/fltpain1.wav");
|
||||
sound_pain2 = gi.soundindex("floater/fltpain2.wav");
|
||||
sound_sight = gi.soundindex("floater/fltsght1.wav");
|
||||
|
||||
gi.soundindex("floater/fltatck1.wav");
|
||||
|
||||
self->s.sound = gi.soundindex("floater/fltsrch1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/float/tris.md2");
|
||||
VectorSet(self->mins, -24, -24, -24);
|
||||
VectorSet(self->maxs, 24, 24, 32);
|
||||
|
||||
self->health = 200;
|
||||
self->gib_health = -80;
|
||||
self->mass = 300;
|
||||
|
||||
self->pain = floater_pain;
|
||||
self->die = floater_die;
|
||||
|
||||
self->monsterinfo.stand = floater_stand;
|
||||
self->monsterinfo.walk = floater_walk;
|
||||
self->monsterinfo.run = floater_run;
|
||||
// self->monsterinfo.dodge = floater_dodge;
|
||||
self->monsterinfo.attack = floater_attack;
|
||||
self->monsterinfo.melee = floater_melee;
|
||||
self->monsterinfo.sight = floater_sight;
|
||||
self->monsterinfo.idle = floater_idle;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &floater_move_stand1;
|
||||
else
|
||||
self->monsterinfo.currentmove = &floater_move_stand2;
|
||||
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/float
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_actvat01 0
|
||||
#define FRAME_actvat02 1
|
||||
#define FRAME_actvat03 2
|
||||
#define FRAME_actvat04 3
|
||||
#define FRAME_actvat05 4
|
||||
#define FRAME_actvat06 5
|
||||
#define FRAME_actvat07 6
|
||||
#define FRAME_actvat08 7
|
||||
#define FRAME_actvat09 8
|
||||
#define FRAME_actvat10 9
|
||||
#define FRAME_actvat11 10
|
||||
#define FRAME_actvat12 11
|
||||
#define FRAME_actvat13 12
|
||||
#define FRAME_actvat14 13
|
||||
#define FRAME_actvat15 14
|
||||
#define FRAME_actvat16 15
|
||||
#define FRAME_actvat17 16
|
||||
#define FRAME_actvat18 17
|
||||
#define FRAME_actvat19 18
|
||||
#define FRAME_actvat20 19
|
||||
#define FRAME_actvat21 20
|
||||
#define FRAME_actvat22 21
|
||||
#define FRAME_actvat23 22
|
||||
#define FRAME_actvat24 23
|
||||
#define FRAME_actvat25 24
|
||||
#define FRAME_actvat26 25
|
||||
#define FRAME_actvat27 26
|
||||
#define FRAME_actvat28 27
|
||||
#define FRAME_actvat29 28
|
||||
#define FRAME_actvat30 29
|
||||
#define FRAME_actvat31 30
|
||||
#define FRAME_attak101 31
|
||||
#define FRAME_attak102 32
|
||||
#define FRAME_attak103 33
|
||||
#define FRAME_attak104 34
|
||||
#define FRAME_attak105 35
|
||||
#define FRAME_attak106 36
|
||||
#define FRAME_attak107 37
|
||||
#define FRAME_attak108 38
|
||||
#define FRAME_attak109 39
|
||||
#define FRAME_attak110 40
|
||||
#define FRAME_attak111 41
|
||||
#define FRAME_attak112 42
|
||||
#define FRAME_attak113 43
|
||||
#define FRAME_attak114 44
|
||||
#define FRAME_attak201 45
|
||||
#define FRAME_attak202 46
|
||||
#define FRAME_attak203 47
|
||||
#define FRAME_attak204 48
|
||||
#define FRAME_attak205 49
|
||||
#define FRAME_attak206 50
|
||||
#define FRAME_attak207 51
|
||||
#define FRAME_attak208 52
|
||||
#define FRAME_attak209 53
|
||||
#define FRAME_attak210 54
|
||||
#define FRAME_attak211 55
|
||||
#define FRAME_attak212 56
|
||||
#define FRAME_attak213 57
|
||||
#define FRAME_attak214 58
|
||||
#define FRAME_attak215 59
|
||||
#define FRAME_attak216 60
|
||||
#define FRAME_attak217 61
|
||||
#define FRAME_attak218 62
|
||||
#define FRAME_attak219 63
|
||||
#define FRAME_attak220 64
|
||||
#define FRAME_attak221 65
|
||||
#define FRAME_attak222 66
|
||||
#define FRAME_attak223 67
|
||||
#define FRAME_attak224 68
|
||||
#define FRAME_attak225 69
|
||||
#define FRAME_attak301 70
|
||||
#define FRAME_attak302 71
|
||||
#define FRAME_attak303 72
|
||||
#define FRAME_attak304 73
|
||||
#define FRAME_attak305 74
|
||||
#define FRAME_attak306 75
|
||||
#define FRAME_attak307 76
|
||||
#define FRAME_attak308 77
|
||||
#define FRAME_attak309 78
|
||||
#define FRAME_attak310 79
|
||||
#define FRAME_attak311 80
|
||||
#define FRAME_attak312 81
|
||||
#define FRAME_attak313 82
|
||||
#define FRAME_attak314 83
|
||||
#define FRAME_attak315 84
|
||||
#define FRAME_attak316 85
|
||||
#define FRAME_attak317 86
|
||||
#define FRAME_attak318 87
|
||||
#define FRAME_attak319 88
|
||||
#define FRAME_attak320 89
|
||||
#define FRAME_attak321 90
|
||||
#define FRAME_attak322 91
|
||||
#define FRAME_attak323 92
|
||||
#define FRAME_attak324 93
|
||||
#define FRAME_attak325 94
|
||||
#define FRAME_attak326 95
|
||||
#define FRAME_attak327 96
|
||||
#define FRAME_attak328 97
|
||||
#define FRAME_attak329 98
|
||||
#define FRAME_attak330 99
|
||||
#define FRAME_attak331 100
|
||||
#define FRAME_attak332 101
|
||||
#define FRAME_attak333 102
|
||||
#define FRAME_attak334 103
|
||||
#define FRAME_death01 104
|
||||
#define FRAME_death02 105
|
||||
#define FRAME_death03 106
|
||||
#define FRAME_death04 107
|
||||
#define FRAME_death05 108
|
||||
#define FRAME_death06 109
|
||||
#define FRAME_death07 110
|
||||
#define FRAME_death08 111
|
||||
#define FRAME_death09 112
|
||||
#define FRAME_death10 113
|
||||
#define FRAME_death11 114
|
||||
#define FRAME_death12 115
|
||||
#define FRAME_death13 116
|
||||
#define FRAME_pain101 117
|
||||
#define FRAME_pain102 118
|
||||
#define FRAME_pain103 119
|
||||
#define FRAME_pain104 120
|
||||
#define FRAME_pain105 121
|
||||
#define FRAME_pain106 122
|
||||
#define FRAME_pain107 123
|
||||
#define FRAME_pain201 124
|
||||
#define FRAME_pain202 125
|
||||
#define FRAME_pain203 126
|
||||
#define FRAME_pain204 127
|
||||
#define FRAME_pain205 128
|
||||
#define FRAME_pain206 129
|
||||
#define FRAME_pain207 130
|
||||
#define FRAME_pain208 131
|
||||
#define FRAME_pain301 132
|
||||
#define FRAME_pain302 133
|
||||
#define FRAME_pain303 134
|
||||
#define FRAME_pain304 135
|
||||
#define FRAME_pain305 136
|
||||
#define FRAME_pain306 137
|
||||
#define FRAME_pain307 138
|
||||
#define FRAME_pain308 139
|
||||
#define FRAME_pain309 140
|
||||
#define FRAME_pain310 141
|
||||
#define FRAME_pain311 142
|
||||
#define FRAME_pain312 143
|
||||
#define FRAME_stand101 144
|
||||
#define FRAME_stand102 145
|
||||
#define FRAME_stand103 146
|
||||
#define FRAME_stand104 147
|
||||
#define FRAME_stand105 148
|
||||
#define FRAME_stand106 149
|
||||
#define FRAME_stand107 150
|
||||
#define FRAME_stand108 151
|
||||
#define FRAME_stand109 152
|
||||
#define FRAME_stand110 153
|
||||
#define FRAME_stand111 154
|
||||
#define FRAME_stand112 155
|
||||
#define FRAME_stand113 156
|
||||
#define FRAME_stand114 157
|
||||
#define FRAME_stand115 158
|
||||
#define FRAME_stand116 159
|
||||
#define FRAME_stand117 160
|
||||
#define FRAME_stand118 161
|
||||
#define FRAME_stand119 162
|
||||
#define FRAME_stand120 163
|
||||
#define FRAME_stand121 164
|
||||
#define FRAME_stand122 165
|
||||
#define FRAME_stand123 166
|
||||
#define FRAME_stand124 167
|
||||
#define FRAME_stand125 168
|
||||
#define FRAME_stand126 169
|
||||
#define FRAME_stand127 170
|
||||
#define FRAME_stand128 171
|
||||
#define FRAME_stand129 172
|
||||
#define FRAME_stand130 173
|
||||
#define FRAME_stand131 174
|
||||
#define FRAME_stand132 175
|
||||
#define FRAME_stand133 176
|
||||
#define FRAME_stand134 177
|
||||
#define FRAME_stand135 178
|
||||
#define FRAME_stand136 179
|
||||
#define FRAME_stand137 180
|
||||
#define FRAME_stand138 181
|
||||
#define FRAME_stand139 182
|
||||
#define FRAME_stand140 183
|
||||
#define FRAME_stand141 184
|
||||
#define FRAME_stand142 185
|
||||
#define FRAME_stand143 186
|
||||
#define FRAME_stand144 187
|
||||
#define FRAME_stand145 188
|
||||
#define FRAME_stand146 189
|
||||
#define FRAME_stand147 190
|
||||
#define FRAME_stand148 191
|
||||
#define FRAME_stand149 192
|
||||
#define FRAME_stand150 193
|
||||
#define FRAME_stand151 194
|
||||
#define FRAME_stand152 195
|
||||
#define FRAME_stand201 196
|
||||
#define FRAME_stand202 197
|
||||
#define FRAME_stand203 198
|
||||
#define FRAME_stand204 199
|
||||
#define FRAME_stand205 200
|
||||
#define FRAME_stand206 201
|
||||
#define FRAME_stand207 202
|
||||
#define FRAME_stand208 203
|
||||
#define FRAME_stand209 204
|
||||
#define FRAME_stand210 205
|
||||
#define FRAME_stand211 206
|
||||
#define FRAME_stand212 207
|
||||
#define FRAME_stand213 208
|
||||
#define FRAME_stand214 209
|
||||
#define FRAME_stand215 210
|
||||
#define FRAME_stand216 211
|
||||
#define FRAME_stand217 212
|
||||
#define FRAME_stand218 213
|
||||
#define FRAME_stand219 214
|
||||
#define FRAME_stand220 215
|
||||
#define FRAME_stand221 216
|
||||
#define FRAME_stand222 217
|
||||
#define FRAME_stand223 218
|
||||
#define FRAME_stand224 219
|
||||
#define FRAME_stand225 220
|
||||
#define FRAME_stand226 221
|
||||
#define FRAME_stand227 222
|
||||
#define FRAME_stand228 223
|
||||
#define FRAME_stand229 224
|
||||
#define FRAME_stand230 225
|
||||
#define FRAME_stand231 226
|
||||
#define FRAME_stand232 227
|
||||
#define FRAME_stand233 228
|
||||
#define FRAME_stand234 229
|
||||
#define FRAME_stand235 230
|
||||
#define FRAME_stand236 231
|
||||
#define FRAME_stand237 232
|
||||
#define FRAME_stand238 233
|
||||
#define FRAME_stand239 234
|
||||
#define FRAME_stand240 235
|
||||
#define FRAME_stand241 236
|
||||
#define FRAME_stand242 237
|
||||
#define FRAME_stand243 238
|
||||
#define FRAME_stand244 239
|
||||
#define FRAME_stand245 240
|
||||
#define FRAME_stand246 241
|
||||
#define FRAME_stand247 242
|
||||
#define FRAME_stand248 243
|
||||
#define FRAME_stand249 244
|
||||
#define FRAME_stand250 245
|
||||
#define FRAME_stand251 246
|
||||
#define FRAME_stand252 247
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
flyer
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_flyer.h"
|
||||
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
|
||||
static int nextmove; // Used for start/stop frames
|
||||
|
||||
static int sound_sight;
|
||||
static int sound_idle;
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_slash;
|
||||
static int sound_sproing;
|
||||
static int sound_die;
|
||||
|
||||
void flyer_check_melee(edict_t * self);
|
||||
void flyer_loop_melee(edict_t * self);
|
||||
void flyer_melee(edict_t * self);
|
||||
void flyer_setstart(edict_t * self);
|
||||
void flyer_stand(edict_t * self);
|
||||
void flyer_nextmove(edict_t * self);
|
||||
|
||||
void flyer_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void flyer_idle(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void flyer_pop_blades(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sproing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_stand = { FRAME_stand01, FRAME_stand45, flyer_frames_stand, NULL };
|
||||
|
||||
mframe_t flyer_frames_walk[] =
|
||||
{
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL
|
||||
};
|
||||
mmove_t flyer_move_walk = { FRAME_stand01, FRAME_stand45, flyer_frames_walk, NULL };
|
||||
|
||||
mframe_t flyer_frames_run[] =
|
||||
{
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL
|
||||
};
|
||||
mmove_t flyer_move_run = { FRAME_stand01, FRAME_stand45, flyer_frames_run, NULL };
|
||||
|
||||
void flyer_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &flyer_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &flyer_move_run;
|
||||
}
|
||||
|
||||
void flyer_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flyer_move_walk;
|
||||
}
|
||||
|
||||
void flyer_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flyer_move_stand;
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_start[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, flyer_nextmove
|
||||
};
|
||||
mmove_t flyer_move_start = { FRAME_start01, FRAME_start06, flyer_frames_start, NULL };
|
||||
|
||||
mframe_t flyer_frames_stop[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, flyer_nextmove
|
||||
};
|
||||
mmove_t flyer_move_stop = { FRAME_stop01, FRAME_stop07, flyer_frames_stop, NULL };
|
||||
|
||||
void flyer_stop(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flyer_move_stop;
|
||||
}
|
||||
|
||||
void flyer_start(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &flyer_move_start;
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_rollright[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_rollright = { FRAME_rollr01, FRAME_rollr09, flyer_frames_rollright, NULL };
|
||||
|
||||
mframe_t flyer_frames_rollleft[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_rollleft = { FRAME_rollf01, FRAME_rollf09, flyer_frames_rollleft, NULL };
|
||||
|
||||
mframe_t flyer_frames_pain3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_pain3 = { FRAME_pain301, FRAME_pain304, flyer_frames_pain3, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_pain2 = { FRAME_pain201, FRAME_pain204, flyer_frames_pain2, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_pain1 = { FRAME_pain101, FRAME_pain109, flyer_frames_pain1, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_defense[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL, // Hold this frame
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_defense = { FRAME_defens01, FRAME_defens06, flyer_frames_defense, NULL };
|
||||
|
||||
mframe_t flyer_frames_bankright[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_bankright = { FRAME_bankr01, FRAME_bankr07, flyer_frames_bankright, NULL };
|
||||
|
||||
mframe_t flyer_frames_bankleft[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_bankleft = { FRAME_bankl01, FRAME_bankl07, flyer_frames_bankleft, NULL };
|
||||
|
||||
void flyer_fire(edict_t * self, int flash_number)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
int effect;
|
||||
|
||||
if ((self->s.frame == FRAME_attak204) || (self->s.frame == FRAME_attak207) || (self->s.frame == FRAME_attak210))
|
||||
effect = EF_HYPERBLASTER;
|
||||
else
|
||||
effect = 0;
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, end);
|
||||
end[2] += self->enemy->viewheight;
|
||||
VectorSubtract(end, start, dir);
|
||||
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, flash_number, effect);
|
||||
}
|
||||
|
||||
void flyer_fireleft(edict_t * self)
|
||||
{
|
||||
flyer_fire(self, MZ2_FLYER_BLASTER_1);
|
||||
}
|
||||
|
||||
void flyer_fireright(edict_t * self)
|
||||
{
|
||||
flyer_fire(self, MZ2_FLYER_BLASTER_2);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_attack2[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, -10, flyer_fireleft, // left gun
|
||||
ai_charge, -10, flyer_fireright, // right gun
|
||||
ai_charge, -10, flyer_fireleft, // left gun
|
||||
ai_charge, -10, flyer_fireright, // right gun
|
||||
ai_charge, -10, flyer_fireleft, // left gun
|
||||
ai_charge, -10, flyer_fireright, // right gun
|
||||
ai_charge, -10, flyer_fireleft, // left gun
|
||||
ai_charge, -10, flyer_fireright, // right gun
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_attack2 = { FRAME_attak201, FRAME_attak217, flyer_frames_attack2, flyer_run };
|
||||
|
||||
void flyer_slash_left(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->mins[0], 0);
|
||||
fire_hit(self, aim, 5, 0);
|
||||
gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void flyer_slash_right(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->maxs[0], 0);
|
||||
fire_hit(self, aim, 5, 0);
|
||||
gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t flyer_frames_start_melee[] =
|
||||
{
|
||||
ai_charge, 0, flyer_pop_blades,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_start_melee = { FRAME_attak101, FRAME_attak106, flyer_frames_start_melee, flyer_loop_melee };
|
||||
|
||||
mframe_t flyer_frames_end_melee[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t flyer_move_end_melee = { FRAME_attak119, FRAME_attak121, flyer_frames_end_melee, flyer_run };
|
||||
|
||||
mframe_t flyer_frames_loop_melee[] =
|
||||
{
|
||||
ai_charge, 0, NULL, // Loop Start
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, flyer_slash_left, // Left Wing Strike
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, flyer_slash_right, // Right Wing Strike
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL // Loop Ends
|
||||
|
||||
};
|
||||
mmove_t flyer_move_loop_melee = { FRAME_attak107, FRAME_attak118, flyer_frames_loop_melee, flyer_check_melee };
|
||||
|
||||
void flyer_loop_melee(edict_t * self)
|
||||
{
|
||||
/* if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &flyer_move_attack1;
|
||||
else */
|
||||
self->monsterinfo.currentmove = &flyer_move_loop_melee;
|
||||
}
|
||||
|
||||
void flyer_attack(edict_t * self)
|
||||
{
|
||||
/* if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &flyer_move_attack1;
|
||||
else */
|
||||
self->monsterinfo.currentmove = &flyer_move_attack2;
|
||||
}
|
||||
|
||||
void flyer_setstart(edict_t * self)
|
||||
{
|
||||
nextmove = ACTION_run;
|
||||
self->monsterinfo.currentmove = &flyer_move_start;
|
||||
}
|
||||
|
||||
void flyer_nextmove(edict_t * self)
|
||||
{
|
||||
if (nextmove == ACTION_attack1)
|
||||
self->monsterinfo.currentmove = &flyer_move_start_melee;
|
||||
else if (nextmove == ACTION_attack2)
|
||||
self->monsterinfo.currentmove = &flyer_move_attack2;
|
||||
else if (nextmove == ACTION_run)
|
||||
self->monsterinfo.currentmove = &flyer_move_run;
|
||||
}
|
||||
|
||||
void flyer_melee(edict_t * self)
|
||||
{
|
||||
// flyer.nextmove = ACTION_attack1;
|
||||
// self->monsterinfo.currentmove = &flyer_move_stop;
|
||||
self->monsterinfo.currentmove = &flyer_move_start_melee;
|
||||
}
|
||||
|
||||
void flyer_check_melee(edict_t * self)
|
||||
{
|
||||
if (range(self, self->enemy) == RANGE_MELEE)
|
||||
if (random() <= 0.8)
|
||||
self->monsterinfo.currentmove = &flyer_move_loop_melee;
|
||||
else
|
||||
self->monsterinfo.currentmove = &flyer_move_end_melee;
|
||||
else
|
||||
self->monsterinfo.currentmove = &flyer_move_end_melee;
|
||||
}
|
||||
|
||||
void flyer_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
n = rand() % 3;
|
||||
if (n == 0)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &flyer_move_pain1;
|
||||
}
|
||||
else if (n == 1)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &flyer_move_pain2;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &flyer_move_pain3;
|
||||
}
|
||||
}
|
||||
|
||||
void flyer_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
BecomeExplosion1(self);
|
||||
}
|
||||
|
||||
/*QUAKED monster_flyer (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_flyer(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// fix a map bug in jail5.bsp
|
||||
if (!Q_stricmp(level.mapname, "jail5") && (self->s.origin[2] == -104))
|
||||
{
|
||||
self->targetname = self->target;
|
||||
self->target = NULL;
|
||||
}
|
||||
|
||||
sound_sight = gi.soundindex("flyer/flysght1.wav");
|
||||
sound_idle = gi.soundindex("flyer/flysrch1.wav");
|
||||
sound_pain1 = gi.soundindex("flyer/flypain1.wav");
|
||||
sound_pain2 = gi.soundindex("flyer/flypain2.wav");
|
||||
sound_slash = gi.soundindex("flyer/flyatck2.wav");
|
||||
sound_sproing = gi.soundindex("flyer/flyatck1.wav");
|
||||
sound_die = gi.soundindex("flyer/flydeth1.wav");
|
||||
|
||||
gi.soundindex("flyer/flyatck3.wav");
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/monsters/flyer/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->s.sound = gi.soundindex("flyer/flyidle1.wav");
|
||||
|
||||
self->health = 50;
|
||||
self->mass = 50;
|
||||
|
||||
self->pain = flyer_pain;
|
||||
self->die = flyer_die;
|
||||
|
||||
self->monsterinfo.stand = flyer_stand;
|
||||
self->monsterinfo.walk = flyer_walk;
|
||||
self->monsterinfo.run = flyer_run;
|
||||
self->monsterinfo.attack = flyer_attack;
|
||||
self->monsterinfo.melee = flyer_melee;
|
||||
self->monsterinfo.sight = flyer_sight;
|
||||
self->monsterinfo.idle = flyer_idle;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &flyer_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/flyer
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define ACTION_nothing 0
|
||||
#define ACTION_attack1 1
|
||||
#define ACTION_attack2 2
|
||||
#define ACTION_run 3
|
||||
#define ACTION_walk 4
|
||||
|
||||
#define FRAME_start01 0
|
||||
#define FRAME_start02 1
|
||||
#define FRAME_start03 2
|
||||
#define FRAME_start04 3
|
||||
#define FRAME_start05 4
|
||||
#define FRAME_start06 5
|
||||
#define FRAME_stop01 6
|
||||
#define FRAME_stop02 7
|
||||
#define FRAME_stop03 8
|
||||
#define FRAME_stop04 9
|
||||
#define FRAME_stop05 10
|
||||
#define FRAME_stop06 11
|
||||
#define FRAME_stop07 12
|
||||
#define FRAME_stand01 13
|
||||
#define FRAME_stand02 14
|
||||
#define FRAME_stand03 15
|
||||
#define FRAME_stand04 16
|
||||
#define FRAME_stand05 17
|
||||
#define FRAME_stand06 18
|
||||
#define FRAME_stand07 19
|
||||
#define FRAME_stand08 20
|
||||
#define FRAME_stand09 21
|
||||
#define FRAME_stand10 22
|
||||
#define FRAME_stand11 23
|
||||
#define FRAME_stand12 24
|
||||
#define FRAME_stand13 25
|
||||
#define FRAME_stand14 26
|
||||
#define FRAME_stand15 27
|
||||
#define FRAME_stand16 28
|
||||
#define FRAME_stand17 29
|
||||
#define FRAME_stand18 30
|
||||
#define FRAME_stand19 31
|
||||
#define FRAME_stand20 32
|
||||
#define FRAME_stand21 33
|
||||
#define FRAME_stand22 34
|
||||
#define FRAME_stand23 35
|
||||
#define FRAME_stand24 36
|
||||
#define FRAME_stand25 37
|
||||
#define FRAME_stand26 38
|
||||
#define FRAME_stand27 39
|
||||
#define FRAME_stand28 40
|
||||
#define FRAME_stand29 41
|
||||
#define FRAME_stand30 42
|
||||
#define FRAME_stand31 43
|
||||
#define FRAME_stand32 44
|
||||
#define FRAME_stand33 45
|
||||
#define FRAME_stand34 46
|
||||
#define FRAME_stand35 47
|
||||
#define FRAME_stand36 48
|
||||
#define FRAME_stand37 49
|
||||
#define FRAME_stand38 50
|
||||
#define FRAME_stand39 51
|
||||
#define FRAME_stand40 52
|
||||
#define FRAME_stand41 53
|
||||
#define FRAME_stand42 54
|
||||
#define FRAME_stand43 55
|
||||
#define FRAME_stand44 56
|
||||
#define FRAME_stand45 57
|
||||
#define FRAME_attak101 58
|
||||
#define FRAME_attak102 59
|
||||
#define FRAME_attak103 60
|
||||
#define FRAME_attak104 61
|
||||
#define FRAME_attak105 62
|
||||
#define FRAME_attak106 63
|
||||
#define FRAME_attak107 64
|
||||
#define FRAME_attak108 65
|
||||
#define FRAME_attak109 66
|
||||
#define FRAME_attak110 67
|
||||
#define FRAME_attak111 68
|
||||
#define FRAME_attak112 69
|
||||
#define FRAME_attak113 70
|
||||
#define FRAME_attak114 71
|
||||
#define FRAME_attak115 72
|
||||
#define FRAME_attak116 73
|
||||
#define FRAME_attak117 74
|
||||
#define FRAME_attak118 75
|
||||
#define FRAME_attak119 76
|
||||
#define FRAME_attak120 77
|
||||
#define FRAME_attak121 78
|
||||
#define FRAME_attak201 79
|
||||
#define FRAME_attak202 80
|
||||
#define FRAME_attak203 81
|
||||
#define FRAME_attak204 82
|
||||
#define FRAME_attak205 83
|
||||
#define FRAME_attak206 84
|
||||
#define FRAME_attak207 85
|
||||
#define FRAME_attak208 86
|
||||
#define FRAME_attak209 87
|
||||
#define FRAME_attak210 88
|
||||
#define FRAME_attak211 89
|
||||
#define FRAME_attak212 90
|
||||
#define FRAME_attak213 91
|
||||
#define FRAME_attak214 92
|
||||
#define FRAME_attak215 93
|
||||
#define FRAME_attak216 94
|
||||
#define FRAME_attak217 95
|
||||
#define FRAME_bankl01 96
|
||||
#define FRAME_bankl02 97
|
||||
#define FRAME_bankl03 98
|
||||
#define FRAME_bankl04 99
|
||||
#define FRAME_bankl05 100
|
||||
#define FRAME_bankl06 101
|
||||
#define FRAME_bankl07 102
|
||||
#define FRAME_bankr01 103
|
||||
#define FRAME_bankr02 104
|
||||
#define FRAME_bankr03 105
|
||||
#define FRAME_bankr04 106
|
||||
#define FRAME_bankr05 107
|
||||
#define FRAME_bankr06 108
|
||||
#define FRAME_bankr07 109
|
||||
#define FRAME_rollf01 110
|
||||
#define FRAME_rollf02 111
|
||||
#define FRAME_rollf03 112
|
||||
#define FRAME_rollf04 113
|
||||
#define FRAME_rollf05 114
|
||||
#define FRAME_rollf06 115
|
||||
#define FRAME_rollf07 116
|
||||
#define FRAME_rollf08 117
|
||||
#define FRAME_rollf09 118
|
||||
#define FRAME_rollr01 119
|
||||
#define FRAME_rollr02 120
|
||||
#define FRAME_rollr03 121
|
||||
#define FRAME_rollr04 122
|
||||
#define FRAME_rollr05 123
|
||||
#define FRAME_rollr06 124
|
||||
#define FRAME_rollr07 125
|
||||
#define FRAME_rollr08 126
|
||||
#define FRAME_rollr09 127
|
||||
#define FRAME_defens01 128
|
||||
#define FRAME_defens02 129
|
||||
#define FRAME_defens03 130
|
||||
#define FRAME_defens04 131
|
||||
#define FRAME_defens05 132
|
||||
#define FRAME_defens06 133
|
||||
#define FRAME_pain101 134
|
||||
#define FRAME_pain102 135
|
||||
#define FRAME_pain103 136
|
||||
#define FRAME_pain104 137
|
||||
#define FRAME_pain105 138
|
||||
#define FRAME_pain106 139
|
||||
#define FRAME_pain107 140
|
||||
#define FRAME_pain108 141
|
||||
#define FRAME_pain109 142
|
||||
#define FRAME_pain201 143
|
||||
#define FRAME_pain202 144
|
||||
#define FRAME_pain203 145
|
||||
#define FRAME_pain204 146
|
||||
#define FRAME_pain301 147
|
||||
#define FRAME_pain302 148
|
||||
#define FRAME_pain303 149
|
||||
#define FRAME_pain304 150
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GLADIATOR
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_gladiator.h"
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_die;
|
||||
static int sound_gun;
|
||||
static int sound_cleaver_swing;
|
||||
static int sound_cleaver_hit;
|
||||
static int sound_cleaver_miss;
|
||||
static int sound_idle;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
void gladiator_idle(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void gladiator_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void gladiator_search(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void gladiator_cleaver_swing(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_cleaver_swing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_stand = { FRAME_stand1, FRAME_stand7, gladiator_frames_stand, NULL };
|
||||
|
||||
void gladiator_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gladiator_move_stand;
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_walk[] =
|
||||
{
|
||||
ai_walk, 15, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 12, NULL,
|
||||
ai_walk, 8, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 1, NULL,
|
||||
ai_walk, 8, NULL
|
||||
};
|
||||
mmove_t gladiator_move_walk = { FRAME_walk1, FRAME_walk16, gladiator_frames_walk, NULL };
|
||||
|
||||
void gladiator_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gladiator_move_walk;
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_run[] =
|
||||
{
|
||||
ai_run, 23, NULL,
|
||||
ai_run, 14, NULL,
|
||||
ai_run, 14, NULL,
|
||||
ai_run, 21, NULL,
|
||||
ai_run, 12, NULL,
|
||||
ai_run, 13, NULL
|
||||
};
|
||||
mmove_t gladiator_move_run = { FRAME_run1, FRAME_run6, gladiator_frames_run, NULL };
|
||||
|
||||
void gladiator_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &gladiator_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &gladiator_move_run;
|
||||
}
|
||||
|
||||
void GaldiatorMelee(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, self->mins[0], -4);
|
||||
if (fire_hit(self, aim, (20 + (rand() % 5)), 300))
|
||||
gi.sound(self, CHAN_AUTO, sound_cleaver_hit, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_AUTO, sound_cleaver_miss, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_attack_melee[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, gladiator_cleaver_swing,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GaldiatorMelee,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, gladiator_cleaver_swing,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GaldiatorMelee,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_attack_melee = { FRAME_melee1, FRAME_melee17, gladiator_frames_attack_melee, gladiator_run };
|
||||
|
||||
void gladiator_melee(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gladiator_move_attack_melee;
|
||||
}
|
||||
|
||||
void GladiatorGun(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right, start);
|
||||
|
||||
// calc direction to where we targted
|
||||
VectorSubtract(self->pos1, start, dir);
|
||||
VectorNormalize(dir);
|
||||
|
||||
monster_fire_railgun(self, start, dir, 50, 100, MZ2_GLADIATOR_RAILGUN_1);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_attack_gun[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GladiatorGun,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_attack_gun = { FRAME_attack1, FRAME_attack9, gladiator_frames_attack_gun, gladiator_run };
|
||||
|
||||
void gladiator_attack(edict_t * self)
|
||||
{
|
||||
float range;
|
||||
vec3_t v;
|
||||
|
||||
// a small safe zone
|
||||
VectorSubtract(self->s.origin, self->enemy->s.origin, v);
|
||||
range = VectorLength(v);
|
||||
if (range <= (MELEE_DISTANCE + 32))
|
||||
return;
|
||||
|
||||
// charge up the railgun
|
||||
gi.sound(self, CHAN_WEAPON, sound_gun, 1, ATTN_NORM, 0);
|
||||
VectorCopy(self->enemy->s.origin, self->pos1); //save for aiming the shot
|
||||
self->pos1[2] += self->enemy->viewheight;
|
||||
self->monsterinfo.currentmove = &gladiator_move_attack_gun;
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_pain[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_pain = { FRAME_pain1, FRAME_pain6, gladiator_frames_pain, gladiator_run };
|
||||
|
||||
mframe_t gladiator_frames_pain_air[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_pain_air = { FRAME_painup1, FRAME_painup7, gladiator_frames_pain_air, gladiator_run };
|
||||
|
||||
void gladiator_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
{
|
||||
if ((self->velocity[2] > 100) && (self->monsterinfo.currentmove == &gladiator_move_pain))
|
||||
self->monsterinfo.currentmove = &gladiator_move_pain_air;
|
||||
return;
|
||||
}
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
if (random() < 0.5)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (self->velocity[2] > 100)
|
||||
self->monsterinfo.currentmove = &gladiator_move_pain_air;
|
||||
else
|
||||
self->monsterinfo.currentmove = &gladiator_move_pain;
|
||||
}
|
||||
|
||||
void gladiator_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t gladiator_frames_death[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t gladiator_move_death = { FRAME_death1, FRAME_death22, gladiator_frames_death, gladiator_dead };
|
||||
|
||||
void gladiator_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
self->monsterinfo.currentmove = &gladiator_move_death;
|
||||
}
|
||||
|
||||
/*QUAKED monster_gladiator (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_gladiator(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("gladiator/pain.wav");
|
||||
sound_pain2 = gi.soundindex("gladiator/gldpain2.wav");
|
||||
sound_die = gi.soundindex("gladiator/glddeth2.wav");
|
||||
sound_gun = gi.soundindex("gladiator/railgun.wav");
|
||||
sound_cleaver_swing = gi.soundindex("gladiator/melee1.wav");
|
||||
sound_cleaver_hit = gi.soundindex("gladiator/melee2.wav");
|
||||
sound_cleaver_miss = gi.soundindex("gladiator/melee3.wav");
|
||||
sound_idle = gi.soundindex("gladiator/gldidle1.wav");
|
||||
sound_search = gi.soundindex("gladiator/gldsrch1.wav");
|
||||
sound_sight = gi.soundindex("gladiator/sight.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/gladiatr/tris.md2");
|
||||
VectorSet(self->mins, -32, -32, -24);
|
||||
VectorSet(self->maxs, 32, 32, 64);
|
||||
|
||||
self->health = 400;
|
||||
self->gib_health = -175;
|
||||
self->mass = 400;
|
||||
|
||||
self->pain = gladiator_pain;
|
||||
self->die = gladiator_die;
|
||||
|
||||
self->monsterinfo.stand = gladiator_stand;
|
||||
self->monsterinfo.walk = gladiator_walk;
|
||||
self->monsterinfo.run = gladiator_run;
|
||||
self->monsterinfo.dodge = NULL;
|
||||
self->monsterinfo.attack = gladiator_attack;
|
||||
self->monsterinfo.melee = gladiator_melee;
|
||||
self->monsterinfo.sight = gladiator_sight;
|
||||
self->monsterinfo.idle = gladiator_idle;
|
||||
self->monsterinfo.search = gladiator_search;
|
||||
|
||||
gi.linkentity(self);
|
||||
self->monsterinfo.currentmove = &gladiator_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/gladiatr
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_stand1 0
|
||||
#define FRAME_stand2 1
|
||||
#define FRAME_stand3 2
|
||||
#define FRAME_stand4 3
|
||||
#define FRAME_stand5 4
|
||||
#define FRAME_stand6 5
|
||||
#define FRAME_stand7 6
|
||||
#define FRAME_walk1 7
|
||||
#define FRAME_walk2 8
|
||||
#define FRAME_walk3 9
|
||||
#define FRAME_walk4 10
|
||||
#define FRAME_walk5 11
|
||||
#define FRAME_walk6 12
|
||||
#define FRAME_walk7 13
|
||||
#define FRAME_walk8 14
|
||||
#define FRAME_walk9 15
|
||||
#define FRAME_walk10 16
|
||||
#define FRAME_walk11 17
|
||||
#define FRAME_walk12 18
|
||||
#define FRAME_walk13 19
|
||||
#define FRAME_walk14 20
|
||||
#define FRAME_walk15 21
|
||||
#define FRAME_walk16 22
|
||||
#define FRAME_run1 23
|
||||
#define FRAME_run2 24
|
||||
#define FRAME_run3 25
|
||||
#define FRAME_run4 26
|
||||
#define FRAME_run5 27
|
||||
#define FRAME_run6 28
|
||||
#define FRAME_melee1 29
|
||||
#define FRAME_melee2 30
|
||||
#define FRAME_melee3 31
|
||||
#define FRAME_melee4 32
|
||||
#define FRAME_melee5 33
|
||||
#define FRAME_melee6 34
|
||||
#define FRAME_melee7 35
|
||||
#define FRAME_melee8 36
|
||||
#define FRAME_melee9 37
|
||||
#define FRAME_melee10 38
|
||||
#define FRAME_melee11 39
|
||||
#define FRAME_melee12 40
|
||||
#define FRAME_melee13 41
|
||||
#define FRAME_melee14 42
|
||||
#define FRAME_melee15 43
|
||||
#define FRAME_melee16 44
|
||||
#define FRAME_melee17 45
|
||||
#define FRAME_attack1 46
|
||||
#define FRAME_attack2 47
|
||||
#define FRAME_attack3 48
|
||||
#define FRAME_attack4 49
|
||||
#define FRAME_attack5 50
|
||||
#define FRAME_attack6 51
|
||||
#define FRAME_attack7 52
|
||||
#define FRAME_attack8 53
|
||||
#define FRAME_attack9 54
|
||||
#define FRAME_pain1 55
|
||||
#define FRAME_pain2 56
|
||||
#define FRAME_pain3 57
|
||||
#define FRAME_pain4 58
|
||||
#define FRAME_pain5 59
|
||||
#define FRAME_pain6 60
|
||||
#define FRAME_death1 61
|
||||
#define FRAME_death2 62
|
||||
#define FRAME_death3 63
|
||||
#define FRAME_death4 64
|
||||
#define FRAME_death5 65
|
||||
#define FRAME_death6 66
|
||||
#define FRAME_death7 67
|
||||
#define FRAME_death8 68
|
||||
#define FRAME_death9 69
|
||||
#define FRAME_death10 70
|
||||
#define FRAME_death11 71
|
||||
#define FRAME_death12 72
|
||||
#define FRAME_death13 73
|
||||
#define FRAME_death14 74
|
||||
#define FRAME_death15 75
|
||||
#define FRAME_death16 76
|
||||
#define FRAME_death17 77
|
||||
#define FRAME_death18 78
|
||||
#define FRAME_death19 79
|
||||
#define FRAME_death20 80
|
||||
#define FRAME_death21 81
|
||||
#define FRAME_death22 82
|
||||
#define FRAME_painup1 83
|
||||
#define FRAME_painup2 84
|
||||
#define FRAME_painup3 85
|
||||
#define FRAME_painup4 86
|
||||
#define FRAME_painup5 87
|
||||
#define FRAME_painup6 88
|
||||
#define FRAME_painup7 89
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GUNNER
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_gunner.h"
|
||||
|
||||
static int sound_pain;
|
||||
static int sound_pain2;
|
||||
static int sound_death;
|
||||
static int sound_idle;
|
||||
static int sound_open;
|
||||
static int sound_search;
|
||||
static int sound_sight;
|
||||
|
||||
void gunner_idlesound(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void gunner_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void gunner_search(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
void GunnerGrenade(edict_t * self);
|
||||
void GunnerFire(edict_t * self);
|
||||
void gunner_fire_chain(edict_t * self);
|
||||
void gunner_refire_chain(edict_t * self);
|
||||
|
||||
void gunner_stand(edict_t * self);
|
||||
|
||||
mframe_t gunner_frames_fidget[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, gunner_idlesound,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_fidget = { FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand };
|
||||
|
||||
void gunner_fidget(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
return;
|
||||
if (random() <= 0.05)
|
||||
self->monsterinfo.currentmove = &gunner_move_fidget;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, gunner_fidget,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, gunner_fidget,
|
||||
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, gunner_fidget
|
||||
};
|
||||
mmove_t gunner_move_stand = { FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL };
|
||||
|
||||
void gunner_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_stand;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_walk[] =
|
||||
{
|
||||
ai_walk, 0, NULL,
|
||||
ai_walk, 3, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 2, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 7, NULL,
|
||||
ai_walk, 4, NULL
|
||||
};
|
||||
mmove_t gunner_move_walk = { FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL };
|
||||
|
||||
void gunner_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_walk;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_run[] =
|
||||
{
|
||||
ai_run, 26, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 9, NULL,
|
||||
ai_run, 15, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 13, NULL,
|
||||
ai_run, 6, NULL
|
||||
};
|
||||
|
||||
mmove_t gunner_move_run = { FRAME_run01, FRAME_run08, gunner_frames_run, NULL };
|
||||
|
||||
void gunner_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &gunner_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &gunner_move_run;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_runandshoot[] =
|
||||
{
|
||||
ai_run, 32, NULL,
|
||||
ai_run, 15, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 18, NULL,
|
||||
ai_run, 8, NULL,
|
||||
ai_run, 20, NULL
|
||||
};
|
||||
|
||||
mmove_t gunner_move_runandshoot = { FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL };
|
||||
|
||||
void gunner_runandshoot(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_runandshoot;
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_pain3[] =
|
||||
{
|
||||
ai_move, -3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 1, NULL
|
||||
};
|
||||
mmove_t gunner_move_pain3 = { FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_pain2[] =
|
||||
{
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 11, NULL,
|
||||
ai_move, 6, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -7, NULL
|
||||
};
|
||||
mmove_t gunner_move_pain2 = { FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_pain1[] =
|
||||
{
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_pain1 = { FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run };
|
||||
|
||||
void gunner_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
if (rand() & 1)
|
||||
gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 10)
|
||||
self->monsterinfo.currentmove = &gunner_move_pain3;
|
||||
else if (damage <= 25)
|
||||
self->monsterinfo.currentmove = &gunner_move_pain2;
|
||||
else
|
||||
self->monsterinfo.currentmove = &gunner_move_pain1;
|
||||
}
|
||||
|
||||
void gunner_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_death[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -7, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -5, NULL,
|
||||
ai_move, 8, NULL,
|
||||
ai_move, 6, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_death = { FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead };
|
||||
|
||||
void gunner_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.currentmove = &gunner_move_death;
|
||||
}
|
||||
|
||||
void gunner_duck_down(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
return;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
if (skill->value >= 2)
|
||||
{
|
||||
if (random() > 0.5)
|
||||
GunnerGrenade(self);
|
||||
}
|
||||
|
||||
self->maxs[2] -= 32;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.pausetime = level.time + 1;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void gunner_duck_hold(edict_t * self)
|
||||
{
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
void gunner_duck_up(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_DUCKED;
|
||||
self->maxs[2] += 32;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_duck[] =
|
||||
{
|
||||
ai_move, 1, gunner_duck_down,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, gunner_duck_hold,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 0, gunner_duck_up,
|
||||
ai_move, -1, NULL
|
||||
};
|
||||
mmove_t gunner_move_duck = { FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run };
|
||||
|
||||
void gunner_dodge(edict_t * self, edict_t * attacker, float eta)
|
||||
{
|
||||
if (random() > 0.25)
|
||||
return;
|
||||
|
||||
if (!self->enemy)
|
||||
self->enemy = attacker;
|
||||
|
||||
self->monsterinfo.currentmove = &gunner_move_duck;
|
||||
}
|
||||
|
||||
void gunner_opengun(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
void GunnerFire(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t target;
|
||||
vec3_t aim;
|
||||
int flash_number;
|
||||
|
||||
flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
// project enemy back a bit and target there
|
||||
VectorCopy(self->enemy->s.origin, target);
|
||||
VectorMA(target, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
|
||||
VectorSubtract(target, start, aim);
|
||||
VectorNormalize(aim);
|
||||
monster_fire_bullet(self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||||
}
|
||||
|
||||
void GunnerGrenade(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t aim;
|
||||
int flash_number;
|
||||
|
||||
if (self->s.frame == FRAME_attak105)
|
||||
flash_number = MZ2_GUNNER_GRENADE_1;
|
||||
else if (self->s.frame == FRAME_attak108)
|
||||
flash_number = MZ2_GUNNER_GRENADE_2;
|
||||
else if (self->s.frame == FRAME_attak111)
|
||||
flash_number = MZ2_GUNNER_GRENADE_3;
|
||||
else // (self->s.frame == FRAME_attak114)
|
||||
flash_number = MZ2_GUNNER_GRENADE_4;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
//FIXME : do a spread -225 -75 75 225 degrees around forward
|
||||
VectorCopy(forward, aim);
|
||||
|
||||
monster_fire_grenade(self, start, aim, 50, 600, flash_number);
|
||||
}
|
||||
|
||||
mframe_t gunner_frames_attack_chain[] =
|
||||
{
|
||||
/*
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
*/
|
||||
ai_charge, 0, gunner_opengun,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_attack_chain = { FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain };
|
||||
|
||||
mframe_t gunner_frames_fire_chain[] =
|
||||
{
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire,
|
||||
ai_charge, 0, GunnerFire
|
||||
};
|
||||
mmove_t gunner_move_fire_chain = { FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain };
|
||||
|
||||
mframe_t gunner_frames_endfire_chain[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_endfire_chain = { FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run };
|
||||
|
||||
mframe_t gunner_frames_attack_grenade[] =
|
||||
{
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GunnerGrenade,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GunnerGrenade,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GunnerGrenade,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, GunnerGrenade,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL,
|
||||
ai_charge, 0, NULL
|
||||
};
|
||||
mmove_t gunner_move_attack_grenade = { FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run };
|
||||
|
||||
void gunner_attack(edict_t * self)
|
||||
{
|
||||
if (range(self, self->enemy) == RANGE_MELEE)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_attack_chain;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (random() <= 0.5)
|
||||
self->monsterinfo.currentmove = &gunner_move_attack_grenade;
|
||||
else
|
||||
self->monsterinfo.currentmove = &gunner_move_attack_chain;
|
||||
}
|
||||
}
|
||||
|
||||
void gunner_fire_chain(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_fire_chain;
|
||||
}
|
||||
|
||||
void gunner_refire_chain(edict_t * self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
if (visible(self, self->enemy))
|
||||
if (random() <= 0.5)
|
||||
{
|
||||
self->monsterinfo.currentmove = &gunner_move_fire_chain;
|
||||
return;
|
||||
}
|
||||
self->monsterinfo.currentmove = &gunner_move_endfire_chain;
|
||||
}
|
||||
|
||||
/*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_gunner(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_death = gi.soundindex("gunner/death1.wav");
|
||||
sound_pain = gi.soundindex("gunner/gunpain2.wav");
|
||||
sound_pain2 = gi.soundindex("gunner/gunpain1.wav");
|
||||
sound_idle = gi.soundindex("gunner/gunidle1.wav");
|
||||
sound_open = gi.soundindex("gunner/gunatck1.wav");
|
||||
sound_search = gi.soundindex("gunner/gunsrch1.wav");
|
||||
sound_sight = gi.soundindex("gunner/sight1.wav");
|
||||
|
||||
gi.soundindex("gunner/gunatck2.wav");
|
||||
gi.soundindex("gunner/gunatck3.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/gunner/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 175;
|
||||
self->gib_health = -70;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = gunner_pain;
|
||||
self->die = gunner_die;
|
||||
|
||||
self->monsterinfo.stand = gunner_stand;
|
||||
self->monsterinfo.walk = gunner_walk;
|
||||
self->monsterinfo.run = gunner_run;
|
||||
self->monsterinfo.dodge = gunner_dodge;
|
||||
self->monsterinfo.attack = gunner_attack;
|
||||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = gunner_sight;
|
||||
self->monsterinfo.search = gunner_search;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &gunner_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/gunner
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_stand01 0
|
||||
#define FRAME_stand02 1
|
||||
#define FRAME_stand03 2
|
||||
#define FRAME_stand04 3
|
||||
#define FRAME_stand05 4
|
||||
#define FRAME_stand06 5
|
||||
#define FRAME_stand07 6
|
||||
#define FRAME_stand08 7
|
||||
#define FRAME_stand09 8
|
||||
#define FRAME_stand10 9
|
||||
#define FRAME_stand11 10
|
||||
#define FRAME_stand12 11
|
||||
#define FRAME_stand13 12
|
||||
#define FRAME_stand14 13
|
||||
#define FRAME_stand15 14
|
||||
#define FRAME_stand16 15
|
||||
#define FRAME_stand17 16
|
||||
#define FRAME_stand18 17
|
||||
#define FRAME_stand19 18
|
||||
#define FRAME_stand20 19
|
||||
#define FRAME_stand21 20
|
||||
#define FRAME_stand22 21
|
||||
#define FRAME_stand23 22
|
||||
#define FRAME_stand24 23
|
||||
#define FRAME_stand25 24
|
||||
#define FRAME_stand26 25
|
||||
#define FRAME_stand27 26
|
||||
#define FRAME_stand28 27
|
||||
#define FRAME_stand29 28
|
||||
#define FRAME_stand30 29
|
||||
#define FRAME_stand31 30
|
||||
#define FRAME_stand32 31
|
||||
#define FRAME_stand33 32
|
||||
#define FRAME_stand34 33
|
||||
#define FRAME_stand35 34
|
||||
#define FRAME_stand36 35
|
||||
#define FRAME_stand37 36
|
||||
#define FRAME_stand38 37
|
||||
#define FRAME_stand39 38
|
||||
#define FRAME_stand40 39
|
||||
#define FRAME_stand41 40
|
||||
#define FRAME_stand42 41
|
||||
#define FRAME_stand43 42
|
||||
#define FRAME_stand44 43
|
||||
#define FRAME_stand45 44
|
||||
#define FRAME_stand46 45
|
||||
#define FRAME_stand47 46
|
||||
#define FRAME_stand48 47
|
||||
#define FRAME_stand49 48
|
||||
#define FRAME_stand50 49
|
||||
#define FRAME_stand51 50
|
||||
#define FRAME_stand52 51
|
||||
#define FRAME_stand53 52
|
||||
#define FRAME_stand54 53
|
||||
#define FRAME_stand55 54
|
||||
#define FRAME_stand56 55
|
||||
#define FRAME_stand57 56
|
||||
#define FRAME_stand58 57
|
||||
#define FRAME_stand59 58
|
||||
#define FRAME_stand60 59
|
||||
#define FRAME_stand61 60
|
||||
#define FRAME_stand62 61
|
||||
#define FRAME_stand63 62
|
||||
#define FRAME_stand64 63
|
||||
#define FRAME_stand65 64
|
||||
#define FRAME_stand66 65
|
||||
#define FRAME_stand67 66
|
||||
#define FRAME_stand68 67
|
||||
#define FRAME_stand69 68
|
||||
#define FRAME_stand70 69
|
||||
#define FRAME_walk01 70
|
||||
#define FRAME_walk02 71
|
||||
#define FRAME_walk03 72
|
||||
#define FRAME_walk04 73
|
||||
#define FRAME_walk05 74
|
||||
#define FRAME_walk06 75
|
||||
#define FRAME_walk07 76
|
||||
#define FRAME_walk08 77
|
||||
#define FRAME_walk09 78
|
||||
#define FRAME_walk10 79
|
||||
#define FRAME_walk11 80
|
||||
#define FRAME_walk12 81
|
||||
#define FRAME_walk13 82
|
||||
#define FRAME_walk14 83
|
||||
#define FRAME_walk15 84
|
||||
#define FRAME_walk16 85
|
||||
#define FRAME_walk17 86
|
||||
#define FRAME_walk18 87
|
||||
#define FRAME_walk19 88
|
||||
#define FRAME_walk20 89
|
||||
#define FRAME_walk21 90
|
||||
#define FRAME_walk22 91
|
||||
#define FRAME_walk23 92
|
||||
#define FRAME_walk24 93
|
||||
#define FRAME_run01 94
|
||||
#define FRAME_run02 95
|
||||
#define FRAME_run03 96
|
||||
#define FRAME_run04 97
|
||||
#define FRAME_run05 98
|
||||
#define FRAME_run06 99
|
||||
#define FRAME_run07 100
|
||||
#define FRAME_run08 101
|
||||
#define FRAME_runs01 102
|
||||
#define FRAME_runs02 103
|
||||
#define FRAME_runs03 104
|
||||
#define FRAME_runs04 105
|
||||
#define FRAME_runs05 106
|
||||
#define FRAME_runs06 107
|
||||
#define FRAME_attak101 108
|
||||
#define FRAME_attak102 109
|
||||
#define FRAME_attak103 110
|
||||
#define FRAME_attak104 111
|
||||
#define FRAME_attak105 112
|
||||
#define FRAME_attak106 113
|
||||
#define FRAME_attak107 114
|
||||
#define FRAME_attak108 115
|
||||
#define FRAME_attak109 116
|
||||
#define FRAME_attak110 117
|
||||
#define FRAME_attak111 118
|
||||
#define FRAME_attak112 119
|
||||
#define FRAME_attak113 120
|
||||
#define FRAME_attak114 121
|
||||
#define FRAME_attak115 122
|
||||
#define FRAME_attak116 123
|
||||
#define FRAME_attak117 124
|
||||
#define FRAME_attak118 125
|
||||
#define FRAME_attak119 126
|
||||
#define FRAME_attak120 127
|
||||
#define FRAME_attak121 128
|
||||
#define FRAME_attak201 129
|
||||
#define FRAME_attak202 130
|
||||
#define FRAME_attak203 131
|
||||
#define FRAME_attak204 132
|
||||
#define FRAME_attak205 133
|
||||
#define FRAME_attak206 134
|
||||
#define FRAME_attak207 135
|
||||
#define FRAME_attak208 136
|
||||
#define FRAME_attak209 137
|
||||
#define FRAME_attak210 138
|
||||
#define FRAME_attak211 139
|
||||
#define FRAME_attak212 140
|
||||
#define FRAME_attak213 141
|
||||
#define FRAME_attak214 142
|
||||
#define FRAME_attak215 143
|
||||
#define FRAME_attak216 144
|
||||
#define FRAME_attak217 145
|
||||
#define FRAME_attak218 146
|
||||
#define FRAME_attak219 147
|
||||
#define FRAME_attak220 148
|
||||
#define FRAME_attak221 149
|
||||
#define FRAME_attak222 150
|
||||
#define FRAME_attak223 151
|
||||
#define FRAME_attak224 152
|
||||
#define FRAME_attak225 153
|
||||
#define FRAME_attak226 154
|
||||
#define FRAME_attak227 155
|
||||
#define FRAME_attak228 156
|
||||
#define FRAME_attak229 157
|
||||
#define FRAME_attak230 158
|
||||
#define FRAME_pain101 159
|
||||
#define FRAME_pain102 160
|
||||
#define FRAME_pain103 161
|
||||
#define FRAME_pain104 162
|
||||
#define FRAME_pain105 163
|
||||
#define FRAME_pain106 164
|
||||
#define FRAME_pain107 165
|
||||
#define FRAME_pain108 166
|
||||
#define FRAME_pain109 167
|
||||
#define FRAME_pain110 168
|
||||
#define FRAME_pain111 169
|
||||
#define FRAME_pain112 170
|
||||
#define FRAME_pain113 171
|
||||
#define FRAME_pain114 172
|
||||
#define FRAME_pain115 173
|
||||
#define FRAME_pain116 174
|
||||
#define FRAME_pain117 175
|
||||
#define FRAME_pain118 176
|
||||
#define FRAME_pain201 177
|
||||
#define FRAME_pain202 178
|
||||
#define FRAME_pain203 179
|
||||
#define FRAME_pain204 180
|
||||
#define FRAME_pain205 181
|
||||
#define FRAME_pain206 182
|
||||
#define FRAME_pain207 183
|
||||
#define FRAME_pain208 184
|
||||
#define FRAME_pain301 185
|
||||
#define FRAME_pain302 186
|
||||
#define FRAME_pain303 187
|
||||
#define FRAME_pain304 188
|
||||
#define FRAME_pain305 189
|
||||
#define FRAME_death01 190
|
||||
#define FRAME_death02 191
|
||||
#define FRAME_death03 192
|
||||
#define FRAME_death04 193
|
||||
#define FRAME_death05 194
|
||||
#define FRAME_death06 195
|
||||
#define FRAME_death07 196
|
||||
#define FRAME_death08 197
|
||||
#define FRAME_death09 198
|
||||
#define FRAME_death10 199
|
||||
#define FRAME_death11 200
|
||||
#define FRAME_duck01 201
|
||||
#define FRAME_duck02 202
|
||||
#define FRAME_duck03 203
|
||||
#define FRAME_duck04 204
|
||||
#define FRAME_duck05 205
|
||||
#define FRAME_duck06 206
|
||||
#define FRAME_duck07 207
|
||||
#define FRAME_duck08 208
|
||||
|
||||
#define MODEL_SCALE 1.150000
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
hover
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_hover.h"
|
||||
|
||||
qboolean visible(edict_t * self, edict_t * other);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_death1;
|
||||
static int sound_death2;
|
||||
static int sound_sight;
|
||||
static int sound_search1;
|
||||
static int sound_search2;
|
||||
|
||||
void hover_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void hover_search(edict_t * self)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void hover_run(edict_t * self);
|
||||
void hover_stand(edict_t * self);
|
||||
void hover_dead(edict_t * self);
|
||||
void hover_attack(edict_t * self);
|
||||
void hover_reattack(edict_t * self);
|
||||
void hover_fire_blaster(edict_t * self);
|
||||
void hover_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point);
|
||||
|
||||
mframe_t hover_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_stand = { FRAME_stand01, FRAME_stand30, hover_frames_stand, NULL };
|
||||
|
||||
mframe_t hover_frames_stop1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_stop1 = { FRAME_stop101, FRAME_stop109, hover_frames_stop1, NULL };
|
||||
|
||||
mframe_t hover_frames_stop2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_stop2 = { FRAME_stop201, FRAME_stop208, hover_frames_stop2, NULL };
|
||||
|
||||
mframe_t hover_frames_takeoff[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -9, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_takeoff = { FRAME_takeof01, FRAME_takeof30, hover_frames_takeoff, NULL };
|
||||
|
||||
mframe_t hover_frames_pain3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_pain3 = { FRAME_pain301, FRAME_pain309, hover_frames_pain3, hover_run };
|
||||
|
||||
mframe_t hover_frames_pain2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_pain2 = { FRAME_pain201, FRAME_pain212, hover_frames_pain2, hover_run };
|
||||
|
||||
mframe_t hover_frames_pain1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, -8, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 7, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 4, NULL
|
||||
};
|
||||
mmove_t hover_move_pain1 = { FRAME_pain101, FRAME_pain128, hover_frames_pain1, hover_run };
|
||||
|
||||
mframe_t hover_frames_land[] =
|
||||
{
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_land = { FRAME_land01, FRAME_land01, hover_frames_land, NULL };
|
||||
|
||||
mframe_t hover_frames_forward[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_forward = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_forward, NULL };
|
||||
|
||||
mframe_t hover_frames_walk[] =
|
||||
{
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL
|
||||
};
|
||||
mmove_t hover_move_walk = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_walk, NULL };
|
||||
|
||||
mframe_t hover_frames_run[] =
|
||||
{
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 10, NULL
|
||||
};
|
||||
mmove_t hover_move_run = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_run, NULL };
|
||||
|
||||
mframe_t hover_frames_death1[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -10, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 7, NULL
|
||||
};
|
||||
mmove_t hover_move_death1 = { FRAME_death101, FRAME_death111, hover_frames_death1, hover_dead };
|
||||
|
||||
mframe_t hover_frames_backward[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t hover_move_backward = { FRAME_backwd01, FRAME_backwd24, hover_frames_backward, NULL };
|
||||
|
||||
mframe_t hover_frames_start_attack[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL
|
||||
};
|
||||
mmove_t hover_move_start_attack = { FRAME_attak101, FRAME_attak103, hover_frames_start_attack, hover_attack };
|
||||
|
||||
mframe_t hover_frames_attack1[] =
|
||||
{
|
||||
ai_charge, -10, hover_fire_blaster,
|
||||
ai_charge, -10, hover_fire_blaster,
|
||||
ai_charge, 0, hover_reattack,
|
||||
};
|
||||
mmove_t hover_move_attack1 = { FRAME_attak104, FRAME_attak106, hover_frames_attack1, NULL };
|
||||
|
||||
mframe_t hover_frames_end_attack[] =
|
||||
{
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL
|
||||
};
|
||||
mmove_t hover_move_end_attack = { FRAME_attak107, FRAME_attak108, hover_frames_end_attack, hover_run };
|
||||
|
||||
void hover_reattack(edict_t * self)
|
||||
{
|
||||
if (self->enemy->health > 0)
|
||||
if (visible(self, self->enemy))
|
||||
if (random() <= 0.6)
|
||||
{
|
||||
self->monsterinfo.currentmove = &hover_move_attack1;
|
||||
return;
|
||||
}
|
||||
self->monsterinfo.currentmove = &hover_move_end_attack;
|
||||
}
|
||||
|
||||
void hover_fire_blaster(edict_t * self)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right;
|
||||
vec3_t end;
|
||||
vec3_t dir;
|
||||
int effect;
|
||||
|
||||
if (self->s.frame == FRAME_attak104)
|
||||
effect = EF_HYPERBLASTER;
|
||||
else
|
||||
effect = 0;
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_HOVER_BLASTER_1], forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, end);
|
||||
end[2] += self->enemy->viewheight;
|
||||
VectorSubtract(end, start, dir);
|
||||
|
||||
monster_fire_blaster(self, start, dir, 1, 1000, MZ2_HOVER_BLASTER_1, effect);
|
||||
}
|
||||
|
||||
void hover_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &hover_move_stand;
|
||||
}
|
||||
|
||||
void hover_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &hover_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &hover_move_run;
|
||||
}
|
||||
|
||||
void hover_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &hover_move_walk;
|
||||
}
|
||||
|
||||
void hover_start_attack(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &hover_move_start_attack;
|
||||
}
|
||||
|
||||
void hover_attack(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &hover_move_attack1;
|
||||
}
|
||||
|
||||
void hover_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
if (damage <= 25)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &hover_move_pain3;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &hover_move_pain2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
self->monsterinfo.currentmove = &hover_move_pain1;
|
||||
}
|
||||
}
|
||||
|
||||
void hover_deadthink(edict_t * self)
|
||||
{
|
||||
if (!self->groundentity && level.time < self->timestamp)
|
||||
{
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
return;
|
||||
}
|
||||
BecomeExplosion1(self);
|
||||
}
|
||||
|
||||
void hover_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->think = hover_deadthink;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->timestamp = level.time + 15;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void hover_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
if (random() < 0.5)
|
||||
gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.currentmove = &hover_move_death1;
|
||||
}
|
||||
|
||||
/*QUAKED monster_hover (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_hover(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("hover/hovpain1.wav");
|
||||
sound_pain2 = gi.soundindex("hover/hovpain2.wav");
|
||||
sound_death1 = gi.soundindex("hover/hovdeth1.wav");
|
||||
sound_death2 = gi.soundindex("hover/hovdeth2.wav");
|
||||
sound_sight = gi.soundindex("hover/hovsght1.wav");
|
||||
sound_search1 = gi.soundindex("hover/hovsrch1.wav");
|
||||
sound_search2 = gi.soundindex("hover/hovsrch2.wav");
|
||||
|
||||
gi.soundindex("hover/hovatck1.wav");
|
||||
|
||||
self->s.sound = gi.soundindex("hover/hovidle1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/hover/tris.md2");
|
||||
VectorSet(self->mins, -24, -24, -24);
|
||||
VectorSet(self->maxs, 24, 24, 32);
|
||||
|
||||
self->health = 240;
|
||||
self->gib_health = -100;
|
||||
self->mass = 150;
|
||||
|
||||
self->pain = hover_pain;
|
||||
self->die = hover_die;
|
||||
|
||||
self->monsterinfo.stand = hover_stand;
|
||||
self->monsterinfo.walk = hover_walk;
|
||||
self->monsterinfo.run = hover_run;
|
||||
// self->monsterinfo.dodge = hover_dodge;
|
||||
self->monsterinfo.attack = hover_start_attack;
|
||||
self->monsterinfo.sight = hover_sight;
|
||||
self->monsterinfo.search = hover_search;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &hover_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
flymonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/hover
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_stand01 0
|
||||
#define FRAME_stand02 1
|
||||
#define FRAME_stand03 2
|
||||
#define FRAME_stand04 3
|
||||
#define FRAME_stand05 4
|
||||
#define FRAME_stand06 5
|
||||
#define FRAME_stand07 6
|
||||
#define FRAME_stand08 7
|
||||
#define FRAME_stand09 8
|
||||
#define FRAME_stand10 9
|
||||
#define FRAME_stand11 10
|
||||
#define FRAME_stand12 11
|
||||
#define FRAME_stand13 12
|
||||
#define FRAME_stand14 13
|
||||
#define FRAME_stand15 14
|
||||
#define FRAME_stand16 15
|
||||
#define FRAME_stand17 16
|
||||
#define FRAME_stand18 17
|
||||
#define FRAME_stand19 18
|
||||
#define FRAME_stand20 19
|
||||
#define FRAME_stand21 20
|
||||
#define FRAME_stand22 21
|
||||
#define FRAME_stand23 22
|
||||
#define FRAME_stand24 23
|
||||
#define FRAME_stand25 24
|
||||
#define FRAME_stand26 25
|
||||
#define FRAME_stand27 26
|
||||
#define FRAME_stand28 27
|
||||
#define FRAME_stand29 28
|
||||
#define FRAME_stand30 29
|
||||
#define FRAME_forwrd01 30
|
||||
#define FRAME_forwrd02 31
|
||||
#define FRAME_forwrd03 32
|
||||
#define FRAME_forwrd04 33
|
||||
#define FRAME_forwrd05 34
|
||||
#define FRAME_forwrd06 35
|
||||
#define FRAME_forwrd07 36
|
||||
#define FRAME_forwrd08 37
|
||||
#define FRAME_forwrd09 38
|
||||
#define FRAME_forwrd10 39
|
||||
#define FRAME_forwrd11 40
|
||||
#define FRAME_forwrd12 41
|
||||
#define FRAME_forwrd13 42
|
||||
#define FRAME_forwrd14 43
|
||||
#define FRAME_forwrd15 44
|
||||
#define FRAME_forwrd16 45
|
||||
#define FRAME_forwrd17 46
|
||||
#define FRAME_forwrd18 47
|
||||
#define FRAME_forwrd19 48
|
||||
#define FRAME_forwrd20 49
|
||||
#define FRAME_forwrd21 50
|
||||
#define FRAME_forwrd22 51
|
||||
#define FRAME_forwrd23 52
|
||||
#define FRAME_forwrd24 53
|
||||
#define FRAME_forwrd25 54
|
||||
#define FRAME_forwrd26 55
|
||||
#define FRAME_forwrd27 56
|
||||
#define FRAME_forwrd28 57
|
||||
#define FRAME_forwrd29 58
|
||||
#define FRAME_forwrd30 59
|
||||
#define FRAME_forwrd31 60
|
||||
#define FRAME_forwrd32 61
|
||||
#define FRAME_forwrd33 62
|
||||
#define FRAME_forwrd34 63
|
||||
#define FRAME_forwrd35 64
|
||||
#define FRAME_stop101 65
|
||||
#define FRAME_stop102 66
|
||||
#define FRAME_stop103 67
|
||||
#define FRAME_stop104 68
|
||||
#define FRAME_stop105 69
|
||||
#define FRAME_stop106 70
|
||||
#define FRAME_stop107 71
|
||||
#define FRAME_stop108 72
|
||||
#define FRAME_stop109 73
|
||||
#define FRAME_stop201 74
|
||||
#define FRAME_stop202 75
|
||||
#define FRAME_stop203 76
|
||||
#define FRAME_stop204 77
|
||||
#define FRAME_stop205 78
|
||||
#define FRAME_stop206 79
|
||||
#define FRAME_stop207 80
|
||||
#define FRAME_stop208 81
|
||||
#define FRAME_takeof01 82
|
||||
#define FRAME_takeof02 83
|
||||
#define FRAME_takeof03 84
|
||||
#define FRAME_takeof04 85
|
||||
#define FRAME_takeof05 86
|
||||
#define FRAME_takeof06 87
|
||||
#define FRAME_takeof07 88
|
||||
#define FRAME_takeof08 89
|
||||
#define FRAME_takeof09 90
|
||||
#define FRAME_takeof10 91
|
||||
#define FRAME_takeof11 92
|
||||
#define FRAME_takeof12 93
|
||||
#define FRAME_takeof13 94
|
||||
#define FRAME_takeof14 95
|
||||
#define FRAME_takeof15 96
|
||||
#define FRAME_takeof16 97
|
||||
#define FRAME_takeof17 98
|
||||
#define FRAME_takeof18 99
|
||||
#define FRAME_takeof19 100
|
||||
#define FRAME_takeof20 101
|
||||
#define FRAME_takeof21 102
|
||||
#define FRAME_takeof22 103
|
||||
#define FRAME_takeof23 104
|
||||
#define FRAME_takeof24 105
|
||||
#define FRAME_takeof25 106
|
||||
#define FRAME_takeof26 107
|
||||
#define FRAME_takeof27 108
|
||||
#define FRAME_takeof28 109
|
||||
#define FRAME_takeof29 110
|
||||
#define FRAME_takeof30 111
|
||||
#define FRAME_land01 112
|
||||
#define FRAME_pain101 113
|
||||
#define FRAME_pain102 114
|
||||
#define FRAME_pain103 115
|
||||
#define FRAME_pain104 116
|
||||
#define FRAME_pain105 117
|
||||
#define FRAME_pain106 118
|
||||
#define FRAME_pain107 119
|
||||
#define FRAME_pain108 120
|
||||
#define FRAME_pain109 121
|
||||
#define FRAME_pain110 122
|
||||
#define FRAME_pain111 123
|
||||
#define FRAME_pain112 124
|
||||
#define FRAME_pain113 125
|
||||
#define FRAME_pain114 126
|
||||
#define FRAME_pain115 127
|
||||
#define FRAME_pain116 128
|
||||
#define FRAME_pain117 129
|
||||
#define FRAME_pain118 130
|
||||
#define FRAME_pain119 131
|
||||
#define FRAME_pain120 132
|
||||
#define FRAME_pain121 133
|
||||
#define FRAME_pain122 134
|
||||
#define FRAME_pain123 135
|
||||
#define FRAME_pain124 136
|
||||
#define FRAME_pain125 137
|
||||
#define FRAME_pain126 138
|
||||
#define FRAME_pain127 139
|
||||
#define FRAME_pain128 140
|
||||
#define FRAME_pain201 141
|
||||
#define FRAME_pain202 142
|
||||
#define FRAME_pain203 143
|
||||
#define FRAME_pain204 144
|
||||
#define FRAME_pain205 145
|
||||
#define FRAME_pain206 146
|
||||
#define FRAME_pain207 147
|
||||
#define FRAME_pain208 148
|
||||
#define FRAME_pain209 149
|
||||
#define FRAME_pain210 150
|
||||
#define FRAME_pain211 151
|
||||
#define FRAME_pain212 152
|
||||
#define FRAME_pain301 153
|
||||
#define FRAME_pain302 154
|
||||
#define FRAME_pain303 155
|
||||
#define FRAME_pain304 156
|
||||
#define FRAME_pain305 157
|
||||
#define FRAME_pain306 158
|
||||
#define FRAME_pain307 159
|
||||
#define FRAME_pain308 160
|
||||
#define FRAME_pain309 161
|
||||
#define FRAME_death101 162
|
||||
#define FRAME_death102 163
|
||||
#define FRAME_death103 164
|
||||
#define FRAME_death104 165
|
||||
#define FRAME_death105 166
|
||||
#define FRAME_death106 167
|
||||
#define FRAME_death107 168
|
||||
#define FRAME_death108 169
|
||||
#define FRAME_death109 170
|
||||
#define FRAME_death110 171
|
||||
#define FRAME_death111 172
|
||||
#define FRAME_backwd01 173
|
||||
#define FRAME_backwd02 174
|
||||
#define FRAME_backwd03 175
|
||||
#define FRAME_backwd04 176
|
||||
#define FRAME_backwd05 177
|
||||
#define FRAME_backwd06 178
|
||||
#define FRAME_backwd07 179
|
||||
#define FRAME_backwd08 180
|
||||
#define FRAME_backwd09 181
|
||||
#define FRAME_backwd10 182
|
||||
#define FRAME_backwd11 183
|
||||
#define FRAME_backwd12 184
|
||||
#define FRAME_backwd13 185
|
||||
#define FRAME_backwd14 186
|
||||
#define FRAME_backwd15 187
|
||||
#define FRAME_backwd16 188
|
||||
#define FRAME_backwd17 189
|
||||
#define FRAME_backwd18 190
|
||||
#define FRAME_backwd19 191
|
||||
#define FRAME_backwd20 192
|
||||
#define FRAME_backwd21 193
|
||||
#define FRAME_backwd22 194
|
||||
#define FRAME_backwd23 195
|
||||
#define FRAME_backwd24 196
|
||||
#define FRAME_attak101 197
|
||||
#define FRAME_attak102 198
|
||||
#define FRAME_attak103 199
|
||||
#define FRAME_attak104 200
|
||||
#define FRAME_attak105 201
|
||||
#define FRAME_attak106 202
|
||||
#define FRAME_attak107 203
|
||||
#define FRAME_attak108 204
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
INFANTRY
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_infantry.h"
|
||||
|
||||
void InfantryMachineGun(edict_t * self);
|
||||
|
||||
static int sound_pain1;
|
||||
static int sound_pain2;
|
||||
static int sound_die1;
|
||||
static int sound_die2;
|
||||
|
||||
static int sound_gunshot;
|
||||
static int sound_weapon_cock;
|
||||
static int sound_punch_swing;
|
||||
static int sound_punch_hit;
|
||||
static int sound_sight;
|
||||
static int sound_search;
|
||||
static int sound_idle;
|
||||
|
||||
mframe_t infantry_frames_stand[] =
|
||||
{
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL
|
||||
};
|
||||
mmove_t infantry_move_stand = { FRAME_stand50, FRAME_stand71, infantry_frames_stand, NULL };
|
||||
|
||||
void infantry_stand(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_stand;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_fidget[] =
|
||||
{
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 3, NULL,
|
||||
ai_stand, 6, NULL,
|
||||
ai_stand, 3, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -2, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, -1, NULL,
|
||||
ai_stand, 0, NULL,
|
||||
ai_stand, -3, NULL,
|
||||
ai_stand, -2, NULL,
|
||||
ai_stand, -3, NULL,
|
||||
ai_stand, -3, NULL,
|
||||
ai_stand, -2, NULL
|
||||
};
|
||||
mmove_t infantry_move_fidget = { FRAME_stand01, FRAME_stand49, infantry_frames_fidget, infantry_stand };
|
||||
|
||||
void infantry_fidget(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_fidget;
|
||||
gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_walk[] =
|
||||
{
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 5, NULL,
|
||||
ai_walk, 6, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 4, NULL,
|
||||
ai_walk, 5, NULL
|
||||
};
|
||||
mmove_t infantry_move_walk = { FRAME_walk03, FRAME_walk14, infantry_frames_walk, NULL };
|
||||
|
||||
void infantry_walk(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_walk;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_run[] =
|
||||
{
|
||||
ai_run, 10, NULL,
|
||||
ai_run, 20, NULL,
|
||||
ai_run, 5, NULL,
|
||||
ai_run, 7, NULL,
|
||||
ai_run, 30, NULL,
|
||||
ai_run, 35, NULL,
|
||||
ai_run, 2, NULL,
|
||||
ai_run, 6, NULL
|
||||
};
|
||||
mmove_t infantry_move_run = { FRAME_run01, FRAME_run08, infantry_frames_run, NULL };
|
||||
|
||||
void infantry_run(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
self->monsterinfo.currentmove = &infantry_move_stand;
|
||||
else
|
||||
self->monsterinfo.currentmove = &infantry_move_run;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_pain1[] =
|
||||
{
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 6, NULL,
|
||||
ai_move, 2, NULL
|
||||
};
|
||||
mmove_t infantry_move_pain1 = { FRAME_pain101, FRAME_pain110, infantry_frames_pain1, infantry_run };
|
||||
|
||||
mframe_t infantry_frames_pain2[] =
|
||||
{
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, 2, NULL
|
||||
};
|
||||
mmove_t infantry_move_pain2 = { FRAME_pain201, FRAME_pain210, infantry_frames_pain2, infantry_run };
|
||||
|
||||
void infantry_pain(edict_t * self, edict_t * other, float kick, int damage)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (self->health < (self->max_health / 2))
|
||||
self->s.skinnum = 1;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
return;
|
||||
|
||||
self->pain_debounce_time = level.time + 3;
|
||||
|
||||
if (skill->value == 3)
|
||||
return; // no pain anims in nightmare
|
||||
|
||||
n = rand() % 2;
|
||||
if (n == 0)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_pain1;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_pain2;
|
||||
gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
vec3_t aimangles[] =
|
||||
{
|
||||
0.0, 5.0, 0.0,
|
||||
10.0, 15.0, 0.0,
|
||||
20.0, 25.0, 0.0,
|
||||
25.0, 35.0, 0.0,
|
||||
30.0, 40.0, 0.0,
|
||||
30.0, 45.0, 0.0,
|
||||
25.0, 50.0, 0.0,
|
||||
20.0, 40.0, 0.0,
|
||||
15.0, 35.0, 0.0,
|
||||
40.0, 35.0, 0.0,
|
||||
70.0, 35.0, 0.0,
|
||||
90.0, 35.0, 0.0
|
||||
};
|
||||
|
||||
void InfantryMachineGun(edict_t * self)
|
||||
{
|
||||
vec3_t start, target;
|
||||
vec3_t forward, right;
|
||||
vec3_t vec;
|
||||
int flash_number;
|
||||
|
||||
if (self->s.frame == FRAME_attak111)
|
||||
{
|
||||
flash_number = MZ2_INFANTRY_MACHINEGUN_1;
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, start, forward);
|
||||
VectorNormalize(forward);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flash_number = MZ2_INFANTRY_MACHINEGUN_2 + (self->s.frame - FRAME_death211);
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start);
|
||||
|
||||
VectorSubtract(self->s.angles, aimangles[flash_number - MZ2_INFANTRY_MACHINEGUN_2], vec);
|
||||
AngleVectors(vec, forward, NULL, NULL);
|
||||
}
|
||||
|
||||
monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
|
||||
}
|
||||
|
||||
void infantry_sight(edict_t * self, edict_t * other)
|
||||
{
|
||||
gi.sound(self, CHAN_BODY, sound_sight, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void infantry_dead(edict_t * self)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
gi.linkentity(self);
|
||||
|
||||
M_FlyCheck(self);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_death1[] =
|
||||
{
|
||||
ai_move, -4, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, -4, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, -2, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 2, NULL,
|
||||
ai_move, 9, NULL,
|
||||
ai_move, 9, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -3, NULL
|
||||
};
|
||||
mmove_t infantry_move_death1 = { FRAME_death101, FRAME_death120, infantry_frames_death1, infantry_dead };
|
||||
|
||||
// Off with his head
|
||||
mframe_t infantry_frames_death2[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 5, NULL,
|
||||
ai_move, -1, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 1, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -2, InfantryMachineGun,
|
||||
ai_move, -2, InfantryMachineGun,
|
||||
ai_move, -3, InfantryMachineGun,
|
||||
ai_move, -1, InfantryMachineGun,
|
||||
ai_move, -2, InfantryMachineGun,
|
||||
ai_move, 0, InfantryMachineGun,
|
||||
ai_move, 2, InfantryMachineGun,
|
||||
ai_move, 2, InfantryMachineGun,
|
||||
ai_move, 3, InfantryMachineGun,
|
||||
ai_move, -10, InfantryMachineGun,
|
||||
ai_move, -7, InfantryMachineGun,
|
||||
ai_move, -8, InfantryMachineGun,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, 4, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t infantry_move_death2 = { FRAME_death201, FRAME_death225, infantry_frames_death2, infantry_dead };
|
||||
|
||||
mframe_t infantry_frames_death3[] =
|
||||
{
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, -6, NULL,
|
||||
ai_move, -11, NULL,
|
||||
ai_move, -3, NULL,
|
||||
ai_move, -11, NULL,
|
||||
ai_move, 0, NULL,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t infantry_move_death3 = { FRAME_death301, FRAME_death309, infantry_frames_death3, infantry_dead };
|
||||
|
||||
void infantry_die(edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||
{
|
||||
int n;
|
||||
|
||||
// check for gib
|
||||
if (self->health <= self->gib_health)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n = 0; n < 2; n++)
|
||||
ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
|
||||
for (n = 0; n < 4; n++)
|
||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
|
||||
self->deadflag = DEAD_DEAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->deadflag == DEAD_DEAD)
|
||||
return;
|
||||
|
||||
// regular death
|
||||
self->deadflag = DEAD_DEAD;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
|
||||
n = rand() % 3;
|
||||
if (n == 0)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_death1;
|
||||
gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else if (n == 1)
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_death2;
|
||||
gi.sound(self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &infantry_move_death3;
|
||||
gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void infantry_duck_down(edict_t * self)
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
return;
|
||||
self->monsterinfo.aiflags |= AI_DUCKED;
|
||||
self->maxs[2] -= 32;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->monsterinfo.pausetime = level.time + 1;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void infantry_duck_hold(edict_t * self)
|
||||
{
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
void infantry_duck_up(edict_t * self)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_DUCKED;
|
||||
self->maxs[2] += 32;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_duck[] =
|
||||
{
|
||||
ai_move, -2, infantry_duck_down,
|
||||
ai_move, -5, infantry_duck_hold,
|
||||
ai_move, 3, NULL,
|
||||
ai_move, 4, infantry_duck_up,
|
||||
ai_move, 0, NULL
|
||||
};
|
||||
mmove_t infantry_move_duck = { FRAME_duck01, FRAME_duck05, infantry_frames_duck, infantry_run };
|
||||
|
||||
void infantry_dodge(edict_t * self, edict_t * attacker, float eta)
|
||||
{
|
||||
if (random() > 0.25)
|
||||
return;
|
||||
|
||||
if (!self->enemy)
|
||||
self->enemy = attacker;
|
||||
|
||||
self->monsterinfo.currentmove = &infantry_move_duck;
|
||||
}
|
||||
|
||||
void infantry_cock_gun(edict_t * self)
|
||||
{
|
||||
int n;
|
||||
|
||||
gi.sound(self, CHAN_WEAPON, sound_weapon_cock, 1, ATTN_NORM, 0);
|
||||
n = (rand() & 15) + 3 + 7;
|
||||
self->monsterinfo.pausetime = level.time + n * FRAMETIME;
|
||||
}
|
||||
|
||||
void infantry_fire(edict_t * self)
|
||||
{
|
||||
InfantryMachineGun(self);
|
||||
|
||||
if (level.time >= self->monsterinfo.pausetime)
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
else
|
||||
self->monsterinfo.aiflags |= AI_HOLD_FRAME;
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_attack1[] =
|
||||
{
|
||||
ai_charge, 4, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, 0, infantry_cock_gun,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 1, NULL,
|
||||
ai_charge, 2, NULL,
|
||||
ai_charge, -2, NULL,
|
||||
ai_charge, -3, NULL,
|
||||
ai_charge, 1, infantry_fire,
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, -1, NULL,
|
||||
ai_charge, -2, NULL,
|
||||
ai_charge, -3, NULL
|
||||
};
|
||||
mmove_t infantry_move_attack1 = { FRAME_attak101, FRAME_attak115, infantry_frames_attack1, infantry_run };
|
||||
|
||||
void infantry_swing(edict_t * self)
|
||||
{
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch_swing, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void infantry_smack(edict_t * self)
|
||||
{
|
||||
vec3_t aim;
|
||||
|
||||
VectorSet(aim, MELEE_DISTANCE, 0, 0);
|
||||
if (fire_hit(self, aim, (5 + (rand() % 5)), 50))
|
||||
gi.sound(self, CHAN_WEAPON, sound_punch_hit, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
mframe_t infantry_frames_attack2[] =
|
||||
{
|
||||
ai_charge, 3, NULL,
|
||||
ai_charge, 6, NULL,
|
||||
ai_charge, 0, infantry_swing,
|
||||
ai_charge, 8, NULL,
|
||||
ai_charge, 5, NULL,
|
||||
ai_charge, 8, infantry_smack,
|
||||
ai_charge, 6, NULL,
|
||||
ai_charge, 3, NULL,
|
||||
};
|
||||
mmove_t infantry_move_attack2 = { FRAME_attak201, FRAME_attak208, infantry_frames_attack2, infantry_run };
|
||||
|
||||
void infantry_attack(edict_t * self)
|
||||
{
|
||||
if (range(self, self->enemy) == RANGE_MELEE)
|
||||
self->monsterinfo.currentmove = &infantry_move_attack2;
|
||||
else
|
||||
self->monsterinfo.currentmove = &infantry_move_attack1;
|
||||
}
|
||||
|
||||
/*QUAKED monster_infantry (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
*/
|
||||
void SP_monster_infantry(edict_t * self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
sound_pain1 = gi.soundindex("infantry/infpain1.wav");
|
||||
sound_pain2 = gi.soundindex("infantry/infpain2.wav");
|
||||
sound_die1 = gi.soundindex("infantry/infdeth1.wav");
|
||||
sound_die2 = gi.soundindex("infantry/infdeth2.wav");
|
||||
|
||||
sound_gunshot = gi.soundindex("infantry/infatck1.wav");
|
||||
sound_weapon_cock = gi.soundindex("infantry/infatck3.wav");
|
||||
sound_punch_swing = gi.soundindex("infantry/infatck2.wav");
|
||||
sound_punch_hit = gi.soundindex("infantry/melee2.wav");
|
||||
|
||||
sound_sight = gi.soundindex("infantry/infsght1.wav");
|
||||
sound_search = gi.soundindex("infantry/infsrch1.wav");
|
||||
sound_idle = gi.soundindex("infantry/infidle1.wav");
|
||||
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 100;
|
||||
self->gib_health = -40;
|
||||
self->mass = 200;
|
||||
|
||||
self->pain = infantry_pain;
|
||||
self->die = infantry_die;
|
||||
|
||||
self->monsterinfo.stand = infantry_stand;
|
||||
self->monsterinfo.walk = infantry_walk;
|
||||
self->monsterinfo.run = infantry_run;
|
||||
self->monsterinfo.dodge = infantry_dodge;
|
||||
self->monsterinfo.attack = infantry_attack;
|
||||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = infantry_sight;
|
||||
self->monsterinfo.idle = infantry_fidget;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.currentmove = &infantry_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
walkmonster_start(self);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
Copyright (C) 1997-2001 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.
|
||||
|
||||
*/
|
||||
// G:\quake2\baseq2\models/monsters/infantry
|
||||
|
||||
// This file generated by ModelGen - Do NOT Modify
|
||||
|
||||
#define FRAME_gun02 0
|
||||
#define FRAME_stand01 1
|
||||
#define FRAME_stand02 2
|
||||
#define FRAME_stand03 3
|
||||
#define FRAME_stand04 4
|
||||
#define FRAME_stand05 5
|
||||
#define FRAME_stand06 6
|
||||
#define FRAME_stand07 7
|
||||
#define FRAME_stand08 8
|
||||
#define FRAME_stand09 9
|
||||
#define FRAME_stand10 10
|
||||
#define FRAME_stand11 11
|
||||
#define FRAME_stand12 12
|
||||
#define FRAME_stand13 13
|
||||
#define FRAME_stand14 14
|
||||
#define FRAME_stand15 15
|
||||
#define FRAME_stand16 16
|
||||
#define FRAME_stand17 17
|
||||
#define FRAME_stand18 18
|
||||
#define FRAME_stand19 19
|
||||
#define FRAME_stand20 20
|
||||
#define FRAME_stand21 21
|
||||
#define FRAME_stand22 22
|
||||
#define FRAME_stand23 23
|
||||
#define FRAME_stand24 24
|
||||
#define FRAME_stand25 25
|
||||
#define FRAME_stand26 26
|
||||
#define FRAME_stand27 27
|
||||
#define FRAME_stand28 28
|
||||
#define FRAME_stand29 29
|
||||
#define FRAME_stand30 30
|
||||
#define FRAME_stand31 31
|
||||
#define FRAME_stand32 32
|
||||
#define FRAME_stand33 33
|
||||
#define FRAME_stand34 34
|
||||
#define FRAME_stand35 35
|
||||
#define FRAME_stand36 36
|
||||
#define FRAME_stand37 37
|
||||
#define FRAME_stand38 38
|
||||
#define FRAME_stand39 39
|
||||
#define FRAME_stand40 40
|
||||
#define FRAME_stand41 41
|
||||
#define FRAME_stand42 42
|
||||
#define FRAME_stand43 43
|
||||
#define FRAME_stand44 44
|
||||
#define FRAME_stand45 45
|
||||
#define FRAME_stand46 46
|
||||
#define FRAME_stand47 47
|
||||
#define FRAME_stand48 48
|
||||
#define FRAME_stand49 49
|
||||
#define FRAME_stand50 50
|
||||
#define FRAME_stand51 51
|
||||
#define FRAME_stand52 52
|
||||
#define FRAME_stand53 53
|
||||
#define FRAME_stand54 54
|
||||
#define FRAME_stand55 55
|
||||
#define FRAME_stand56 56
|
||||
#define FRAME_stand57 57
|
||||
#define FRAME_stand58 58
|
||||
#define FRAME_stand59 59
|
||||
#define FRAME_stand60 60
|
||||
#define FRAME_stand61 61
|
||||
#define FRAME_stand62 62
|
||||
#define FRAME_stand63 63
|
||||
#define FRAME_stand64 64
|
||||
#define FRAME_stand65 65
|
||||
#define FRAME_stand66 66
|
||||
#define FRAME_stand67 67
|
||||
#define FRAME_stand68 68
|
||||
#define FRAME_stand69 69
|
||||
#define FRAME_stand70 70
|
||||
#define FRAME_stand71 71
|
||||
#define FRAME_walk01 72
|
||||
#define FRAME_walk02 73
|
||||
#define FRAME_walk03 74
|
||||
#define FRAME_walk04 75
|
||||
#define FRAME_walk05 76
|
||||
#define FRAME_walk06 77
|
||||
#define FRAME_walk07 78
|
||||
#define FRAME_walk08 79
|
||||
#define FRAME_walk09 80
|
||||
#define FRAME_walk10 81
|
||||
#define FRAME_walk11 82
|
||||
#define FRAME_walk12 83
|
||||
#define FRAME_walk13 84
|
||||
#define FRAME_walk14 85
|
||||
#define FRAME_walk15 86
|
||||
#define FRAME_walk16 87
|
||||
#define FRAME_walk17 88
|
||||
#define FRAME_walk18 89
|
||||
#define FRAME_walk19 90
|
||||
#define FRAME_walk20 91
|
||||
#define FRAME_run01 92
|
||||
#define FRAME_run02 93
|
||||
#define FRAME_run03 94
|
||||
#define FRAME_run04 95
|
||||
#define FRAME_run05 96
|
||||
#define FRAME_run06 97
|
||||
#define FRAME_run07 98
|
||||
#define FRAME_run08 99
|
||||
#define FRAME_pain101 100
|
||||
#define FRAME_pain102 101
|
||||
#define FRAME_pain103 102
|
||||
#define FRAME_pain104 103
|
||||
#define FRAME_pain105 104
|
||||
#define FRAME_pain106 105
|
||||
#define FRAME_pain107 106
|
||||
#define FRAME_pain108 107
|
||||
#define FRAME_pain109 108
|
||||
#define FRAME_pain110 109
|
||||
#define FRAME_pain201 110
|
||||
#define FRAME_pain202 111
|
||||
#define FRAME_pain203 112
|
||||
#define FRAME_pain204 113
|
||||
#define FRAME_pain205 114
|
||||
#define FRAME_pain206 115
|
||||
#define FRAME_pain207 116
|
||||
#define FRAME_pain208 117
|
||||
#define FRAME_pain209 118
|
||||
#define FRAME_pain210 119
|
||||
#define FRAME_duck01 120
|
||||
#define FRAME_duck02 121
|
||||
#define FRAME_duck03 122
|
||||
#define FRAME_duck04 123
|
||||
#define FRAME_duck05 124
|
||||
#define FRAME_death101 125
|
||||
#define FRAME_death102 126
|
||||
#define FRAME_death103 127
|
||||
#define FRAME_death104 128
|
||||
#define FRAME_death105 129
|
||||
#define FRAME_death106 130
|
||||
#define FRAME_death107 131
|
||||
#define FRAME_death108 132
|
||||
#define FRAME_death109 133
|
||||
#define FRAME_death110 134
|
||||
#define FRAME_death111 135
|
||||
#define FRAME_death112 136
|
||||
#define FRAME_death113 137
|
||||
#define FRAME_death114 138
|
||||
#define FRAME_death115 139
|
||||
#define FRAME_death116 140
|
||||
#define FRAME_death117 141
|
||||
#define FRAME_death118 142
|
||||
#define FRAME_death119 143
|
||||
#define FRAME_death120 144
|
||||
#define FRAME_death201 145
|
||||
#define FRAME_death202 146
|
||||
#define FRAME_death203 147
|
||||
#define FRAME_death204 148
|
||||
#define FRAME_death205 149
|
||||
#define FRAME_death206 150
|
||||
#define FRAME_death207 151
|
||||
#define FRAME_death208 152
|
||||
#define FRAME_death209 153
|
||||
#define FRAME_death210 154
|
||||
#define FRAME_death211 155
|
||||
#define FRAME_death212 156
|
||||
#define FRAME_death213 157
|
||||
#define FRAME_death214 158
|
||||
#define FRAME_death215 159
|
||||
#define FRAME_death216 160
|
||||
#define FRAME_death217 161
|
||||
#define FRAME_death218 162
|
||||
#define FRAME_death219 163
|
||||
#define FRAME_death220 164
|
||||
#define FRAME_death221 165
|
||||
#define FRAME_death222 166
|
||||
#define FRAME_death223 167
|
||||
#define FRAME_death224 168
|
||||
#define FRAME_death225 169
|
||||
#define FRAME_death301 170
|
||||
#define FRAME_death302 171
|
||||
#define FRAME_death303 172
|
||||
#define FRAME_death304 173
|
||||
#define FRAME_death305 174
|
||||
#define FRAME_death306 175
|
||||
#define FRAME_death307 176
|
||||
#define FRAME_death308 177
|
||||
#define FRAME_death309 178
|
||||
#define FRAME_block01 179
|
||||
#define FRAME_block02 180
|
||||
#define FRAME_block03 181
|
||||
#define FRAME_block04 182
|
||||
#define FRAME_block05 183
|
||||
#define FRAME_attak101 184
|
||||
#define FRAME_attak102 185
|
||||
#define FRAME_attak103 186
|
||||
#define FRAME_attak104 187
|
||||
#define FRAME_attak105 188
|
||||
#define FRAME_attak106 189
|
||||
#define FRAME_attak107 190
|
||||
#define FRAME_attak108 191
|
||||
#define FRAME_attak109 192
|
||||
#define FRAME_attak110 193
|
||||
#define FRAME_attak111 194
|
||||
#define FRAME_attak112 195
|
||||
#define FRAME_attak113 196
|
||||
#define FRAME_attak114 197
|
||||
#define FRAME_attak115 198
|
||||
#define FRAME_attak201 199
|
||||
#define FRAME_attak202 200
|
||||
#define FRAME_attak203 201
|
||||
#define FRAME_attak204 202
|
||||
#define FRAME_attak205 203
|
||||
#define FRAME_attak206 204
|
||||
#define FRAME_attak207 205
|
||||
#define FRAME_attak208 206
|
||||
|
||||
#define MODEL_SCALE 1.000000
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user