From c2f62eb98a2280ee6b18ec9cd7281ab511b1ec1c Mon Sep 17 00:00:00 2001 From: s1lent Date: Sun, 22 Sep 2019 21:29:29 +0700 Subject: [PATCH] Fix newlines --- regamedll/common/const.h | 1562 +++--- regamedll/dlls/animation.cpp | 2384 ++++----- regamedll/dlls/bot/cs_bot_manager.cpp | 3166 ++++++------ regamedll/dlls/bot/cs_bot_manager.h | 540 +- regamedll/dlls/bot/cs_bot_update.cpp | 1608 +++--- regamedll/dlls/cdll_dll.h | 238 +- regamedll/dlls/combat.cpp | 748 +-- regamedll/dlls/extdll.h | 170 +- regamedll/dlls/func_break.cpp | 2206 ++++----- regamedll/dlls/func_tank.cpp | 1826 +++---- regamedll/dlls/game.cpp | 732 +-- regamedll/dlls/game.h | 358 +- regamedll/dlls/gamerules.h | 1830 +++---- regamedll/dlls/globals.cpp | 28 +- regamedll/dlls/globals.h | 82 +- regamedll/dlls/h_battery.cpp | 440 +- regamedll/dlls/healthkit.cpp | 488 +- regamedll/dlls/player.h | 1966 ++++---- regamedll/dlls/triggers.cpp | 4918 +++++++++---------- regamedll/dlls/util.cpp | 3696 +++++++------- regamedll/dlls/util.h | 758 +-- regamedll/dlls/vector.h | 852 ++-- regamedll/dlls/weapons.cpp | 4882 +++++++++---------- regamedll/dlls/weapons.h | 4042 +++++++-------- regamedll/dlls/weapontype.h | 928 ++-- regamedll/engine/eiface.h | 1082 ++-- regamedll/engine/maintypes.h | 152 +- regamedll/game_shared/bot/bot.cpp | 1016 ++-- regamedll/pm_shared/pm_shared.cpp | 6494 ++++++++++++------------- 29 files changed, 24596 insertions(+), 24596 deletions(-) diff --git a/regamedll/common/const.h b/regamedll/common/const.h index 323f5ab0..3c987311 100644 --- a/regamedll/common/const.h +++ b/regamedll/common/const.h @@ -1,781 +1,781 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#ifndef CONST_H -#define CONST_H -#ifdef _WIN32 -#pragma once -#endif - -// Max # of clients allowed in a server. -#define MAX_CLIENTS 32 - -// How many bits to use to encode an edict. -#define MAX_EDICT_BITS 11 // # of bits needed to represent max edicts - -// Max # of edicts in a level (2048) -#define MAX_EDICTS BIT(MAX_EDICT_BITS) - -// How many data slots to use when in multiplayer (must be power of 2) -#define MULTIPLAYER_BACKUP 64 - -// Same for single player -#define SINGLEPLAYER_BACKUP 8 -// -// Constants shared by the engine and dlls -// This header file included by engine files and DLL files. -// Most came from server.h - -// edict->flags -#define FL_FLY BIT(0) // Changes the SV_Movestep() behavior to not need to be on ground -#define FL_SWIM BIT(1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) -#define FL_CONVEYOR BIT(2) -#define FL_CLIENT BIT(3) -#define FL_INWATER BIT(4) -#define FL_MONSTER BIT(5) -#define FL_GODMODE BIT(6) -#define FL_NOTARGET BIT(7) -#define FL_SKIPLOCALHOST BIT(8) // Don't send entity to local host, it's predicting this entity itself -#define FL_ONGROUND BIT(9) // At rest / on the ground -#define FL_PARTIALGROUND BIT(10) // not all corners are valid -#define FL_WATERJUMP BIT(11) // player jumping out of water -#define FL_FROZEN BIT(12) // Player is frozen for 3rd person camera -#define FL_FAKECLIENT BIT(13) // JAC: fake client, simulated server side; don't send network messages to them -#define FL_DUCKING BIT(14) // Player flag -- Player is fully crouched -#define FL_FLOAT BIT(15) // Apply floating force to this entity when in water -#define FL_GRAPHED BIT(16) // worldgraph has this ent listed as something that blocks a connection - -// UNDONE: Do we need these? -#define FL_IMMUNE_WATER BIT(17) -#define FL_IMMUNE_SLIME BIT(18) -#define FL_IMMUNE_LAVA BIT(19) - -#define FL_PROXY BIT(20) // This is a spectator proxy -#define FL_ALWAYSTHINK BIT(21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) -#define FL_BASEVELOCITY BIT(22) // Base velocity has been applied this frame (used to convert base velocity into momentum) -#define FL_MONSTERCLIP BIT(23) // Only collide in with monsters who have FL_MONSTERCLIP set -#define FL_ONTRAIN BIT(24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. -#define FL_WORLDBRUSH BIT(25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) -#define FL_SPECTATOR BIT(26) // This client is a spectator, don't run touch functions, etc. -#define FL_CUSTOMENTITY BIT(29) // This is a custom entity -#define FL_KILLME BIT(30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time -#define FL_DORMANT BIT(31) // Entity is dormant, no updates to client - -// SV_EmitSound2 flags -#define SND_EMIT2_NOPAS BIT(0) // never to do check PAS -#define SND_EMIT2_INVOKER BIT(1) // do not send to the client invoker - -// Engine edict->spawnflags -#define SF_NOTINDEATHMATCH BIT(11) // Do not spawn when deathmatch and loading entities from a file - -// Goes into globalvars_t.trace_flags -#define FTRACE_SIMPLEBOX BIT(0) // Traceline with a simple box - -// walkmove modes -#define WALKMOVE_NORMAL 0 // normal walkmove -#define WALKMOVE_WORLDONLY 1 // doesn't hit ANY entities, no matter what the solid type -#define WALKMOVE_CHECKONLY 2 // move, but don't touch triggers - -// edict->movetype values -#define MOVETYPE_NONE 0 // never moves -//#define MOVETYPE_ANGLENOCLIP 1 -//#define MOVETYPE_ANGLECLIP 2 -#define MOVETYPE_WALK 3 // Player only - moving on the ground -#define MOVETYPE_STEP 4 // gravity, special edge handling -- monsters use this -#define MOVETYPE_FLY 5 // No gravity, but still collides with stuff -#define MOVETYPE_TOSS 6 // gravity/collisions -#define MOVETYPE_PUSH 7 // no clip to world, push and crush -#define MOVETYPE_NOCLIP 8 // No gravity, no collisions, still do velocity/avelocity -#define MOVETYPE_FLYMISSILE 9 // extra size to monsters -#define MOVETYPE_BOUNCE 10 // Just like Toss, but reflect velocity when contacting surfaces -#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity -#define MOVETYPE_FOLLOW 12 // track movement of aiment -#define MOVETYPE_PUSHSTEP 13 // BSP model that needs physics/world collisions (uses nearest hull for world collision) - -// edict->solid values -// NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves -// SOLID only effects OTHER entities colliding with this one when they move - UGH! -#define SOLID_NOT 0 // no interaction with other objects -#define SOLID_TRIGGER 1 // touch on edge, but not blocking -#define SOLID_BBOX 2 // touch on edge, block -#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground -#define SOLID_BSP 4 // bsp clip, touch on edge, block - -// edict->deadflag values -#define DEAD_NO 0 // alive -#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground -#define DEAD_DEAD 2 // dead. lying still. -#define DEAD_RESPAWNABLE 3 // do respawn the entity -#define DEAD_DISCARDBODY 4 - -#define DAMAGE_NO 0 -#define DAMAGE_YES 1 -#define DAMAGE_AIM 2 - -// edict->effects values -#define EF_BRIGHTFIELD BIT(0) // swirling cloud of particles -#define EF_MUZZLEFLASH BIT(1) // single frame ELIGHT on entity attachment 0 -#define EF_BRIGHTLIGHT BIT(2) // DLIGHT centered at entity origin -#define EF_DIMLIGHT BIT(3) // player flashlight -#define EF_INVLIGHT BIT(4) // get lighting from ceiling -#define EF_NOINTERP BIT(5) // don't interpolate the next frame -#define EF_LIGHT BIT(6) // rocket flare glow sprite -#define EF_NODRAW BIT(7) // don't draw entity -#define EF_NIGHTVISION BIT(8) // player nightvision -#define EF_SNIPERLASER BIT(9) // sniper laser effect -#define EF_FIBERCAMERA BIT(10) // fiber camera -#define EF_FORCEVISIBILITY BIT(11) // force visibility -#define EF_OWNER_VISIBILITY BIT(12) // visibility for owner -#define EF_OWNER_NO_VISIBILITY BIT(13) // no visibility for owner - -// state->eflags values -#define EFLAG_SLERP 1 // do studio interpolation of this entity - -// -// temp entity events -// -#define TE_BEAMPOINTS 0 // beam effect between two points -// coord coord coord (start position) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMENTPOINT 1 // beam effect between point and entity -// short (start entity) -// coord coord coord (end position) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_GUNSHOT 2 // particle effect plus ricochet sound -// coord coord coord (position) - -#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps -// coord coord coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (framerate) -// byte (flags) -// -// The Explosion effect has some flags to control performance/aesthetic features: -#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion -#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) -#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights -#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound -#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles - -#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound -// coord coord coord (position) - -#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps -// coord coord coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (framerate) - -#define TE_TRACER 6 // tracer effect from point to point -// coord, coord, coord (start) -// coord, coord, coord (end) - -#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters -// coord, coord, coord (start) -// coord, coord, coord (end) -// byte (life in 0.1's) -// byte (width in 0.1's) -// byte (amplitude in 0.01's) -// short (sprite model index) - -#define TE_BEAMENTS 8 -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite -// coord coord coord (position) - -#define TE_LAVASPLASH 10 // Quake1 lava splash -// coord coord coord (position) - -#define TE_TELEPORT 11 // Quake1 teleport splash -// coord coord coord (position) - -#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound -// coord coord coord (position) -// byte (starting color) -// byte (num colors) - -#define TE_BSPDECAL 13 // Decal from the .BSP file -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// short (texture index of precached decal texture name) -// short (entity index) -// [optional - only included if previous short is non-zero (not the world)] short (index of model of above entity) - -#define TE_IMPLOSION 14 // tracers moving toward a point -// coord, coord, coord (position) -// byte (radius) -// byte (count) -// byte (life in 0.1's) - -#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions -// coord, coord, coord (start) -// coord, coord, coord (end) -// short (sprite index) -// byte (count) -// byte (life in 0.1's) -// byte (scale in 0.1's) -// byte (velocity along vector in 10's) -// byte (randomness of velocity in 10's) - -#define TE_BEAM 16 // obsolete - -#define TE_SPRITE 17 // additive sprite, plays 1 cycle -// coord, coord, coord (position) -// short (sprite index) -// byte (scale in 0.1's) -// byte (brightness) - -#define TE_BEAMSPRITE 18 // A beam with a sprite at the end -// coord, coord, coord (start position) -// coord, coord, coord (end position) -// short (beam sprite index) -// short (end sprite index) - -#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime -// coord coord coord (center position) -// coord coord coord (axis and radius) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving -// short (entity:attachment to follow) -// short (sprite index) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte,byte,byte (color) -// byte (brightness) - -#define TE_GLOWSPRITE 23 -// coord, coord, coord (pos) short (model index) byte (scale / 10) - -#define TE_BEAMRING 24 // connect a beam ring to two entities -// short (start entity) -// short (end entity) -// short (sprite index) -// byte (starting frame) -// byte (frame rate in 0.1's) -// byte (life in 0.1's) -// byte (line width in 0.1's) -// byte (noise amplitude in 0.01's) -// byte,byte,byte (color) -// byte (brightness) -// byte (scroll speed in 0.1's) - -#define TE_STREAK_SPLASH 25 // oriented shower of tracers -// coord coord coord (start position) -// coord coord coord (direction vector) -// byte (color) -// short (count) -// short (base speed) -// short (ramdon velocity) - -#define TE_BEAMHOSE 26 // obsolete - -#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect -// coord, coord, coord (pos) -// byte (radius in 10's) -// byte byte byte (color) -// byte (brightness) -// byte (life in 10's) -// byte (decay rate in 10's) - -#define TE_ELIGHT 28 // point entity light, no world effect -// short (entity:attachment to follow) -// coord coord coord (initial position) -// coord (radius) -// byte byte byte (color) -// byte (life in 0.1's) -// coord (decay rate) - -#define TE_TEXTMESSAGE 29 -// short 1.2.13 x (-1 = center) -// short 1.2.13 y (-1 = center) -// byte Effect 0 = fade in/fade out -// 1 is flickery credits -// 2 is write out (training room) - -// 4 bytes r,g,b,a color1 (text color) -// 4 bytes r,g,b,a color2 (effect color) -// ushort 8.8 fadein time -// ushort 8.8 fadeout time -// ushort 8.8 hold time -// optional ushort 8.8 fxtime (time the highlight lags behing the leading text in effect 2) -// string text message (512 chars max sz string) -#define TE_LINE 30 -// coord, coord, coord (startpos) -// coord, coord, coord (endpos) -// short life in 0.1 s -// 3 bytes r, g, b - -#define TE_BOX 31 -// coord, coord, coord (boxmins) -// coord, coord, coord (boxmaxs) -// short life in 0.1 s -// 3 bytes r, g, b - -#define TE_KILLBEAM 99 // kill all beams attached to entity -// short (entity) - -#define TE_LARGEFUNNEL 100 -// coord coord coord (funnel position) -// short (sprite index) -// short (flags) - -#define TE_BLOODSTREAM 101 // particle spray -// coord coord coord (start position) -// coord coord coord (spray vector) -// byte (color) -// byte (speed) - -#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds -// coord coord coord (start position) -// coord coord coord (end position) - -#define TE_BLOOD 103 // particle spray -// coord coord coord (start position) -// coord coord coord (spray vector) -// byte (color) -// byte (speed) - -#define TE_DECAL 104 // Decal applied to a brush entity (not the world) -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name) -// short (entity index) - -#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards -// short (entity) -// short (sprite index) -// byte (density) - -#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// angle (initial yaw) -// short (model index) -// byte (bounce sound type) -// byte (life in 0.1's) - -#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set -// coord, coord, coord (origin) -// coord (velocity) -// short (model index) -// short (count) -// byte (life in 0.1's) - -#define TE_BREAKMODEL 108 // box of models or sprites -// coord, coord, coord (position) -// coord, coord, coord (size) -// coord, coord, coord (velocity) -// byte (random velocity in 10's) -// short (sprite or model index) -// byte (count) -// byte (life in 0.1 secs) -// byte (flags) - -#define TE_GUNSHOTDECAL 109 // decal and ricochet sound -// coord, coord, coord (position) -// short (entity index???) -// byte (decal???) - -#define TE_SPRITE_SPRAY 110 // spay of alpha sprites -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// short (sprite index) -// byte (count) -// byte (speed) -// byte (noise) - -#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. -// coord, coord, coord (position) -// byte (scale in 0.1's) - -#define TE_PLAYERDECAL 112 // ??? -// byte (playerindex) -// coord, coord, coord (position) -// short (entity???) -// byte (decal number???) -// [optional] short (model index???) - -#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards -// coord, coord, coord (min start position) -// coord, coord, coord (max start position) -// coord (float height) -// short (model index) -// byte (count) -// coord (speed) - -#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards -// coord, coord, coord (min start position) -// coord, coord, coord (max start position) -// coord (float height) -// short (model index) -// byte (count) -// coord (speed) - -#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) -// coord, coord, coord (position) -// short (sprite1 index) -// short (sprite2 index) -// byte (color) -// byte (scale) - -#define TE_WORLDDECAL 116 // Decal applied to the world brush -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name) - -#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name - 256) - -#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 -// coord, coord, coord (x,y,z), decal position (center of texture in world) -// byte (texture index of precached decal texture name - 256) -// short (entity index) - -#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) -// coord, coord, coord (position) -// coord, coord, coord (velocity) -// short (modelindex) -// byte (life) -// byte (owner) projectile won't collide with owner (if owner == 0, projectile will hit any client). - -#define TE_SPRAY 120 // Throws a shower of sprites or models -// coord, coord, coord (position) -// coord, coord, coord (direction) -// short (modelindex) -// byte (count) -// byte (speed) -// byte (noise) -// byte (rendermode) - -#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) -// byte (playernum) -// short (sprite modelindex) -// byte (count) -// byte (variance) (0 = no variance in size) (10 = 10% variance in size) - -#define TE_PARTICLEBURST 122 // very similar to lavasplash. -// coord (origin) -// short (radius) -// byte (particle color) -// byte (duration * 10) (will be randomized a bit) - -#define TE_FIREFIELD 123 // makes a field of fire. -// coord (origin) -// short (radius) (fire is made in a square around origin. -radius, -radius to radius, radius) -// short (modelindex) -// byte (count) -// byte (flags) -// byte (duration (in seconds) * 10) (will be randomized a bit) -// -// to keep network traffic low, this message has associated flags that fit into a byte: -#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate -#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) -#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. -#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque -#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. -#define TEFIRE_FLAG_ADDITIVE 32 // if set, sprite is rendered non-opaque with additive - -#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) -// byte (entity index of player) -// coord (vertical offset) (attachment origin.z = player origin.z + vertical offset) -// short (model index) -// short (life * 10) - -#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. -// byte (entity index of player) - -#define TE_MULTIGUNSHOT 126 // much more compact shotgun message -// This message is used to make a client approximate a 'spray' of gunfire. -// Any weapon that fires more than one bullet per frame and fires in a bit of a spread is -// a good candidate for MULTIGUNSHOT use. (shotguns) -// -// NOTE: This effect makes the client do traces for each bullet, these client traces ignore -// entities that have studio models.Traces are 4096 long. -// -// coord (origin) -// coord (origin) -// coord (origin) -// coord (direction) -// coord (direction) -// coord (direction) -// coord (x noise * 100) -// coord (y noise * 100) -// byte (count) -// byte (bullethole decal texture index) - -#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. -// coord (origin) -// coord (origin) -// coord (origin) -// coord (velocity) -// coord (velocity) -// coord (velocity) -// byte (life * 10) -// byte (color) this is an index into an array of color vectors in the engine. (0 -) -// byte (length * 10) - -#define MSG_BROADCAST 0 // unreliable to all -#define MSG_ONE 1 // reliable to one (msg_entity) -#define MSG_ALL 2 // reliable to all -#define MSG_INIT 3 // write to the init string -#define MSG_PVS 4 // Ents in PVS of org -#define MSG_PAS 5 // Ents in PAS of org -#define MSG_PVS_R 6 // Reliable to PVS -#define MSG_PAS_R 7 // Reliable to PAS -#define MSG_ONE_UNRELIABLE 8 // Send to one client, but don't put in reliable stream, put in unreliable datagram ( could be dropped ) -#define MSG_SPEC 9 // Sends to all spectator proxies - -// contents of a spot in the world -#define CONTENTS_EMPTY -1 -#define CONTENTS_SOLID -2 -#define CONTENTS_WATER -3 -#define CONTENTS_SLIME -4 -#define CONTENTS_LAVA -5 -#define CONTENTS_SKY -6 - -#define CONTENTS_LADDER -16 - -#define CONTENT_FLYFIELD -17 -#define CONTENT_GRAVITY_FLYFIELD -18 -#define CONTENT_FOG -19 - -#define CONTENT_EMPTY -1 -#define CONTENT_SOLID -2 -#define CONTENT_WATER -3 -#define CONTENT_SLIME -4 -#define CONTENT_LAVA -5 -#define CONTENT_SKY -6 - -// channels -#define CHAN_AUTO 0 -#define CHAN_WEAPON 1 -#define CHAN_VOICE 2 -#define CHAN_ITEM 3 -#define CHAN_BODY 4 -#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area -#define CHAN_STATIC 6 // allocate channel from the static area -#define CHAN_NETWORKVOICE_BASE 7 // voice data coming across the network -#define CHAN_NETWORKVOICE_END 500 // network voice data reserves slots (CHAN_NETWORKVOICE_BASE through CHAN_NETWORKVOICE_END). -#define CHAN_BOT 501 // channel used for bot chatter. - -// attenuation values -#define ATTN_NONE 0 -#define ATTN_NORM 0.8f -#define ATTN_IDLE 2.0f -#define ATTN_STATIC 1.25f - -// pitch values -#define PITCH_NORM 100 // non-pitch shifted -#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high -#define PITCH_HIGH 120 - -// volume values -#define VOL_NORM 1.0f - -// buttons -#ifndef IN_BUTTONS_H -#include "in_buttons.h" -#endif - -// Break Model Defines -#define BREAK_TYPEMASK 0x4F -#define BREAK_GLASS 0x01 -#define BREAK_METAL 0x02 -#define BREAK_FLESH 0x04 -#define BREAK_WOOD 0x08 - -#define BREAK_SMOKE 0x10 -#define BREAK_TRANS 0x20 -#define BREAK_CONCRETE 0x40 -#define BREAK_2 0x80 - -// Colliding temp entity sounds -#define BOUNCE_GLASS BREAK_GLASS -#define BOUNCE_METAL BREAK_METAL -#define BOUNCE_FLESH BREAK_FLESH -#define BOUNCE_WOOD BREAK_WOOD -#define BOUNCE_SHRAP 0x10 -#define BOUNCE_SHELL 0x20 -#define BOUNCE_CONCRETE BREAK_CONCRETE -#define BOUNCE_SHOTSHELL 0x80 - -// Temp entity bounce sound types -#define TE_BOUNCE_NULL 0 -#define TE_BOUNCE_SHELL 1 -#define TE_BOUNCE_SHOTSHELL 2 - -// Rendering constants -enum -{ - kRenderNormal, // src - kRenderTransColor, // c*a+dest*(1-a) - kRenderTransTexture, // src*a+dest*(1-a) - kRenderGlow, // src*a+dest -- No Z buffer checks - kRenderTransAlpha, // src*srca+dest*(1-srca) - kRenderTransAdd, // src*a+dest -}; - -enum -{ - kRenderFxNone = 0, - kRenderFxPulseSlow, - kRenderFxPulseFast, - kRenderFxPulseSlowWide, - kRenderFxPulseFastWide, - kRenderFxFadeSlow, - kRenderFxFadeFast, - kRenderFxSolidSlow, - kRenderFxSolidFast, - kRenderFxStrobeSlow, - kRenderFxStrobeFast, - kRenderFxStrobeFaster, - kRenderFxFlickerSlow, - kRenderFxFlickerFast, - kRenderFxNoDissipation, - kRenderFxDistort, // Distort/scale/translate flicker - kRenderFxHologram, // kRenderFxDistort + distance fade - kRenderFxDeadPlayer, // kRenderAmt is the player index - kRenderFxExplode, // Scale up really big! - kRenderFxGlowShell, // Glowing Shell - kRenderFxClampMinScale, // Keep this sprite from getting very small (SPRITES only!) - kRenderFxLightMultiplier, // CTM !!!CZERO added to tell the studiorender that the value in iuser2 is a lightmultiplier -}; - -typedef struct -{ - byte r, g, b; -} color24; - -typedef struct -{ - unsigned r, g, b, a; -} colorVec; - -#ifdef _WIN32 -#pragma pack(push, 2) -#endif - -typedef struct -{ - unsigned short r, g, b, a; -} PackedColorVec; - -#ifdef _WIN32 -#pragma pack(pop) -#endif - -typedef struct link_s -{ - struct link_s *prev, *next; -} link_t; - -typedef struct edict_s edict_t; - -typedef struct -{ - vec3_t normal; - float dist; -} plane_t; - -typedef struct -{ - qboolean allsolid; // if true, plane is not valid - qboolean startsolid; // if true, the initial point was in a solid area - qboolean inopen, inwater; - float fraction; // time completed, 1.0 = didn't hit anything - vec3_t endpos; // final position - plane_t plane; // surface normal at impact - edict_t *ent; // entity the surface is on - int hitgroup; // 0 == generic, non zero is specific body part -} trace_t; - -#endif // CONST_H +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#ifndef CONST_H +#define CONST_H +#ifdef _WIN32 +#pragma once +#endif + +// Max # of clients allowed in a server. +#define MAX_CLIENTS 32 + +// How many bits to use to encode an edict. +#define MAX_EDICT_BITS 11 // # of bits needed to represent max edicts + +// Max # of edicts in a level (2048) +#define MAX_EDICTS BIT(MAX_EDICT_BITS) + +// How many data slots to use when in multiplayer (must be power of 2) +#define MULTIPLAYER_BACKUP 64 + +// Same for single player +#define SINGLEPLAYER_BACKUP 8 +// +// Constants shared by the engine and dlls +// This header file included by engine files and DLL files. +// Most came from server.h + +// edict->flags +#define FL_FLY BIT(0) // Changes the SV_Movestep() behavior to not need to be on ground +#define FL_SWIM BIT(1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) +#define FL_CONVEYOR BIT(2) +#define FL_CLIENT BIT(3) +#define FL_INWATER BIT(4) +#define FL_MONSTER BIT(5) +#define FL_GODMODE BIT(6) +#define FL_NOTARGET BIT(7) +#define FL_SKIPLOCALHOST BIT(8) // Don't send entity to local host, it's predicting this entity itself +#define FL_ONGROUND BIT(9) // At rest / on the ground +#define FL_PARTIALGROUND BIT(10) // not all corners are valid +#define FL_WATERJUMP BIT(11) // player jumping out of water +#define FL_FROZEN BIT(12) // Player is frozen for 3rd person camera +#define FL_FAKECLIENT BIT(13) // JAC: fake client, simulated server side; don't send network messages to them +#define FL_DUCKING BIT(14) // Player flag -- Player is fully crouched +#define FL_FLOAT BIT(15) // Apply floating force to this entity when in water +#define FL_GRAPHED BIT(16) // worldgraph has this ent listed as something that blocks a connection + +// UNDONE: Do we need these? +#define FL_IMMUNE_WATER BIT(17) +#define FL_IMMUNE_SLIME BIT(18) +#define FL_IMMUNE_LAVA BIT(19) + +#define FL_PROXY BIT(20) // This is a spectator proxy +#define FL_ALWAYSTHINK BIT(21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) +#define FL_BASEVELOCITY BIT(22) // Base velocity has been applied this frame (used to convert base velocity into momentum) +#define FL_MONSTERCLIP BIT(23) // Only collide in with monsters who have FL_MONSTERCLIP set +#define FL_ONTRAIN BIT(24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. +#define FL_WORLDBRUSH BIT(25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) +#define FL_SPECTATOR BIT(26) // This client is a spectator, don't run touch functions, etc. +#define FL_CUSTOMENTITY BIT(29) // This is a custom entity +#define FL_KILLME BIT(30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time +#define FL_DORMANT BIT(31) // Entity is dormant, no updates to client + +// SV_EmitSound2 flags +#define SND_EMIT2_NOPAS BIT(0) // never to do check PAS +#define SND_EMIT2_INVOKER BIT(1) // do not send to the client invoker + +// Engine edict->spawnflags +#define SF_NOTINDEATHMATCH BIT(11) // Do not spawn when deathmatch and loading entities from a file + +// Goes into globalvars_t.trace_flags +#define FTRACE_SIMPLEBOX BIT(0) // Traceline with a simple box + +// walkmove modes +#define WALKMOVE_NORMAL 0 // normal walkmove +#define WALKMOVE_WORLDONLY 1 // doesn't hit ANY entities, no matter what the solid type +#define WALKMOVE_CHECKONLY 2 // move, but don't touch triggers + +// edict->movetype values +#define MOVETYPE_NONE 0 // never moves +//#define MOVETYPE_ANGLENOCLIP 1 +//#define MOVETYPE_ANGLECLIP 2 +#define MOVETYPE_WALK 3 // Player only - moving on the ground +#define MOVETYPE_STEP 4 // gravity, special edge handling -- monsters use this +#define MOVETYPE_FLY 5 // No gravity, but still collides with stuff +#define MOVETYPE_TOSS 6 // gravity/collisions +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 // No gravity, no collisions, still do velocity/avelocity +#define MOVETYPE_FLYMISSILE 9 // extra size to monsters +#define MOVETYPE_BOUNCE 10 // Just like Toss, but reflect velocity when contacting surfaces +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment +#define MOVETYPE_PUSHSTEP 13 // BSP model that needs physics/world collisions (uses nearest hull for world collision) + +// edict->solid values +// NOTE: Some movetypes will cause collisions independent of SOLID_NOT/SOLID_TRIGGER when the entity moves +// SOLID only effects OTHER entities colliding with this one when they move - UGH! +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block + +// edict->deadflag values +#define DEAD_NO 0 // alive +#define DEAD_DYING 1 // playing death animation or still falling off of a ledge waiting to hit ground +#define DEAD_DEAD 2 // dead. lying still. +#define DEAD_RESPAWNABLE 3 // do respawn the entity +#define DEAD_DISCARDBODY 4 + +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 + +// edict->effects values +#define EF_BRIGHTFIELD BIT(0) // swirling cloud of particles +#define EF_MUZZLEFLASH BIT(1) // single frame ELIGHT on entity attachment 0 +#define EF_BRIGHTLIGHT BIT(2) // DLIGHT centered at entity origin +#define EF_DIMLIGHT BIT(3) // player flashlight +#define EF_INVLIGHT BIT(4) // get lighting from ceiling +#define EF_NOINTERP BIT(5) // don't interpolate the next frame +#define EF_LIGHT BIT(6) // rocket flare glow sprite +#define EF_NODRAW BIT(7) // don't draw entity +#define EF_NIGHTVISION BIT(8) // player nightvision +#define EF_SNIPERLASER BIT(9) // sniper laser effect +#define EF_FIBERCAMERA BIT(10) // fiber camera +#define EF_FORCEVISIBILITY BIT(11) // force visibility +#define EF_OWNER_VISIBILITY BIT(12) // visibility for owner +#define EF_OWNER_NO_VISIBILITY BIT(13) // no visibility for owner + +// state->eflags values +#define EFLAG_SLERP 1 // do studio interpolation of this entity + +// +// temp entity events +// +#define TE_BEAMPOINTS 0 // beam effect between two points +// coord coord coord (start position) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMENTPOINT 1 // beam effect between point and entity +// short (start entity) +// coord coord coord (end position) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_GUNSHOT 2 // particle effect plus ricochet sound +// coord coord coord (position) + +#define TE_EXPLOSION 3 // additive sprite, 2 dynamic lights, flickering particles, explosion sound, move vertically 8 pps +// coord coord coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (framerate) +// byte (flags) +// +// The Explosion effect has some flags to control performance/aesthetic features: +#define TE_EXPLFLAG_NONE 0 // all flags clear makes default Half-Life explosion +#define TE_EXPLFLAG_NOADDITIVE 1 // sprite will be drawn opaque (ensure that the sprite you send is a non-additive sprite) +#define TE_EXPLFLAG_NODLIGHTS 2 // do not render dynamic lights +#define TE_EXPLFLAG_NOSOUND 4 // do not play client explosion sound +#define TE_EXPLFLAG_NOPARTICLES 8 // do not draw particles + +#define TE_TAREXPLOSION 4 // Quake1 "tarbaby" explosion with sound +// coord coord coord (position) + +#define TE_SMOKE 5 // alphablend sprite, move vertically 30 pps +// coord coord coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (framerate) + +#define TE_TRACER 6 // tracer effect from point to point +// coord, coord, coord (start) +// coord, coord, coord (end) + +#define TE_LIGHTNING 7 // TE_BEAMPOINTS with simplified parameters +// coord, coord, coord (start) +// coord, coord, coord (end) +// byte (life in 0.1's) +// byte (width in 0.1's) +// byte (amplitude in 0.01's) +// short (sprite model index) + +#define TE_BEAMENTS 8 +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_SPARKS 9 // 8 random tracers with gravity, ricochet sprite +// coord coord coord (position) + +#define TE_LAVASPLASH 10 // Quake1 lava splash +// coord coord coord (position) + +#define TE_TELEPORT 11 // Quake1 teleport splash +// coord coord coord (position) + +#define TE_EXPLOSION2 12 // Quake1 colormaped (base palette) particle explosion with sound +// coord coord coord (position) +// byte (starting color) +// byte (num colors) + +#define TE_BSPDECAL 13 // Decal from the .BSP file +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// short (texture index of precached decal texture name) +// short (entity index) +// [optional - only included if previous short is non-zero (not the world)] short (index of model of above entity) + +#define TE_IMPLOSION 14 // tracers moving toward a point +// coord, coord, coord (position) +// byte (radius) +// byte (count) +// byte (life in 0.1's) + +#define TE_SPRITETRAIL 15 // line of moving glow sprites with gravity, fadeout, and collisions +// coord, coord, coord (start) +// coord, coord, coord (end) +// short (sprite index) +// byte (count) +// byte (life in 0.1's) +// byte (scale in 0.1's) +// byte (velocity along vector in 10's) +// byte (randomness of velocity in 10's) + +#define TE_BEAM 16 // obsolete + +#define TE_SPRITE 17 // additive sprite, plays 1 cycle +// coord, coord, coord (position) +// short (sprite index) +// byte (scale in 0.1's) +// byte (brightness) + +#define TE_BEAMSPRITE 18 // A beam with a sprite at the end +// coord, coord, coord (start position) +// coord, coord, coord (end position) +// short (beam sprite index) +// short (end sprite index) + +#define TE_BEAMTORUS 19 // screen aligned beam ring, expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMDISK 20 // disk that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMCYLINDER 21 // cylinder that expands to max radius over lifetime +// coord coord coord (center position) +// coord coord coord (axis and radius) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_BEAMFOLLOW 22 // create a line of decaying beam segments until entity stops moving +// short (entity:attachment to follow) +// short (sprite index) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte,byte,byte (color) +// byte (brightness) + +#define TE_GLOWSPRITE 23 +// coord, coord, coord (pos) short (model index) byte (scale / 10) + +#define TE_BEAMRING 24 // connect a beam ring to two entities +// short (start entity) +// short (end entity) +// short (sprite index) +// byte (starting frame) +// byte (frame rate in 0.1's) +// byte (life in 0.1's) +// byte (line width in 0.1's) +// byte (noise amplitude in 0.01's) +// byte,byte,byte (color) +// byte (brightness) +// byte (scroll speed in 0.1's) + +#define TE_STREAK_SPLASH 25 // oriented shower of tracers +// coord coord coord (start position) +// coord coord coord (direction vector) +// byte (color) +// short (count) +// short (base speed) +// short (ramdon velocity) + +#define TE_BEAMHOSE 26 // obsolete + +#define TE_DLIGHT 27 // dynamic light, effect world, minor entity effect +// coord, coord, coord (pos) +// byte (radius in 10's) +// byte byte byte (color) +// byte (brightness) +// byte (life in 10's) +// byte (decay rate in 10's) + +#define TE_ELIGHT 28 // point entity light, no world effect +// short (entity:attachment to follow) +// coord coord coord (initial position) +// coord (radius) +// byte byte byte (color) +// byte (life in 0.1's) +// coord (decay rate) + +#define TE_TEXTMESSAGE 29 +// short 1.2.13 x (-1 = center) +// short 1.2.13 y (-1 = center) +// byte Effect 0 = fade in/fade out +// 1 is flickery credits +// 2 is write out (training room) + +// 4 bytes r,g,b,a color1 (text color) +// 4 bytes r,g,b,a color2 (effect color) +// ushort 8.8 fadein time +// ushort 8.8 fadeout time +// ushort 8.8 hold time +// optional ushort 8.8 fxtime (time the highlight lags behing the leading text in effect 2) +// string text message (512 chars max sz string) +#define TE_LINE 30 +// coord, coord, coord (startpos) +// coord, coord, coord (endpos) +// short life in 0.1 s +// 3 bytes r, g, b + +#define TE_BOX 31 +// coord, coord, coord (boxmins) +// coord, coord, coord (boxmaxs) +// short life in 0.1 s +// 3 bytes r, g, b + +#define TE_KILLBEAM 99 // kill all beams attached to entity +// short (entity) + +#define TE_LARGEFUNNEL 100 +// coord coord coord (funnel position) +// short (sprite index) +// short (flags) + +#define TE_BLOODSTREAM 101 // particle spray +// coord coord coord (start position) +// coord coord coord (spray vector) +// byte (color) +// byte (speed) + +#define TE_SHOWLINE 102 // line of particles every 5 units, dies in 30 seconds +// coord coord coord (start position) +// coord coord coord (end position) + +#define TE_BLOOD 103 // particle spray +// coord coord coord (start position) +// coord coord coord (spray vector) +// byte (color) +// byte (speed) + +#define TE_DECAL 104 // Decal applied to a brush entity (not the world) +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name) +// short (entity index) + +#define TE_FIZZ 105 // create alpha sprites inside of entity, float upwards +// short (entity) +// short (sprite index) +// byte (density) + +#define TE_MODEL 106 // create a moving model that bounces and makes a sound when it hits +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// angle (initial yaw) +// short (model index) +// byte (bounce sound type) +// byte (life in 0.1's) + +#define TE_EXPLODEMODEL 107 // spherical shower of models, picks from set +// coord, coord, coord (origin) +// coord (velocity) +// short (model index) +// short (count) +// byte (life in 0.1's) + +#define TE_BREAKMODEL 108 // box of models or sprites +// coord, coord, coord (position) +// coord, coord, coord (size) +// coord, coord, coord (velocity) +// byte (random velocity in 10's) +// short (sprite or model index) +// byte (count) +// byte (life in 0.1 secs) +// byte (flags) + +#define TE_GUNSHOTDECAL 109 // decal and ricochet sound +// coord, coord, coord (position) +// short (entity index???) +// byte (decal???) + +#define TE_SPRITE_SPRAY 110 // spay of alpha sprites +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// short (sprite index) +// byte (count) +// byte (speed) +// byte (noise) + +#define TE_ARMOR_RICOCHET 111 // quick spark sprite, client ricochet sound. +// coord, coord, coord (position) +// byte (scale in 0.1's) + +#define TE_PLAYERDECAL 112 // ??? +// byte (playerindex) +// coord, coord, coord (position) +// short (entity???) +// byte (decal number???) +// [optional] short (model index???) + +#define TE_BUBBLES 113 // create alpha sprites inside of box, float upwards +// coord, coord, coord (min start position) +// coord, coord, coord (max start position) +// coord (float height) +// short (model index) +// byte (count) +// coord (speed) + +#define TE_BUBBLETRAIL 114 // create alpha sprites along a line, float upwards +// coord, coord, coord (min start position) +// coord, coord, coord (max start position) +// coord (float height) +// short (model index) +// byte (count) +// coord (speed) + +#define TE_BLOODSPRITE 115 // spray of opaque sprite1's that fall, single sprite2 for 1..2 secs (this is a high-priority tent) +// coord, coord, coord (position) +// short (sprite1 index) +// short (sprite2 index) +// byte (color) +// byte (scale) + +#define TE_WORLDDECAL 116 // Decal applied to the world brush +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name) + +#define TE_WORLDDECALHIGH 117 // Decal (with texture index > 256) applied to world brush +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name - 256) + +#define TE_DECALHIGH 118 // Same as TE_DECAL, but the texture index was greater than 256 +// coord, coord, coord (x,y,z), decal position (center of texture in world) +// byte (texture index of precached decal texture name - 256) +// short (entity index) + +#define TE_PROJECTILE 119 // Makes a projectile (like a nail) (this is a high-priority tent) +// coord, coord, coord (position) +// coord, coord, coord (velocity) +// short (modelindex) +// byte (life) +// byte (owner) projectile won't collide with owner (if owner == 0, projectile will hit any client). + +#define TE_SPRAY 120 // Throws a shower of sprites or models +// coord, coord, coord (position) +// coord, coord, coord (direction) +// short (modelindex) +// byte (count) +// byte (speed) +// byte (noise) +// byte (rendermode) + +#define TE_PLAYERSPRITES 121 // sprites emit from a player's bounding box (ONLY use for players!) +// byte (playernum) +// short (sprite modelindex) +// byte (count) +// byte (variance) (0 = no variance in size) (10 = 10% variance in size) + +#define TE_PARTICLEBURST 122 // very similar to lavasplash. +// coord (origin) +// short (radius) +// byte (particle color) +// byte (duration * 10) (will be randomized a bit) + +#define TE_FIREFIELD 123 // makes a field of fire. +// coord (origin) +// short (radius) (fire is made in a square around origin. -radius, -radius to radius, radius) +// short (modelindex) +// byte (count) +// byte (flags) +// byte (duration (in seconds) * 10) (will be randomized a bit) +// +// to keep network traffic low, this message has associated flags that fit into a byte: +#define TEFIRE_FLAG_ALLFLOAT 1 // all sprites will drift upwards as they animate +#define TEFIRE_FLAG_SOMEFLOAT 2 // some of the sprites will drift upwards. (50% chance) +#define TEFIRE_FLAG_LOOP 4 // if set, sprite plays at 15 fps, otherwise plays at whatever rate stretches the animation over the sprite's duration. +#define TEFIRE_FLAG_ALPHA 8 // if set, sprite is rendered alpha blended at 50% else, opaque +#define TEFIRE_FLAG_PLANAR 16 // if set, all fire sprites have same initial Z instead of randomly filling a cube. +#define TEFIRE_FLAG_ADDITIVE 32 // if set, sprite is rendered non-opaque with additive + +#define TE_PLAYERATTACHMENT 124 // attaches a TENT to a player (this is a high-priority tent) +// byte (entity index of player) +// coord (vertical offset) (attachment origin.z = player origin.z + vertical offset) +// short (model index) +// short (life * 10) + +#define TE_KILLPLAYERATTACHMENTS 125 // will expire all TENTS attached to a player. +// byte (entity index of player) + +#define TE_MULTIGUNSHOT 126 // much more compact shotgun message +// This message is used to make a client approximate a 'spray' of gunfire. +// Any weapon that fires more than one bullet per frame and fires in a bit of a spread is +// a good candidate for MULTIGUNSHOT use. (shotguns) +// +// NOTE: This effect makes the client do traces for each bullet, these client traces ignore +// entities that have studio models.Traces are 4096 long. +// +// coord (origin) +// coord (origin) +// coord (origin) +// coord (direction) +// coord (direction) +// coord (direction) +// coord (x noise * 100) +// coord (y noise * 100) +// byte (count) +// byte (bullethole decal texture index) + +#define TE_USERTRACER 127 // larger message than the standard tracer, but allows some customization. +// coord (origin) +// coord (origin) +// coord (origin) +// coord (velocity) +// coord (velocity) +// coord (velocity) +// byte (life * 10) +// byte (color) this is an index into an array of color vectors in the engine. (0 -) +// byte (length * 10) + +#define MSG_BROADCAST 0 // unreliable to all +#define MSG_ONE 1 // reliable to one (msg_entity) +#define MSG_ALL 2 // reliable to all +#define MSG_INIT 3 // write to the init string +#define MSG_PVS 4 // Ents in PVS of org +#define MSG_PAS 5 // Ents in PAS of org +#define MSG_PVS_R 6 // Reliable to PVS +#define MSG_PAS_R 7 // Reliable to PAS +#define MSG_ONE_UNRELIABLE 8 // Send to one client, but don't put in reliable stream, put in unreliable datagram ( could be dropped ) +#define MSG_SPEC 9 // Sends to all spectator proxies + +// contents of a spot in the world +#define CONTENTS_EMPTY -1 +#define CONTENTS_SOLID -2 +#define CONTENTS_WATER -3 +#define CONTENTS_SLIME -4 +#define CONTENTS_LAVA -5 +#define CONTENTS_SKY -6 + +#define CONTENTS_LADDER -16 + +#define CONTENT_FLYFIELD -17 +#define CONTENT_GRAVITY_FLYFIELD -18 +#define CONTENT_FOG -19 + +#define CONTENT_EMPTY -1 +#define CONTENT_SOLID -2 +#define CONTENT_WATER -3 +#define CONTENT_SLIME -4 +#define CONTENT_LAVA -5 +#define CONTENT_SKY -6 + +// channels +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 +#define CHAN_STREAM 5 // allocate stream channel from the static or dynamic area +#define CHAN_STATIC 6 // allocate channel from the static area +#define CHAN_NETWORKVOICE_BASE 7 // voice data coming across the network +#define CHAN_NETWORKVOICE_END 500 // network voice data reserves slots (CHAN_NETWORKVOICE_BASE through CHAN_NETWORKVOICE_END). +#define CHAN_BOT 501 // channel used for bot chatter. + +// attenuation values +#define ATTN_NONE 0 +#define ATTN_NORM 0.8f +#define ATTN_IDLE 2.0f +#define ATTN_STATIC 1.25f + +// pitch values +#define PITCH_NORM 100 // non-pitch shifted +#define PITCH_LOW 95 // other values are possible - 0-255, where 255 is very high +#define PITCH_HIGH 120 + +// volume values +#define VOL_NORM 1.0f + +// buttons +#ifndef IN_BUTTONS_H +#include "in_buttons.h" +#endif + +// Break Model Defines +#define BREAK_TYPEMASK 0x4F +#define BREAK_GLASS 0x01 +#define BREAK_METAL 0x02 +#define BREAK_FLESH 0x04 +#define BREAK_WOOD 0x08 + +#define BREAK_SMOKE 0x10 +#define BREAK_TRANS 0x20 +#define BREAK_CONCRETE 0x40 +#define BREAK_2 0x80 + +// Colliding temp entity sounds +#define BOUNCE_GLASS BREAK_GLASS +#define BOUNCE_METAL BREAK_METAL +#define BOUNCE_FLESH BREAK_FLESH +#define BOUNCE_WOOD BREAK_WOOD +#define BOUNCE_SHRAP 0x10 +#define BOUNCE_SHELL 0x20 +#define BOUNCE_CONCRETE BREAK_CONCRETE +#define BOUNCE_SHOTSHELL 0x80 + +// Temp entity bounce sound types +#define TE_BOUNCE_NULL 0 +#define TE_BOUNCE_SHELL 1 +#define TE_BOUNCE_SHOTSHELL 2 + +// Rendering constants +enum +{ + kRenderNormal, // src + kRenderTransColor, // c*a+dest*(1-a) + kRenderTransTexture, // src*a+dest*(1-a) + kRenderGlow, // src*a+dest -- No Z buffer checks + kRenderTransAlpha, // src*srca+dest*(1-srca) + kRenderTransAdd, // src*a+dest +}; + +enum +{ + kRenderFxNone = 0, + kRenderFxPulseSlow, + kRenderFxPulseFast, + kRenderFxPulseSlowWide, + kRenderFxPulseFastWide, + kRenderFxFadeSlow, + kRenderFxFadeFast, + kRenderFxSolidSlow, + kRenderFxSolidFast, + kRenderFxStrobeSlow, + kRenderFxStrobeFast, + kRenderFxStrobeFaster, + kRenderFxFlickerSlow, + kRenderFxFlickerFast, + kRenderFxNoDissipation, + kRenderFxDistort, // Distort/scale/translate flicker + kRenderFxHologram, // kRenderFxDistort + distance fade + kRenderFxDeadPlayer, // kRenderAmt is the player index + kRenderFxExplode, // Scale up really big! + kRenderFxGlowShell, // Glowing Shell + kRenderFxClampMinScale, // Keep this sprite from getting very small (SPRITES only!) + kRenderFxLightMultiplier, // CTM !!!CZERO added to tell the studiorender that the value in iuser2 is a lightmultiplier +}; + +typedef struct +{ + byte r, g, b; +} color24; + +typedef struct +{ + unsigned r, g, b, a; +} colorVec; + +#ifdef _WIN32 +#pragma pack(push, 2) +#endif + +typedef struct +{ + unsigned short r, g, b, a; +} PackedColorVec; + +#ifdef _WIN32 +#pragma pack(pop) +#endif + +typedef struct link_s +{ + struct link_s *prev, *next; +} link_t; + +typedef struct edict_s edict_t; + +typedef struct +{ + vec3_t normal; + float dist; +} plane_t; + +typedef struct +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + qboolean inopen, inwater; + float fraction; // time completed, 1.0 = didn't hit anything + vec3_t endpos; // final position + plane_t plane; // surface normal at impact + edict_t *ent; // entity the surface is on + int hitgroup; // 0 == generic, non zero is specific body part +} trace_t; + +#endif // CONST_H diff --git a/regamedll/dlls/animation.cpp b/regamedll/dlls/animation.cpp index 2fb51853..83c0bd40 100644 --- a/regamedll/dlls/animation.cpp +++ b/regamedll/dlls/animation.cpp @@ -1,1192 +1,1192 @@ -#include "precompiled.h" - -void EXT_FUNC SV_StudioSetupBones(model_t *pModel, float frame, int sequence, const vec_t *angles, const vec_t *origin, const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict); - -sv_blending_interface_t svBlending = -{ - SV_BLENDING_INTERFACE_VERSION, - SV_StudioSetupBones -}; - -server_studio_api_t IEngineStudio; -studiohdr_t *g_pstudiohdr; - -float (*g_pRotationMatrix)[3][4]; -float (*g_pBoneTransform)[128][3][4]; - -int ExtractBbox(void *pmodel, int sequence, float *mins, float *maxs) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return 0; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - mins[0] = pseqdesc[sequence].bbmin[0]; - mins[1] = pseqdesc[sequence].bbmin[1]; - mins[2] = pseqdesc[sequence].bbmin[2]; - - maxs[0] = pseqdesc[sequence].bbmax[0]; - maxs[1] = pseqdesc[sequence].bbmax[1]; - maxs[2] = pseqdesc[sequence].bbmax[2]; - - return 1; -} - -int LookupActivity(void *pmodel, entvars_t *pev, int activity) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return 0; - } - - mstudioseqdesc_t *pseqdesc; - - int i; - int weightTotal = 0; - int activitySequenceCount = 0; - int weight = 0; - int select; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - for (i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].activity == activity) - { - weightTotal += pseqdesc[i].actweight; - activitySequenceCount++; - } - } - - if (activitySequenceCount > 0) - { - if (weightTotal) - { - int which = RANDOM_LONG(0, weightTotal - 1); - - for (i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].activity == activity) - { - weight += pseqdesc[i].actweight; - - if (weight > which) - { - return i; - } - } - } - } - else - { - select = RANDOM_LONG(0, activitySequenceCount - 1); - - for (i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].activity == activity) - { - if (select == 0) - { - return i; - } - - select--; - } - } - } - } - - return ACT_INVALID; -} - -int LookupActivityHeaviest(void *pmodel, entvars_t *pev, int activity) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return 0; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - int weight = 0; - int seq = ACT_INVALID; - - for (int i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].activity == activity) - { - if (pseqdesc[i].actweight > weight) - { - weight = pseqdesc[i].actweight; - seq = i; - } - } - } - - return seq; -} - -NOXREF void GetEyePosition(void *pmodel, float *vecEyePosition) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - ALERT(at_console, "GetEyePosition() Can't get pstudiohdr ptr!\n"); - return; - } - - vecEyePosition[0] = pstudiohdr->eyeposition[0]; - vecEyePosition[1] = pstudiohdr->eyeposition[1]; - vecEyePosition[2] = pstudiohdr->eyeposition[2]; -} - -int LookupSequence(void *pmodel, const char *label) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return 0; - } - - // Look up by sequence name. - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - for (int i = 0; i < pstudiohdr->numseq; i++) - { - if (!Q_stricmp(pseqdesc[i].label, label)) - return i; - } - - // Not found - return ACT_INVALID; -} - -int IsSoundEvent(int eventNumber) -{ - if (eventNumber == SCRIPT_EVENT_SOUND || eventNumber == SCRIPT_EVENT_SOUND_VOICE) - { - return 1; - } - - return 0; -} - -NOXREF void SequencePrecache(void *pmodel, const char *pSequenceName) -{ - int index = LookupSequence(pmodel, pSequenceName); - - if (index >= 0) - { - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr || index >= pstudiohdr->numseq) - { - return; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index; - mstudioevent_t *pevent = (mstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex); - - for (int i = 0; i < pseqdesc->numevents; i++) - { - // Don't send client-side events to the server AI - if (pevent[i].event >= EVENT_CLIENT) - continue; - - // UNDONE: Add a callback to check to see if a sound is precached yet and don't allocate a copy - // of it's name if it is. - if (IsSoundEvent(pevent[i].event)) - { - if (!Q_strlen(pevent[i].options)) - { - ALERT(at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options); - } - - PRECACHE_SOUND((char *)(gpGlobals->pStringBase + ALLOC_STRING(pevent[i].options))); - } - } - } -} - -void GetSequenceInfo(void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return; - } - - if (pev->sequence >= pstudiohdr->numseq) - { - *pflFrameRate = 0; - *pflGroundSpeed = 0; - return; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); - if (pseqdesc->numframes <= 1) - { - *pflFrameRate = 256.0f; - *pflGroundSpeed = 0.0f; - return; - } - - *pflFrameRate = pseqdesc->fps * 256.0f / (pseqdesc->numframes - 1); - *pflGroundSpeed = Q_sqrt(pseqdesc->linearmovement[0] * pseqdesc->linearmovement[0] + pseqdesc->linearmovement[1] * pseqdesc->linearmovement[1] + pseqdesc->linearmovement[2] * pseqdesc->linearmovement[2]); - *pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1); -} - -int GetSequenceFlags(void *pmodel, entvars_t *pev) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr || pev->sequence >= pstudiohdr->numseq) - { - return 0; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); - return pseqdesc->flags; -} - -int GetAnimationEvent(void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent) - { - return 0; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); - mstudioevent_t *pevent = (mstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex); - - if (pseqdesc->numevents == 0 || index > pseqdesc->numevents) - { - return 0; - } - - if (pseqdesc->numframes > 1) - { - flStart *= (pseqdesc->numframes - 1) / 256.0; - flEnd *= (pseqdesc->numframes - 1) / 256.0; - } - else - { - flStart = 0; - flEnd = 1.0; - } - - for (; index < pseqdesc->numevents; index++) - { - // Don't send client-side events to the server AI - if (pevent[index].event >= EVENT_CLIENT) - continue; - - if ((pevent[index].frame >= flStart && pevent[index].frame < flEnd) || - ((pseqdesc->flags & STUDIO_LOOPING) - && flEnd >= pseqdesc->numframes - 1 - && pevent[index].frame < flEnd - pseqdesc->numframes + 1)) - { - pMonsterEvent->event = pevent[index].event; - pMonsterEvent->options = pevent[index].options; - - return index + 1; - } - } - - return 0; -} - -float SetController(void *pmodel, entvars_t *pev, int iController, float flValue) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr) - { - return flValue; - } - - int i; - mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); - for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) - { - if (pbonecontroller->index == iController) - break; - } - - if (i >= pstudiohdr->numbonecontrollers) - return flValue; - - if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) - { - if (pbonecontroller->end < pbonecontroller->start) - flValue = -flValue; - - if (pbonecontroller->end > pbonecontroller->start + 359.0) - { - if (flValue > 360.0) - flValue = flValue - int64(flValue / 360.0) * 360.0; - - else if (flValue < 0.0) - flValue = flValue + int64((flValue / -360.0) + 1) * 360.0; - } - else - { - if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2) + 180) - flValue -= 360; - - if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2) - 180) - flValue += 360; - } - } - - int setting = int64(255.0f * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start)); - setting = Q_clamp(setting, 0, 255); - - pev->controller[iController] = setting; - - return setting * (1.0f / 255.0f) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; -} - -float SetBlending(void *pmodel, entvars_t *pev, int iBlender, float flValue) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr) - { - return flValue; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); - - if (pseqdesc->blendtype[iBlender] == 0) - { - return flValue; - } - - if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) - { - // ugly hack, invert value if end < start - if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender]) - flValue = -flValue; - - // does the controller not wrap? - if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender]) - { - if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180) - { - flValue = flValue - 360; - } - - if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180) - { - flValue = flValue + 360; - } - } - } - - int setting = int64(255.0f * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender])); - setting = Q_clamp(setting, 0, 255); - - pev->blending[iBlender] = setting; - - return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender]; -} - -int FindTransition(void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr) - { - return iGoalAnim; - } - - mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - // bail if we're going to or from a node 0 - if (pseqdesc[iEndingAnim].entrynode == 0 || pseqdesc[iGoalAnim].entrynode == 0) - { - return iGoalAnim; - } - - int iEndNode; - - if (*piDir > 0) - { - iEndNode = pseqdesc[iEndingAnim].exitnode; - } - else - { - iEndNode = pseqdesc[iEndingAnim].entrynode; - } - - if (iEndNode == pseqdesc[iGoalAnim].entrynode) - { - *piDir = 1; - return iGoalAnim; - } - - byte *pTransition = ((byte *)pstudiohdr + pstudiohdr->transitionindex); - - int iInternNode = pTransition[(iEndNode - 1)*pstudiohdr->numtransitions + (pseqdesc[iGoalAnim].entrynode - 1)]; - - if (iInternNode == 0) - { - return iGoalAnim; - } - - // look for someone going - for (int i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].entrynode == iEndNode && pseqdesc[i].exitnode == iInternNode) - { - *piDir = 1; - return i; - } - if (pseqdesc[i].nodeflags) - { - if (pseqdesc[i].exitnode == iEndNode && pseqdesc[i].entrynode == iInternNode) - { - *piDir = -1; - return i; - } - } - } - - ALERT(at_console, "error in transition graph"); - return iGoalAnim; -} - -void SetBodygroup(void *pmodel, entvars_t *pev, int iGroup, int iValue) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - if (!pstudiohdr) - { - return; - } - - if (iGroup > pstudiohdr->numbodyparts) - { - return; - } - - mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup; - - if (iValue >= pbodypart->nummodels) - { - return; - } - - int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels; - pev->body += (iValue - iCurrent) * pbodypart->base; -} - -int GetBodygroup(void *pmodel, entvars_t *pev, int iGroup) -{ - studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; - - if (!pstudiohdr || iGroup > pstudiohdr->numbodyparts) - { - return 0; - } - - mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup; - - if (pbodypart->nummodels <= 1) - return 0; - - int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels; - return iCurrent; -} - -C_DLLEXPORT int Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float *rotationmatrix, float *bonetransform) -{ - if (version != SV_BLENDING_INTERFACE_VERSION) - return 0; - - *ppinterface = &svBlending; - - IEngineStudio.Mem_Calloc = pstudio->Mem_Calloc; - IEngineStudio.Cache_Check = pstudio->Cache_Check; - IEngineStudio.LoadCacheFile = pstudio->LoadCacheFile; - IEngineStudio.Mod_Extradata = ((struct server_studio_api_s *)pstudio)->Mod_Extradata; - - g_pRotationMatrix = (float (*)[3][4])rotationmatrix; - g_pBoneTransform = (float (*)[128][3][4])bonetransform; - - return 1; -} - -#if defined(REGAMEDLL_FIXES) && defined(HAVE_SSE) // SSE2 version -void AngleQuaternion(vec_t *angles, vec_t *quaternion) -{ - static const ALIGN16_BEG size_t ps_signmask[4] ALIGN16_END = { 0x80000000, 0, 0x80000000, 0 }; - - __m128 a = _mm_loadu_ps(angles); - a = _mm_mul_ps(a, _mm_load_ps(_ps_0p5)); //a *= 0.5 - __m128 s, c; - sincos_ps(a, &s, &c); - - __m128 im1 = _mm_shuffle_ps(s, c, _MM_SHUFFLE(1, 0, 1, 0)); //im1 = {sin[0], sin[1], cos[0], cos[1] } - __m128 im2 = _mm_shuffle_ps(c, s, _MM_SHUFFLE(2, 2, 2, 2)); //im2 = {cos[2], cos[2], sin[2], sin[2] } - - __m128 part1 = _mm_mul_ps( - _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(1, 2, 2, 0)), - _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(0, 3, 1, 3)) - ); - part1 = _mm_mul_ps(part1, im2); - - __m128 part2 = _mm_mul_ps( - _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(2, 1, 0, 2)), - _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(3, 0, 3, 1)) - ); - - part2 = _mm_mul_ps(part2, _mm_shuffle_ps(im2, im2, _MM_SHUFFLE(0, 0, 2, 2))); - - __m128 signmask = _mm_load_ps((float*)ps_signmask); - part2 = _mm_xor_ps(part2, signmask); - - __m128 res = _mm_add_ps(part1, part2); - _mm_storeu_ps(quaternion, res); -} -#else // REGAMEDLL_FIXES -void AngleQuaternion(vec_t *angles, vec_t *quaternion) -{ - real_t sy, cy, sp_, cp; - real_t angle; - float sr, cr; - - float ftmp0; - float ftmp1; - float ftmp2; - - angle = angles[ROLL] * 0.5; - sy = Q_sin(angle); - cy = Q_cos(angle); - - angle = angles[YAW] * 0.5; - sp_ = Q_sin(angle); - cp = Q_cos(angle); - - angle = angles[PITCH] * 0.5; - sr = Q_sin(angle); - cr = Q_cos(angle); - - ftmp0 = sr * cp; - ftmp1 = cr * sp_; - - *quaternion = ftmp0 * cy - ftmp1 * sy; - quaternion[1] = ftmp1 * cy + ftmp0 * sy; - - ftmp2 = cr * cp; - quaternion[2] = ftmp2 * sy - sp_ * sr * cy; - quaternion[3] = sp_ * sr * sy + ftmp2 * cy; -} -#endif // REGAMEDLL_FIXES - -void QuaternionSlerp(vec_t *p, vec_t *q, float t, vec_t *qt) -{ - int i; - real_t a = 0; - real_t b = 0; - - for (i = 0; i < 4; i++) - { - a += (p[i] - q[i]) * (p[i] - q[i]); - b += (p[i] + q[i]) * (p[i] + q[i]); - } - - if (a > b) - { - for (i = 0; i < 4; i++) - q[i] = -q[i]; - } - - float sclp, sclq; - real_t cosom = (p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3]); - - if ((1.0 + cosom) > 0.000001) - { - if ((1.0 - cosom) > 0.000001) - { - real_t cosomega = Q_acos(real_t(cosom)); - - float omega = cosomega; - float sinom = Q_sin(cosomega); - - sclp = Q_sin((1.0f - t) * omega) / sinom; - sclq = Q_sin(real_t(omega * t)) / sinom; - } - else - { - sclq = t; - sclp = 1.0f - t; - } - - for (i = 0; i < 4; i++) - qt[i] = sclp * p[i] + sclq * q[i]; - } - else - { - qt[0] = -q[1]; - qt[1] = q[0]; - qt[2] = -q[3]; - qt[3] = q[2]; - - sclp = Q_sin((1.0f - t) * (0.5f * M_PI)); - sclq = Q_sin(t * (0.5f * M_PI)); - - for (i = 0; i < 3; i++) - qt[i] = sclp * p[i] + sclq * qt[i]; - } -} - -void QuaternionMatrix(vec_t *quaternion, float (*matrix)[4]) -{ - matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2]; - matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2]; - matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1]; - - matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2]; - matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2]; - matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0]; - - matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1]; - matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0]; - matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1]; -} - -mstudioanim_t *StudioGetAnim(model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc) -{ - mstudioseqgroup_t *pseqgroup; - cache_user_t *paSequences; - - pseqgroup = (mstudioseqgroup_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqgroupindex) + pseqdesc->seqgroup; - - if (pseqdesc->seqgroup == 0) - { - return (mstudioanim_t *)((byte *)g_pstudiohdr + pseqdesc->animindex); - } - - paSequences = (cache_user_t *)m_pSubModel->submodels; - - if (!paSequences) - { - paSequences = (cache_user_t *)IEngineStudio.Mem_Calloc(16, sizeof(cache_user_t)); // UNDONE: leak! - m_pSubModel->submodels = (dmodel_t *)paSequences; - } - - if (!IEngineStudio.Cache_Check((struct cache_user_s *)&(paSequences[pseqdesc->seqgroup]))) - { - IEngineStudio.LoadCacheFile(pseqgroup->name, (struct cache_user_s *)&paSequences[pseqdesc->seqgroup]); - } - - return (mstudioanim_t *)((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex); -} - -mstudioanim_t *LookupAnimation(model_t *model, mstudioseqdesc_t *pseqdesc, int index) -{ - mstudioanim_t *panim = StudioGetAnim(model, pseqdesc); - if (index >= 0 && index <= (pseqdesc->numblends - 1)) - panim += index * g_pstudiohdr->numbones; - - return panim; -} - -void StudioCalcBoneAdj(float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen) -{ - int i, j; - float value; - mstudiobonecontroller_t *pbonecontroller; - - pbonecontroller = (mstudiobonecontroller_t *)((byte *)g_pstudiohdr + g_pstudiohdr->bonecontrollerindex); - - for (j = 0; j < g_pstudiohdr->numbonecontrollers; j++) - { - i = pbonecontroller[j].index; - if (i <= 3) - { - // check for 360% wrapping - if (pbonecontroller[j].type & STUDIO_RLOOP) - { - if (Q_abs(pcontroller1[i] - pcontroller2[i]) > 128) - { - int a, b; - a = (pcontroller1[j] + 128) % 256; - b = (pcontroller2[j] + 128) % 256; - value = ((a * dadt) + (b * (1 - dadt)) - 128) * (360.0 / 256.0) + pbonecontroller[j].start; - } - else - { - value = (pcontroller1[i] * dadt + (pcontroller2[i]) * (1.0 - dadt)) * (360.0 / 256.0) + pbonecontroller[j].start; - } - } - else - { - value = (pcontroller1[i] * dadt + pcontroller2[i] * (1.0 - dadt)) / 255.0; - value = Q_clamp(value, 0.0f, 1.0f); - value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; - } - } - else - { - value = mouthopen / 64.0; - - if (value > 1.0) - value = 1.0; - - value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; - } - switch (pbonecontroller[j].type & STUDIO_TYPES) - { - case STUDIO_XR: - case STUDIO_YR: - case STUDIO_ZR: - adj[j] = value * (M_PI / 180.0); - break; - case STUDIO_X: - case STUDIO_Y: - case STUDIO_Z: - adj[j] = value; - break; - } - } -} - -void StudioCalcBoneQuaterion(int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q) -{ - int j, k; - vec4_t q1, q2; - vec3_t angle1, angle2; - mstudioanimvalue_t *panimvalue; - - for (j = 0; j < 3; j++) - { - if (panim->offset[j + 3] == 0) - { - // default - angle2[j] = angle1[j] = pbone->value[j + 3]; - } - else - { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j + 3]); - k = frame; - - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - - while (panimvalue->num.total <= k) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - } - - // Bah, missing blend! - if (panimvalue->num.valid > k) - { - angle1[j] = panimvalue[k + 1].value; - - if (panimvalue->num.valid > k + 1) - { - angle2[j] = panimvalue[k + 2].value; - } - else - { - if (panimvalue->num.total > k + 1) - angle2[j] = angle1[j]; - else - angle2[j] = panimvalue[panimvalue->num.valid + 2].value; - } - } - else - { - angle1[j] = panimvalue[panimvalue->num.valid].value; - if (panimvalue->num.total > k + 1) - { - angle2[j] = angle1[j]; - } - else - { - angle2[j] = panimvalue[panimvalue->num.valid + 2].value; - } - } - angle1[j] = pbone->value[j + 3] + angle1[j] * pbone->scale[j + 3]; - angle2[j] = pbone->value[j + 3] + angle2[j] * pbone->scale[j + 3]; - } - - if (pbone->bonecontroller[j + 3] != -1) - { - angle1[j] += adj[pbone->bonecontroller[j + 3]]; - angle2[j] += adj[pbone->bonecontroller[j + 3]]; - } - } - - if (!VectorCompare(angle1, angle2)) - { - AngleQuaternion(angle1, q1); - AngleQuaternion(angle2, q2); - QuaternionSlerp(q1, q2, s, q); - } - else - AngleQuaternion(angle1, q); -} - -void StudioCalcBonePosition(int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *pos) -{ - int j, k; - mstudioanimvalue_t *panimvalue; - - for (j = 0; j < 3; j++) - { - // default; - pos[j] = pbone->value[j]; - if (panim->offset[j] != 0) - { - panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); - - k = frame; - - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - - // find span of values that includes the frame we want - while (panimvalue->num.total <= k) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - - if (panimvalue->num.total < panimvalue->num.valid) - k = 0; - } - - // if we're inside the span - if (panimvalue->num.valid > k) - { - // and there's more data in the span - if (panimvalue->num.valid > k + 1) - pos[j] += (panimvalue[k + 1].value * (1.0 - s) + s * panimvalue[k + 2].value) * pbone->scale[j]; - else - pos[j] += panimvalue[k + 1].value * pbone->scale[j]; - } - else - { - // are we at the end of the repeating values section and there's another section with data? - if (panimvalue->num.total <= k + 1) - pos[j] += (panimvalue[panimvalue->num.valid].value * (1.0 - s) + s * panimvalue[panimvalue->num.valid + 2].value) * pbone->scale[j]; - - else - pos[j] += panimvalue[panimvalue->num.valid].value * pbone->scale[j]; - } - } - - if (pbone->bonecontroller[j] != -1 && adj) - { - pos[j] += adj[pbone->bonecontroller[j]]; - } - } -} - -void StudioSlerpBones(vec4_t *q1, float pos1[][3], vec4_t *q2, float pos2[][3], float s) -{ - int i; - vec4_t q3; - float s1; - - s = Q_clamp(s, 0.0f, 1.0f); - s1 = 1.0f - s; - - for (i = 0; i < g_pstudiohdr->numbones; i++) - { - QuaternionSlerp(q1[i], q2[i], s, q3); - - q1[i][0] = q3[0]; - q1[i][1] = q3[1]; - q1[i][2] = q3[2]; - q1[i][3] = q3[3]; - - pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s; - pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s; - pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s; - } -} - -void StudioCalcRotations(mstudiobone_t *pbones, int *chain, int chainlength, float *adj, float pos[128][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f, float s) -{ - int i; - int j; - - for (i = chainlength - 1; i >= 0; i--) - { - j = chain[i]; - - StudioCalcBoneQuaterion((int)f, s, &pbones[j], &panim[j], adj, q[j]); - StudioCalcBonePosition((int)f, s, &pbones[j], &panim[j], adj, pos[j]); - } -} - -void ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) -{ - out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; - out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; - out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; - out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; - - out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; - out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; - out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; - out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; - - out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; - out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; - out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; - out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; -} - -real_t StudioEstimateFrame(float frame, mstudioseqdesc_t *pseqdesc) -{ - if (pseqdesc->numframes <= 1) - return 0; - - return real_t(pseqdesc->numframes - 1) * frame / 256; -} - -void SV_StudioSetupBones(model_t *pModel, float frame, int sequence, const vec_t *angles, const vec_t *origin, const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict) -{ - int i, j, chainlength = 0; - int chain[MAXSTUDIOBONES]; - double f; - - float subframe; - float adj[MAXSTUDIOCONTROLLERS]; - mstudiobone_t *pbones; - mstudioseqdesc_t *pseqdesc; - mstudioanim_t *panim; - float bonematrix[3][4]; - vec3_t temp_angles; - - static float pos[MAXSTUDIOBONES][3] = {}, pos2[MAXSTUDIOBONES][3] = {}; - static float q[MAXSTUDIOBONES][4] = {}, q2[MAXSTUDIOBONES][4] = {}; - - g_pstudiohdr = (studiohdr_t *)IEngineStudio.Mod_Extradata(pModel); - - // Bound sequence number - if (sequence < 0 || sequence >= g_pstudiohdr->numseq) - sequence = 0; - - pbones = (mstudiobone_t *)((byte *)g_pstudiohdr + g_pstudiohdr->boneindex); - pseqdesc = (mstudioseqdesc_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqindex) + sequence; - panim = StudioGetAnim(pModel, pseqdesc); - - if (iBone < -1 || iBone >= g_pstudiohdr->numbones) - iBone = 0; - - if (iBone == -1) - { - chainlength = g_pstudiohdr->numbones; - - for (i = 0; i < chainlength; i++) - chain[(chainlength - i) - 1] = i; - } - else - { - // only the parent bones - for (i = iBone; i != -1; i = pbones[i].parent) - chain[chainlength++] = i; - } - - f = StudioEstimateFrame(frame, pseqdesc); - subframe = int(f); - f -= subframe; - - StudioCalcBoneAdj(0, adj, pcontroller, pcontroller, 0); - StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); - - if (pseqdesc->numblends != NUM_BLENDING) - { - if (pseqdesc->numblends > 1) - { - float b = real_t(pblending[0]) / 255.0f; - - panim = StudioGetAnim(pModel, pseqdesc); - panim += g_pstudiohdr->numbones; - - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); - StudioSlerpBones(q, pos, q2, pos2, b); - } - } - // This game knows how to do nine way blending - else - { - static float pos3[MAXSTUDIOBONES][3] = {}, pos4[MAXSTUDIOBONES][3] = {}; - static float q3[MAXSTUDIOBONES][4] = {}, q4[MAXSTUDIOBONES][4] = {}; - - real_t s, t; - s = GetPlayerYaw(pEdict); - t = GetPlayerPitch(pEdict); - - // Blending is 0-127 == Left to Middle, 128 to 255 == Middle to right - if (s <= 127.0f) - { - // Scale 0-127 blending up to 0-255 - s = (s * 2.0f); - - if (t <= 127.0f) - { - t = (t * 2.0f); - - StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 1); - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 3); - StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 4); - StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); - } - else - { - t = 2.0f * (t - 127.0f); - - panim = LookupAnimation(pModel, pseqdesc, 3); - StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 4); - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 6); - StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 7); - StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); - } - } - else - { - // Scale 127-255 blending up to 0-255 - s = 2.0f * (s - 127.0f); - - if (t <= 127.0f) - { - t = (t * 2.0f); - - panim = LookupAnimation(pModel, pseqdesc, 1); - StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 2); - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 4); - StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 5); - StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); - } - else - { - t = 2.0f * (t - 127.0f); - - panim = LookupAnimation(pModel, pseqdesc, 4); - StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 5); - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 7); - StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); - - panim = LookupAnimation(pModel, pseqdesc, 8); - StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); - } - } - - // Normalize interpolant - s /= 255.0f; - t /= 255.0f; - - // Spherically interpolate the bones - StudioSlerpBones(q, pos, q2, pos2, s); - StudioSlerpBones(q3, pos3, q4, pos4, s); - StudioSlerpBones(q, pos, q3, pos3, t); - } - - if (pseqdesc->numblends == 9 && sequence < ANIM_FIRST_DEATH_SEQUENCE && sequence != ANIM_SWIM_1 && sequence != ANIM_SWIM_2) - { - bool bCopy = true; - int gaitsequence = GetPlayerGaitsequence(pEdict); // calc gait animation - - if (gaitsequence < 0 || gaitsequence >= g_pstudiohdr->numseq) - gaitsequence = 0; - - pseqdesc = (mstudioseqdesc_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqindex) + gaitsequence; - panim = StudioGetAnim(pModel, pseqdesc); - StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, 0, 0); - - for (i = 0; i < g_pstudiohdr->numbones; i++) - { - if (!Q_strcmp(pbones[i].name, "Bip01 Spine")) - { - bCopy = false; - } - else if (!Q_strcmp(pbones[pbones[i].parent].name, "Bip01 Pelvis")) - { - bCopy = true; - } - - if (bCopy) - { - Q_memcpy(pos[i], pos2[i], sizeof(pos[i])); - Q_memcpy(q[i], q2[i], sizeof(q[i])); - } - } - } - - VectorCopy(angles, temp_angles); - -#ifndef REGAMEDLL_FIXES - if (pEdict) -#else - if (pEdict && CBaseEntity::Instance(const_cast(pEdict))->IsPlayer()) -#endif - { - temp_angles[1] = UTIL_GetPlayerGaitYaw(ENTINDEX(pEdict)); - - if (temp_angles[1] < 0) - temp_angles[1] += 360.0f; - } - - AngleMatrix(temp_angles, (*g_pRotationMatrix)); - - (*g_pRotationMatrix)[0][3] = origin[0]; - (*g_pRotationMatrix)[1][3] = origin[1]; - (*g_pRotationMatrix)[2][3] = origin[2]; - - for (i = chainlength - 1; i >= 0; i--) - { - j = chain[i]; - QuaternionMatrix(q[j], bonematrix); - - bonematrix[0][3] = pos[j][0]; - bonematrix[1][3] = pos[j][1]; - bonematrix[2][3] = pos[j][2]; - - if (pbones[j].parent == -1) - ConcatTransforms((*g_pRotationMatrix), bonematrix, (*g_pBoneTransform)[j]); - else - ConcatTransforms((*g_pBoneTransform)[pbones[j].parent], bonematrix, (*g_pBoneTransform)[j]); - } -} +#include "precompiled.h" + +void EXT_FUNC SV_StudioSetupBones(model_t *pModel, float frame, int sequence, const vec_t *angles, const vec_t *origin, const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict); + +sv_blending_interface_t svBlending = +{ + SV_BLENDING_INTERFACE_VERSION, + SV_StudioSetupBones +}; + +server_studio_api_t IEngineStudio; +studiohdr_t *g_pstudiohdr; + +float (*g_pRotationMatrix)[3][4]; +float (*g_pBoneTransform)[128][3][4]; + +int ExtractBbox(void *pmodel, int sequence, float *mins, float *maxs) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return 0; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + mins[0] = pseqdesc[sequence].bbmin[0]; + mins[1] = pseqdesc[sequence].bbmin[1]; + mins[2] = pseqdesc[sequence].bbmin[2]; + + maxs[0] = pseqdesc[sequence].bbmax[0]; + maxs[1] = pseqdesc[sequence].bbmax[1]; + maxs[2] = pseqdesc[sequence].bbmax[2]; + + return 1; +} + +int LookupActivity(void *pmodel, entvars_t *pev, int activity) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return 0; + } + + mstudioseqdesc_t *pseqdesc; + + int i; + int weightTotal = 0; + int activitySequenceCount = 0; + int weight = 0; + int select; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + for (i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].activity == activity) + { + weightTotal += pseqdesc[i].actweight; + activitySequenceCount++; + } + } + + if (activitySequenceCount > 0) + { + if (weightTotal) + { + int which = RANDOM_LONG(0, weightTotal - 1); + + for (i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].activity == activity) + { + weight += pseqdesc[i].actweight; + + if (weight > which) + { + return i; + } + } + } + } + else + { + select = RANDOM_LONG(0, activitySequenceCount - 1); + + for (i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].activity == activity) + { + if (select == 0) + { + return i; + } + + select--; + } + } + } + } + + return ACT_INVALID; +} + +int LookupActivityHeaviest(void *pmodel, entvars_t *pev, int activity) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return 0; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + int weight = 0; + int seq = ACT_INVALID; + + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].activity == activity) + { + if (pseqdesc[i].actweight > weight) + { + weight = pseqdesc[i].actweight; + seq = i; + } + } + } + + return seq; +} + +NOXREF void GetEyePosition(void *pmodel, float *vecEyePosition) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + ALERT(at_console, "GetEyePosition() Can't get pstudiohdr ptr!\n"); + return; + } + + vecEyePosition[0] = pstudiohdr->eyeposition[0]; + vecEyePosition[1] = pstudiohdr->eyeposition[1]; + vecEyePosition[2] = pstudiohdr->eyeposition[2]; +} + +int LookupSequence(void *pmodel, const char *label) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return 0; + } + + // Look up by sequence name. + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (!Q_stricmp(pseqdesc[i].label, label)) + return i; + } + + // Not found + return ACT_INVALID; +} + +int IsSoundEvent(int eventNumber) +{ + if (eventNumber == SCRIPT_EVENT_SOUND || eventNumber == SCRIPT_EVENT_SOUND_VOICE) + { + return 1; + } + + return 0; +} + +NOXREF void SequencePrecache(void *pmodel, const char *pSequenceName) +{ + int index = LookupSequence(pmodel, pSequenceName); + + if (index >= 0) + { + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + if (!pstudiohdr || index >= pstudiohdr->numseq) + { + return; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index; + mstudioevent_t *pevent = (mstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex); + + for (int i = 0; i < pseqdesc->numevents; i++) + { + // Don't send client-side events to the server AI + if (pevent[i].event >= EVENT_CLIENT) + continue; + + // UNDONE: Add a callback to check to see if a sound is precached yet and don't allocate a copy + // of it's name if it is. + if (IsSoundEvent(pevent[i].event)) + { + if (!Q_strlen(pevent[i].options)) + { + ALERT(at_error, "Bad sound event %d in sequence %s :: %s (sound is \"%s\")\n", pevent[i].event, pstudiohdr->name, pSequenceName, pevent[i].options); + } + + PRECACHE_SOUND((char *)(gpGlobals->pStringBase + ALLOC_STRING(pevent[i].options))); + } + } + } +} + +void GetSequenceInfo(void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return; + } + + if (pev->sequence >= pstudiohdr->numseq) + { + *pflFrameRate = 0; + *pflGroundSpeed = 0; + return; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); + if (pseqdesc->numframes <= 1) + { + *pflFrameRate = 256.0f; + *pflGroundSpeed = 0.0f; + return; + } + + *pflFrameRate = pseqdesc->fps * 256.0f / (pseqdesc->numframes - 1); + *pflGroundSpeed = Q_sqrt(pseqdesc->linearmovement[0] * pseqdesc->linearmovement[0] + pseqdesc->linearmovement[1] * pseqdesc->linearmovement[1] + pseqdesc->linearmovement[2] * pseqdesc->linearmovement[2]); + *pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1); +} + +int GetSequenceFlags(void *pmodel, entvars_t *pev) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr || pev->sequence >= pstudiohdr->numseq) + { + return 0; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); + return pseqdesc->flags; +} + +int GetAnimationEvent(void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent) + { + return 0; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); + mstudioevent_t *pevent = (mstudioevent_t *)((byte *)pstudiohdr + pseqdesc->eventindex); + + if (pseqdesc->numevents == 0 || index > pseqdesc->numevents) + { + return 0; + } + + if (pseqdesc->numframes > 1) + { + flStart *= (pseqdesc->numframes - 1) / 256.0; + flEnd *= (pseqdesc->numframes - 1) / 256.0; + } + else + { + flStart = 0; + flEnd = 1.0; + } + + for (; index < pseqdesc->numevents; index++) + { + // Don't send client-side events to the server AI + if (pevent[index].event >= EVENT_CLIENT) + continue; + + if ((pevent[index].frame >= flStart && pevent[index].frame < flEnd) || + ((pseqdesc->flags & STUDIO_LOOPING) + && flEnd >= pseqdesc->numframes - 1 + && pevent[index].frame < flEnd - pseqdesc->numframes + 1)) + { + pMonsterEvent->event = pevent[index].event; + pMonsterEvent->options = pevent[index].options; + + return index + 1; + } + } + + return 0; +} + +float SetController(void *pmodel, entvars_t *pev, int iController, float flValue) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr) + { + return flValue; + } + + int i; + mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); + for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) + { + if (pbonecontroller->index == iController) + break; + } + + if (i >= pstudiohdr->numbonecontrollers) + return flValue; + + if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) + { + if (pbonecontroller->end < pbonecontroller->start) + flValue = -flValue; + + if (pbonecontroller->end > pbonecontroller->start + 359.0) + { + if (flValue > 360.0) + flValue = flValue - int64(flValue / 360.0) * 360.0; + + else if (flValue < 0.0) + flValue = flValue + int64((flValue / -360.0) + 1) * 360.0; + } + else + { + if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2) + 180) + flValue -= 360; + + if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2) - 180) + flValue += 360; + } + } + + int setting = int64(255.0f * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start)); + setting = Q_clamp(setting, 0, 255); + + pev->controller[iController] = setting; + + return setting * (1.0f / 255.0f) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; +} + +float SetBlending(void *pmodel, entvars_t *pev, int iBlender, float flValue) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + if (!pstudiohdr) + { + return flValue; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + int(pev->sequence); + + if (pseqdesc->blendtype[iBlender] == 0) + { + return flValue; + } + + if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) + { + // ugly hack, invert value if end < start + if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender]) + flValue = -flValue; + + // does the controller not wrap? + if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender]) + { + if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180) + { + flValue = flValue - 360; + } + + if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180) + { + flValue = flValue + 360; + } + } + } + + int setting = int64(255.0f * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender])); + setting = Q_clamp(setting, 0, 255); + + pev->blending[iBlender] = setting; + + return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender]; +} + +int FindTransition(void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + if (!pstudiohdr) + { + return iGoalAnim; + } + + mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + // bail if we're going to or from a node 0 + if (pseqdesc[iEndingAnim].entrynode == 0 || pseqdesc[iGoalAnim].entrynode == 0) + { + return iGoalAnim; + } + + int iEndNode; + + if (*piDir > 0) + { + iEndNode = pseqdesc[iEndingAnim].exitnode; + } + else + { + iEndNode = pseqdesc[iEndingAnim].entrynode; + } + + if (iEndNode == pseqdesc[iGoalAnim].entrynode) + { + *piDir = 1; + return iGoalAnim; + } + + byte *pTransition = ((byte *)pstudiohdr + pstudiohdr->transitionindex); + + int iInternNode = pTransition[(iEndNode - 1)*pstudiohdr->numtransitions + (pseqdesc[iGoalAnim].entrynode - 1)]; + + if (iInternNode == 0) + { + return iGoalAnim; + } + + // look for someone going + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].entrynode == iEndNode && pseqdesc[i].exitnode == iInternNode) + { + *piDir = 1; + return i; + } + if (pseqdesc[i].nodeflags) + { + if (pseqdesc[i].exitnode == iEndNode && pseqdesc[i].entrynode == iInternNode) + { + *piDir = -1; + return i; + } + } + } + + ALERT(at_console, "error in transition graph"); + return iGoalAnim; +} + +void SetBodygroup(void *pmodel, entvars_t *pev, int iGroup, int iValue) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + if (!pstudiohdr) + { + return; + } + + if (iGroup > pstudiohdr->numbodyparts) + { + return; + } + + mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup; + + if (iValue >= pbodypart->nummodels) + { + return; + } + + int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels; + pev->body += (iValue - iCurrent) * pbodypart->base; +} + +int GetBodygroup(void *pmodel, entvars_t *pev, int iGroup) +{ + studiohdr_t *pstudiohdr = (studiohdr_t *)pmodel; + + if (!pstudiohdr || iGroup > pstudiohdr->numbodyparts) + { + return 0; + } + + mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex) + iGroup; + + if (pbodypart->nummodels <= 1) + return 0; + + int iCurrent = (pev->body / pbodypart->base) % pbodypart->nummodels; + return iCurrent; +} + +C_DLLEXPORT int Server_GetBlendingInterface(int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float *rotationmatrix, float *bonetransform) +{ + if (version != SV_BLENDING_INTERFACE_VERSION) + return 0; + + *ppinterface = &svBlending; + + IEngineStudio.Mem_Calloc = pstudio->Mem_Calloc; + IEngineStudio.Cache_Check = pstudio->Cache_Check; + IEngineStudio.LoadCacheFile = pstudio->LoadCacheFile; + IEngineStudio.Mod_Extradata = ((struct server_studio_api_s *)pstudio)->Mod_Extradata; + + g_pRotationMatrix = (float (*)[3][4])rotationmatrix; + g_pBoneTransform = (float (*)[128][3][4])bonetransform; + + return 1; +} + +#if defined(REGAMEDLL_FIXES) && defined(HAVE_SSE) // SSE2 version +void AngleQuaternion(vec_t *angles, vec_t *quaternion) +{ + static const ALIGN16_BEG size_t ps_signmask[4] ALIGN16_END = { 0x80000000, 0, 0x80000000, 0 }; + + __m128 a = _mm_loadu_ps(angles); + a = _mm_mul_ps(a, _mm_load_ps(_ps_0p5)); //a *= 0.5 + __m128 s, c; + sincos_ps(a, &s, &c); + + __m128 im1 = _mm_shuffle_ps(s, c, _MM_SHUFFLE(1, 0, 1, 0)); //im1 = {sin[0], sin[1], cos[0], cos[1] } + __m128 im2 = _mm_shuffle_ps(c, s, _MM_SHUFFLE(2, 2, 2, 2)); //im2 = {cos[2], cos[2], sin[2], sin[2] } + + __m128 part1 = _mm_mul_ps( + _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(1, 2, 2, 0)), + _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(0, 3, 1, 3)) + ); + part1 = _mm_mul_ps(part1, im2); + + __m128 part2 = _mm_mul_ps( + _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(2, 1, 0, 2)), + _mm_shuffle_ps(im1, im1, _MM_SHUFFLE(3, 0, 3, 1)) + ); + + part2 = _mm_mul_ps(part2, _mm_shuffle_ps(im2, im2, _MM_SHUFFLE(0, 0, 2, 2))); + + __m128 signmask = _mm_load_ps((float*)ps_signmask); + part2 = _mm_xor_ps(part2, signmask); + + __m128 res = _mm_add_ps(part1, part2); + _mm_storeu_ps(quaternion, res); +} +#else // REGAMEDLL_FIXES +void AngleQuaternion(vec_t *angles, vec_t *quaternion) +{ + real_t sy, cy, sp_, cp; + real_t angle; + float sr, cr; + + float ftmp0; + float ftmp1; + float ftmp2; + + angle = angles[ROLL] * 0.5; + sy = Q_sin(angle); + cy = Q_cos(angle); + + angle = angles[YAW] * 0.5; + sp_ = Q_sin(angle); + cp = Q_cos(angle); + + angle = angles[PITCH] * 0.5; + sr = Q_sin(angle); + cr = Q_cos(angle); + + ftmp0 = sr * cp; + ftmp1 = cr * sp_; + + *quaternion = ftmp0 * cy - ftmp1 * sy; + quaternion[1] = ftmp1 * cy + ftmp0 * sy; + + ftmp2 = cr * cp; + quaternion[2] = ftmp2 * sy - sp_ * sr * cy; + quaternion[3] = sp_ * sr * sy + ftmp2 * cy; +} +#endif // REGAMEDLL_FIXES + +void QuaternionSlerp(vec_t *p, vec_t *q, float t, vec_t *qt) +{ + int i; + real_t a = 0; + real_t b = 0; + + for (i = 0; i < 4; i++) + { + a += (p[i] - q[i]) * (p[i] - q[i]); + b += (p[i] + q[i]) * (p[i] + q[i]); + } + + if (a > b) + { + for (i = 0; i < 4; i++) + q[i] = -q[i]; + } + + float sclp, sclq; + real_t cosom = (p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3]); + + if ((1.0 + cosom) > 0.000001) + { + if ((1.0 - cosom) > 0.000001) + { + real_t cosomega = Q_acos(real_t(cosom)); + + float omega = cosomega; + float sinom = Q_sin(cosomega); + + sclp = Q_sin((1.0f - t) * omega) / sinom; + sclq = Q_sin(real_t(omega * t)) / sinom; + } + else + { + sclq = t; + sclp = 1.0f - t; + } + + for (i = 0; i < 4; i++) + qt[i] = sclp * p[i] + sclq * q[i]; + } + else + { + qt[0] = -q[1]; + qt[1] = q[0]; + qt[2] = -q[3]; + qt[3] = q[2]; + + sclp = Q_sin((1.0f - t) * (0.5f * M_PI)); + sclq = Q_sin(t * (0.5f * M_PI)); + + for (i = 0; i < 3; i++) + qt[i] = sclp * p[i] + sclq * qt[i]; + } +} + +void QuaternionMatrix(vec_t *quaternion, float (*matrix)[4]) +{ + matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2]; + matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2]; + matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1]; + + matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2]; + matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2]; + matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0]; + + matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1]; + matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0]; + matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1]; +} + +mstudioanim_t *StudioGetAnim(model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc) +{ + mstudioseqgroup_t *pseqgroup; + cache_user_t *paSequences; + + pseqgroup = (mstudioseqgroup_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqgroupindex) + pseqdesc->seqgroup; + + if (pseqdesc->seqgroup == 0) + { + return (mstudioanim_t *)((byte *)g_pstudiohdr + pseqdesc->animindex); + } + + paSequences = (cache_user_t *)m_pSubModel->submodels; + + if (!paSequences) + { + paSequences = (cache_user_t *)IEngineStudio.Mem_Calloc(16, sizeof(cache_user_t)); // UNDONE: leak! + m_pSubModel->submodels = (dmodel_t *)paSequences; + } + + if (!IEngineStudio.Cache_Check((struct cache_user_s *)&(paSequences[pseqdesc->seqgroup]))) + { + IEngineStudio.LoadCacheFile(pseqgroup->name, (struct cache_user_s *)&paSequences[pseqdesc->seqgroup]); + } + + return (mstudioanim_t *)((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex); +} + +mstudioanim_t *LookupAnimation(model_t *model, mstudioseqdesc_t *pseqdesc, int index) +{ + mstudioanim_t *panim = StudioGetAnim(model, pseqdesc); + if (index >= 0 && index <= (pseqdesc->numblends - 1)) + panim += index * g_pstudiohdr->numbones; + + return panim; +} + +void StudioCalcBoneAdj(float dadt, float *adj, const byte *pcontroller1, const byte *pcontroller2, byte mouthopen) +{ + int i, j; + float value; + mstudiobonecontroller_t *pbonecontroller; + + pbonecontroller = (mstudiobonecontroller_t *)((byte *)g_pstudiohdr + g_pstudiohdr->bonecontrollerindex); + + for (j = 0; j < g_pstudiohdr->numbonecontrollers; j++) + { + i = pbonecontroller[j].index; + if (i <= 3) + { + // check for 360% wrapping + if (pbonecontroller[j].type & STUDIO_RLOOP) + { + if (Q_abs(pcontroller1[i] - pcontroller2[i]) > 128) + { + int a, b; + a = (pcontroller1[j] + 128) % 256; + b = (pcontroller2[j] + 128) % 256; + value = ((a * dadt) + (b * (1 - dadt)) - 128) * (360.0 / 256.0) + pbonecontroller[j].start; + } + else + { + value = (pcontroller1[i] * dadt + (pcontroller2[i]) * (1.0 - dadt)) * (360.0 / 256.0) + pbonecontroller[j].start; + } + } + else + { + value = (pcontroller1[i] * dadt + pcontroller2[i] * (1.0 - dadt)) / 255.0; + value = Q_clamp(value, 0.0f, 1.0f); + value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; + } + } + else + { + value = mouthopen / 64.0; + + if (value > 1.0) + value = 1.0; + + value = (1.0 - value) * pbonecontroller[j].start + value * pbonecontroller[j].end; + } + switch (pbonecontroller[j].type & STUDIO_TYPES) + { + case STUDIO_XR: + case STUDIO_YR: + case STUDIO_ZR: + adj[j] = value * (M_PI / 180.0); + break; + case STUDIO_X: + case STUDIO_Y: + case STUDIO_Z: + adj[j] = value; + break; + } + } +} + +void StudioCalcBoneQuaterion(int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *q) +{ + int j, k; + vec4_t q1, q2; + vec3_t angle1, angle2; + mstudioanimvalue_t *panimvalue; + + for (j = 0; j < 3; j++) + { + if (panim->offset[j + 3] == 0) + { + // default + angle2[j] = angle1[j] = pbone->value[j + 3]; + } + else + { + panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j + 3]); + k = frame; + + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + + while (panimvalue->num.total <= k) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + } + + // Bah, missing blend! + if (panimvalue->num.valid > k) + { + angle1[j] = panimvalue[k + 1].value; + + if (panimvalue->num.valid > k + 1) + { + angle2[j] = panimvalue[k + 2].value; + } + else + { + if (panimvalue->num.total > k + 1) + angle2[j] = angle1[j]; + else + angle2[j] = panimvalue[panimvalue->num.valid + 2].value; + } + } + else + { + angle1[j] = panimvalue[panimvalue->num.valid].value; + if (panimvalue->num.total > k + 1) + { + angle2[j] = angle1[j]; + } + else + { + angle2[j] = panimvalue[panimvalue->num.valid + 2].value; + } + } + angle1[j] = pbone->value[j + 3] + angle1[j] * pbone->scale[j + 3]; + angle2[j] = pbone->value[j + 3] + angle2[j] * pbone->scale[j + 3]; + } + + if (pbone->bonecontroller[j + 3] != -1) + { + angle1[j] += adj[pbone->bonecontroller[j + 3]]; + angle2[j] += adj[pbone->bonecontroller[j + 3]]; + } + } + + if (!VectorCompare(angle1, angle2)) + { + AngleQuaternion(angle1, q1); + AngleQuaternion(angle2, q2); + QuaternionSlerp(q1, q2, s, q); + } + else + AngleQuaternion(angle1, q); +} + +void StudioCalcBonePosition(int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, float *pos) +{ + int j, k; + mstudioanimvalue_t *panimvalue; + + for (j = 0; j < 3; j++) + { + // default; + pos[j] = pbone->value[j]; + if (panim->offset[j] != 0) + { + panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); + + k = frame; + + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + + // find span of values that includes the frame we want + while (panimvalue->num.total <= k) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + + if (panimvalue->num.total < panimvalue->num.valid) + k = 0; + } + + // if we're inside the span + if (panimvalue->num.valid > k) + { + // and there's more data in the span + if (panimvalue->num.valid > k + 1) + pos[j] += (panimvalue[k + 1].value * (1.0 - s) + s * panimvalue[k + 2].value) * pbone->scale[j]; + else + pos[j] += panimvalue[k + 1].value * pbone->scale[j]; + } + else + { + // are we at the end of the repeating values section and there's another section with data? + if (panimvalue->num.total <= k + 1) + pos[j] += (panimvalue[panimvalue->num.valid].value * (1.0 - s) + s * panimvalue[panimvalue->num.valid + 2].value) * pbone->scale[j]; + + else + pos[j] += panimvalue[panimvalue->num.valid].value * pbone->scale[j]; + } + } + + if (pbone->bonecontroller[j] != -1 && adj) + { + pos[j] += adj[pbone->bonecontroller[j]]; + } + } +} + +void StudioSlerpBones(vec4_t *q1, float pos1[][3], vec4_t *q2, float pos2[][3], float s) +{ + int i; + vec4_t q3; + float s1; + + s = Q_clamp(s, 0.0f, 1.0f); + s1 = 1.0f - s; + + for (i = 0; i < g_pstudiohdr->numbones; i++) + { + QuaternionSlerp(q1[i], q2[i], s, q3); + + q1[i][0] = q3[0]; + q1[i][1] = q3[1]; + q1[i][2] = q3[2]; + q1[i][3] = q3[3]; + + pos1[i][0] = pos1[i][0] * s1 + pos2[i][0] * s; + pos1[i][1] = pos1[i][1] * s1 + pos2[i][1] * s; + pos1[i][2] = pos1[i][2] * s1 + pos2[i][2] * s; + } +} + +void StudioCalcRotations(mstudiobone_t *pbones, int *chain, int chainlength, float *adj, float pos[128][3], vec4_t *q, mstudioseqdesc_t *pseqdesc, mstudioanim_t *panim, float f, float s) +{ + int i; + int j; + + for (i = chainlength - 1; i >= 0; i--) + { + j = chain[i]; + + StudioCalcBoneQuaterion((int)f, s, &pbones[j], &panim[j], adj, q[j]); + StudioCalcBonePosition((int)f, s, &pbones[j], &panim[j], adj, pos[j]); + } +} + +void ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; + out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; + + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; + out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; + + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; + out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; +} + +real_t StudioEstimateFrame(float frame, mstudioseqdesc_t *pseqdesc) +{ + if (pseqdesc->numframes <= 1) + return 0; + + return real_t(pseqdesc->numframes - 1) * frame / 256; +} + +void SV_StudioSetupBones(model_t *pModel, float frame, int sequence, const vec_t *angles, const vec_t *origin, const byte *pcontroller, const byte *pblending, int iBone, const edict_t *pEdict) +{ + int i, j, chainlength = 0; + int chain[MAXSTUDIOBONES]; + double f; + + float subframe; + float adj[MAXSTUDIOCONTROLLERS]; + mstudiobone_t *pbones; + mstudioseqdesc_t *pseqdesc; + mstudioanim_t *panim; + float bonematrix[3][4]; + vec3_t temp_angles; + + static float pos[MAXSTUDIOBONES][3] = {}, pos2[MAXSTUDIOBONES][3] = {}; + static float q[MAXSTUDIOBONES][4] = {}, q2[MAXSTUDIOBONES][4] = {}; + + g_pstudiohdr = (studiohdr_t *)IEngineStudio.Mod_Extradata(pModel); + + // Bound sequence number + if (sequence < 0 || sequence >= g_pstudiohdr->numseq) + sequence = 0; + + pbones = (mstudiobone_t *)((byte *)g_pstudiohdr + g_pstudiohdr->boneindex); + pseqdesc = (mstudioseqdesc_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqindex) + sequence; + panim = StudioGetAnim(pModel, pseqdesc); + + if (iBone < -1 || iBone >= g_pstudiohdr->numbones) + iBone = 0; + + if (iBone == -1) + { + chainlength = g_pstudiohdr->numbones; + + for (i = 0; i < chainlength; i++) + chain[(chainlength - i) - 1] = i; + } + else + { + // only the parent bones + for (i = iBone; i != -1; i = pbones[i].parent) + chain[chainlength++] = i; + } + + f = StudioEstimateFrame(frame, pseqdesc); + subframe = int(f); + f -= subframe; + + StudioCalcBoneAdj(0, adj, pcontroller, pcontroller, 0); + StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); + + if (pseqdesc->numblends != NUM_BLENDING) + { + if (pseqdesc->numblends > 1) + { + float b = real_t(pblending[0]) / 255.0f; + + panim = StudioGetAnim(pModel, pseqdesc); + panim += g_pstudiohdr->numbones; + + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); + StudioSlerpBones(q, pos, q2, pos2, b); + } + } + // This game knows how to do nine way blending + else + { + static float pos3[MAXSTUDIOBONES][3] = {}, pos4[MAXSTUDIOBONES][3] = {}; + static float q3[MAXSTUDIOBONES][4] = {}, q4[MAXSTUDIOBONES][4] = {}; + + real_t s, t; + s = GetPlayerYaw(pEdict); + t = GetPlayerPitch(pEdict); + + // Blending is 0-127 == Left to Middle, 128 to 255 == Middle to right + if (s <= 127.0f) + { + // Scale 0-127 blending up to 0-255 + s = (s * 2.0f); + + if (t <= 127.0f) + { + t = (t * 2.0f); + + StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 1); + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 3); + StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 4); + StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); + } + else + { + t = 2.0f * (t - 127.0f); + + panim = LookupAnimation(pModel, pseqdesc, 3); + StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 4); + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 6); + StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 7); + StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); + } + } + else + { + // Scale 127-255 blending up to 0-255 + s = 2.0f * (s - 127.0f); + + if (t <= 127.0f) + { + t = (t * 2.0f); + + panim = LookupAnimation(pModel, pseqdesc, 1); + StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 2); + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 4); + StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 5); + StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); + } + else + { + t = 2.0f * (t - 127.0f); + + panim = LookupAnimation(pModel, pseqdesc, 4); + StudioCalcRotations(pbones, chain, chainlength, adj, pos, q, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 5); + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 7); + StudioCalcRotations(pbones, chain, chainlength, adj, pos3, q3, pseqdesc, panim, subframe, f); + + panim = LookupAnimation(pModel, pseqdesc, 8); + StudioCalcRotations(pbones, chain, chainlength, adj, pos4, q4, pseqdesc, panim, subframe, f); + } + } + + // Normalize interpolant + s /= 255.0f; + t /= 255.0f; + + // Spherically interpolate the bones + StudioSlerpBones(q, pos, q2, pos2, s); + StudioSlerpBones(q3, pos3, q4, pos4, s); + StudioSlerpBones(q, pos, q3, pos3, t); + } + + if (pseqdesc->numblends == 9 && sequence < ANIM_FIRST_DEATH_SEQUENCE && sequence != ANIM_SWIM_1 && sequence != ANIM_SWIM_2) + { + bool bCopy = true; + int gaitsequence = GetPlayerGaitsequence(pEdict); // calc gait animation + + if (gaitsequence < 0 || gaitsequence >= g_pstudiohdr->numseq) + gaitsequence = 0; + + pseqdesc = (mstudioseqdesc_t *)((byte *)g_pstudiohdr + g_pstudiohdr->seqindex) + gaitsequence; + panim = StudioGetAnim(pModel, pseqdesc); + StudioCalcRotations(pbones, chain, chainlength, adj, pos2, q2, pseqdesc, panim, 0, 0); + + for (i = 0; i < g_pstudiohdr->numbones; i++) + { + if (!Q_strcmp(pbones[i].name, "Bip01 Spine")) + { + bCopy = false; + } + else if (!Q_strcmp(pbones[pbones[i].parent].name, "Bip01 Pelvis")) + { + bCopy = true; + } + + if (bCopy) + { + Q_memcpy(pos[i], pos2[i], sizeof(pos[i])); + Q_memcpy(q[i], q2[i], sizeof(q[i])); + } + } + } + + VectorCopy(angles, temp_angles); + +#ifndef REGAMEDLL_FIXES + if (pEdict) +#else + if (pEdict && CBaseEntity::Instance(const_cast(pEdict))->IsPlayer()) +#endif + { + temp_angles[1] = UTIL_GetPlayerGaitYaw(ENTINDEX(pEdict)); + + if (temp_angles[1] < 0) + temp_angles[1] += 360.0f; + } + + AngleMatrix(temp_angles, (*g_pRotationMatrix)); + + (*g_pRotationMatrix)[0][3] = origin[0]; + (*g_pRotationMatrix)[1][3] = origin[1]; + (*g_pRotationMatrix)[2][3] = origin[2]; + + for (i = chainlength - 1; i >= 0; i--) + { + j = chain[i]; + QuaternionMatrix(q[j], bonematrix); + + bonematrix[0][3] = pos[j][0]; + bonematrix[1][3] = pos[j][1]; + bonematrix[2][3] = pos[j][2]; + + if (pbones[j].parent == -1) + ConcatTransforms((*g_pRotationMatrix), bonematrix, (*g_pBoneTransform)[j]); + else + ConcatTransforms((*g_pBoneTransform)[pbones[j].parent], bonematrix, (*g_pBoneTransform)[j]); + } +} diff --git a/regamedll/dlls/bot/cs_bot_manager.cpp b/regamedll/dlls/bot/cs_bot_manager.cpp index 3ce41d40..c21c6ebe 100644 --- a/regamedll/dlls/bot/cs_bot_manager.cpp +++ b/regamedll/dlls/bot/cs_bot_manager.cpp @@ -1,1583 +1,1583 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#include "precompiled.h" - -CBotManager *TheBots = nullptr; - -float CCSBotManager::m_flNextCVarCheck = 0.0f; -bool CCSBotManager::m_isMapDataLoaded = false; -bool CCSBotManager::m_isLearningMap = false; -bool CCSBotManager::m_isAnalysisRequested = false; -NavEditCmdType CCSBotManager::m_editCmd = EDIT_NONE; - -CCSBotManager::CCSBotManager() -{ - m_flNextCVarCheck = 0.0f; - - m_zoneCount = 0; - SetLooseBomb(nullptr); - - m_isBombPlanted = false; - m_bombDefuser = nullptr; - - m_isLearningMap = false; - m_isAnalysisRequested = false; - m_editCmd = EDIT_NONE; - - m_navPlace = 0; - m_roundStartTimestamp = 0.0f; - - m_bServerActive = false; - - TheBotPhrases = new BotPhraseManager; - // load the database of bot radio chatter - TheBotPhrases->Initialize("BotChatter.db", 0); - - TheBotProfiles = new BotProfileManager; - // make sure default voice bank is first - TheBotProfiles->FindVoiceBankIndex("BotChatter.db"); - - const char *filename; - if (IS_CAREER_MATCH()) - { - filename = "MissionPacks/BotPackList.db"; - } - else - { - filename = "BotPackList.db"; - } - - // read in the list of bot profile DBs - int dataLength; - char *dataPointer = (char *)LOAD_FILE_FOR_ME((char *)filename, &dataLength); - if (!dataPointer) - { - TheBotProfiles->Init("BotProfile.db"); - } - else - { - char *dataFile = SharedParse(dataPointer); - char *token; - - while (dataFile) - { - token = SharedGetToken(); - char *clone = CloneString(token); - TheBotProfiles->Init(clone); - delete[] clone; - dataFile = SharedParse(dataFile); - } - - FREE_FILE(dataPointer); - } - - // Now that we've parsed all the profiles, we have a list of the voice banks they're using. - // Go back and parse the custom voice speakables. - const BotProfileManager::VoiceBankList *pVoiceBanks = TheBotProfiles->GetVoiceBanks(); - for (uint32 i = 1; i < pVoiceBanks->size(); i++) - { - TheBotPhrases->Initialize((*pVoiceBanks)[i], i); - } - -#ifdef REGAMEDLL_FIXES - AddServerCommands(); -#endif -} - -// Invoked when a new round begins -void CCSBotManager::RestartRound() -{ - // extend - CBotManager::RestartRound(); - - SetLooseBomb(nullptr); - m_isBombPlanted = false; - m_earliestBombPlantTimestamp = gpGlobals->time + RANDOM_FLOAT(10.0f, 30.0f); - m_bombDefuser = nullptr; - - m_editCmd = EDIT_NONE; - - ResetRadioMessageTimestamps(); - - m_lastSeenEnemyTimestamp = -9999.9f; - m_roundStartTimestamp = gpGlobals->time + freezetime.value; - - // randomly decide if defensive team wants to "rush" as a whole - const float defenseRushChance = 33.3f; // 25.0f; - m_isDefenseRushing = (RANDOM_FLOAT(0.0f, 100.0f) <= defenseRushChance) ? true : false; - - TheBotPhrases->OnRoundRestart(); - - m_isRoundOver = false; - m_isRespawnStarted = false; - m_canRespawn = true; -} - -// Called each frame -void CCSBotManager::StartFrame() -{ - // EXTEND - CBotManager::StartFrame(); - MonitorBotCVars(); - - // debug zone extent visualization - if (cv_bot_debug.value == 5.0f) - { - for (int z = 0; z < m_zoneCount; z++) - { - Zone *zone = &m_zone[z]; - UTIL_DrawBox(&zone->m_extent, 1, 255, 100, 0); - } - } -} - -// Return true if the bot can use this weapon -bool CCSBotManager::IsWeaponUseable(CBasePlayerItem *item) const -{ - if (!item) - { - return false; - } - - if (item->m_iId == WEAPON_C4) - return true; - - WeaponClassType weaponClass = WeaponIDToWeaponClass(item->m_iId); - - if ((!AllowShotguns() && weaponClass == WEAPONCLASS_SHOTGUN) - || (!AllowMachineGuns() && weaponClass == WEAPONCLASS_MACHINEGUN) - || (!AllowRifles() && weaponClass == WEAPONCLASS_RIFLE) - || (!AllowSnipers() && weaponClass == WEAPONCLASS_SNIPERRIFLE) - || (!AllowSubMachineGuns() && weaponClass == WEAPONCLASS_SUBMACHINEGUN) - || (!AllowTacticalShield() && item->m_iId == WEAPON_SHIELDGUN) - || (!AllowPistols() && weaponClass == WEAPONCLASS_PISTOL) - || (!AllowGrenades() && weaponClass == WEAPONCLASS_GRENADE)) - { - return false; - } - - return true; -} - -bool CCSBotManager::IsWeaponUseable(ArmouryItemPack item) const -{ - WeaponClassType weaponClass = WeaponIDToWeaponClass(item); - - if ((!AllowShotguns() && weaponClass == WEAPONCLASS_SHOTGUN) - || (!AllowMachineGuns() && weaponClass == WEAPONCLASS_MACHINEGUN) - || (!AllowRifles() && weaponClass == WEAPONCLASS_RIFLE) - || (!AllowSnipers() && weaponClass == WEAPONCLASS_SNIPERRIFLE) - || (!AllowSubMachineGuns() && weaponClass == WEAPONCLASS_SUBMACHINEGUN) - || (!AllowTacticalShield() && item == ARMOURY_SHIELD) - || (!AllowPistols() && weaponClass == WEAPONCLASS_PISTOL) - || (!AllowGrenades() && weaponClass == WEAPONCLASS_GRENADE)) - { - return false; - } - - return true; -} - -// Return true if this player is on "defense" -bool CCSBotManager::IsOnDefense(CBasePlayer *pPlayer) const -{ - switch (GetScenario()) - { - case SCENARIO_DEFUSE_BOMB: - return (pPlayer->m_iTeam == CT); - - case SCENARIO_RESCUE_HOSTAGES: - return (pPlayer->m_iTeam == TERRORIST); - - case SCENARIO_ESCORT_VIP: - return (pPlayer->m_iTeam == TERRORIST); - } - - return false; -} - -// Return true if this player is on "offense" -bool CCSBotManager::IsOnOffense(CBasePlayer *pPlayer) const -{ - return !IsOnDefense(pPlayer); -} - -// Invoked when a map has just been loaded -void CCSBotManager::ServerActivate() -{ - DestroyNavigationMap(); - m_isMapDataLoaded = false; - - m_zoneCount = 0; - m_gameScenario = SCENARIO_DEATHMATCH; - - ValidateMapData(); - RestartRound(); - - m_isLearningMap = false; - m_isAnalysisRequested = false; - - m_bServerActive = true; - -#ifndef REGAMEDLL_FIXES - AddServerCommands(); -#endif - - TheBotPhrases->OnMapChange(); -} - -void CCSBotManager::AddServerCommand(const char *cmd) -{ - ADD_SERVER_COMMAND((char *)cmd, Bot_ServerCommand); -} - -void CCSBotManager::AddServerCommands() -{ - static bool fFirstTime = true; - - if (!fFirstTime) - return; - - fFirstTime = false; - - if (!AreBotsAllowed()) - return; - - AddServerCommand("bot_about"); - AddServerCommand("bot_add"); - AddServerCommand("bot_add_t"); - AddServerCommand("bot_add_ct"); - AddServerCommand("bot_kill"); - AddServerCommand("bot_kick"); - AddServerCommand("bot_knives_only"); - AddServerCommand("bot_pistols_only"); - AddServerCommand("bot_snipers_only"); - AddServerCommand("bot_all_weapons"); - AddServerCommand("entity_dump"); - AddServerCommand("bot_nav_delete"); - AddServerCommand("bot_nav_split"); - AddServerCommand("bot_nav_merge"); - AddServerCommand("bot_nav_mark"); - AddServerCommand("bot_nav_begin_area"); - AddServerCommand("bot_nav_end_area"); - AddServerCommand("bot_nav_connect"); - AddServerCommand("bot_nav_disconnect"); - AddServerCommand("bot_nav_splice"); - AddServerCommand("bot_nav_crouch"); - AddServerCommand("bot_nav_jump"); - AddServerCommand("bot_nav_precise"); - AddServerCommand("bot_nav_no_jump"); - AddServerCommand("bot_nav_analyze"); - AddServerCommand("bot_nav_strip"); - AddServerCommand("bot_nav_save"); - AddServerCommand("bot_nav_load"); - AddServerCommand("bot_nav_use_place"); - AddServerCommand("bot_nav_place_floodfill"); - AddServerCommand("bot_nav_place_pick"); - AddServerCommand("bot_nav_toggle_place_mode"); - AddServerCommand("bot_nav_toggle_place_painting"); - AddServerCommand("bot_goto_mark"); - AddServerCommand("bot_memory_usage"); - AddServerCommand("bot_nav_mark_unnamed"); - AddServerCommand("bot_nav_warp"); - AddServerCommand("bot_nav_corner_select"); - AddServerCommand("bot_nav_corner_raise"); - AddServerCommand("bot_nav_corner_lower"); - AddServerCommand("bot_nav_check_consistency"); -} - -void CCSBotManager::ServerDeactivate() -{ - m_bServerActive = false; -} - -void CCSBotManager::ClientDisconnect(CBasePlayer *pPlayer) -{ - if (!pPlayer || !pPlayer->IsBot()) - return; - - auto pevTemp = VARS(pPlayer->edict()); - - CCSBot *pBot = static_cast(pPlayer); - pBot->Disconnect(); - - if (!FStringNull(pPlayer->pev->classname)) - { - RemoveEntityHashValue(pPlayer->pev, STRING(pPlayer->pev->classname), CLASSNAME); - } - - FREE_PRIVATE(pPlayer->edict()); - - pPlayer = GetClassPtr((CBasePlayer *)pevTemp); - AddEntityHashValue(pPlayer->pev, STRING(pPlayer->pev->classname), CLASSNAME); - pPlayer->pev->flags = FL_DORMANT; -} - -void PrintAllEntities() -{ - for (int i = 1; i < gpGlobals->maxEntities; i++) - { - edict_t *edict = INDEXENT(i); - - if (!edict || FStringNull(edict->v.classname)) - continue; - - CONSOLE_ECHO(" %s\n", STRING(edict->v.classname)); - } -} - -void CCSBotManager::ServerCommand(const char *pcmd) -{ - if (!m_bServerActive || !AreBotsAllowed()) - return; - - char buffer[400]; - const char *msg = CMD_ARGV(1); - - if (FStrEq(pcmd, "bot_about")) - { - Q_snprintf(buffer, sizeof(buffer), - "\n--------------------------------------------------------------------------\n" - "The Official Counter-Strike Bot V%d.%02d\n" - "Created by Michael S. Booth\n" - "Web: www.turtlerockstudios.com\\csbot\n" - "E-mail: csbot@turtlerockstudios.com\n" - "--------------------------------------------------------------------------\n\n", BOT_VERSION_MAJOR, BOT_VERSION_MINOR); - - CONSOLE_ECHO(buffer); - HintMessageToAllPlayers(buffer); - } - else if (FStrEq(pcmd, "bot_add")) - { - BotAddCommand(BOT_TEAM_ANY, FROM_CONSOLE); - } - else if (FStrEq(pcmd, "bot_add_t")) - { - BotAddCommand(BOT_TEAM_T, FROM_CONSOLE); - } - else if (FStrEq(pcmd, "bot_add_ct")) - { - BotAddCommand(BOT_TEAM_CT, FROM_CONSOLE); - } - else if (FStrEq(pcmd, "bot_kill")) - { - bool killThemAll; - if (CMD_ARGC() == 1 || FStrEq(msg, "all")) - killThemAll = true; - else - killThemAll = false; - - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) - continue; - - const char *name = STRING(pPlayer->pev->netname); - if (FStrEq(name, "")) - continue; - - if (pPlayer->IsBot()) - { - if (killThemAll || FStrEq(name, msg)) - { -#ifdef REGAMEDLL_FIXES - ClientKill(pPlayer->edict()); -#else - pPlayer->TakeDamage(pPlayer->pev, pPlayer->pev, 9999.9f, DMG_CRUSH); -#endif - } - } - } - } - else if (FStrEq(pcmd, "bot_kick")) - { - bool kickThemAll; - if (CMD_ARGC() == 1 || FStrEq(msg, "all")) - kickThemAll = true; - else - kickThemAll = false; - - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) - continue; - - const char *name = STRING(pPlayer->pev->netname); - - if (FStrEq(name, "")) - continue; - - if (pPlayer->IsBot()) - { - if (kickThemAll || FStrEq(name, msg)) - { - // adjust bot quota so kicked bot is not immediately added back in - int newQuota = cv_bot_quota.value - 1; - SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", name)); - CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, int(cv_bot_quota.value))); - } - } - } - - if (kickThemAll || cv_bot_quota.value < 0.0f) - { - CVAR_SET_FLOAT("bot_quota", 0); - } - } - else if (FStrEq(pcmd, "bot_knives_only")) - { - CVAR_SET_FLOAT("bot_allow_pistols", 0); - CVAR_SET_FLOAT("bot_allow_shotguns", 0); - CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_rifles", 0); - CVAR_SET_FLOAT("bot_allow_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_grenades", 0); - CVAR_SET_FLOAT("bot_allow_snipers", 0); - CVAR_SET_FLOAT("bot_allow_shield", 0); - } - else if (FStrEq(pcmd, "bot_pistols_only")) - { - CVAR_SET_FLOAT("bot_allow_pistols", 1); - CVAR_SET_FLOAT("bot_allow_shotguns", 0); - CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_rifles", 0); - CVAR_SET_FLOAT("bot_allow_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_grenades", 0); - CVAR_SET_FLOAT("bot_allow_snipers", 0); - CVAR_SET_FLOAT("bot_allow_shield", 0); - } - else if (FStrEq(pcmd, "bot_snipers_only")) - { - CVAR_SET_FLOAT("bot_allow_pistols", 1); - CVAR_SET_FLOAT("bot_allow_shotguns", 0); - CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_rifles", 0); - CVAR_SET_FLOAT("bot_allow_machine_guns", 0); - CVAR_SET_FLOAT("bot_allow_grenades", 0); - CVAR_SET_FLOAT("bot_allow_snipers", 1); - CVAR_SET_FLOAT("bot_allow_shield", 0); - } - else if (FStrEq(pcmd, "bot_all_weapons")) - { - CVAR_SET_FLOAT("bot_allow_pistols", 1); - CVAR_SET_FLOAT("bot_allow_shotguns", 1); - CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 1); - CVAR_SET_FLOAT("bot_allow_rifles", 1); - CVAR_SET_FLOAT("bot_allow_machine_guns", 1); - CVAR_SET_FLOAT("bot_allow_grenades", 1); - CVAR_SET_FLOAT("bot_allow_snipers", 1); - CVAR_SET_FLOAT("bot_allow_shield", 1); - } - else if (FStrEq(pcmd, "entity_dump")) - { - PrintAllEntities(); - } - else if (FStrEq(pcmd, "bot_nav_delete")) - { - m_editCmd = EDIT_DELETE; - } - else if (FStrEq(pcmd, "bot_nav_split")) - { - m_editCmd = EDIT_SPLIT; - } - else if (FStrEq(pcmd, "bot_nav_merge")) - { - m_editCmd = EDIT_MERGE; - } - else if (FStrEq(pcmd, "bot_nav_mark")) - { - m_editCmd = EDIT_MARK; - } - else if (FStrEq(pcmd, "bot_nav_begin_area")) - { - m_editCmd = EDIT_BEGIN_AREA; - } - else if (FStrEq(pcmd, "bot_nav_end_area")) - { - m_editCmd = EDIT_END_AREA; - } - else if (FStrEq(pcmd, "bot_nav_connect")) - { - m_editCmd = EDIT_CONNECT; - } - else if (FStrEq(pcmd, "bot_nav_disconnect")) - { - m_editCmd = EDIT_DISCONNECT; - } - else if (FStrEq(pcmd, "bot_nav_splice")) - { - m_editCmd = EDIT_SPLICE; - } - else if (FStrEq(pcmd, "bot_nav_crouch")) - { - m_editCmd = EDIT_ATTRIB_CROUCH; - } - else if (FStrEq(pcmd, "bot_nav_jump")) - { - m_editCmd = EDIT_ATTRIB_JUMP; - } - else if (FStrEq(pcmd, "bot_nav_precise")) - { - m_editCmd = EDIT_ATTRIB_PRECISE; - } - else if (FStrEq(pcmd, "bot_nav_no_jump")) - { - m_editCmd = EDIT_ATTRIB_NO_JUMP; - } - else if (FStrEq(pcmd, "bot_nav_analyze")) - { - m_isAnalysisRequested = true; - } - else if (FStrEq(pcmd, "bot_nav_strip")) - { - StripNavigationAreas(); - } - else if (FStrEq(pcmd, "bot_nav_save")) - { - GET_GAME_DIR(buffer); - Q_strcat(buffer, "\\"); - Q_strcat(buffer, CBotManager::GetNavMapFilename()); - - if (SaveNavigationMap(buffer)) - CONSOLE_ECHO("Navigation map '%s' saved.\n", buffer); - else - CONSOLE_ECHO("ERROR: Cannot save navigation map '%s'.\n", buffer); - } - else if (FStrEq(pcmd, "bot_nav_load")) - { - ValidateMapData(); - } - else if (FStrEq(pcmd, "bot_nav_use_place")) - { - if (CMD_ARGC() == 1) - { - // no arguments = list all available places - int i = 0; - const BotPhraseList *placeList = TheBotPhrases->GetPlaceList(); - for (auto phrase : *placeList) - { - if (phrase->GetID() == GetNavPlace()) - CONSOLE_ECHO("--> %-26s", phrase->GetName()); - else - CONSOLE_ECHO("%-30s", phrase->GetName()); - - if (!(i % 3)) - CONSOLE_ECHO("\n"); - i++; - } - CONSOLE_ECHO("\n"); - } - else - { - // single argument = set current place - const BotPhraseList *placeList = TheBotPhrases->GetPlaceList(); - const BotPhrase *found = nullptr; - bool isAmbiguous = false; - for (auto phrase : *placeList) - { - if (!Q_strnicmp(phrase->GetName(), msg, Q_strlen(msg))) - { - // check for exact match in case of subsets of other strings - if (!Q_strcmp(phrase->GetName(), msg)) - { - found = phrase; - isAmbiguous = false; - break; - } - - if (found) - { - isAmbiguous = true; - } - else - { - found = phrase; - } - } - } - - if (isAmbiguous) - { - CONSOLE_ECHO("Ambiguous\n"); - } - else - { - CONSOLE_ECHO("Current place set to '%s'\n", found->GetName()); - SetNavPlace(found->GetID()); - } - } - } - else if (FStrEq(pcmd, "bot_nav_toggle_place_mode")) - { - m_editCmd = EDIT_TOGGLE_PLACE_MODE; - } - else if (FStrEq(pcmd, "bot_nav_place_floodfill")) - { - m_editCmd = EDIT_PLACE_FLOODFILL; - } - else if (FStrEq(pcmd, "bot_nav_place_pick")) - { - m_editCmd = EDIT_PLACE_PICK; - } - else if (FStrEq(pcmd, "bot_nav_toggle_place_painting")) - { - m_editCmd = EDIT_TOGGLE_PLACE_PAINTING; - } - else if (FStrEq(pcmd, "bot_goto_mark")) - { - // tell the first bot we find to go to our marked area - CNavArea *area = GetMarkedArea(); - if (area) - { - CBaseEntity *pEntity = nullptr; - while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) - { - if (!pEntity->IsPlayer()) - continue; - - if (pEntity->IsDormant()) - continue; - - CBasePlayer *playerOrBot = GetClassPtr((CBasePlayer *)pEntity->pev); - - if (playerOrBot->IsBot()) - { - CCSBot *pBot = static_cast(playerOrBot); - if (pBot) - { - pBot->MoveTo(&area->m_center, FASTEST_ROUTE); - } - - break; - } - } - } - } - else if (FStrEq(pcmd, "bot_memory_usage")) - { - CONSOLE_ECHO("Memory usage:\n"); - CONSOLE_ECHO(" %d bytes per bot\b", sizeof(CCSBot)); - CONSOLE_ECHO(" %d Navigation Areas @ %d bytes each = %d bytes\n", - TheNavAreaGrid.GetNavAreaCount(), - sizeof(CNavArea), - TheNavAreaGrid.GetNavAreaCount() * sizeof(CNavArea)); - CONSOLE_ECHO(" %d Hiding Spots @ %d bytes each = %d bytes\n", - TheHidingSpotList.size(), - sizeof(HidingSpot), - sizeof(HidingSpot) * TheHidingSpotList.size()); - - unsigned int encounterMem = 0; - for (auto area : TheNavAreaList) - { - for (auto se : area->m_spotEncounterList) - { - encounterMem += sizeof(SpotEncounter); - encounterMem += sizeof(SpotOrder) * se.spotList.size(); - } - } - - CONSOLE_ECHO(" Encounter Spot data = %d bytes\n", encounterMem); - } - else if (FStrEq(pcmd, "bot_nav_mark_unnamed")) - { - m_editCmd = EDIT_MARK_UNNAMED; - } - else if (FStrEq(pcmd, "bot_nav_warp")) - { - m_editCmd = EDIT_WARP_TO_MARK; - } - else if (FStrEq(pcmd, "bot_nav_corner_select")) - { - m_editCmd = EDIT_SELECT_CORNER; - } - else if (FStrEq(pcmd, "bot_nav_corner_raise")) - { - m_editCmd = EDIT_RAISE_CORNER; - } - else if (FStrEq(pcmd, "bot_nav_corner_lower")) - { - m_editCmd = EDIT_LOWER_CORNER; - } - else if (FStrEq(pcmd, "bot_nav_check_consistency")) - { - if (CMD_ARGC() != 2) - { - CONSOLE_ECHO("usage: bot_nav_check_consistency \n"); - return; - } - - SanityCheckNavigationMap(msg); - } -} - -BOOL CCSBotManager::ClientCommand(CBasePlayer *pPlayer, const char *pcmd) -{ - return FALSE; -} - -// Process the "bot_add" console command -bool CCSBotManager::BotAddCommand(BotProfileTeamType team, bool isFromConsole) -{ - // dont allow bots to join if the Navigation Area is being generated - if (m_isLearningMap) - return false; - - const BotProfile *profile = nullptr; - if (!isFromConsole || CMD_ARGC() < 2) - { - // if team not specified, check cv_bot_join_team cvar for preference - if (team == BOT_TEAM_ANY) - { - if (!Q_stricmp(cv_bot_join_team.string, "T")) - team = BOT_TEAM_T; - - else if (!Q_stricmp(cv_bot_join_team.string, "CT")) - team = BOT_TEAM_CT; - else - { - TeamName defaultTeam = CSGameRules()->SelectDefaultTeam(); - if (defaultTeam == TERRORIST) - team = BOT_TEAM_T; - - else if (defaultTeam == CT) - team = BOT_TEAM_CT; - } - } - - // try to add a bot by name - profile = TheBotProfiles->GetRandomProfile(GetDifficultyLevel(), team); - if (!profile) - { - CONSOLE_ECHO("All bot profiles at this difficulty level are in use.\n"); - return true; - } - } - else - { - // in career, ignore humans - bool ignoreHumans = false; - if (CSGameRules() && CSGameRules()->IsCareer()) - ignoreHumans = true; - - if (UTIL_IsNameTaken(CMD_ARGV(1), ignoreHumans)) - { - CONSOLE_ECHO("Error - %s is already in the game.\n", CMD_ARGV(1)); - return true; - } - - profile = TheBotProfiles->GetProfile(CMD_ARGV(1), team); - if (!profile) - { - CONSOLE_ECHO("Error - no profile for '%s' exists.\n", CMD_ARGV(1)); - return true; - } - } - - // create the bot - if (AddBot(profile, team)) - { - if (isFromConsole) - { - // increase the bot quota to account for manually added bot - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value + 1); - } - } -#ifdef REGAMEDLL_FIXES - else - { - // decrease the bot quota - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1); - } -#endif - - return true; -} - -// Keep a minimum quota of bots in the game -void CCSBotManager::MaintainBotQuota() -{ -#ifdef REGAMEDLL_FIXES - if (!AreBotsAllowed()) - return; -#endif - - if (m_isLearningMap) - return; - - int totalHumansInGame = UTIL_HumansInGame(); - int humanPlayersInGame = UTIL_HumansInGame(IGNORE_SPECTATORS); - - // don't add bots until local player has been registered, to make sure he's player ID #1 - if (!IS_DEDICATED_SERVER() && totalHumansInGame == 0) - return; - - int desiredBotCount = int(cv_bot_quota.value); - int occupiedBotSlots = UTIL_BotsInGame(); - - // isRoundInProgress is true if the round has progressed far enough that new players will join as dead. - bool isRoundInProgress = CSGameRules()->IsGameStarted() && - !TheCSBots()->IsRoundOver() && - (CSGameRules()->GetRoundElapsedTime() >= CSGameRules()->GetRoundRespawnTime()); - -#ifdef REGAMEDLL_ADD - if (FStrEq(cv_bot_quota_mode.string, "fill")) - { - // If bot_quota_mode is 'fill', we want the number of bots and humans together to equal bot_quota - // unless the round is already in progress, in which case we play with what we've been dealt - if (!isRoundInProgress) - { - desiredBotCount = Q_max(0, desiredBotCount - humanPlayersInGame); - } - else - { - desiredBotCount = occupiedBotSlots; - } - } - else if (FStrEq(cv_bot_quota_mode.string, "match")) - { - // If bot_quota_mode is 'match', we want the number of bots to be bot_quota * total humans - // unless the round is already in progress, in which case we play with what we've been dealt - if (!isRoundInProgress) - { - desiredBotCount = Q_max(0, cv_bot_quota.value * humanPlayersInGame); - } - else - { - desiredBotCount = occupiedBotSlots; - } - } -#else // #ifdef REGAMEDLL_ADD - if (cv_bot_quota_match.value > 0.0) - { - desiredBotCount = int(humanPlayersInGame * cv_bot_quota_match.value); - } -#endif // #ifdef REGAMEDLL_ADD - - // wait for a player to join, if necessary - if (cv_bot_join_after_player.value > 0.0) - { - if (humanPlayersInGame == 0) - desiredBotCount = 0; - } - -#ifdef REGAMEDLL_ADD - // wait until the map has been loaded for a bit, to allow players to transition across - // the transition without missing the pistol round - if (static_cast(cv_bot_join_delay.value) > CSGameRules()->GetMapElapsedTime()) - { - desiredBotCount = 0; - } -#endif - - // if bots will auto-vacate, we need to keep one slot open to allow players to join - if (cv_bot_auto_vacate.value > 0.0) - desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - (totalHumansInGame + 1)); - else - desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - totalHumansInGame); - -#ifdef REGAMEDLL_FIXES - // Try to balance teams, if we are in the first specified seconds of a round and bots can join either team. - if (occupiedBotSlots > 0 && desiredBotCount == occupiedBotSlots && CSGameRules()->IsGameStarted()) - { - if (CSGameRules()->GetRoundElapsedTime() < CSGameRules()->GetRoundRespawnTime()) // new bots can still spawn during this time - { - if (autoteambalance.value > 0.0f) - { - int numAliveTerrorist; - int numAliveCT; - int numDeadTerrorist; - int numDeadCT; - - CSGameRules()->InitializePlayerCounts(numAliveTerrorist, numAliveCT, numDeadTerrorist, numDeadCT); - - if (!FStrEq(cv_bot_join_team.string, "T") && - !FStrEq(cv_bot_join_team.string, "CT")) - { - if (numAliveTerrorist > CSGameRules()->m_iNumCT + 1) - { - if (UTIL_KickBotFromTeam(TERRORIST)) - return; - } - else if (numAliveCT > CSGameRules()->m_iNumTerrorist + 1) - { - if (UTIL_KickBotFromTeam(CT)) - return; - } - } - } - } - } -#endif // #ifdef REGAMEDLL_FIXES - - // add bots if necessary - if (desiredBotCount > occupiedBotSlots) - { - // don't try to add a bot if all teams are full - if (!CSGameRules()->TeamFull(TERRORIST) || !CSGameRules()->TeamFull(CT)) - { -#ifndef REGAMEDLL_FIXES - if (AreBotsAllowed()) -#endif - { - BotAddCommand(BOT_TEAM_ANY); - } - } - } - else if (desiredBotCount < occupiedBotSlots) - { - // kick a bot to maintain quota - - // first remove any unassigned bots - if (UTIL_KickBotFromTeam(UNASSIGNED)) - return; - - TeamName kickTeam; - - // remove from the team that has more players - if (CSGameRules()->m_iNumTerrorist > CSGameRules()->m_iNumCT) - { - kickTeam = TERRORIST; - } - else if (CSGameRules()->m_iNumTerrorist < CSGameRules()->m_iNumCT) - { - kickTeam = CT; - } - // remove from the team that's winning - else if (CSGameRules()->m_iNumTerroristWins > CSGameRules()->m_iNumCTWins) - { - kickTeam = TERRORIST; - } - else if (CSGameRules()->m_iNumCTWins > CSGameRules()->m_iNumTerroristWins) - { - kickTeam = CT; - } - else - { - // teams and scores are equal, pick a team at random - kickTeam = (RANDOM_LONG(0, 1) == 0) ? CT : TERRORIST; - } - - // attempt to kick a bot from the given team - bool atLeastOneKicked = UTIL_KickBotFromTeam(kickTeam); - - if (!atLeastOneKicked) - { - // if there were no bots on the team, kick a bot from the other team - if (kickTeam == TERRORIST) - atLeastOneKicked = UTIL_KickBotFromTeam(CT); - else - atLeastOneKicked = UTIL_KickBotFromTeam(TERRORIST); - } - - if (atLeastOneKicked) - { - CONSOLE_ECHO("These bots kicked to maintain quota.\n"); - } - } - else - { - if (CSGameRules() && !CSGameRules()->IsCareer()) - return; - - bool humansAreCTs = (Q_strcmp(humans_join_team.string, "CT") == 0); - - if (humansAreCTs) - { - if (CSGameRules()->m_iNumCT <= 6) - return; - - UTIL_KickBotFromTeam(CT); - } - else - { - if (CSGameRules()->m_iNumTerrorist <= 6) - return; - - UTIL_KickBotFromTeam(TERRORIST); - } - - CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1.0f); - } -} - -void CCSBotManager::MonitorBotCVars() -{ - if (cv_bot_nav_edit.value != 0.0f) - { - EditNavAreas(m_editCmd); - m_editCmd = EDIT_NONE; - } - - if (gpGlobals->time >= m_flNextCVarCheck) - { - if (cv_bot_show_danger.value != 0.0f) - DrawDanger(); - - MaintainBotQuota(); - m_flNextCVarCheck = gpGlobals->time + 0.3f; - } -} - -// Collect all nav areas that overlap the given zone -class CollectOverlappingAreas -{ -public: - CollectOverlappingAreas(CCSBotManager::Zone *zone) - { - m_zone = zone; - zone->m_areaCount = 0; - } - bool operator()(CNavArea *area) - { - const Extent *areaExtent = area->GetExtent(); - - if (areaExtent->hi.x >= m_zone->m_extent.lo.x && areaExtent->lo.x <= m_zone->m_extent.hi.x - && areaExtent->hi.y >= m_zone->m_extent.lo.y && areaExtent->lo.y <= m_zone->m_extent.hi.y - && areaExtent->hi.z >= m_zone->m_extent.lo.z && areaExtent->lo.z <= m_zone->m_extent.hi.z) - { - // area overlaps m_zone - m_zone->m_area[m_zone->m_areaCount++] = area; - if (m_zone->m_areaCount == CCSBotManager::MAX_ZONE_NAV_AREAS) - { - return false; - } - } - - return true; - } - -private: - CCSBotManager::Zone *m_zone; -}; - -// Search the map entities to determine the game scenario and define important zones. -void CCSBotManager::ValidateMapData() -{ - if (m_isMapDataLoaded || !AreBotsAllowed()) - return; - - m_isMapDataLoaded = true; - - if (LoadNavigationMap()) - { - CONSOLE_ECHO("Failed to load navigation map.\n"); - return; - } - - CONSOLE_ECHO("Navigation map loaded.\n"); - - m_zoneCount = 0; - m_gameScenario = SCENARIO_DEATHMATCH; - - // Search all entities in the map and set the game type and store all zones (bomb target, etc). - CBaseEntity *pEntity = nullptr; - for (int i = 1; i < gpGlobals->maxEntities; i++) - { - pEntity = CBaseEntity::Instance(INDEXENT(i)); - if (!pEntity) - continue; - - bool found = false; - bool isLegacy = false; - - if (FClassnameIs(pEntity->pev, "func_bomb_target")) - { - m_gameScenario = SCENARIO_DEFUSE_BOMB; - found = true; - isLegacy = false; - } - else if (FClassnameIs(pEntity->pev, "info_bomb_target")) - { - m_gameScenario = SCENARIO_DEFUSE_BOMB; - found = true; - isLegacy = true; - } - else if (FClassnameIs(pEntity->pev, "func_hostage_rescue")) - { - m_gameScenario = SCENARIO_RESCUE_HOSTAGES; - found = true; - isLegacy = false; - } - else if (FClassnameIs(pEntity->pev, "info_hostage_rescue")) - { - m_gameScenario = SCENARIO_RESCUE_HOSTAGES; - found = true; - isLegacy = true; - } - else if (FClassnameIs(pEntity->pev, "hostage_entity")) - { - // some very old maps (ie: cs_assault) use info_player_start - // as rescue zones, so set the scenario if there are hostages - // in the map - m_gameScenario = SCENARIO_RESCUE_HOSTAGES; - } - else if (FClassnameIs(pEntity->pev, "func_vip_safetyzone")) - { - m_gameScenario = SCENARIO_ESCORT_VIP; - found = true; - isLegacy = false; - } - - if (found) - { - if (m_zoneCount < MAX_ZONES) - { - m_zone[m_zoneCount].m_center = isLegacy ? pEntity->pev->origin : (pEntity->pev->absmax + pEntity->pev->absmin) / 2.0f; - m_zone[m_zoneCount].m_isLegacy = isLegacy; - m_zone[m_zoneCount].m_index = m_zoneCount; - m_zone[m_zoneCount].m_entity = pEntity; - m_zoneCount++; - } - else - { - CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n"); - } - } - } - - // If there are no zones and the scenario is hostage rescue, - // use the info_player_start entities as rescue zones. - if (m_zoneCount == 0 && m_gameScenario == SCENARIO_RESCUE_HOSTAGES) - { - pEntity = nullptr; - - while ((pEntity = UTIL_FindEntityByClassname(pEntity, "info_player_start"))) - { -#ifdef REGAMEDLL_FIXES - if (m_zoneCount >= MAX_ZONES) - break; -#endif - - if (FNullEnt(pEntity->edict())) - break; - - if (m_zoneCount < MAX_ZONES) - { - m_zone[m_zoneCount].m_center = pEntity->pev->origin; - m_zone[m_zoneCount].m_isLegacy = true; - m_zone[m_zoneCount].m_index = m_zoneCount; - m_zone[m_zoneCount].m_entity = pEntity; - m_zoneCount++; - } - else - { - CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n"); - } - } - } - - // Collect nav areas that overlap each zone - for (int i = 0; i < m_zoneCount; i++) - { - Zone *zone = &m_zone[i]; - - if (zone->m_isLegacy) - { - const float legacyRange = 256.0f; - zone->m_extent.lo.x = zone->m_center.x - legacyRange; - zone->m_extent.lo.y = zone->m_center.y - legacyRange; - zone->m_extent.lo.z = zone->m_center.z - legacyRange; - zone->m_extent.hi.x = zone->m_center.x + legacyRange; - zone->m_extent.hi.y = zone->m_center.y + legacyRange; - zone->m_extent.hi.z = zone->m_center.z + legacyRange; - } - else - { - zone->m_extent.lo = zone->m_entity->pev->absmin; - zone->m_extent.hi = zone->m_entity->pev->absmax; - } - - // ensure Z overlap - const float zFudge = 50.0f; - zone->m_extent.lo.z -= zFudge; - zone->m_extent.hi.z += zFudge; - - // build a list of nav areas that overlap this zone - CollectOverlappingAreas collector(zone); - ForAllAreas(collector); - } -} - -bool CCSBotManager::AddBot(const BotProfile *profile, BotProfileTeamType team) -{ - if (!AreBotsAllowed()) - return false; - - int nTeamSlot = UNASSIGNED; - if (team == BOT_TEAM_ANY) - { - // if team not specified, check cv_bot_join_team cvar for preference - if (!Q_stricmp(cv_bot_join_team.string, "T")) - nTeamSlot = TERRORIST; - - else if (!Q_stricmp(cv_bot_join_team.string, "CT")) - nTeamSlot = CT; - } - else if (team == BOT_TEAM_CT) - nTeamSlot = CT; - - else if (team == BOT_TEAM_T) - nTeamSlot = TERRORIST; - - if (nTeamSlot == UNASSIGNED) - { - nTeamSlot = CSGameRules()->SelectDefaultTeam(); - } - - if (nTeamSlot == UNASSIGNED || CSGameRules()->TeamFull(nTeamSlot)) - { - CONSOLE_ECHO("Could not add bot to the game: Team is full\n"); - return false; - } - - if (CSGameRules()->TeamStacked(nTeamSlot, UNASSIGNED)) - { - CONSOLE_ECHO("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round).\n"); - return false; - } - - CCSBot *pBot = CreateBot(profile); - if (!pBot) - { - return false; - } - - //int nJoinedTeam; - ClientPutInServer(pBot->edict()); - SET_CLIENT_KEY_VALUE(pBot->entindex(), GET_INFO_BUFFER(pBot->edict()), "*bot", "1"); - - pBot->m_iMenu = Menu_ChooseTeam; - pBot->m_iJoiningState = PICKINGTEAM; - - if (HandleMenu_ChooseTeam(pBot, nTeamSlot)) - { - int skin = profile->GetSkin(); - if (!skin) - skin = 6; - - HandleMenu_ChooseAppearance(pBot, skin); - - if (IS_DEDICATED_SERVER()) - { - UTIL_DPrintf("Added bot %s to server\n", STRING(pBot->pev->netname)); - } - - return true; - } - - SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", STRING(pBot->pev->netname))); - CONSOLE_ECHO("Could not add bot to the game.\n"); - return false; -} - -// Return the zone that contains the given position -const CCSBotManager::Zone *CCSBotManager::GetZone(const Vector *pos) const -{ - for (int z = 0; z < m_zoneCount; z++) - { - if (m_zone[z].m_extent.Contains(pos)) - { - return &m_zone[z]; - } - } - - return nullptr; -} - -// Return the closest zone to the given position -const CCSBotManager::Zone *CCSBotManager::GetClosestZone(const Vector *pos) const -{ - const Zone *close = nullptr; - float closeRangeSq = 1e9f; - - for (int z = 0; z < m_zoneCount; z++) - { - float rangeSq = (m_zone[z].m_center - (*pos)).LengthSquared(); - - if (rangeSq < closeRangeSq) - { - closeRangeSq = rangeSq; - close = &m_zone[z]; - } - } - - return close; -} - -// Return a random position inside the given zone -const Vector *CCSBotManager::GetRandomPositionInZone(const Zone *zone) const -{ - static Vector pos; - - if (!zone) - return nullptr; - - if (zone->m_areaCount == 0) - return nullptr; - - // pick a random overlapping area - CNavArea *area = GetRandomAreaInZone(zone); - - // pick a location inside both the nav area and the zone - // TODO: Randomize this - if (zone->m_isLegacy) - { - // TODO: It is possible that the radius might not overlap this area at all... - area->GetClosestPointOnArea(&zone->m_center, &pos); - } - else - { - const Extent &areaExtent = *area->GetExtent(); - Extent overlap; - overlap.lo.x = Q_max(areaExtent.lo.x, zone->m_extent.lo.x); - overlap.lo.y = Q_max(areaExtent.lo.y, zone->m_extent.lo.y); - overlap.hi.x = Q_min(areaExtent.hi.x, zone->m_extent.hi.x); - overlap.hi.y = Q_min(areaExtent.hi.y, zone->m_extent.hi.y); - - pos.x = (overlap.lo.x + overlap.hi.x) / 2.0f; - pos.y = (overlap.lo.y + overlap.hi.y) / 2.0f; - pos.z = area->GetZ(&pos); - } - - return &pos; -} - -// Return a random area inside the given zone -CNavArea *CCSBotManager::GetRandomAreaInZone(const Zone *zone) const -{ - // TODO: improvement is needed - if (!zone->m_areaCount) - return nullptr; - - return zone->m_area[RANDOM_LONG(0, zone->m_areaCount - 1)]; -} - -void CCSBotManager::OnEvent(GameEventType event, CBaseEntity *pEntity, CBaseEntity *pOther) -{ - switch (event) - { - case EVENT_BOMB_PLANTED: - m_isBombPlanted = true; - m_bombPlantTimestamp = gpGlobals->time; - break; - - case EVENT_BOMB_DEFUSING: - m_bombDefuser = static_cast(pEntity); - break; - - case EVENT_BOMB_DEFUSE_ABORTED: - m_bombDefuser = nullptr; - break; - - case EVENT_BOMB_DEFUSED: - m_isBombPlanted = false; - m_bombDefuser = nullptr; - break; - - case EVENT_TERRORISTS_WIN: - case EVENT_CTS_WIN: - case EVENT_ROUND_DRAW: - m_isRoundOver = true; - break; - - case EVENT_RADIO_ENEMY_SPOTTED: - m_lastSeenEnemyTimestamp = gpGlobals->time; - SetLastSeenEnemyTimestamp(); - break; - - default: - break; - } - - CBotManager::OnEvent(event, pEntity, pOther); -} - -// Get the time remaining before the planted bomb explodes -float CCSBotManager::GetBombTimeLeft() const -{ - return (CSGameRules()->m_iC4Timer - (gpGlobals->time - m_bombPlantTimestamp)); -} - -void CCSBotManager::SetLooseBomb(CBaseEntity *bomb) -{ - m_looseBomb = bomb; - - if (bomb) - { - m_looseBombArea = TheNavAreaGrid.GetNearestNavArea(&bomb->pev->origin); - } - else - { - m_looseBombArea = nullptr; - } -} - -// Return true if player is important to scenario (VIP, bomb carrier, etc) -bool CCSBotManager::IsImportantPlayer(CBasePlayer *pPlayer) const -{ - switch (GetScenario()) - { - case SCENARIO_DEFUSE_BOMB: - { - if (pPlayer->m_iTeam == TERRORIST && pPlayer->IsBombGuy()) - return true; - - // TODO: TEAM_CT's defusing the bomb are important - return false; - } - case SCENARIO_ESCORT_VIP: - { - if (pPlayer->m_iTeam == CT && pPlayer->m_bIsVIP) - return true; - - return false; - } - case SCENARIO_RESCUE_HOSTAGES: - { - // TODO: TEAM_CT's escorting hostages are important - return false; - } - } - - // everyone is equally important in a deathmatch - return false; -} - -// Return priority of player (0 = max pri) -unsigned int CCSBotManager::GetPlayerPriority(CBasePlayer *pPlayer) const -{ - const unsigned int lowestPriority = 0xFFFFFFFF; - - if (!pPlayer->IsPlayer()) - return lowestPriority; - - // human players have highest priority - if (!pPlayer->IsBot()) - return 0; - - CCSBot *pBot = static_cast(pPlayer); - - // bots doing something important for the current scenario have high priority - switch (GetScenario()) - { - case SCENARIO_DEFUSE_BOMB: - { - // the bomb carrier has high priority - if (pBot->m_iTeam == TERRORIST && pBot->m_bHasC4) - return 1; - - break; - } - case SCENARIO_ESCORT_VIP: - { - // the VIP has high priority - if (pBot->m_iTeam == CT && pBot->m_bIsVIP) - return 1; - - break; - } - case SCENARIO_RESCUE_HOSTAGES: - { - // TEAM_CT's rescuing hostages have high priority - if (pBot->m_iTeam == CT && pBot->GetHostageEscortCount()) - return 1; - - break; - } - } - - // everyone else is ranked by their unique ID (which cannot be zero) - return 1 + pBot->GetID(); -} - -// Return the last time the given radio message was sent for given team -// 'teamID' can be TEAM_CT or TEAM_TERRORIST -float CCSBotManager::GetRadioMessageTimestamp(GameEventType event, int teamID) const -{ - if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) - return 0.0f; - - int i = (teamID == TERRORIST) ? 0 : 1; - return m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i]; -} - -// Return the interval since the last time this message was sent -float CCSBotManager::GetRadioMessageInterval(GameEventType event, int teamID) const -{ - if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) - return 99999999.9f; - - int i = (teamID == TERRORIST) ? 0 : 1; - return gpGlobals->time - m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i]; -} - -// Set the given radio message timestamp. -// 'teamID' can be TEAM_CT or TEAM_TERRORIST -void CCSBotManager::SetRadioMessageTimestamp(GameEventType event, int teamID) -{ - if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) - return; - - int i = (teamID == TERRORIST) ? 0 : 1; - m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i] = gpGlobals->time; -} - -// Reset all radio message timestamps -void CCSBotManager::ResetRadioMessageTimestamps() -{ - Q_memset(m_radioMsgTimestamp, 0, sizeof(m_radioMsgTimestamp)); -} - -void CCSBotManager::OnFreeEntPrivateData(CBaseEntity *pEntity) -{ - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || pPlayer->IsDormant()) - continue; - - if (pPlayer->IsBot()) - { - CCSBot *pBot = static_cast(pPlayer); - if (pBot->m_attacker == pEntity) - pBot->m_attacker = nullptr; - - if (pBot->m_bomber == pEntity) - pBot->m_bomber = nullptr; - } - } -} +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +CBotManager *TheBots = nullptr; + +float CCSBotManager::m_flNextCVarCheck = 0.0f; +bool CCSBotManager::m_isMapDataLoaded = false; +bool CCSBotManager::m_isLearningMap = false; +bool CCSBotManager::m_isAnalysisRequested = false; +NavEditCmdType CCSBotManager::m_editCmd = EDIT_NONE; + +CCSBotManager::CCSBotManager() +{ + m_flNextCVarCheck = 0.0f; + + m_zoneCount = 0; + SetLooseBomb(nullptr); + + m_isBombPlanted = false; + m_bombDefuser = nullptr; + + m_isLearningMap = false; + m_isAnalysisRequested = false; + m_editCmd = EDIT_NONE; + + m_navPlace = 0; + m_roundStartTimestamp = 0.0f; + + m_bServerActive = false; + + TheBotPhrases = new BotPhraseManager; + // load the database of bot radio chatter + TheBotPhrases->Initialize("BotChatter.db", 0); + + TheBotProfiles = new BotProfileManager; + // make sure default voice bank is first + TheBotProfiles->FindVoiceBankIndex("BotChatter.db"); + + const char *filename; + if (IS_CAREER_MATCH()) + { + filename = "MissionPacks/BotPackList.db"; + } + else + { + filename = "BotPackList.db"; + } + + // read in the list of bot profile DBs + int dataLength; + char *dataPointer = (char *)LOAD_FILE_FOR_ME((char *)filename, &dataLength); + if (!dataPointer) + { + TheBotProfiles->Init("BotProfile.db"); + } + else + { + char *dataFile = SharedParse(dataPointer); + char *token; + + while (dataFile) + { + token = SharedGetToken(); + char *clone = CloneString(token); + TheBotProfiles->Init(clone); + delete[] clone; + dataFile = SharedParse(dataFile); + } + + FREE_FILE(dataPointer); + } + + // Now that we've parsed all the profiles, we have a list of the voice banks they're using. + // Go back and parse the custom voice speakables. + const BotProfileManager::VoiceBankList *pVoiceBanks = TheBotProfiles->GetVoiceBanks(); + for (uint32 i = 1; i < pVoiceBanks->size(); i++) + { + TheBotPhrases->Initialize((*pVoiceBanks)[i], i); + } + +#ifdef REGAMEDLL_FIXES + AddServerCommands(); +#endif +} + +// Invoked when a new round begins +void CCSBotManager::RestartRound() +{ + // extend + CBotManager::RestartRound(); + + SetLooseBomb(nullptr); + m_isBombPlanted = false; + m_earliestBombPlantTimestamp = gpGlobals->time + RANDOM_FLOAT(10.0f, 30.0f); + m_bombDefuser = nullptr; + + m_editCmd = EDIT_NONE; + + ResetRadioMessageTimestamps(); + + m_lastSeenEnemyTimestamp = -9999.9f; + m_roundStartTimestamp = gpGlobals->time + freezetime.value; + + // randomly decide if defensive team wants to "rush" as a whole + const float defenseRushChance = 33.3f; // 25.0f; + m_isDefenseRushing = (RANDOM_FLOAT(0.0f, 100.0f) <= defenseRushChance) ? true : false; + + TheBotPhrases->OnRoundRestart(); + + m_isRoundOver = false; + m_isRespawnStarted = false; + m_canRespawn = true; +} + +// Called each frame +void CCSBotManager::StartFrame() +{ + // EXTEND + CBotManager::StartFrame(); + MonitorBotCVars(); + + // debug zone extent visualization + if (cv_bot_debug.value == 5.0f) + { + for (int z = 0; z < m_zoneCount; z++) + { + Zone *zone = &m_zone[z]; + UTIL_DrawBox(&zone->m_extent, 1, 255, 100, 0); + } + } +} + +// Return true if the bot can use this weapon +bool CCSBotManager::IsWeaponUseable(CBasePlayerItem *item) const +{ + if (!item) + { + return false; + } + + if (item->m_iId == WEAPON_C4) + return true; + + WeaponClassType weaponClass = WeaponIDToWeaponClass(item->m_iId); + + if ((!AllowShotguns() && weaponClass == WEAPONCLASS_SHOTGUN) + || (!AllowMachineGuns() && weaponClass == WEAPONCLASS_MACHINEGUN) + || (!AllowRifles() && weaponClass == WEAPONCLASS_RIFLE) + || (!AllowSnipers() && weaponClass == WEAPONCLASS_SNIPERRIFLE) + || (!AllowSubMachineGuns() && weaponClass == WEAPONCLASS_SUBMACHINEGUN) + || (!AllowTacticalShield() && item->m_iId == WEAPON_SHIELDGUN) + || (!AllowPistols() && weaponClass == WEAPONCLASS_PISTOL) + || (!AllowGrenades() && weaponClass == WEAPONCLASS_GRENADE)) + { + return false; + } + + return true; +} + +bool CCSBotManager::IsWeaponUseable(ArmouryItemPack item) const +{ + WeaponClassType weaponClass = WeaponIDToWeaponClass(item); + + if ((!AllowShotguns() && weaponClass == WEAPONCLASS_SHOTGUN) + || (!AllowMachineGuns() && weaponClass == WEAPONCLASS_MACHINEGUN) + || (!AllowRifles() && weaponClass == WEAPONCLASS_RIFLE) + || (!AllowSnipers() && weaponClass == WEAPONCLASS_SNIPERRIFLE) + || (!AllowSubMachineGuns() && weaponClass == WEAPONCLASS_SUBMACHINEGUN) + || (!AllowTacticalShield() && item == ARMOURY_SHIELD) + || (!AllowPistols() && weaponClass == WEAPONCLASS_PISTOL) + || (!AllowGrenades() && weaponClass == WEAPONCLASS_GRENADE)) + { + return false; + } + + return true; +} + +// Return true if this player is on "defense" +bool CCSBotManager::IsOnDefense(CBasePlayer *pPlayer) const +{ + switch (GetScenario()) + { + case SCENARIO_DEFUSE_BOMB: + return (pPlayer->m_iTeam == CT); + + case SCENARIO_RESCUE_HOSTAGES: + return (pPlayer->m_iTeam == TERRORIST); + + case SCENARIO_ESCORT_VIP: + return (pPlayer->m_iTeam == TERRORIST); + } + + return false; +} + +// Return true if this player is on "offense" +bool CCSBotManager::IsOnOffense(CBasePlayer *pPlayer) const +{ + return !IsOnDefense(pPlayer); +} + +// Invoked when a map has just been loaded +void CCSBotManager::ServerActivate() +{ + DestroyNavigationMap(); + m_isMapDataLoaded = false; + + m_zoneCount = 0; + m_gameScenario = SCENARIO_DEATHMATCH; + + ValidateMapData(); + RestartRound(); + + m_isLearningMap = false; + m_isAnalysisRequested = false; + + m_bServerActive = true; + +#ifndef REGAMEDLL_FIXES + AddServerCommands(); +#endif + + TheBotPhrases->OnMapChange(); +} + +void CCSBotManager::AddServerCommand(const char *cmd) +{ + ADD_SERVER_COMMAND((char *)cmd, Bot_ServerCommand); +} + +void CCSBotManager::AddServerCommands() +{ + static bool fFirstTime = true; + + if (!fFirstTime) + return; + + fFirstTime = false; + + if (!AreBotsAllowed()) + return; + + AddServerCommand("bot_about"); + AddServerCommand("bot_add"); + AddServerCommand("bot_add_t"); + AddServerCommand("bot_add_ct"); + AddServerCommand("bot_kill"); + AddServerCommand("bot_kick"); + AddServerCommand("bot_knives_only"); + AddServerCommand("bot_pistols_only"); + AddServerCommand("bot_snipers_only"); + AddServerCommand("bot_all_weapons"); + AddServerCommand("entity_dump"); + AddServerCommand("bot_nav_delete"); + AddServerCommand("bot_nav_split"); + AddServerCommand("bot_nav_merge"); + AddServerCommand("bot_nav_mark"); + AddServerCommand("bot_nav_begin_area"); + AddServerCommand("bot_nav_end_area"); + AddServerCommand("bot_nav_connect"); + AddServerCommand("bot_nav_disconnect"); + AddServerCommand("bot_nav_splice"); + AddServerCommand("bot_nav_crouch"); + AddServerCommand("bot_nav_jump"); + AddServerCommand("bot_nav_precise"); + AddServerCommand("bot_nav_no_jump"); + AddServerCommand("bot_nav_analyze"); + AddServerCommand("bot_nav_strip"); + AddServerCommand("bot_nav_save"); + AddServerCommand("bot_nav_load"); + AddServerCommand("bot_nav_use_place"); + AddServerCommand("bot_nav_place_floodfill"); + AddServerCommand("bot_nav_place_pick"); + AddServerCommand("bot_nav_toggle_place_mode"); + AddServerCommand("bot_nav_toggle_place_painting"); + AddServerCommand("bot_goto_mark"); + AddServerCommand("bot_memory_usage"); + AddServerCommand("bot_nav_mark_unnamed"); + AddServerCommand("bot_nav_warp"); + AddServerCommand("bot_nav_corner_select"); + AddServerCommand("bot_nav_corner_raise"); + AddServerCommand("bot_nav_corner_lower"); + AddServerCommand("bot_nav_check_consistency"); +} + +void CCSBotManager::ServerDeactivate() +{ + m_bServerActive = false; +} + +void CCSBotManager::ClientDisconnect(CBasePlayer *pPlayer) +{ + if (!pPlayer || !pPlayer->IsBot()) + return; + + auto pevTemp = VARS(pPlayer->edict()); + + CCSBot *pBot = static_cast(pPlayer); + pBot->Disconnect(); + + if (!FStringNull(pPlayer->pev->classname)) + { + RemoveEntityHashValue(pPlayer->pev, STRING(pPlayer->pev->classname), CLASSNAME); + } + + FREE_PRIVATE(pPlayer->edict()); + + pPlayer = GetClassPtr((CBasePlayer *)pevTemp); + AddEntityHashValue(pPlayer->pev, STRING(pPlayer->pev->classname), CLASSNAME); + pPlayer->pev->flags = FL_DORMANT; +} + +void PrintAllEntities() +{ + for (int i = 1; i < gpGlobals->maxEntities; i++) + { + edict_t *edict = INDEXENT(i); + + if (!edict || FStringNull(edict->v.classname)) + continue; + + CONSOLE_ECHO(" %s\n", STRING(edict->v.classname)); + } +} + +void CCSBotManager::ServerCommand(const char *pcmd) +{ + if (!m_bServerActive || !AreBotsAllowed()) + return; + + char buffer[400]; + const char *msg = CMD_ARGV(1); + + if (FStrEq(pcmd, "bot_about")) + { + Q_snprintf(buffer, sizeof(buffer), + "\n--------------------------------------------------------------------------\n" + "The Official Counter-Strike Bot V%d.%02d\n" + "Created by Michael S. Booth\n" + "Web: www.turtlerockstudios.com\\csbot\n" + "E-mail: csbot@turtlerockstudios.com\n" + "--------------------------------------------------------------------------\n\n", BOT_VERSION_MAJOR, BOT_VERSION_MINOR); + + CONSOLE_ECHO(buffer); + HintMessageToAllPlayers(buffer); + } + else if (FStrEq(pcmd, "bot_add")) + { + BotAddCommand(BOT_TEAM_ANY, FROM_CONSOLE); + } + else if (FStrEq(pcmd, "bot_add_t")) + { + BotAddCommand(BOT_TEAM_T, FROM_CONSOLE); + } + else if (FStrEq(pcmd, "bot_add_ct")) + { + BotAddCommand(BOT_TEAM_CT, FROM_CONSOLE); + } + else if (FStrEq(pcmd, "bot_kill")) + { + bool killThemAll; + if (CMD_ARGC() == 1 || FStrEq(msg, "all")) + killThemAll = true; + else + killThemAll = false; + + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer) + continue; + + if (FNullEnt(pPlayer->pev)) + continue; + + const char *name = STRING(pPlayer->pev->netname); + if (FStrEq(name, "")) + continue; + + if (pPlayer->IsBot()) + { + if (killThemAll || FStrEq(name, msg)) + { +#ifdef REGAMEDLL_FIXES + ClientKill(pPlayer->edict()); +#else + pPlayer->TakeDamage(pPlayer->pev, pPlayer->pev, 9999.9f, DMG_CRUSH); +#endif + } + } + } + } + else if (FStrEq(pcmd, "bot_kick")) + { + bool kickThemAll; + if (CMD_ARGC() == 1 || FStrEq(msg, "all")) + kickThemAll = true; + else + kickThemAll = false; + + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer) + continue; + + if (FNullEnt(pPlayer->pev)) + continue; + + const char *name = STRING(pPlayer->pev->netname); + + if (FStrEq(name, "")) + continue; + + if (pPlayer->IsBot()) + { + if (kickThemAll || FStrEq(name, msg)) + { + // adjust bot quota so kicked bot is not immediately added back in + int newQuota = cv_bot_quota.value - 1; + SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", name)); + CVAR_SET_FLOAT("bot_quota", clamp(newQuota, 0, int(cv_bot_quota.value))); + } + } + } + + if (kickThemAll || cv_bot_quota.value < 0.0f) + { + CVAR_SET_FLOAT("bot_quota", 0); + } + } + else if (FStrEq(pcmd, "bot_knives_only")) + { + CVAR_SET_FLOAT("bot_allow_pistols", 0); + CVAR_SET_FLOAT("bot_allow_shotguns", 0); + CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_rifles", 0); + CVAR_SET_FLOAT("bot_allow_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_grenades", 0); + CVAR_SET_FLOAT("bot_allow_snipers", 0); + CVAR_SET_FLOAT("bot_allow_shield", 0); + } + else if (FStrEq(pcmd, "bot_pistols_only")) + { + CVAR_SET_FLOAT("bot_allow_pistols", 1); + CVAR_SET_FLOAT("bot_allow_shotguns", 0); + CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_rifles", 0); + CVAR_SET_FLOAT("bot_allow_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_grenades", 0); + CVAR_SET_FLOAT("bot_allow_snipers", 0); + CVAR_SET_FLOAT("bot_allow_shield", 0); + } + else if (FStrEq(pcmd, "bot_snipers_only")) + { + CVAR_SET_FLOAT("bot_allow_pistols", 1); + CVAR_SET_FLOAT("bot_allow_shotguns", 0); + CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_rifles", 0); + CVAR_SET_FLOAT("bot_allow_machine_guns", 0); + CVAR_SET_FLOAT("bot_allow_grenades", 0); + CVAR_SET_FLOAT("bot_allow_snipers", 1); + CVAR_SET_FLOAT("bot_allow_shield", 0); + } + else if (FStrEq(pcmd, "bot_all_weapons")) + { + CVAR_SET_FLOAT("bot_allow_pistols", 1); + CVAR_SET_FLOAT("bot_allow_shotguns", 1); + CVAR_SET_FLOAT("bot_allow_sub_machine_guns", 1); + CVAR_SET_FLOAT("bot_allow_rifles", 1); + CVAR_SET_FLOAT("bot_allow_machine_guns", 1); + CVAR_SET_FLOAT("bot_allow_grenades", 1); + CVAR_SET_FLOAT("bot_allow_snipers", 1); + CVAR_SET_FLOAT("bot_allow_shield", 1); + } + else if (FStrEq(pcmd, "entity_dump")) + { + PrintAllEntities(); + } + else if (FStrEq(pcmd, "bot_nav_delete")) + { + m_editCmd = EDIT_DELETE; + } + else if (FStrEq(pcmd, "bot_nav_split")) + { + m_editCmd = EDIT_SPLIT; + } + else if (FStrEq(pcmd, "bot_nav_merge")) + { + m_editCmd = EDIT_MERGE; + } + else if (FStrEq(pcmd, "bot_nav_mark")) + { + m_editCmd = EDIT_MARK; + } + else if (FStrEq(pcmd, "bot_nav_begin_area")) + { + m_editCmd = EDIT_BEGIN_AREA; + } + else if (FStrEq(pcmd, "bot_nav_end_area")) + { + m_editCmd = EDIT_END_AREA; + } + else if (FStrEq(pcmd, "bot_nav_connect")) + { + m_editCmd = EDIT_CONNECT; + } + else if (FStrEq(pcmd, "bot_nav_disconnect")) + { + m_editCmd = EDIT_DISCONNECT; + } + else if (FStrEq(pcmd, "bot_nav_splice")) + { + m_editCmd = EDIT_SPLICE; + } + else if (FStrEq(pcmd, "bot_nav_crouch")) + { + m_editCmd = EDIT_ATTRIB_CROUCH; + } + else if (FStrEq(pcmd, "bot_nav_jump")) + { + m_editCmd = EDIT_ATTRIB_JUMP; + } + else if (FStrEq(pcmd, "bot_nav_precise")) + { + m_editCmd = EDIT_ATTRIB_PRECISE; + } + else if (FStrEq(pcmd, "bot_nav_no_jump")) + { + m_editCmd = EDIT_ATTRIB_NO_JUMP; + } + else if (FStrEq(pcmd, "bot_nav_analyze")) + { + m_isAnalysisRequested = true; + } + else if (FStrEq(pcmd, "bot_nav_strip")) + { + StripNavigationAreas(); + } + else if (FStrEq(pcmd, "bot_nav_save")) + { + GET_GAME_DIR(buffer); + Q_strcat(buffer, "\\"); + Q_strcat(buffer, CBotManager::GetNavMapFilename()); + + if (SaveNavigationMap(buffer)) + CONSOLE_ECHO("Navigation map '%s' saved.\n", buffer); + else + CONSOLE_ECHO("ERROR: Cannot save navigation map '%s'.\n", buffer); + } + else if (FStrEq(pcmd, "bot_nav_load")) + { + ValidateMapData(); + } + else if (FStrEq(pcmd, "bot_nav_use_place")) + { + if (CMD_ARGC() == 1) + { + // no arguments = list all available places + int i = 0; + const BotPhraseList *placeList = TheBotPhrases->GetPlaceList(); + for (auto phrase : *placeList) + { + if (phrase->GetID() == GetNavPlace()) + CONSOLE_ECHO("--> %-26s", phrase->GetName()); + else + CONSOLE_ECHO("%-30s", phrase->GetName()); + + if (!(i % 3)) + CONSOLE_ECHO("\n"); + i++; + } + CONSOLE_ECHO("\n"); + } + else + { + // single argument = set current place + const BotPhraseList *placeList = TheBotPhrases->GetPlaceList(); + const BotPhrase *found = nullptr; + bool isAmbiguous = false; + for (auto phrase : *placeList) + { + if (!Q_strnicmp(phrase->GetName(), msg, Q_strlen(msg))) + { + // check for exact match in case of subsets of other strings + if (!Q_strcmp(phrase->GetName(), msg)) + { + found = phrase; + isAmbiguous = false; + break; + } + + if (found) + { + isAmbiguous = true; + } + else + { + found = phrase; + } + } + } + + if (isAmbiguous) + { + CONSOLE_ECHO("Ambiguous\n"); + } + else + { + CONSOLE_ECHO("Current place set to '%s'\n", found->GetName()); + SetNavPlace(found->GetID()); + } + } + } + else if (FStrEq(pcmd, "bot_nav_toggle_place_mode")) + { + m_editCmd = EDIT_TOGGLE_PLACE_MODE; + } + else if (FStrEq(pcmd, "bot_nav_place_floodfill")) + { + m_editCmd = EDIT_PLACE_FLOODFILL; + } + else if (FStrEq(pcmd, "bot_nav_place_pick")) + { + m_editCmd = EDIT_PLACE_PICK; + } + else if (FStrEq(pcmd, "bot_nav_toggle_place_painting")) + { + m_editCmd = EDIT_TOGGLE_PLACE_PAINTING; + } + else if (FStrEq(pcmd, "bot_goto_mark")) + { + // tell the first bot we find to go to our marked area + CNavArea *area = GetMarkedArea(); + if (area) + { + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) + { + if (!pEntity->IsPlayer()) + continue; + + if (pEntity->IsDormant()) + continue; + + CBasePlayer *playerOrBot = GetClassPtr((CBasePlayer *)pEntity->pev); + + if (playerOrBot->IsBot()) + { + CCSBot *pBot = static_cast(playerOrBot); + if (pBot) + { + pBot->MoveTo(&area->m_center, FASTEST_ROUTE); + } + + break; + } + } + } + } + else if (FStrEq(pcmd, "bot_memory_usage")) + { + CONSOLE_ECHO("Memory usage:\n"); + CONSOLE_ECHO(" %d bytes per bot\b", sizeof(CCSBot)); + CONSOLE_ECHO(" %d Navigation Areas @ %d bytes each = %d bytes\n", + TheNavAreaGrid.GetNavAreaCount(), + sizeof(CNavArea), + TheNavAreaGrid.GetNavAreaCount() * sizeof(CNavArea)); + CONSOLE_ECHO(" %d Hiding Spots @ %d bytes each = %d bytes\n", + TheHidingSpotList.size(), + sizeof(HidingSpot), + sizeof(HidingSpot) * TheHidingSpotList.size()); + + unsigned int encounterMem = 0; + for (auto area : TheNavAreaList) + { + for (auto se : area->m_spotEncounterList) + { + encounterMem += sizeof(SpotEncounter); + encounterMem += sizeof(SpotOrder) * se.spotList.size(); + } + } + + CONSOLE_ECHO(" Encounter Spot data = %d bytes\n", encounterMem); + } + else if (FStrEq(pcmd, "bot_nav_mark_unnamed")) + { + m_editCmd = EDIT_MARK_UNNAMED; + } + else if (FStrEq(pcmd, "bot_nav_warp")) + { + m_editCmd = EDIT_WARP_TO_MARK; + } + else if (FStrEq(pcmd, "bot_nav_corner_select")) + { + m_editCmd = EDIT_SELECT_CORNER; + } + else if (FStrEq(pcmd, "bot_nav_corner_raise")) + { + m_editCmd = EDIT_RAISE_CORNER; + } + else if (FStrEq(pcmd, "bot_nav_corner_lower")) + { + m_editCmd = EDIT_LOWER_CORNER; + } + else if (FStrEq(pcmd, "bot_nav_check_consistency")) + { + if (CMD_ARGC() != 2) + { + CONSOLE_ECHO("usage: bot_nav_check_consistency \n"); + return; + } + + SanityCheckNavigationMap(msg); + } +} + +BOOL CCSBotManager::ClientCommand(CBasePlayer *pPlayer, const char *pcmd) +{ + return FALSE; +} + +// Process the "bot_add" console command +bool CCSBotManager::BotAddCommand(BotProfileTeamType team, bool isFromConsole) +{ + // dont allow bots to join if the Navigation Area is being generated + if (m_isLearningMap) + return false; + + const BotProfile *profile = nullptr; + if (!isFromConsole || CMD_ARGC() < 2) + { + // if team not specified, check cv_bot_join_team cvar for preference + if (team == BOT_TEAM_ANY) + { + if (!Q_stricmp(cv_bot_join_team.string, "T")) + team = BOT_TEAM_T; + + else if (!Q_stricmp(cv_bot_join_team.string, "CT")) + team = BOT_TEAM_CT; + else + { + TeamName defaultTeam = CSGameRules()->SelectDefaultTeam(); + if (defaultTeam == TERRORIST) + team = BOT_TEAM_T; + + else if (defaultTeam == CT) + team = BOT_TEAM_CT; + } + } + + // try to add a bot by name + profile = TheBotProfiles->GetRandomProfile(GetDifficultyLevel(), team); + if (!profile) + { + CONSOLE_ECHO("All bot profiles at this difficulty level are in use.\n"); + return true; + } + } + else + { + // in career, ignore humans + bool ignoreHumans = false; + if (CSGameRules() && CSGameRules()->IsCareer()) + ignoreHumans = true; + + if (UTIL_IsNameTaken(CMD_ARGV(1), ignoreHumans)) + { + CONSOLE_ECHO("Error - %s is already in the game.\n", CMD_ARGV(1)); + return true; + } + + profile = TheBotProfiles->GetProfile(CMD_ARGV(1), team); + if (!profile) + { + CONSOLE_ECHO("Error - no profile for '%s' exists.\n", CMD_ARGV(1)); + return true; + } + } + + // create the bot + if (AddBot(profile, team)) + { + if (isFromConsole) + { + // increase the bot quota to account for manually added bot + CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value + 1); + } + } +#ifdef REGAMEDLL_FIXES + else + { + // decrease the bot quota + CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1); + } +#endif + + return true; +} + +// Keep a minimum quota of bots in the game +void CCSBotManager::MaintainBotQuota() +{ +#ifdef REGAMEDLL_FIXES + if (!AreBotsAllowed()) + return; +#endif + + if (m_isLearningMap) + return; + + int totalHumansInGame = UTIL_HumansInGame(); + int humanPlayersInGame = UTIL_HumansInGame(IGNORE_SPECTATORS); + + // don't add bots until local player has been registered, to make sure he's player ID #1 + if (!IS_DEDICATED_SERVER() && totalHumansInGame == 0) + return; + + int desiredBotCount = int(cv_bot_quota.value); + int occupiedBotSlots = UTIL_BotsInGame(); + + // isRoundInProgress is true if the round has progressed far enough that new players will join as dead. + bool isRoundInProgress = CSGameRules()->IsGameStarted() && + !TheCSBots()->IsRoundOver() && + (CSGameRules()->GetRoundElapsedTime() >= CSGameRules()->GetRoundRespawnTime()); + +#ifdef REGAMEDLL_ADD + if (FStrEq(cv_bot_quota_mode.string, "fill")) + { + // If bot_quota_mode is 'fill', we want the number of bots and humans together to equal bot_quota + // unless the round is already in progress, in which case we play with what we've been dealt + if (!isRoundInProgress) + { + desiredBotCount = Q_max(0, desiredBotCount - humanPlayersInGame); + } + else + { + desiredBotCount = occupiedBotSlots; + } + } + else if (FStrEq(cv_bot_quota_mode.string, "match")) + { + // If bot_quota_mode is 'match', we want the number of bots to be bot_quota * total humans + // unless the round is already in progress, in which case we play with what we've been dealt + if (!isRoundInProgress) + { + desiredBotCount = Q_max(0, cv_bot_quota.value * humanPlayersInGame); + } + else + { + desiredBotCount = occupiedBotSlots; + } + } +#else // #ifdef REGAMEDLL_ADD + if (cv_bot_quota_match.value > 0.0) + { + desiredBotCount = int(humanPlayersInGame * cv_bot_quota_match.value); + } +#endif // #ifdef REGAMEDLL_ADD + + // wait for a player to join, if necessary + if (cv_bot_join_after_player.value > 0.0) + { + if (humanPlayersInGame == 0) + desiredBotCount = 0; + } + +#ifdef REGAMEDLL_ADD + // wait until the map has been loaded for a bit, to allow players to transition across + // the transition without missing the pistol round + if (static_cast(cv_bot_join_delay.value) > CSGameRules()->GetMapElapsedTime()) + { + desiredBotCount = 0; + } +#endif + + // if bots will auto-vacate, we need to keep one slot open to allow players to join + if (cv_bot_auto_vacate.value > 0.0) + desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - (totalHumansInGame + 1)); + else + desiredBotCount = Q_min(desiredBotCount, gpGlobals->maxClients - totalHumansInGame); + +#ifdef REGAMEDLL_FIXES + // Try to balance teams, if we are in the first specified seconds of a round and bots can join either team. + if (occupiedBotSlots > 0 && desiredBotCount == occupiedBotSlots && CSGameRules()->IsGameStarted()) + { + if (CSGameRules()->GetRoundElapsedTime() < CSGameRules()->GetRoundRespawnTime()) // new bots can still spawn during this time + { + if (autoteambalance.value > 0.0f) + { + int numAliveTerrorist; + int numAliveCT; + int numDeadTerrorist; + int numDeadCT; + + CSGameRules()->InitializePlayerCounts(numAliveTerrorist, numAliveCT, numDeadTerrorist, numDeadCT); + + if (!FStrEq(cv_bot_join_team.string, "T") && + !FStrEq(cv_bot_join_team.string, "CT")) + { + if (numAliveTerrorist > CSGameRules()->m_iNumCT + 1) + { + if (UTIL_KickBotFromTeam(TERRORIST)) + return; + } + else if (numAliveCT > CSGameRules()->m_iNumTerrorist + 1) + { + if (UTIL_KickBotFromTeam(CT)) + return; + } + } + } + } + } +#endif // #ifdef REGAMEDLL_FIXES + + // add bots if necessary + if (desiredBotCount > occupiedBotSlots) + { + // don't try to add a bot if all teams are full + if (!CSGameRules()->TeamFull(TERRORIST) || !CSGameRules()->TeamFull(CT)) + { +#ifndef REGAMEDLL_FIXES + if (AreBotsAllowed()) +#endif + { + BotAddCommand(BOT_TEAM_ANY); + } + } + } + else if (desiredBotCount < occupiedBotSlots) + { + // kick a bot to maintain quota + + // first remove any unassigned bots + if (UTIL_KickBotFromTeam(UNASSIGNED)) + return; + + TeamName kickTeam; + + // remove from the team that has more players + if (CSGameRules()->m_iNumTerrorist > CSGameRules()->m_iNumCT) + { + kickTeam = TERRORIST; + } + else if (CSGameRules()->m_iNumTerrorist < CSGameRules()->m_iNumCT) + { + kickTeam = CT; + } + // remove from the team that's winning + else if (CSGameRules()->m_iNumTerroristWins > CSGameRules()->m_iNumCTWins) + { + kickTeam = TERRORIST; + } + else if (CSGameRules()->m_iNumCTWins > CSGameRules()->m_iNumTerroristWins) + { + kickTeam = CT; + } + else + { + // teams and scores are equal, pick a team at random + kickTeam = (RANDOM_LONG(0, 1) == 0) ? CT : TERRORIST; + } + + // attempt to kick a bot from the given team + bool atLeastOneKicked = UTIL_KickBotFromTeam(kickTeam); + + if (!atLeastOneKicked) + { + // if there were no bots on the team, kick a bot from the other team + if (kickTeam == TERRORIST) + atLeastOneKicked = UTIL_KickBotFromTeam(CT); + else + atLeastOneKicked = UTIL_KickBotFromTeam(TERRORIST); + } + + if (atLeastOneKicked) + { + CONSOLE_ECHO("These bots kicked to maintain quota.\n"); + } + } + else + { + if (CSGameRules() && !CSGameRules()->IsCareer()) + return; + + bool humansAreCTs = (Q_strcmp(humans_join_team.string, "CT") == 0); + + if (humansAreCTs) + { + if (CSGameRules()->m_iNumCT <= 6) + return; + + UTIL_KickBotFromTeam(CT); + } + else + { + if (CSGameRules()->m_iNumTerrorist <= 6) + return; + + UTIL_KickBotFromTeam(TERRORIST); + } + + CVAR_SET_FLOAT("bot_quota", cv_bot_quota.value - 1.0f); + } +} + +void CCSBotManager::MonitorBotCVars() +{ + if (cv_bot_nav_edit.value != 0.0f) + { + EditNavAreas(m_editCmd); + m_editCmd = EDIT_NONE; + } + + if (gpGlobals->time >= m_flNextCVarCheck) + { + if (cv_bot_show_danger.value != 0.0f) + DrawDanger(); + + MaintainBotQuota(); + m_flNextCVarCheck = gpGlobals->time + 0.3f; + } +} + +// Collect all nav areas that overlap the given zone +class CollectOverlappingAreas +{ +public: + CollectOverlappingAreas(CCSBotManager::Zone *zone) + { + m_zone = zone; + zone->m_areaCount = 0; + } + bool operator()(CNavArea *area) + { + const Extent *areaExtent = area->GetExtent(); + + if (areaExtent->hi.x >= m_zone->m_extent.lo.x && areaExtent->lo.x <= m_zone->m_extent.hi.x + && areaExtent->hi.y >= m_zone->m_extent.lo.y && areaExtent->lo.y <= m_zone->m_extent.hi.y + && areaExtent->hi.z >= m_zone->m_extent.lo.z && areaExtent->lo.z <= m_zone->m_extent.hi.z) + { + // area overlaps m_zone + m_zone->m_area[m_zone->m_areaCount++] = area; + if (m_zone->m_areaCount == CCSBotManager::MAX_ZONE_NAV_AREAS) + { + return false; + } + } + + return true; + } + +private: + CCSBotManager::Zone *m_zone; +}; + +// Search the map entities to determine the game scenario and define important zones. +void CCSBotManager::ValidateMapData() +{ + if (m_isMapDataLoaded || !AreBotsAllowed()) + return; + + m_isMapDataLoaded = true; + + if (LoadNavigationMap()) + { + CONSOLE_ECHO("Failed to load navigation map.\n"); + return; + } + + CONSOLE_ECHO("Navigation map loaded.\n"); + + m_zoneCount = 0; + m_gameScenario = SCENARIO_DEATHMATCH; + + // Search all entities in the map and set the game type and store all zones (bomb target, etc). + CBaseEntity *pEntity = nullptr; + for (int i = 1; i < gpGlobals->maxEntities; i++) + { + pEntity = CBaseEntity::Instance(INDEXENT(i)); + if (!pEntity) + continue; + + bool found = false; + bool isLegacy = false; + + if (FClassnameIs(pEntity->pev, "func_bomb_target")) + { + m_gameScenario = SCENARIO_DEFUSE_BOMB; + found = true; + isLegacy = false; + } + else if (FClassnameIs(pEntity->pev, "info_bomb_target")) + { + m_gameScenario = SCENARIO_DEFUSE_BOMB; + found = true; + isLegacy = true; + } + else if (FClassnameIs(pEntity->pev, "func_hostage_rescue")) + { + m_gameScenario = SCENARIO_RESCUE_HOSTAGES; + found = true; + isLegacy = false; + } + else if (FClassnameIs(pEntity->pev, "info_hostage_rescue")) + { + m_gameScenario = SCENARIO_RESCUE_HOSTAGES; + found = true; + isLegacy = true; + } + else if (FClassnameIs(pEntity->pev, "hostage_entity")) + { + // some very old maps (ie: cs_assault) use info_player_start + // as rescue zones, so set the scenario if there are hostages + // in the map + m_gameScenario = SCENARIO_RESCUE_HOSTAGES; + } + else if (FClassnameIs(pEntity->pev, "func_vip_safetyzone")) + { + m_gameScenario = SCENARIO_ESCORT_VIP; + found = true; + isLegacy = false; + } + + if (found) + { + if (m_zoneCount < MAX_ZONES) + { + m_zone[m_zoneCount].m_center = isLegacy ? pEntity->pev->origin : (pEntity->pev->absmax + pEntity->pev->absmin) / 2.0f; + m_zone[m_zoneCount].m_isLegacy = isLegacy; + m_zone[m_zoneCount].m_index = m_zoneCount; + m_zone[m_zoneCount].m_entity = pEntity; + m_zoneCount++; + } + else + { + CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n"); + } + } + } + + // If there are no zones and the scenario is hostage rescue, + // use the info_player_start entities as rescue zones. + if (m_zoneCount == 0 && m_gameScenario == SCENARIO_RESCUE_HOSTAGES) + { + pEntity = nullptr; + + while ((pEntity = UTIL_FindEntityByClassname(pEntity, "info_player_start"))) + { +#ifdef REGAMEDLL_FIXES + if (m_zoneCount >= MAX_ZONES) + break; +#endif + + if (FNullEnt(pEntity->edict())) + break; + + if (m_zoneCount < MAX_ZONES) + { + m_zone[m_zoneCount].m_center = pEntity->pev->origin; + m_zone[m_zoneCount].m_isLegacy = true; + m_zone[m_zoneCount].m_index = m_zoneCount; + m_zone[m_zoneCount].m_entity = pEntity; + m_zoneCount++; + } + else + { + CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n"); + } + } + } + + // Collect nav areas that overlap each zone + for (int i = 0; i < m_zoneCount; i++) + { + Zone *zone = &m_zone[i]; + + if (zone->m_isLegacy) + { + const float legacyRange = 256.0f; + zone->m_extent.lo.x = zone->m_center.x - legacyRange; + zone->m_extent.lo.y = zone->m_center.y - legacyRange; + zone->m_extent.lo.z = zone->m_center.z - legacyRange; + zone->m_extent.hi.x = zone->m_center.x + legacyRange; + zone->m_extent.hi.y = zone->m_center.y + legacyRange; + zone->m_extent.hi.z = zone->m_center.z + legacyRange; + } + else + { + zone->m_extent.lo = zone->m_entity->pev->absmin; + zone->m_extent.hi = zone->m_entity->pev->absmax; + } + + // ensure Z overlap + const float zFudge = 50.0f; + zone->m_extent.lo.z -= zFudge; + zone->m_extent.hi.z += zFudge; + + // build a list of nav areas that overlap this zone + CollectOverlappingAreas collector(zone); + ForAllAreas(collector); + } +} + +bool CCSBotManager::AddBot(const BotProfile *profile, BotProfileTeamType team) +{ + if (!AreBotsAllowed()) + return false; + + int nTeamSlot = UNASSIGNED; + if (team == BOT_TEAM_ANY) + { + // if team not specified, check cv_bot_join_team cvar for preference + if (!Q_stricmp(cv_bot_join_team.string, "T")) + nTeamSlot = TERRORIST; + + else if (!Q_stricmp(cv_bot_join_team.string, "CT")) + nTeamSlot = CT; + } + else if (team == BOT_TEAM_CT) + nTeamSlot = CT; + + else if (team == BOT_TEAM_T) + nTeamSlot = TERRORIST; + + if (nTeamSlot == UNASSIGNED) + { + nTeamSlot = CSGameRules()->SelectDefaultTeam(); + } + + if (nTeamSlot == UNASSIGNED || CSGameRules()->TeamFull(nTeamSlot)) + { + CONSOLE_ECHO("Could not add bot to the game: Team is full\n"); + return false; + } + + if (CSGameRules()->TeamStacked(nTeamSlot, UNASSIGNED)) + { + CONSOLE_ECHO("Could not add bot to the game: Team is stacked (to disable this check, set mp_limitteams and mp_autoteambalance to zero and restart the round).\n"); + return false; + } + + CCSBot *pBot = CreateBot(profile); + if (!pBot) + { + return false; + } + + //int nJoinedTeam; + ClientPutInServer(pBot->edict()); + SET_CLIENT_KEY_VALUE(pBot->entindex(), GET_INFO_BUFFER(pBot->edict()), "*bot", "1"); + + pBot->m_iMenu = Menu_ChooseTeam; + pBot->m_iJoiningState = PICKINGTEAM; + + if (HandleMenu_ChooseTeam(pBot, nTeamSlot)) + { + int skin = profile->GetSkin(); + if (!skin) + skin = 6; + + HandleMenu_ChooseAppearance(pBot, skin); + + if (IS_DEDICATED_SERVER()) + { + UTIL_DPrintf("Added bot %s to server\n", STRING(pBot->pev->netname)); + } + + return true; + } + + SERVER_COMMAND(UTIL_VarArgs("kick \"%s\"\n", STRING(pBot->pev->netname))); + CONSOLE_ECHO("Could not add bot to the game.\n"); + return false; +} + +// Return the zone that contains the given position +const CCSBotManager::Zone *CCSBotManager::GetZone(const Vector *pos) const +{ + for (int z = 0; z < m_zoneCount; z++) + { + if (m_zone[z].m_extent.Contains(pos)) + { + return &m_zone[z]; + } + } + + return nullptr; +} + +// Return the closest zone to the given position +const CCSBotManager::Zone *CCSBotManager::GetClosestZone(const Vector *pos) const +{ + const Zone *close = nullptr; + float closeRangeSq = 1e9f; + + for (int z = 0; z < m_zoneCount; z++) + { + float rangeSq = (m_zone[z].m_center - (*pos)).LengthSquared(); + + if (rangeSq < closeRangeSq) + { + closeRangeSq = rangeSq; + close = &m_zone[z]; + } + } + + return close; +} + +// Return a random position inside the given zone +const Vector *CCSBotManager::GetRandomPositionInZone(const Zone *zone) const +{ + static Vector pos; + + if (!zone) + return nullptr; + + if (zone->m_areaCount == 0) + return nullptr; + + // pick a random overlapping area + CNavArea *area = GetRandomAreaInZone(zone); + + // pick a location inside both the nav area and the zone + // TODO: Randomize this + if (zone->m_isLegacy) + { + // TODO: It is possible that the radius might not overlap this area at all... + area->GetClosestPointOnArea(&zone->m_center, &pos); + } + else + { + const Extent &areaExtent = *area->GetExtent(); + Extent overlap; + overlap.lo.x = Q_max(areaExtent.lo.x, zone->m_extent.lo.x); + overlap.lo.y = Q_max(areaExtent.lo.y, zone->m_extent.lo.y); + overlap.hi.x = Q_min(areaExtent.hi.x, zone->m_extent.hi.x); + overlap.hi.y = Q_min(areaExtent.hi.y, zone->m_extent.hi.y); + + pos.x = (overlap.lo.x + overlap.hi.x) / 2.0f; + pos.y = (overlap.lo.y + overlap.hi.y) / 2.0f; + pos.z = area->GetZ(&pos); + } + + return &pos; +} + +// Return a random area inside the given zone +CNavArea *CCSBotManager::GetRandomAreaInZone(const Zone *zone) const +{ + // TODO: improvement is needed + if (!zone->m_areaCount) + return nullptr; + + return zone->m_area[RANDOM_LONG(0, zone->m_areaCount - 1)]; +} + +void CCSBotManager::OnEvent(GameEventType event, CBaseEntity *pEntity, CBaseEntity *pOther) +{ + switch (event) + { + case EVENT_BOMB_PLANTED: + m_isBombPlanted = true; + m_bombPlantTimestamp = gpGlobals->time; + break; + + case EVENT_BOMB_DEFUSING: + m_bombDefuser = static_cast(pEntity); + break; + + case EVENT_BOMB_DEFUSE_ABORTED: + m_bombDefuser = nullptr; + break; + + case EVENT_BOMB_DEFUSED: + m_isBombPlanted = false; + m_bombDefuser = nullptr; + break; + + case EVENT_TERRORISTS_WIN: + case EVENT_CTS_WIN: + case EVENT_ROUND_DRAW: + m_isRoundOver = true; + break; + + case EVENT_RADIO_ENEMY_SPOTTED: + m_lastSeenEnemyTimestamp = gpGlobals->time; + SetLastSeenEnemyTimestamp(); + break; + + default: + break; + } + + CBotManager::OnEvent(event, pEntity, pOther); +} + +// Get the time remaining before the planted bomb explodes +float CCSBotManager::GetBombTimeLeft() const +{ + return (CSGameRules()->m_iC4Timer - (gpGlobals->time - m_bombPlantTimestamp)); +} + +void CCSBotManager::SetLooseBomb(CBaseEntity *bomb) +{ + m_looseBomb = bomb; + + if (bomb) + { + m_looseBombArea = TheNavAreaGrid.GetNearestNavArea(&bomb->pev->origin); + } + else + { + m_looseBombArea = nullptr; + } +} + +// Return true if player is important to scenario (VIP, bomb carrier, etc) +bool CCSBotManager::IsImportantPlayer(CBasePlayer *pPlayer) const +{ + switch (GetScenario()) + { + case SCENARIO_DEFUSE_BOMB: + { + if (pPlayer->m_iTeam == TERRORIST && pPlayer->IsBombGuy()) + return true; + + // TODO: TEAM_CT's defusing the bomb are important + return false; + } + case SCENARIO_ESCORT_VIP: + { + if (pPlayer->m_iTeam == CT && pPlayer->m_bIsVIP) + return true; + + return false; + } + case SCENARIO_RESCUE_HOSTAGES: + { + // TODO: TEAM_CT's escorting hostages are important + return false; + } + } + + // everyone is equally important in a deathmatch + return false; +} + +// Return priority of player (0 = max pri) +unsigned int CCSBotManager::GetPlayerPriority(CBasePlayer *pPlayer) const +{ + const unsigned int lowestPriority = 0xFFFFFFFF; + + if (!pPlayer->IsPlayer()) + return lowestPriority; + + // human players have highest priority + if (!pPlayer->IsBot()) + return 0; + + CCSBot *pBot = static_cast(pPlayer); + + // bots doing something important for the current scenario have high priority + switch (GetScenario()) + { + case SCENARIO_DEFUSE_BOMB: + { + // the bomb carrier has high priority + if (pBot->m_iTeam == TERRORIST && pBot->m_bHasC4) + return 1; + + break; + } + case SCENARIO_ESCORT_VIP: + { + // the VIP has high priority + if (pBot->m_iTeam == CT && pBot->m_bIsVIP) + return 1; + + break; + } + case SCENARIO_RESCUE_HOSTAGES: + { + // TEAM_CT's rescuing hostages have high priority + if (pBot->m_iTeam == CT && pBot->GetHostageEscortCount()) + return 1; + + break; + } + } + + // everyone else is ranked by their unique ID (which cannot be zero) + return 1 + pBot->GetID(); +} + +// Return the last time the given radio message was sent for given team +// 'teamID' can be TEAM_CT or TEAM_TERRORIST +float CCSBotManager::GetRadioMessageTimestamp(GameEventType event, int teamID) const +{ + if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) + return 0.0f; + + int i = (teamID == TERRORIST) ? 0 : 1; + return m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i]; +} + +// Return the interval since the last time this message was sent +float CCSBotManager::GetRadioMessageInterval(GameEventType event, int teamID) const +{ + if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) + return 99999999.9f; + + int i = (teamID == TERRORIST) ? 0 : 1; + return gpGlobals->time - m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i]; +} + +// Set the given radio message timestamp. +// 'teamID' can be TEAM_CT or TEAM_TERRORIST +void CCSBotManager::SetRadioMessageTimestamp(GameEventType event, int teamID) +{ + if (event <= EVENT_START_RADIO_1 || event >= EVENT_END_RADIO) + return; + + int i = (teamID == TERRORIST) ? 0 : 1; + m_radioMsgTimestamp[event - EVENT_START_RADIO_1][i] = gpGlobals->time; +} + +// Reset all radio message timestamps +void CCSBotManager::ResetRadioMessageTimestamps() +{ + Q_memset(m_radioMsgTimestamp, 0, sizeof(m_radioMsgTimestamp)); +} + +void CCSBotManager::OnFreeEntPrivateData(CBaseEntity *pEntity) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer || pPlayer->IsDormant()) + continue; + + if (pPlayer->IsBot()) + { + CCSBot *pBot = static_cast(pPlayer); + if (pBot->m_attacker == pEntity) + pBot->m_attacker = nullptr; + + if (pBot->m_bomber == pEntity) + pBot->m_bomber = nullptr; + } + } +} diff --git a/regamedll/dlls/bot/cs_bot_manager.h b/regamedll/dlls/bot/cs_bot_manager.h index 132d1596..1faea546 100644 --- a/regamedll/dlls/bot/cs_bot_manager.h +++ b/regamedll/dlls/bot/cs_bot_manager.h @@ -1,270 +1,270 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#include "cs_bot_init.h" - -extern CBotManager *TheBots; - -// The manager for Counter-Strike specific bots -class CCSBotManager: public CBotManager -{ -public: - CCSBotManager(); - - virtual void ClientDisconnect(CBasePlayer *pPlayer); - virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd); - - virtual void ServerActivate(); - virtual void ServerDeactivate(); - - virtual void ServerCommand(const char *pcmd); - virtual void AddServerCommand(const char *cmd); - virtual void AddServerCommands(); - - virtual void RestartRound(); // (EXTEND) invoked when a new round begins - virtual void StartFrame(); // (EXTEND) called each frame - - virtual void OnEvent(GameEventType event, CBaseEntity *pEntity = nullptr, CBaseEntity *pOther = nullptr); - virtual unsigned int GetPlayerPriority(CBasePlayer *pPlayer) const; // return priority of pPlayer (0 = max pri) - virtual bool IsImportantPlayer(CBasePlayer *pPlayer) const; // return true if pPlayer is important to scenario (VIP, bomb carrier, etc) - -public: - void ValidateMapData(); - void OnFreeEntPrivateData(CBaseEntity *pEntity); - bool IsLearningMap() const { return m_isLearningMap; } - void SetLearningMapFlag() { m_isLearningMap = true; } - bool IsAnalysisRequested() const { return m_isAnalysisRequested; } - void RequestAnalysis() { m_isAnalysisRequested = true; } - void AckAnalysisRequest() { m_isAnalysisRequested = false; } - - // difficulty levels - static BotDifficultyType GetDifficultyLevel() - { - if (cv_bot_difficulty.value < 0.9f) - return BOT_EASY; - - if (cv_bot_difficulty.value < 1.9f) - return BOT_NORMAL; - - if (cv_bot_difficulty.value < 2.9f) - return BOT_HARD; - - return BOT_EXPERT; - } - - // the supported game scenarios - enum GameScenarioType - { - SCENARIO_DEATHMATCH, - SCENARIO_DEFUSE_BOMB, - SCENARIO_RESCUE_HOSTAGES, - SCENARIO_ESCORT_VIP - }; - - GameScenarioType GetScenario() const - { -#ifdef REGAMEDLL_ADD - // if we have included deathmatch mode, so set the game type like SCENARIO_DEATHMATCH - if (cv_bot_deathmatch.value > 0) - return SCENARIO_DEATHMATCH; -#endif - - return m_gameScenario; - } - - // "zones" - // depending on the game mode, these are bomb zones, rescue zones, etc. - enum { MAX_ZONES = 4 }; // max # of zones in a map - enum { MAX_ZONE_NAV_AREAS = 16 }; // max # of nav areas in a zone - struct Zone - { - CBaseEntity *m_entity; // the map entity - CNavArea *m_area[MAX_ZONE_NAV_AREAS]; // nav areas that overlap this zone - int m_areaCount; - Vector m_center; - bool m_isLegacy; // if true, use pev->origin and 256 unit radius as zone - int m_index; - Extent m_extent; - }; - - const Zone *GetZone(int i) const { return &m_zone[i]; } - const Zone *GetZone(const Vector *pos) const; // return the zone that contains the given position - const Zone *GetClosestZone(const Vector *pos) const; // return the closest zone to the given position - const Zone *GetClosestZone(const CBaseEntity *pEntity) const { return GetClosestZone(&pEntity->pev->origin); } // return the closest zone to the given entity - int GetZoneCount() const { return m_zoneCount; } - - const Vector *GetRandomPositionInZone(const Zone *zone) const; - CNavArea *GetRandomAreaInZone(const Zone *zone) const; - - // Return the zone closest to the given position, using the given cost heuristic - template - const Zone *GetClosestZone(CNavArea *startArea, CostFunctor costFunc, float *travelDistance = nullptr) const - { - const Zone *closeZone = nullptr; - float closeDist = 99999999.9f; - - if (startArea == nullptr) - return nullptr; - - for (int i = 0; i < m_zoneCount; i++) - { - if (m_zone[i].m_areaCount == 0) - continue; - - // just use the first overlapping nav area as a reasonable approximation - real_t dist = NavAreaTravelDistance(startArea, m_zone[i].m_area[0], costFunc); - - if (dist >= 0.0f && dist < closeDist) - { - closeZone = &m_zone[i]; - closeDist = dist; - } - } - - if (travelDistance) - *travelDistance = closeDist; - - return closeZone; - } - - // pick a zone at random and return it - const Zone *GetRandomZone() const - { - if (!m_zoneCount) - return nullptr; - - return &m_zone[RANDOM_LONG(0, m_zoneCount - 1)]; - } - - bool IsBombPlanted() const { return m_isBombPlanted; } // returns true if bomb has been planted - float GetBombPlantTimestamp() const { return m_bombPlantTimestamp; } // return time bomb was planted - bool IsTimeToPlantBomb() const { return (gpGlobals->time >= m_earliestBombPlantTimestamp); } // return true if it's ok to try to plant bomb - CBasePlayer *GetBombDefuser() const { return m_bombDefuser; } // return the player currently defusing the bomb, or NULL - float GetBombTimeLeft() const; // get the time remaining before the planted bomb explodes - CBaseEntity *GetLooseBomb() { return m_looseBomb; } // return the bomb if it is loose on the ground - CNavArea *GetLooseBombArea() const { return m_looseBombArea; } // return area that bomb is in/near - void SetLooseBomb(CBaseEntity *bomb); - - float GetRadioMessageTimestamp(GameEventType event, int teamID) const; // return the last time the given radio message was sent for given team - float GetRadioMessageInterval(GameEventType event, int teamID) const; // return the interval since the last time this message was sent - void SetRadioMessageTimestamp(GameEventType event, int teamID); - void ResetRadioMessageTimestamps(); - - float GetLastSeenEnemyTimestamp() const { return m_lastSeenEnemyTimestamp; } // return the last time anyone has seen an enemy - void SetLastSeenEnemyTimestamp() { m_lastSeenEnemyTimestamp = gpGlobals->time; } - - float GetRoundStartTime() const { return m_roundStartTimestamp; } - float GetElapsedRoundTime() const { return gpGlobals->time - m_roundStartTimestamp; } // return the elapsed time since the current round began - - bool AllowRogues() const { return cv_bot_allow_rogues.value != 0.0f; } - bool AllowPistols() const { return cv_bot_allow_pistols.value != 0.0f; } - bool AllowShotguns() const { return cv_bot_allow_shotguns.value != 0.0f; } - bool AllowSubMachineGuns() const { return cv_bot_allow_sub_machine_guns.value != 0.0f; } - bool AllowRifles() const { return cv_bot_allow_rifles.value != 0.0f; } - bool AllowMachineGuns() const { return cv_bot_allow_machine_guns.value != 0.0f; } - bool AllowGrenades() const { return cv_bot_allow_grenades.value != 0.0f; } - bool AllowSnipers() const { return cv_bot_allow_snipers.value != 0.0f; } - bool AllowTacticalShield() const { return cv_bot_allow_shield.value != 0.0f; } - bool AllowFriendlyFireDamage() const { return friendlyfire.value != 0.0f; } - - bool IsWeaponUseable(CBasePlayerItem *item) const; // return true if the bot can use this weapon - bool IsWeaponUseable(ArmouryItemPack item) const; - - bool IsDefenseRushing() const { return m_isDefenseRushing; } // returns true if defense team has "decided" to rush this round - bool IsOnDefense(CBasePlayer *pPlayer) const; // return true if this player is on "defense" - bool IsOnOffense(CBasePlayer *pPlayer) const; // return true if this player is on "offense" - - bool IsRoundOver() const { return m_isRoundOver; } // return true if the round has ended - - unsigned int GetNavPlace() const { return m_navPlace; } - void SetNavPlace(unsigned int place) { m_navPlace = place; } - - enum SkillType { LOW, AVERAGE, HIGH, RANDOM }; - const char *GetRandomBotName(SkillType skill); - - void MonitorBotCVars(); - void MaintainBotQuota(); - bool AddBot(const BotProfile *profile, BotProfileTeamType team); - - #define FROM_CONSOLE true - bool BotAddCommand(BotProfileTeamType team, bool isFromConsole = false); // process the "bot_add" console command - -private: - static float m_flNextCVarCheck; - static bool m_isMapDataLoaded; // true if we've attempted to load map data - static bool m_isLearningMap; - static bool m_isAnalysisRequested; - - GameScenarioType m_gameScenario; // what kind of game are we playing - - Zone m_zone[MAX_ZONES]; - int m_zoneCount; - - bool m_isBombPlanted; // true if bomb has been planted - float m_bombPlantTimestamp; // time bomb was planted - float m_earliestBombPlantTimestamp; // don't allow planting until after this time has elapsed - CBasePlayer *m_bombDefuser; // the player currently defusing a bomb - EHANDLE m_looseBomb; // will be non-NULL if bomb is loose on the ground - CNavArea *m_looseBombArea; // area that bomb is is/near - - bool m_isRoundOver; // true if the round has ended - - float m_radioMsgTimestamp[24][2]; - - float m_lastSeenEnemyTimestamp; - float m_roundStartTimestamp; // the time when the current round began - - bool m_isDefenseRushing; // whether defensive team is rushing this round or not - - static NavEditCmdType m_editCmd; - unsigned int m_navPlace; - CountdownTimer m_respawnTimer; - bool m_isRespawnStarted; - bool m_canRespawn; - bool m_bServerActive; -}; - -inline int OtherTeam(int team) -{ - return (team == TERRORIST) ? CT : TERRORIST; -} - -inline CCSBotManager *TheCSBots() -{ - return reinterpret_cast(TheBots); -} - -// Determine whether bots can be used or not -inline bool AreBotsAllowed() -{ - return g_bAllowedCSBot; -} - -void PrintAllEntities(); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "cs_bot_init.h" + +extern CBotManager *TheBots; + +// The manager for Counter-Strike specific bots +class CCSBotManager: public CBotManager +{ +public: + CCSBotManager(); + + virtual void ClientDisconnect(CBasePlayer *pPlayer); + virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd); + + virtual void ServerActivate(); + virtual void ServerDeactivate(); + + virtual void ServerCommand(const char *pcmd); + virtual void AddServerCommand(const char *cmd); + virtual void AddServerCommands(); + + virtual void RestartRound(); // (EXTEND) invoked when a new round begins + virtual void StartFrame(); // (EXTEND) called each frame + + virtual void OnEvent(GameEventType event, CBaseEntity *pEntity = nullptr, CBaseEntity *pOther = nullptr); + virtual unsigned int GetPlayerPriority(CBasePlayer *pPlayer) const; // return priority of pPlayer (0 = max pri) + virtual bool IsImportantPlayer(CBasePlayer *pPlayer) const; // return true if pPlayer is important to scenario (VIP, bomb carrier, etc) + +public: + void ValidateMapData(); + void OnFreeEntPrivateData(CBaseEntity *pEntity); + bool IsLearningMap() const { return m_isLearningMap; } + void SetLearningMapFlag() { m_isLearningMap = true; } + bool IsAnalysisRequested() const { return m_isAnalysisRequested; } + void RequestAnalysis() { m_isAnalysisRequested = true; } + void AckAnalysisRequest() { m_isAnalysisRequested = false; } + + // difficulty levels + static BotDifficultyType GetDifficultyLevel() + { + if (cv_bot_difficulty.value < 0.9f) + return BOT_EASY; + + if (cv_bot_difficulty.value < 1.9f) + return BOT_NORMAL; + + if (cv_bot_difficulty.value < 2.9f) + return BOT_HARD; + + return BOT_EXPERT; + } + + // the supported game scenarios + enum GameScenarioType + { + SCENARIO_DEATHMATCH, + SCENARIO_DEFUSE_BOMB, + SCENARIO_RESCUE_HOSTAGES, + SCENARIO_ESCORT_VIP + }; + + GameScenarioType GetScenario() const + { +#ifdef REGAMEDLL_ADD + // if we have included deathmatch mode, so set the game type like SCENARIO_DEATHMATCH + if (cv_bot_deathmatch.value > 0) + return SCENARIO_DEATHMATCH; +#endif + + return m_gameScenario; + } + + // "zones" + // depending on the game mode, these are bomb zones, rescue zones, etc. + enum { MAX_ZONES = 4 }; // max # of zones in a map + enum { MAX_ZONE_NAV_AREAS = 16 }; // max # of nav areas in a zone + struct Zone + { + CBaseEntity *m_entity; // the map entity + CNavArea *m_area[MAX_ZONE_NAV_AREAS]; // nav areas that overlap this zone + int m_areaCount; + Vector m_center; + bool m_isLegacy; // if true, use pev->origin and 256 unit radius as zone + int m_index; + Extent m_extent; + }; + + const Zone *GetZone(int i) const { return &m_zone[i]; } + const Zone *GetZone(const Vector *pos) const; // return the zone that contains the given position + const Zone *GetClosestZone(const Vector *pos) const; // return the closest zone to the given position + const Zone *GetClosestZone(const CBaseEntity *pEntity) const { return GetClosestZone(&pEntity->pev->origin); } // return the closest zone to the given entity + int GetZoneCount() const { return m_zoneCount; } + + const Vector *GetRandomPositionInZone(const Zone *zone) const; + CNavArea *GetRandomAreaInZone(const Zone *zone) const; + + // Return the zone closest to the given position, using the given cost heuristic + template + const Zone *GetClosestZone(CNavArea *startArea, CostFunctor costFunc, float *travelDistance = nullptr) const + { + const Zone *closeZone = nullptr; + float closeDist = 99999999.9f; + + if (startArea == nullptr) + return nullptr; + + for (int i = 0; i < m_zoneCount; i++) + { + if (m_zone[i].m_areaCount == 0) + continue; + + // just use the first overlapping nav area as a reasonable approximation + real_t dist = NavAreaTravelDistance(startArea, m_zone[i].m_area[0], costFunc); + + if (dist >= 0.0f && dist < closeDist) + { + closeZone = &m_zone[i]; + closeDist = dist; + } + } + + if (travelDistance) + *travelDistance = closeDist; + + return closeZone; + } + + // pick a zone at random and return it + const Zone *GetRandomZone() const + { + if (!m_zoneCount) + return nullptr; + + return &m_zone[RANDOM_LONG(0, m_zoneCount - 1)]; + } + + bool IsBombPlanted() const { return m_isBombPlanted; } // returns true if bomb has been planted + float GetBombPlantTimestamp() const { return m_bombPlantTimestamp; } // return time bomb was planted + bool IsTimeToPlantBomb() const { return (gpGlobals->time >= m_earliestBombPlantTimestamp); } // return true if it's ok to try to plant bomb + CBasePlayer *GetBombDefuser() const { return m_bombDefuser; } // return the player currently defusing the bomb, or NULL + float GetBombTimeLeft() const; // get the time remaining before the planted bomb explodes + CBaseEntity *GetLooseBomb() { return m_looseBomb; } // return the bomb if it is loose on the ground + CNavArea *GetLooseBombArea() const { return m_looseBombArea; } // return area that bomb is in/near + void SetLooseBomb(CBaseEntity *bomb); + + float GetRadioMessageTimestamp(GameEventType event, int teamID) const; // return the last time the given radio message was sent for given team + float GetRadioMessageInterval(GameEventType event, int teamID) const; // return the interval since the last time this message was sent + void SetRadioMessageTimestamp(GameEventType event, int teamID); + void ResetRadioMessageTimestamps(); + + float GetLastSeenEnemyTimestamp() const { return m_lastSeenEnemyTimestamp; } // return the last time anyone has seen an enemy + void SetLastSeenEnemyTimestamp() { m_lastSeenEnemyTimestamp = gpGlobals->time; } + + float GetRoundStartTime() const { return m_roundStartTimestamp; } + float GetElapsedRoundTime() const { return gpGlobals->time - m_roundStartTimestamp; } // return the elapsed time since the current round began + + bool AllowRogues() const { return cv_bot_allow_rogues.value != 0.0f; } + bool AllowPistols() const { return cv_bot_allow_pistols.value != 0.0f; } + bool AllowShotguns() const { return cv_bot_allow_shotguns.value != 0.0f; } + bool AllowSubMachineGuns() const { return cv_bot_allow_sub_machine_guns.value != 0.0f; } + bool AllowRifles() const { return cv_bot_allow_rifles.value != 0.0f; } + bool AllowMachineGuns() const { return cv_bot_allow_machine_guns.value != 0.0f; } + bool AllowGrenades() const { return cv_bot_allow_grenades.value != 0.0f; } + bool AllowSnipers() const { return cv_bot_allow_snipers.value != 0.0f; } + bool AllowTacticalShield() const { return cv_bot_allow_shield.value != 0.0f; } + bool AllowFriendlyFireDamage() const { return friendlyfire.value != 0.0f; } + + bool IsWeaponUseable(CBasePlayerItem *item) const; // return true if the bot can use this weapon + bool IsWeaponUseable(ArmouryItemPack item) const; + + bool IsDefenseRushing() const { return m_isDefenseRushing; } // returns true if defense team has "decided" to rush this round + bool IsOnDefense(CBasePlayer *pPlayer) const; // return true if this player is on "defense" + bool IsOnOffense(CBasePlayer *pPlayer) const; // return true if this player is on "offense" + + bool IsRoundOver() const { return m_isRoundOver; } // return true if the round has ended + + unsigned int GetNavPlace() const { return m_navPlace; } + void SetNavPlace(unsigned int place) { m_navPlace = place; } + + enum SkillType { LOW, AVERAGE, HIGH, RANDOM }; + const char *GetRandomBotName(SkillType skill); + + void MonitorBotCVars(); + void MaintainBotQuota(); + bool AddBot(const BotProfile *profile, BotProfileTeamType team); + + #define FROM_CONSOLE true + bool BotAddCommand(BotProfileTeamType team, bool isFromConsole = false); // process the "bot_add" console command + +private: + static float m_flNextCVarCheck; + static bool m_isMapDataLoaded; // true if we've attempted to load map data + static bool m_isLearningMap; + static bool m_isAnalysisRequested; + + GameScenarioType m_gameScenario; // what kind of game are we playing + + Zone m_zone[MAX_ZONES]; + int m_zoneCount; + + bool m_isBombPlanted; // true if bomb has been planted + float m_bombPlantTimestamp; // time bomb was planted + float m_earliestBombPlantTimestamp; // don't allow planting until after this time has elapsed + CBasePlayer *m_bombDefuser; // the player currently defusing a bomb + EHANDLE m_looseBomb; // will be non-NULL if bomb is loose on the ground + CNavArea *m_looseBombArea; // area that bomb is is/near + + bool m_isRoundOver; // true if the round has ended + + float m_radioMsgTimestamp[24][2]; + + float m_lastSeenEnemyTimestamp; + float m_roundStartTimestamp; // the time when the current round began + + bool m_isDefenseRushing; // whether defensive team is rushing this round or not + + static NavEditCmdType m_editCmd; + unsigned int m_navPlace; + CountdownTimer m_respawnTimer; + bool m_isRespawnStarted; + bool m_canRespawn; + bool m_bServerActive; +}; + +inline int OtherTeam(int team) +{ + return (team == TERRORIST) ? CT : TERRORIST; +} + +inline CCSBotManager *TheCSBots() +{ + return reinterpret_cast(TheBots); +} + +// Determine whether bots can be used or not +inline bool AreBotsAllowed() +{ + return g_bAllowedCSBot; +} + +void PrintAllEntities(); diff --git a/regamedll/dlls/bot/cs_bot_update.cpp b/regamedll/dlls/bot/cs_bot_update.cpp index 9de07687..0ef8e4ee 100644 --- a/regamedll/dlls/bot/cs_bot_update.cpp +++ b/regamedll/dlls/bot/cs_bot_update.cpp @@ -1,804 +1,804 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#include "precompiled.h" - -// Lightweight maintenance, invoked frequently -void CCSBot::Upkeep() -{ - if (TheCSBots()->IsLearningMap() || !IsAlive()) - return; - - if (m_isRapidFiring) - TogglePrimaryAttack(); - - // aiming must be smooth - update often - if (IsAimingAtEnemy()) - { - UpdateAimOffset(); - - // aim at enemy, if he's still alive - if (m_enemy.IsValid()) - { - float feetOffset = pev->origin.z - GetFeetZ(); - - if (IsEnemyVisible()) - { - if (GetProfile()->GetSkill() > 0.5f) - { - const float k = 3.0f; - m_aimSpot = (m_enemy->pev->velocity - pev->velocity) * g_flBotCommandInterval * k + m_enemy->pev->origin; - } - else - m_aimSpot = m_enemy->pev->origin; - - bool aimBlocked = false; - const float sharpshooter = 0.8f; - - if (IsUsingAWP() || IsUsingShotgun() || IsUsingMachinegun() || GetProfile()->GetSkill() < sharpshooter - || (IsActiveWeaponRecoilHigh() && !IsUsingPistol() && !IsUsingSniperRifle())) - { - if (IsEnemyPartVisible(CHEST)) - { - // No headshots, go for the chest. - aimBlocked = true; - } - } - - if (aimBlocked) - m_aimSpot.z -= feetOffset * 0.25f; - - else if (!IsEnemyPartVisible(HEAD)) - { - if (IsEnemyPartVisible(CHEST)) - { - m_aimSpot.z -= feetOffset * 0.5f; - } - else if (IsEnemyPartVisible(LEFT_SIDE)) - { - Vector2D to = (m_enemy->pev->origin - pev->origin).Make2D(); - to.NormalizeInPlace(); - - m_aimSpot.x -= to.y * 16.0f; - m_aimSpot.y += to.x * 16.0f; - m_aimSpot.z -= feetOffset * 0.5f; - } - else if (IsEnemyPartVisible(RIGHT_SIDE)) - { - Vector2D to = (m_enemy->pev->origin - pev->origin).Make2D(); - to.NormalizeInPlace(); - - m_aimSpot.x += to.y * 16.0f; - m_aimSpot.y -= to.x * 16.0f; - m_aimSpot.z -= feetOffset * 0.5f; - } - else // FEET - { - m_aimSpot.z -= (feetOffset + feetOffset); - } - } - } - else - { - m_aimSpot = m_lastEnemyPosition; - } - - // add in aim error - m_aimSpot.x += m_aimOffset.x; - m_aimSpot.y += m_aimOffset.y; - m_aimSpot.z += m_aimOffset.z; - - Vector toEnemy = m_aimSpot - pev->origin; - Vector idealAngle = UTIL_VecToAngles(toEnemy); - - idealAngle.x = 360.0 - idealAngle.x; - SetLookAngles(idealAngle.y, idealAngle.x); - } - } - else - { - if (m_lookAtSpotClearIfClose) - { - // dont look at spots just in front of our face - it causes erratic view rotation - const float tooCloseRange = 100.0f; - if ((m_lookAtSpot - pev->origin).IsLengthLessThan(tooCloseRange)) - m_lookAtSpotState = NOT_LOOKING_AT_SPOT; - } - - switch (m_lookAtSpotState) - { - case NOT_LOOKING_AT_SPOT: - { - // look ahead - SetLookAngles(m_lookAheadAngle, 0); - break; - } - case LOOK_TOWARDS_SPOT: - { - UpdateLookAt(); - - if (IsLookingAtPosition(&m_lookAtSpot, m_lookAtSpotAngleTolerance)) - { - m_lookAtSpotState = LOOK_AT_SPOT; - m_lookAtSpotTimestamp = gpGlobals->time; - } - break; - } - case LOOK_AT_SPOT: - { - UpdateLookAt(); - - if (m_lookAtSpotDuration >= 0.0f && gpGlobals->time - m_lookAtSpotTimestamp > m_lookAtSpotDuration) - { - m_lookAtSpotState = NOT_LOOKING_AT_SPOT; - m_lookAtSpotDuration = 0.0f; - } - break; - } - } - - float driftAmplitude = 2.0f; - - // have view "drift" very slowly, so view looks "alive" - if (IsUsingSniperRifle() && IsUsingScope()) - { - driftAmplitude = 0.5f; - } - - m_lookYaw += driftAmplitude * BotCOS(33.0f * gpGlobals->time); - m_lookPitch += driftAmplitude * BotSIN(13.0f * gpGlobals->time); - } - -#ifdef REGAMEDLL_FIXES - // Don't update view angles at frozen state - if (!(pev->flags & FL_FROZEN)) -#endif - { - // view angles can change quickly - UpdateLookAngles(); - } -} - -// Heavyweight processing, invoked less often -void CCSBot::Update() -{ - if (TheCSBots()->IsAnalysisRequested() && m_processMode == PROCESS_NORMAL) - { - TheCSBots()->AckAnalysisRequest(); - StartAnalyzeAlphaProcess(); - } - - switch (m_processMode) - { - case PROCESS_LEARN: UpdateLearnProcess(); return; - case PROCESS_ANALYZE_ALPHA: UpdateAnalyzeAlphaProcess(); return; - case PROCESS_ANALYZE_BETA: UpdateAnalyzeBetaProcess(); return; - case PROCESS_SAVE: UpdateSaveProcess(); return; - } - - // update our radio chatter - // need to allow bots to finish their chatter even if they are dead - GetChatter()->Update(); - - if (m_voiceFeedbackEndTimestamp != 0.0f - && (m_voiceFeedbackEndTimestamp <= gpGlobals->time || gpGlobals->time < m_voiceFeedbackStartTimestamp)) - { - EndVoiceFeedback(NO_FORCE); - } - - // check if we are dead - if (!IsAlive()) - { - // remember that we died - m_diedLastRound = true; - BotDeathThink(); - return; - } - - // show line of fire - if ((cv_bot_traceview.value == 100.0 && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 101.0) - { - UTIL_MakeVectors(pev->punchangle + pev->v_angle); - - if (!IsFriendInLineOfFire()) - { - Vector vecAiming = gpGlobals->v_forward; - Vector vecSrc = GetGunPosition(); - - if (m_iTeam == TERRORIST) - UTIL_DrawBeamPoints(vecSrc, vecSrc + 2000.0f * vecAiming, 1, 255, 0, 0); - else - UTIL_DrawBeamPoints(vecSrc, vecSrc + 2000.0f * vecAiming, 1, 0, 50, 255); - } - } - - // - // Debug beam rendering - // - - // show approach points - if ((cv_bot_traceview.value == 2.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 3.0f) - DrawApproachPoints(); - - // show encounter spot data - if ((cv_bot_traceview.value == 4.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 5.0f) - { - if (m_spotEncounter) - { - UTIL_DrawBeamPoints(m_spotEncounter->path.from, m_spotEncounter->path.to, 3, 0, 0, 255); - - Vector dir = m_spotEncounter->path.to - m_spotEncounter->path.from; - float length = dir.NormalizeInPlace(); - - for (auto &order : m_spotEncounter->spotList) { - UTIL_DrawBeamPoints(m_spotEncounter->path.from + order.t * length * dir, *order.spot->GetPosition(), 3, 0, 255, 255); - } - } - } - - // show path navigation data - if (cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) - { - Vector from = GetEyePosition(); - const float size = 50.0f; - - Vector arrow(size * float(Q_cos(m_lookAheadAngle * M_PI / 180.0f)), size * float(Q_sin(m_lookAheadAngle * M_PI / 180.0f)), 0.0f); - UTIL_DrawBeamPoints(from, from + arrow, 1, 0, 255, 255); - } - - if (cv_bot_stop.value != 0.0f) - return; - - // check if we are stuck - StuckCheck(); - - // if our current 'noise' was heard a long time ago, forget it - const float rememberNoiseDuration = 20.0f; - if (m_noiseTimestamp > 0.0f && gpGlobals->time - m_noiseTimestamp > rememberNoiseDuration) - { - ForgetNoise(); - } - - // where are we - if (!m_currentArea || !m_currentArea->Contains(&pev->origin)) - { - m_currentArea = TheNavAreaGrid.GetNavArea(&pev->origin); - } - - // track the last known area we were in - if (m_currentArea && m_currentArea != m_lastKnownArea) - { - m_lastKnownArea = m_currentArea; - // assume that we "clear" an area of enemies when we enter it - m_currentArea->SetClearedTimestamp(m_iTeam - 1); - } - - // update approach points - const float recomputeApproachPointTolerance = 50.0f; - if ((m_approachPointViewPosition - pev->origin).IsLengthGreaterThan(recomputeApproachPointTolerance)) - { - ComputeApproachPoints(); - m_approachPointViewPosition = pev->origin; - } - - if (cv_bot_show_nav.value > 0.0f && m_lastKnownArea) - { - m_lastKnownArea->DrawConnectedAreas(); - } - - // if we're blind, retreat! - if (IsBlind()) - { - if (!IsAtHidingSpot()) - { - switch (m_blindMoveDir) - { - case FORWARD: MoveForward(); break; - case RIGHT: StrafeRight(); break; - case BACKWARD: MoveBackward(); break; - case LEFT: StrafeLeft(); break; - default: Crouch(); break; - } - } - - if (m_blindFire) - { - PrimaryAttack(); - } - - return; - } - - // Enemy acquisition and attack initiation - // take a snapshot and update our reaction time queue - UpdateReactionQueue(); - - // "threat" may be the same as our current enemy - CBasePlayer *threat = GetRecognizedEnemy(); - if (threat) - { - // adjust our personal "safe" time - AdjustSafeTime(); - - if (IsUsingGrenade()) - { - ThrowGrenade(&threat->pev->origin); - } - else - { - // Decide if we should attack - bool doAttack = false; - switch (GetDisposition()) - { - case IGNORE_ENEMIES: - { - // never attack - doAttack = false; - break; - } - case SELF_DEFENSE: - { - // attack if fired on - doAttack = IsPlayerLookingAtMe(threat); - - // attack if enemy very close - if (!doAttack) - { - const float selfDefenseRange = 750.0f; - doAttack = (pev->origin - threat->pev->origin).IsLengthLessThan(selfDefenseRange); - } - break; - } - case ENGAGE_AND_INVESTIGATE: - case OPPORTUNITY_FIRE: - { - // normal combat range - doAttack = true; - break; - } - } - - if (doAttack) - { - if (!GetEnemy() || threat != GetEnemy() || !IsAttacking()) - { - if (IsUsingKnife() && IsHiding()) - { - // if hiding with a knife, wait until threat is close - const float knifeAttackRange = 250.0f; - if ((pev->origin - threat->pev->origin).IsLengthLessThan(knifeAttackRange)) - { - Attack(threat); - } - } - else - { - Attack(threat); - } - } - } - else - { - // dont attack, but keep track of nearby enemies - SetEnemy(threat); - m_isEnemyVisible = true; - } - } - - // if we aren't attacking but we are being attacked, retaliate - if (GetDisposition() != IGNORE_ENEMIES && !IsAttacking()) - { - const float recentAttackDuration = 1.0f; - if (GetTimeSinceAttacked() < recentAttackDuration) - { - // we may not be attacking our attacker, but at least we're not just taking it - // (since m_attacker isn't reaction-time delayed, we can't directly use it) - Attack(threat); - PrintIfWatched("Ouch! Retaliating!\n"); - } - } - - TheCSBots()->SetLastSeenEnemyTimestamp(); - } - - // Validate existing enemy, if any - if (m_enemy.IsValid()) - { - if (IsAwareOfEnemyDeath()) - { - // we have noticed that our enemy has died - m_enemy = nullptr; - m_isEnemyVisible = false; - } - else - { - const int dada = offsetof(CCSBot, m_visibleEnemyParts); - // check LOS to current enemy (chest & head), in case he's dead (GetNearestEnemy() only returns live players) - // note we're not checking FOV - once we've acquired an enemy (which does check FOV), assume we know roughly where he is - if (IsVisible(m_enemy, false, &m_visibleEnemyParts)) - { - m_isEnemyVisible = true; - m_lastSawEnemyTimestamp = gpGlobals->time; - m_lastEnemyPosition = m_enemy->pev->origin; - } - else - { - m_isEnemyVisible = false; - } - - // check if enemy died - if (m_enemy->IsAlive()) - { - m_enemyDeathTimestamp = 0.0f; - m_isLastEnemyDead = false; - } - else if (m_enemyDeathTimestamp == 0.0f) - { - // note time of death (to allow bots to overshoot for a time) - m_enemyDeathTimestamp = gpGlobals->time; - m_isLastEnemyDead = true; - } - } - } - else - { - m_isEnemyVisible = false; - } - - // if we have seen an enemy recently, keep an eye on him if we can - const float seenRecentTime = 3.0f; - if (m_enemy.IsValid() && GetTimeSinceLastSawEnemy() < seenRecentTime) - { - AimAtEnemy(); - } - else - { - StopAiming(); - } - - // Hack to fire while retreating - // TODO: Encapsulate aiming and firing on enemies separately from current task - if (GetDisposition() == IGNORE_ENEMIES) - { - FireWeaponAtEnemy(); - } - - if (IsEndOfSafeTime() && IsUsingGrenade() && (IsWellPastSafe() || !IsUsingHEGrenade()) && !m_isWaitingToTossGrenade) - { - Vector target; - if (FindGrenadeTossPathTarget(&target)) - { - ThrowGrenade(&target); - } - } - - if (IsUsingGrenade()) - { - bool doToss = (m_isWaitingToTossGrenade && (m_tossGrenadeTimer.IsElapsed() || m_lookAtSpotState == LOOK_AT_SPOT)); - - if (doToss) - { - ClearPrimaryAttack(); - m_isWaitingToTossGrenade = false; - } - else - { - PrimaryAttack(); - } - } - else - { - m_isWaitingToTossGrenade = false; - } - - if (IsHunting() && IsWellPastSafe() && IsUsingGrenade()) - { - EquipBestWeapon(MUST_EQUIP); - } - - // check if our weapon is totally out of ammo - // or if we no longer feel "safe", equip our weapon - if (!IsSafe() && !IsUsingGrenade() && IsActiveWeaponOutOfAmmo()) - { - EquipBestWeapon(); - } - - // TODO: This doesn't work if we are restricted to just knives and sniper rifles because we cant use the rifle at close range - if (!IsSafe() && !IsUsingGrenade() && IsUsingKnife() && !IsEscapingFromBomb()) - { - EquipBestWeapon(); - } - - // if we haven't seen an enemy in awhile, and we switched to our pistol during combat, - // switch back to our primary weapon (if it still has ammo left) - const float safeRearmTime = 5.0f; - if (!IsActiveWeaponReloading() && IsUsingPistol() && !IsPrimaryWeaponEmpty() && GetTimeSinceLastSawEnemy() > safeRearmTime) - { - EquipBestWeapon(); - } - - // reload our weapon if we must - ReloadCheck(); - - // equip silencer - SilencerCheck(); - - // listen to the radio - RespondToRadioCommands(); - - // make way - const float avoidTime = 0.33f; - if (gpGlobals->time - m_avoidTimestamp < avoidTime && m_avoid) - { - StrafeAwayFromPosition(&m_avoid->pev->origin); - } - else - { - m_avoid = nullptr; - } - - if (m_isJumpCrouching) - { - const float duration = 0.75f; - const float crouchDelayTime = 0.05f; - const float standUpTime = 0.6f; - float elapsed = gpGlobals->time - m_jumpCrouchTimestamp; - - if (elapsed > crouchDelayTime && elapsed < standUpTime) - Crouch(); - - if (elapsed >= standUpTime) - StandUp(); - - if (elapsed > duration) - m_isJumpCrouching = false; - } - - // if we're using a sniper rifle and are no longer attacking, stop looking thru scope - if (!IsAtHidingSpot() && !IsAttacking() && IsUsingSniperRifle() && IsUsingScope()) - { - SecondaryAttack(); - } - - // check encounter spots - UpdatePeripheralVision(); - - // Update gamestate - if (m_bomber) - { - GetChatter()->SpottedBomber(GetBomber()); - } - - if (CanSeeLooseBomb()) - { - GetChatter()->SpottedLooseBomb(TheCSBots()->GetLooseBomb()); - } - - // Scenario interrupts - switch (TheCSBots()->GetScenario()) - { - case CCSBotManager::SCENARIO_DEFUSE_BOMB: - { - // flee if the bomb is ready to blow and we aren't defusing it or attacking and we know where the bomb is - // (aggressive players wait until its almost too late) - float gonnaBlowTime = 8.0f - (2.0f * GetProfile()->GetAggression()); - - // if we have a defuse kit, can wait longer - if (m_bHasDefuser) - gonnaBlowTime *= 0.66f; - - if (!IsEscapingFromBomb() // we aren't already escaping the bomb - && TheCSBots()->IsBombPlanted() // is the bomb planted - && GetGameState()->IsPlantedBombLocationKnown() // we know where the bomb is - && TheCSBots()->GetBombTimeLeft() < gonnaBlowTime // is the bomb about to explode - && !IsDefusingBomb() // we aren't defusing the bomb - && !IsAttacking()) // we aren't in the midst of a firefight - { - EscapeFromBomb(); - break; - } - break; - } - case CCSBotManager::SCENARIO_RESCUE_HOSTAGES: - { - if (m_iTeam == CT) - { - UpdateHostageEscortCount(); - } - else - { - // Terrorists have imperfect information on status of hostages - CSGameState::ValidateStatusType status = GetGameState()->ValidateHostagePositions(); - - if (status & CSGameState::HOSTAGES_ALL_GONE) - { - GetChatter()->HostagesTaken(); - Idle(); - } - else if (status & CSGameState::HOSTAGE_GONE) - { - GetGameState()->HostageWasTaken(); - Idle(); - } - } - break; - } - } - - // Follow nearby humans if our co-op is high and we have nothing else to do - // If we were just following someone, don't auto-follow again for a short while to - // give us a chance to do something else. - const float earliestAutoFollowTime = 5.0f; - const float minAutoFollowTeamwork = 0.4f; - if (TheCSBots()->GetElapsedRoundTime() > earliestAutoFollowTime - && GetProfile()->GetTeamwork() > minAutoFollowTeamwork - && CanAutoFollow() - && !IsBusy() - && !IsFollowing() - && !GetGameState()->IsAtPlantedBombsite()) - { - // chance of following is proportional to teamwork attribute - if (GetProfile()->GetTeamwork() > RANDOM_FLOAT(0.0f, 1.0f)) - { - CBasePlayer *pLeader = GetClosestVisibleHumanFriend(); - if (pLeader && pLeader->IsAutoFollowAllowed()) - { - // count how many bots are already following this player - const float maxFollowCount = 2; - if (GetBotFollowCount(pLeader) < maxFollowCount) - { - const float autoFollowRange = 300.0f; - if ((pLeader->pev->origin - pev->origin).IsLengthLessThan(autoFollowRange)) - { - CNavArea *leaderArea = TheNavAreaGrid.GetNavArea(&pLeader->pev->origin); - if (leaderArea) - { - PathCost cost(this, FASTEST_ROUTE); - float travelRange = NavAreaTravelDistance(GetLastKnownArea(), leaderArea, cost); - if (/*travelRange >= 0.0f &&*/ travelRange < autoFollowRange) - { - // follow this human - Follow(pLeader); - PrintIfWatched("Auto-Following %s\n", STRING(pLeader->pev->netname)); - - if (CSGameRules()->IsCareer()) - { - GetChatter()->Say("FollowingCommander", 10.0f); - } - else - { - GetChatter()->Say("FollowingSir", 10.0f); - } - } - } - } - } - } - } - else - { - // we decided not to follow, don't re-check for a duration - m_allowAutoFollowTime = gpGlobals->time + 15.0f + (1.0f - GetProfile()->GetTeamwork()) * 30.0f; - } - } - - if (IsFollowing()) - { - // if we are following someone, make sure they are still alive - CBaseEntity *pLeader = m_leader; - if (!pLeader || !pLeader->IsAlive()) - { - StopFollowing(); - } - - // decide whether to continue following them - const float highTeamwork = 0.85f; - if (GetProfile()->GetTeamwork() < highTeamwork) - { - float minFollowDuration = 15.0f; - if (GetFollowDuration() > minFollowDuration + 40.0f * GetProfile()->GetTeamwork()) - { - // we are bored of following our leader - StopFollowing(); - PrintIfWatched("Stopping following - bored\n"); - } - } - } - else - { - if (GetMorale() < NEUTRAL && IsSafe() && GetSafeTimeRemaining() < 2.0f && IsHunting()) - { - if (GetMorale() * -40.0 > RANDOM_FLOAT(0.0f, 100.0f)) - { - if (TheCSBots()->IsOnOffense(this) || !TheCSBots()->IsDefenseRushing()) - { - SetDisposition(OPPORTUNITY_FIRE); - Hide(m_lastKnownArea, RANDOM_FLOAT(3.0f, 15.0f)); - GetChatter()->Say("WaitingHere"); - } - } - } - } - - // Execute state machine - if (m_isAttacking) - { - m_attackState.OnUpdate(this); - } - else - { - m_state->OnUpdate(this); - } - - if (m_isWaitingToTossGrenade) - { - ResetStuckMonitor(); - ClearMovement(); - } - - // don't move while reloading unless we see an enemy - if (IsReloading() && !m_isEnemyVisible) - { - ResetStuckMonitor(); - ClearMovement(); - } - - // if we get too far ahead of the hostages we are escorting, wait for them - if (!IsAttacking() && m_inhibitWaitingForHostageTimer.IsElapsed()) - { - const float waitForHostageRange = 500.0f; - if (GetTask() == RESCUE_HOSTAGES && GetRangeToFarthestEscortedHostage() > waitForHostageRange) - { - if (!m_isWaitingForHostage) - { - // just started waiting - m_isWaitingForHostage = true; - m_waitForHostageTimer.Start(10.0f); - } - else - { - // we've been waiting - if (m_waitForHostageTimer.IsElapsed()) - { - // give up waiting for awhile - m_isWaitingForHostage = false; - m_inhibitWaitingForHostageTimer.Start(3.0f); - } - else - { - // keep waiting - ResetStuckMonitor(); - ClearMovement(); - } - } - } - } - - // remember our prior safe time status - m_wasSafe = IsSafe(); -} +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +// Lightweight maintenance, invoked frequently +void CCSBot::Upkeep() +{ + if (TheCSBots()->IsLearningMap() || !IsAlive()) + return; + + if (m_isRapidFiring) + TogglePrimaryAttack(); + + // aiming must be smooth - update often + if (IsAimingAtEnemy()) + { + UpdateAimOffset(); + + // aim at enemy, if he's still alive + if (m_enemy.IsValid()) + { + float feetOffset = pev->origin.z - GetFeetZ(); + + if (IsEnemyVisible()) + { + if (GetProfile()->GetSkill() > 0.5f) + { + const float k = 3.0f; + m_aimSpot = (m_enemy->pev->velocity - pev->velocity) * g_flBotCommandInterval * k + m_enemy->pev->origin; + } + else + m_aimSpot = m_enemy->pev->origin; + + bool aimBlocked = false; + const float sharpshooter = 0.8f; + + if (IsUsingAWP() || IsUsingShotgun() || IsUsingMachinegun() || GetProfile()->GetSkill() < sharpshooter + || (IsActiveWeaponRecoilHigh() && !IsUsingPistol() && !IsUsingSniperRifle())) + { + if (IsEnemyPartVisible(CHEST)) + { + // No headshots, go for the chest. + aimBlocked = true; + } + } + + if (aimBlocked) + m_aimSpot.z -= feetOffset * 0.25f; + + else if (!IsEnemyPartVisible(HEAD)) + { + if (IsEnemyPartVisible(CHEST)) + { + m_aimSpot.z -= feetOffset * 0.5f; + } + else if (IsEnemyPartVisible(LEFT_SIDE)) + { + Vector2D to = (m_enemy->pev->origin - pev->origin).Make2D(); + to.NormalizeInPlace(); + + m_aimSpot.x -= to.y * 16.0f; + m_aimSpot.y += to.x * 16.0f; + m_aimSpot.z -= feetOffset * 0.5f; + } + else if (IsEnemyPartVisible(RIGHT_SIDE)) + { + Vector2D to = (m_enemy->pev->origin - pev->origin).Make2D(); + to.NormalizeInPlace(); + + m_aimSpot.x += to.y * 16.0f; + m_aimSpot.y -= to.x * 16.0f; + m_aimSpot.z -= feetOffset * 0.5f; + } + else // FEET + { + m_aimSpot.z -= (feetOffset + feetOffset); + } + } + } + else + { + m_aimSpot = m_lastEnemyPosition; + } + + // add in aim error + m_aimSpot.x += m_aimOffset.x; + m_aimSpot.y += m_aimOffset.y; + m_aimSpot.z += m_aimOffset.z; + + Vector toEnemy = m_aimSpot - pev->origin; + Vector idealAngle = UTIL_VecToAngles(toEnemy); + + idealAngle.x = 360.0 - idealAngle.x; + SetLookAngles(idealAngle.y, idealAngle.x); + } + } + else + { + if (m_lookAtSpotClearIfClose) + { + // dont look at spots just in front of our face - it causes erratic view rotation + const float tooCloseRange = 100.0f; + if ((m_lookAtSpot - pev->origin).IsLengthLessThan(tooCloseRange)) + m_lookAtSpotState = NOT_LOOKING_AT_SPOT; + } + + switch (m_lookAtSpotState) + { + case NOT_LOOKING_AT_SPOT: + { + // look ahead + SetLookAngles(m_lookAheadAngle, 0); + break; + } + case LOOK_TOWARDS_SPOT: + { + UpdateLookAt(); + + if (IsLookingAtPosition(&m_lookAtSpot, m_lookAtSpotAngleTolerance)) + { + m_lookAtSpotState = LOOK_AT_SPOT; + m_lookAtSpotTimestamp = gpGlobals->time; + } + break; + } + case LOOK_AT_SPOT: + { + UpdateLookAt(); + + if (m_lookAtSpotDuration >= 0.0f && gpGlobals->time - m_lookAtSpotTimestamp > m_lookAtSpotDuration) + { + m_lookAtSpotState = NOT_LOOKING_AT_SPOT; + m_lookAtSpotDuration = 0.0f; + } + break; + } + } + + float driftAmplitude = 2.0f; + + // have view "drift" very slowly, so view looks "alive" + if (IsUsingSniperRifle() && IsUsingScope()) + { + driftAmplitude = 0.5f; + } + + m_lookYaw += driftAmplitude * BotCOS(33.0f * gpGlobals->time); + m_lookPitch += driftAmplitude * BotSIN(13.0f * gpGlobals->time); + } + +#ifdef REGAMEDLL_FIXES + // Don't update view angles at frozen state + if (!(pev->flags & FL_FROZEN)) +#endif + { + // view angles can change quickly + UpdateLookAngles(); + } +} + +// Heavyweight processing, invoked less often +void CCSBot::Update() +{ + if (TheCSBots()->IsAnalysisRequested() && m_processMode == PROCESS_NORMAL) + { + TheCSBots()->AckAnalysisRequest(); + StartAnalyzeAlphaProcess(); + } + + switch (m_processMode) + { + case PROCESS_LEARN: UpdateLearnProcess(); return; + case PROCESS_ANALYZE_ALPHA: UpdateAnalyzeAlphaProcess(); return; + case PROCESS_ANALYZE_BETA: UpdateAnalyzeBetaProcess(); return; + case PROCESS_SAVE: UpdateSaveProcess(); return; + } + + // update our radio chatter + // need to allow bots to finish their chatter even if they are dead + GetChatter()->Update(); + + if (m_voiceFeedbackEndTimestamp != 0.0f + && (m_voiceFeedbackEndTimestamp <= gpGlobals->time || gpGlobals->time < m_voiceFeedbackStartTimestamp)) + { + EndVoiceFeedback(NO_FORCE); + } + + // check if we are dead + if (!IsAlive()) + { + // remember that we died + m_diedLastRound = true; + BotDeathThink(); + return; + } + + // show line of fire + if ((cv_bot_traceview.value == 100.0 && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 101.0) + { + UTIL_MakeVectors(pev->punchangle + pev->v_angle); + + if (!IsFriendInLineOfFire()) + { + Vector vecAiming = gpGlobals->v_forward; + Vector vecSrc = GetGunPosition(); + + if (m_iTeam == TERRORIST) + UTIL_DrawBeamPoints(vecSrc, vecSrc + 2000.0f * vecAiming, 1, 255, 0, 0); + else + UTIL_DrawBeamPoints(vecSrc, vecSrc + 2000.0f * vecAiming, 1, 0, 50, 255); + } + } + + // + // Debug beam rendering + // + + // show approach points + if ((cv_bot_traceview.value == 2.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 3.0f) + DrawApproachPoints(); + + // show encounter spot data + if ((cv_bot_traceview.value == 4.0f && IsLocalPlayerWatchingMe()) || cv_bot_traceview.value == 5.0f) + { + if (m_spotEncounter) + { + UTIL_DrawBeamPoints(m_spotEncounter->path.from, m_spotEncounter->path.to, 3, 0, 0, 255); + + Vector dir = m_spotEncounter->path.to - m_spotEncounter->path.from; + float length = dir.NormalizeInPlace(); + + for (auto &order : m_spotEncounter->spotList) { + UTIL_DrawBeamPoints(m_spotEncounter->path.from + order.t * length * dir, *order.spot->GetPosition(), 3, 0, 255, 255); + } + } + } + + // show path navigation data + if (cv_bot_traceview.value == 1.0f && IsLocalPlayerWatchingMe()) + { + Vector from = GetEyePosition(); + const float size = 50.0f; + + Vector arrow(size * float(Q_cos(m_lookAheadAngle * M_PI / 180.0f)), size * float(Q_sin(m_lookAheadAngle * M_PI / 180.0f)), 0.0f); + UTIL_DrawBeamPoints(from, from + arrow, 1, 0, 255, 255); + } + + if (cv_bot_stop.value != 0.0f) + return; + + // check if we are stuck + StuckCheck(); + + // if our current 'noise' was heard a long time ago, forget it + const float rememberNoiseDuration = 20.0f; + if (m_noiseTimestamp > 0.0f && gpGlobals->time - m_noiseTimestamp > rememberNoiseDuration) + { + ForgetNoise(); + } + + // where are we + if (!m_currentArea || !m_currentArea->Contains(&pev->origin)) + { + m_currentArea = TheNavAreaGrid.GetNavArea(&pev->origin); + } + + // track the last known area we were in + if (m_currentArea && m_currentArea != m_lastKnownArea) + { + m_lastKnownArea = m_currentArea; + // assume that we "clear" an area of enemies when we enter it + m_currentArea->SetClearedTimestamp(m_iTeam - 1); + } + + // update approach points + const float recomputeApproachPointTolerance = 50.0f; + if ((m_approachPointViewPosition - pev->origin).IsLengthGreaterThan(recomputeApproachPointTolerance)) + { + ComputeApproachPoints(); + m_approachPointViewPosition = pev->origin; + } + + if (cv_bot_show_nav.value > 0.0f && m_lastKnownArea) + { + m_lastKnownArea->DrawConnectedAreas(); + } + + // if we're blind, retreat! + if (IsBlind()) + { + if (!IsAtHidingSpot()) + { + switch (m_blindMoveDir) + { + case FORWARD: MoveForward(); break; + case RIGHT: StrafeRight(); break; + case BACKWARD: MoveBackward(); break; + case LEFT: StrafeLeft(); break; + default: Crouch(); break; + } + } + + if (m_blindFire) + { + PrimaryAttack(); + } + + return; + } + + // Enemy acquisition and attack initiation + // take a snapshot and update our reaction time queue + UpdateReactionQueue(); + + // "threat" may be the same as our current enemy + CBasePlayer *threat = GetRecognizedEnemy(); + if (threat) + { + // adjust our personal "safe" time + AdjustSafeTime(); + + if (IsUsingGrenade()) + { + ThrowGrenade(&threat->pev->origin); + } + else + { + // Decide if we should attack + bool doAttack = false; + switch (GetDisposition()) + { + case IGNORE_ENEMIES: + { + // never attack + doAttack = false; + break; + } + case SELF_DEFENSE: + { + // attack if fired on + doAttack = IsPlayerLookingAtMe(threat); + + // attack if enemy very close + if (!doAttack) + { + const float selfDefenseRange = 750.0f; + doAttack = (pev->origin - threat->pev->origin).IsLengthLessThan(selfDefenseRange); + } + break; + } + case ENGAGE_AND_INVESTIGATE: + case OPPORTUNITY_FIRE: + { + // normal combat range + doAttack = true; + break; + } + } + + if (doAttack) + { + if (!GetEnemy() || threat != GetEnemy() || !IsAttacking()) + { + if (IsUsingKnife() && IsHiding()) + { + // if hiding with a knife, wait until threat is close + const float knifeAttackRange = 250.0f; + if ((pev->origin - threat->pev->origin).IsLengthLessThan(knifeAttackRange)) + { + Attack(threat); + } + } + else + { + Attack(threat); + } + } + } + else + { + // dont attack, but keep track of nearby enemies + SetEnemy(threat); + m_isEnemyVisible = true; + } + } + + // if we aren't attacking but we are being attacked, retaliate + if (GetDisposition() != IGNORE_ENEMIES && !IsAttacking()) + { + const float recentAttackDuration = 1.0f; + if (GetTimeSinceAttacked() < recentAttackDuration) + { + // we may not be attacking our attacker, but at least we're not just taking it + // (since m_attacker isn't reaction-time delayed, we can't directly use it) + Attack(threat); + PrintIfWatched("Ouch! Retaliating!\n"); + } + } + + TheCSBots()->SetLastSeenEnemyTimestamp(); + } + + // Validate existing enemy, if any + if (m_enemy.IsValid()) + { + if (IsAwareOfEnemyDeath()) + { + // we have noticed that our enemy has died + m_enemy = nullptr; + m_isEnemyVisible = false; + } + else + { + const int dada = offsetof(CCSBot, m_visibleEnemyParts); + // check LOS to current enemy (chest & head), in case he's dead (GetNearestEnemy() only returns live players) + // note we're not checking FOV - once we've acquired an enemy (which does check FOV), assume we know roughly where he is + if (IsVisible(m_enemy, false, &m_visibleEnemyParts)) + { + m_isEnemyVisible = true; + m_lastSawEnemyTimestamp = gpGlobals->time; + m_lastEnemyPosition = m_enemy->pev->origin; + } + else + { + m_isEnemyVisible = false; + } + + // check if enemy died + if (m_enemy->IsAlive()) + { + m_enemyDeathTimestamp = 0.0f; + m_isLastEnemyDead = false; + } + else if (m_enemyDeathTimestamp == 0.0f) + { + // note time of death (to allow bots to overshoot for a time) + m_enemyDeathTimestamp = gpGlobals->time; + m_isLastEnemyDead = true; + } + } + } + else + { + m_isEnemyVisible = false; + } + + // if we have seen an enemy recently, keep an eye on him if we can + const float seenRecentTime = 3.0f; + if (m_enemy.IsValid() && GetTimeSinceLastSawEnemy() < seenRecentTime) + { + AimAtEnemy(); + } + else + { + StopAiming(); + } + + // Hack to fire while retreating + // TODO: Encapsulate aiming and firing on enemies separately from current task + if (GetDisposition() == IGNORE_ENEMIES) + { + FireWeaponAtEnemy(); + } + + if (IsEndOfSafeTime() && IsUsingGrenade() && (IsWellPastSafe() || !IsUsingHEGrenade()) && !m_isWaitingToTossGrenade) + { + Vector target; + if (FindGrenadeTossPathTarget(&target)) + { + ThrowGrenade(&target); + } + } + + if (IsUsingGrenade()) + { + bool doToss = (m_isWaitingToTossGrenade && (m_tossGrenadeTimer.IsElapsed() || m_lookAtSpotState == LOOK_AT_SPOT)); + + if (doToss) + { + ClearPrimaryAttack(); + m_isWaitingToTossGrenade = false; + } + else + { + PrimaryAttack(); + } + } + else + { + m_isWaitingToTossGrenade = false; + } + + if (IsHunting() && IsWellPastSafe() && IsUsingGrenade()) + { + EquipBestWeapon(MUST_EQUIP); + } + + // check if our weapon is totally out of ammo + // or if we no longer feel "safe", equip our weapon + if (!IsSafe() && !IsUsingGrenade() && IsActiveWeaponOutOfAmmo()) + { + EquipBestWeapon(); + } + + // TODO: This doesn't work if we are restricted to just knives and sniper rifles because we cant use the rifle at close range + if (!IsSafe() && !IsUsingGrenade() && IsUsingKnife() && !IsEscapingFromBomb()) + { + EquipBestWeapon(); + } + + // if we haven't seen an enemy in awhile, and we switched to our pistol during combat, + // switch back to our primary weapon (if it still has ammo left) + const float safeRearmTime = 5.0f; + if (!IsActiveWeaponReloading() && IsUsingPistol() && !IsPrimaryWeaponEmpty() && GetTimeSinceLastSawEnemy() > safeRearmTime) + { + EquipBestWeapon(); + } + + // reload our weapon if we must + ReloadCheck(); + + // equip silencer + SilencerCheck(); + + // listen to the radio + RespondToRadioCommands(); + + // make way + const float avoidTime = 0.33f; + if (gpGlobals->time - m_avoidTimestamp < avoidTime && m_avoid) + { + StrafeAwayFromPosition(&m_avoid->pev->origin); + } + else + { + m_avoid = nullptr; + } + + if (m_isJumpCrouching) + { + const float duration = 0.75f; + const float crouchDelayTime = 0.05f; + const float standUpTime = 0.6f; + float elapsed = gpGlobals->time - m_jumpCrouchTimestamp; + + if (elapsed > crouchDelayTime && elapsed < standUpTime) + Crouch(); + + if (elapsed >= standUpTime) + StandUp(); + + if (elapsed > duration) + m_isJumpCrouching = false; + } + + // if we're using a sniper rifle and are no longer attacking, stop looking thru scope + if (!IsAtHidingSpot() && !IsAttacking() && IsUsingSniperRifle() && IsUsingScope()) + { + SecondaryAttack(); + } + + // check encounter spots + UpdatePeripheralVision(); + + // Update gamestate + if (m_bomber) + { + GetChatter()->SpottedBomber(GetBomber()); + } + + if (CanSeeLooseBomb()) + { + GetChatter()->SpottedLooseBomb(TheCSBots()->GetLooseBomb()); + } + + // Scenario interrupts + switch (TheCSBots()->GetScenario()) + { + case CCSBotManager::SCENARIO_DEFUSE_BOMB: + { + // flee if the bomb is ready to blow and we aren't defusing it or attacking and we know where the bomb is + // (aggressive players wait until its almost too late) + float gonnaBlowTime = 8.0f - (2.0f * GetProfile()->GetAggression()); + + // if we have a defuse kit, can wait longer + if (m_bHasDefuser) + gonnaBlowTime *= 0.66f; + + if (!IsEscapingFromBomb() // we aren't already escaping the bomb + && TheCSBots()->IsBombPlanted() // is the bomb planted + && GetGameState()->IsPlantedBombLocationKnown() // we know where the bomb is + && TheCSBots()->GetBombTimeLeft() < gonnaBlowTime // is the bomb about to explode + && !IsDefusingBomb() // we aren't defusing the bomb + && !IsAttacking()) // we aren't in the midst of a firefight + { + EscapeFromBomb(); + break; + } + break; + } + case CCSBotManager::SCENARIO_RESCUE_HOSTAGES: + { + if (m_iTeam == CT) + { + UpdateHostageEscortCount(); + } + else + { + // Terrorists have imperfect information on status of hostages + CSGameState::ValidateStatusType status = GetGameState()->ValidateHostagePositions(); + + if (status & CSGameState::HOSTAGES_ALL_GONE) + { + GetChatter()->HostagesTaken(); + Idle(); + } + else if (status & CSGameState::HOSTAGE_GONE) + { + GetGameState()->HostageWasTaken(); + Idle(); + } + } + break; + } + } + + // Follow nearby humans if our co-op is high and we have nothing else to do + // If we were just following someone, don't auto-follow again for a short while to + // give us a chance to do something else. + const float earliestAutoFollowTime = 5.0f; + const float minAutoFollowTeamwork = 0.4f; + if (TheCSBots()->GetElapsedRoundTime() > earliestAutoFollowTime + && GetProfile()->GetTeamwork() > minAutoFollowTeamwork + && CanAutoFollow() + && !IsBusy() + && !IsFollowing() + && !GetGameState()->IsAtPlantedBombsite()) + { + // chance of following is proportional to teamwork attribute + if (GetProfile()->GetTeamwork() > RANDOM_FLOAT(0.0f, 1.0f)) + { + CBasePlayer *pLeader = GetClosestVisibleHumanFriend(); + if (pLeader && pLeader->IsAutoFollowAllowed()) + { + // count how many bots are already following this player + const float maxFollowCount = 2; + if (GetBotFollowCount(pLeader) < maxFollowCount) + { + const float autoFollowRange = 300.0f; + if ((pLeader->pev->origin - pev->origin).IsLengthLessThan(autoFollowRange)) + { + CNavArea *leaderArea = TheNavAreaGrid.GetNavArea(&pLeader->pev->origin); + if (leaderArea) + { + PathCost cost(this, FASTEST_ROUTE); + float travelRange = NavAreaTravelDistance(GetLastKnownArea(), leaderArea, cost); + if (/*travelRange >= 0.0f &&*/ travelRange < autoFollowRange) + { + // follow this human + Follow(pLeader); + PrintIfWatched("Auto-Following %s\n", STRING(pLeader->pev->netname)); + + if (CSGameRules()->IsCareer()) + { + GetChatter()->Say("FollowingCommander", 10.0f); + } + else + { + GetChatter()->Say("FollowingSir", 10.0f); + } + } + } + } + } + } + } + else + { + // we decided not to follow, don't re-check for a duration + m_allowAutoFollowTime = gpGlobals->time + 15.0f + (1.0f - GetProfile()->GetTeamwork()) * 30.0f; + } + } + + if (IsFollowing()) + { + // if we are following someone, make sure they are still alive + CBaseEntity *pLeader = m_leader; + if (!pLeader || !pLeader->IsAlive()) + { + StopFollowing(); + } + + // decide whether to continue following them + const float highTeamwork = 0.85f; + if (GetProfile()->GetTeamwork() < highTeamwork) + { + float minFollowDuration = 15.0f; + if (GetFollowDuration() > minFollowDuration + 40.0f * GetProfile()->GetTeamwork()) + { + // we are bored of following our leader + StopFollowing(); + PrintIfWatched("Stopping following - bored\n"); + } + } + } + else + { + if (GetMorale() < NEUTRAL && IsSafe() && GetSafeTimeRemaining() < 2.0f && IsHunting()) + { + if (GetMorale() * -40.0 > RANDOM_FLOAT(0.0f, 100.0f)) + { + if (TheCSBots()->IsOnOffense(this) || !TheCSBots()->IsDefenseRushing()) + { + SetDisposition(OPPORTUNITY_FIRE); + Hide(m_lastKnownArea, RANDOM_FLOAT(3.0f, 15.0f)); + GetChatter()->Say("WaitingHere"); + } + } + } + } + + // Execute state machine + if (m_isAttacking) + { + m_attackState.OnUpdate(this); + } + else + { + m_state->OnUpdate(this); + } + + if (m_isWaitingToTossGrenade) + { + ResetStuckMonitor(); + ClearMovement(); + } + + // don't move while reloading unless we see an enemy + if (IsReloading() && !m_isEnemyVisible) + { + ResetStuckMonitor(); + ClearMovement(); + } + + // if we get too far ahead of the hostages we are escorting, wait for them + if (!IsAttacking() && m_inhibitWaitingForHostageTimer.IsElapsed()) + { + const float waitForHostageRange = 500.0f; + if (GetTask() == RESCUE_HOSTAGES && GetRangeToFarthestEscortedHostage() > waitForHostageRange) + { + if (!m_isWaitingForHostage) + { + // just started waiting + m_isWaitingForHostage = true; + m_waitForHostageTimer.Start(10.0f); + } + else + { + // we've been waiting + if (m_waitForHostageTimer.IsElapsed()) + { + // give up waiting for awhile + m_isWaitingForHostage = false; + m_inhibitWaitingForHostageTimer.Start(3.0f); + } + else + { + // keep waiting + ResetStuckMonitor(); + ClearMovement(); + } + } + } + } + + // remember our prior safe time status + m_wasSafe = IsSafe(); +} diff --git a/regamedll/dlls/cdll_dll.h b/regamedll/dlls/cdll_dll.h index de8f386e..6b4dbf9b 100644 --- a/regamedll/dlls/cdll_dll.h +++ b/regamedll/dlls/cdll_dll.h @@ -1,119 +1,119 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -const int MAX_WEAPON_SLOTS = 5; // hud item selection slots -const int MAX_ITEM_TYPES = 6; // hud item selection slots -const int MAX_AMMO_SLOTS = 32; // not really slots -const int MAX_ITEMS = 4; // hard coded item types - -const int DEFAULT_FOV = 90; // the default field of view - -#define HIDEHUD_WEAPONS BIT(0) -#define HIDEHUD_FLASHLIGHT BIT(1) -#define HIDEHUD_ALL BIT(2) -#define HIDEHUD_HEALTH BIT(3) -#define HIDEHUD_TIMER BIT(4) -#define HIDEHUD_MONEY BIT(5) -#define HIDEHUD_CROSSHAIR BIT(6) -#define HIDEHUD_OBSERVER_CROSSHAIR BIT(7) - -#define STATUSICON_HIDE 0 -#define STATUSICON_SHOW 1 -#define STATUSICON_FLASH 2 - -#define HUD_PRINTNOTIFY 1 -#define HUD_PRINTCONSOLE 2 -#define HUD_PRINTTALK 3 -#define HUD_PRINTCENTER 4 -#define HUD_PRINTRADIO 5 - -#define STATUS_NIGHTVISION_ON 1 -#define STATUS_NIGHTVISION_OFF 0 - -#define ITEM_STATUS_NIGHTVISION BIT(0) -#define ITEM_STATUS_DEFUSER BIT(1) - -#define SCORE_STATUS_DEAD BIT(0) -#define SCORE_STATUS_BOMB BIT(1) -#define SCORE_STATUS_VIP BIT(2) -#define SCORE_STATUS_DEFKIT BIT(3) - -// player data iuser3 -#define PLAYER_CAN_SHOOT BIT(0) -#define PLAYER_FREEZE_TIME_OVER BIT(1) -#define PLAYER_IN_BOMB_ZONE BIT(2) -#define PLAYER_HOLDING_SHIELD BIT(3) -#define PLAYER_PREVENT_DUCK BIT(4) -#define PLAYER_PREVENT_CLIMB BIT(5) // The player can't climb ladder -#define PLAYER_PREVENT_JUMP BIT(6) - -#define MENU_KEY_1 BIT(0) -#define MENU_KEY_2 BIT(1) -#define MENU_KEY_3 BIT(2) -#define MENU_KEY_4 BIT(3) -#define MENU_KEY_5 BIT(4) -#define MENU_KEY_6 BIT(5) -#define MENU_KEY_7 BIT(6) -#define MENU_KEY_8 BIT(7) -#define MENU_KEY_9 BIT(8) -#define MENU_KEY_0 BIT(9) - -#define WEAPON_SUIT 31 -#define WEAPON_ALLWEAPONS (~(1<maxClients; i++) - { - CBasePlayer *pObserver = UTIL_PlayerByIndex(i); - if (pObserver && pObserver->IsObservingPlayer(pPlayer)) - { - UTIL_ScreenFade(pObserver, color, fadeTime, fadeHold, alpha, 0); - } - } - } - - pPlayer->Blind(fadeTime * 0.33, fadeHold, fadeTime, alpha); - - if (TheBots) - { - TheBots->OnEvent(EVENT_PLAYER_BLINDED_BY_FLASHBANG, pPlayer); - } -} - -void RadiusFlash_TraceLine_hook(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAttacker, Vector &vecSrc, Vector &vecSpot, TraceResult *tr) -{ - UTIL_TraceLine(vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), tr); -} - -void RadiusFlash(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType) -{ - CBaseEntity *pEntity = nullptr; - TraceResult tr; - float flAdjustedDamage, falloff; - Vector vecSpot; - float flRadius = 1500; - - if (flRadius) - falloff = flDamage / flRadius; - else - falloff = 1; - - int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); - - vecSrc.z += 1; - - while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, 1500.0))) - { - TraceResult tr2; - Vector vecLOS; - float flDot; - float fadeTime; - float fadeHold; - int alpha; - CBasePlayer *pPlayer; - float currentHoldTime; - - if (!pEntity->IsPlayer()) - continue; - - pPlayer = (CBasePlayer *)pEntity; - - if (pPlayer->pev->takedamage == DAMAGE_NO || pPlayer->pev->deadflag != DEAD_NO) - continue; - - if (bInWater && pPlayer->pev->waterlevel == 0) - continue; - - if (!bInWater && pPlayer->pev->waterlevel == 3) - continue; - -#ifdef REGAMEDLL_FIXES - vecSpot = pPlayer->EyePosition(); -#else - // NOTE: See CBasePlayer::BodyTarget - vecSpot = pPlayer->BodyTarget(vecSrc); -#endif - - g_ReGameHookchains.m_RadiusFlash_TraceLine.callChain(RadiusFlash_TraceLine_hook, pPlayer, pevInflictor, pevAttacker, vecSrc, vecSpot, &tr); - - if (tr.flFraction != 1.0f && tr.pHit != pPlayer->pev->pContainingEntity) - continue; - - g_ReGameHookchains.m_RadiusFlash_TraceLine.callChain(RadiusFlash_TraceLine_hook, pPlayer, VARS(tr.pHit), pevAttacker, vecSpot, vecSrc, &tr2); - - if (tr2.flFraction >= 1.0) - { - if (tr.fStartSolid) - { - tr.vecEndPos = vecSrc; - tr.flFraction = 0; - } - - flAdjustedDamage = flDamage - (vecSrc - tr.vecEndPos).Length() * falloff; - - if (flAdjustedDamage < 0) - flAdjustedDamage = 0; - - UTIL_MakeVectors(pPlayer->pev->v_angle); - vecLOS = vecSrc - pPlayer->EarPosition(); - flDot = DotProduct(vecLOS, gpGlobals->v_forward); - - if (flDot < 0) - { - alpha = 200; - fadeTime = flAdjustedDamage * 1.75; - fadeHold = flAdjustedDamage / 3.5; - } - else - { - alpha = 255; - fadeTime = flAdjustedDamage * 3; - fadeHold = flAdjustedDamage / 1.5; - } - - currentHoldTime = pPlayer->m_blindStartTime + pPlayer->m_blindHoldTime - gpGlobals->time; - - if (currentHoldTime > 0.0 && alpha == 255) - fadeHold += currentHoldTime; - - if (pPlayer->m_blindStartTime != 0.0f && pPlayer->m_blindFadeTime != 0.0f) - { - if ((pPlayer->m_blindStartTime + pPlayer->m_blindFadeTime + pPlayer->m_blindHoldTime) > gpGlobals->time) - { - if (pPlayer->m_blindFadeTime > fadeTime) - fadeTime = pPlayer->m_blindFadeTime; - - if (pPlayer->m_blindAlpha >= alpha) - alpha = pPlayer->m_blindAlpha; - } - } - - Vector color(255, 255, 255); - g_ReGameHookchains.m_PlayerBlind.callChain(PlayerBlind, pPlayer, pevInflictor, pevAttacker, fadeTime, fadeHold, alpha, color); - } - } -} - -float GetAmountOfPlayerVisible(Vector vecSrc, CBaseEntity *pEntity) -{ - float retval = 0.0f; - TraceResult tr; - - const float topOfHead = 25.0f; - const float standFeet = 34.0f; - const float crouchFeet = 14.0f; - const float edgeOffset = 13.0f; - - const float damagePercentageChest = 0.40f; - const float damagePercentageHead = 0.20f; - const float damagePercentageFeet = 0.20f; - const float damagePercentageRightSide = 0.10f; - const float damagePercentageLeftSide = 0.10f; - - if (!pEntity->IsPlayer()) - { - // the entity is not a player, so the damage is all or nothing. - UTIL_TraceLine(vecSrc, pEntity->pev->origin, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval = 1.0f; - - return retval; - } - - // check chest - Vector vecChest = pEntity->pev->origin; - UTIL_TraceLine(vecSrc, vecChest, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval += damagePercentageChest; - - // check top of head - Vector vecHead = pEntity->pev->origin + Vector(0, 0, topOfHead); - UTIL_TraceLine(vecSrc, vecHead, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval += damagePercentageHead; - - // check feet - Vector vecFeet = pEntity->pev->origin; - vecFeet.z -= (pEntity->pev->flags & FL_DUCKING) ? crouchFeet : standFeet; - - UTIL_TraceLine(vecSrc, vecFeet, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval += damagePercentageFeet; - - Vector2D dir = (pEntity->pev->origin - vecSrc).Make2D(); - dir.NormalizeInPlace(); - - Vector2D perp(-dir.y * edgeOffset, dir.x * edgeOffset); - Vector vecRightSide = pEntity->pev->origin + Vector(perp.x, perp.y, 0); - Vector vecLeftSide = pEntity->pev->origin - Vector(perp.x, perp.y, 0); - - // check right "edge" - UTIL_TraceLine(vecSrc, vecRightSide, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval += damagePercentageRightSide; - - // check left "edge" - UTIL_TraceLine(vecSrc, vecLeftSide, ignore_monsters, nullptr, &tr); - - if (tr.flFraction == 1.0f) - retval += damagePercentageLeftSide; - - return retval; -} - -void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType) -{ - CBaseEntity *pEntity = nullptr; - TraceResult tr; - float flAdjustedDamage, falloff; - Vector vecSpot; - - if (flRadius) - falloff = flDamage / flRadius; - else - falloff = 1.0; - - int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); - - // in case grenade is lying on the ground - vecSrc.z += 1; - - if (!pevAttacker) - pevAttacker = pevInflictor; - - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, flRadius))) - { - if (pEntity->pev->takedamage != DAMAGE_NO) - { - // UNDONE: this should check a damage mask, not an ignore - if (iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore) - continue; - - // blast's don't tavel into or out of water - if (bInWater && pEntity->pev->waterlevel == 0) - continue; - - if (!bInWater && pEntity->pev->waterlevel == 3) - continue; - - bool useLOS = false; - float damageRatio = 1.0f; - - if ((bitsDamageType & DMG_EXPLOSION) && AreRunningCZero()) - { - useLOS = true; - damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); - } - - damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); - - float length; -#ifdef REGAMEDLL_ADD - // allow to damage breakable objects - if (FClassnameIs(pEntity->pev, "func_breakable")) - length = (vecSrc - pEntity->Center()).Length(); - else -#endif - length = (vecSrc - pEntity->pev->origin).Length(); - - if (useLOS) - { - if (!flRadius) - flRadius = flDamage; - - if (!flDamage) - flRadius = 0; - - flAdjustedDamage = (flRadius - length) * (flRadius - length) * 1.25 / (flRadius * flRadius) * (damageRatio * flDamage) * 1.5; - } - else - { - flAdjustedDamage = flDamage - length * falloff; -#ifdef REGAMEDLL_ADD - // disable grenade damage through walls? - if (hegrenade_penetration.string[0] == '1' && (bitsDamageType & DMG_EXPLOSION)) - { - UTIL_TraceLine(vecSrc, pEntity->pev->origin, ignore_monsters, nullptr, &tr); - - if (tr.flFraction != 1.0f) - flAdjustedDamage = 0.0f; - } -#endif - } - -#ifdef REGAMEDLL_FIXES - // not allow inflict to the player damage is less than 1.0f and to entities not less than 0.0f - if ((pEntity->Classify() == CLASS_PLAYER && flAdjustedDamage < 1.0f) || flAdjustedDamage <= 0.0f) - continue; -#else - if (flAdjustedDamage < 0) - flAdjustedDamage = 0; -#endif - pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType); - } - } -} - -void RadiusDamage2(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType) -{ - CBaseEntity *pEntity = nullptr; - TraceResult tr; - float flAdjustedDamage, falloff; - Vector vecSpot; - - if (flRadius) - falloff = flDamage / flRadius; - else - falloff = 1.0; - - int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); - - // in case grenade is lying on the ground - vecSrc.z += 1; - - if (!pevAttacker) - pevAttacker = pevInflictor; - - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, flRadius))) - { - if (pEntity->pev->takedamage != DAMAGE_NO) - { - // UNDONE: this should check a damage mask, not an ignore - if (iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore) - continue; - - // blast's don't tavel into or out of water - if (bInWater && pEntity->pev->waterlevel == 0) - continue; - - if (!bInWater && pEntity->pev->waterlevel == 3) - continue; - - vecSpot = pEntity->BodyTarget(vecSrc); - UTIL_TraceLine(vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr); - - if (tr.flFraction == 1.0f || tr.pHit == pEntity->edict()) - { - if (tr.fStartSolid) - { - tr.vecEndPos = vecSrc; - tr.flFraction = 0; - } - - flAdjustedDamage = flDamage - (vecSrc - pEntity->pev->origin).Length() * falloff; - - if (flAdjustedDamage < 0) - flAdjustedDamage = 0; - - else if (flAdjustedDamage > 75) - flAdjustedDamage = 75; - - if (tr.flFraction == 1.0f) - pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType); - - else - { - ClearMultiDamage(); - pEntity->TraceAttack(pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType); - ApplyMultiDamage(pevInflictor, pevAttacker); - } - } - } - } -} +#include "precompiled.h" + +void PlayerBlind(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAttacker, float fadeTime, float fadeHold, int alpha, Vector &color) +{ + UTIL_ScreenFade(pPlayer, color, fadeTime, fadeHold, alpha, 0); + + if (!fadetoblack.value) + { + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pObserver = UTIL_PlayerByIndex(i); + if (pObserver && pObserver->IsObservingPlayer(pPlayer)) + { + UTIL_ScreenFade(pObserver, color, fadeTime, fadeHold, alpha, 0); + } + } + } + + pPlayer->Blind(fadeTime * 0.33, fadeHold, fadeTime, alpha); + + if (TheBots) + { + TheBots->OnEvent(EVENT_PLAYER_BLINDED_BY_FLASHBANG, pPlayer); + } +} + +void RadiusFlash_TraceLine_hook(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAttacker, Vector &vecSrc, Vector &vecSpot, TraceResult *tr) +{ + UTIL_TraceLine(vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), tr); +} + +void RadiusFlash(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType) +{ + CBaseEntity *pEntity = nullptr; + TraceResult tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + float flRadius = 1500; + + if (flRadius) + falloff = flDamage / flRadius; + else + falloff = 1; + + int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); + + vecSrc.z += 1; + + while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, 1500.0))) + { + TraceResult tr2; + Vector vecLOS; + float flDot; + float fadeTime; + float fadeHold; + int alpha; + CBasePlayer *pPlayer; + float currentHoldTime; + + if (!pEntity->IsPlayer()) + continue; + + pPlayer = (CBasePlayer *)pEntity; + + if (pPlayer->pev->takedamage == DAMAGE_NO || pPlayer->pev->deadflag != DEAD_NO) + continue; + + if (bInWater && pPlayer->pev->waterlevel == 0) + continue; + + if (!bInWater && pPlayer->pev->waterlevel == 3) + continue; + +#ifdef REGAMEDLL_FIXES + vecSpot = pPlayer->EyePosition(); +#else + // NOTE: See CBasePlayer::BodyTarget + vecSpot = pPlayer->BodyTarget(vecSrc); +#endif + + g_ReGameHookchains.m_RadiusFlash_TraceLine.callChain(RadiusFlash_TraceLine_hook, pPlayer, pevInflictor, pevAttacker, vecSrc, vecSpot, &tr); + + if (tr.flFraction != 1.0f && tr.pHit != pPlayer->pev->pContainingEntity) + continue; + + g_ReGameHookchains.m_RadiusFlash_TraceLine.callChain(RadiusFlash_TraceLine_hook, pPlayer, VARS(tr.pHit), pevAttacker, vecSpot, vecSrc, &tr2); + + if (tr2.flFraction >= 1.0) + { + if (tr.fStartSolid) + { + tr.vecEndPos = vecSrc; + tr.flFraction = 0; + } + + flAdjustedDamage = flDamage - (vecSrc - tr.vecEndPos).Length() * falloff; + + if (flAdjustedDamage < 0) + flAdjustedDamage = 0; + + UTIL_MakeVectors(pPlayer->pev->v_angle); + vecLOS = vecSrc - pPlayer->EarPosition(); + flDot = DotProduct(vecLOS, gpGlobals->v_forward); + + if (flDot < 0) + { + alpha = 200; + fadeTime = flAdjustedDamage * 1.75; + fadeHold = flAdjustedDamage / 3.5; + } + else + { + alpha = 255; + fadeTime = flAdjustedDamage * 3; + fadeHold = flAdjustedDamage / 1.5; + } + + currentHoldTime = pPlayer->m_blindStartTime + pPlayer->m_blindHoldTime - gpGlobals->time; + + if (currentHoldTime > 0.0 && alpha == 255) + fadeHold += currentHoldTime; + + if (pPlayer->m_blindStartTime != 0.0f && pPlayer->m_blindFadeTime != 0.0f) + { + if ((pPlayer->m_blindStartTime + pPlayer->m_blindFadeTime + pPlayer->m_blindHoldTime) > gpGlobals->time) + { + if (pPlayer->m_blindFadeTime > fadeTime) + fadeTime = pPlayer->m_blindFadeTime; + + if (pPlayer->m_blindAlpha >= alpha) + alpha = pPlayer->m_blindAlpha; + } + } + + Vector color(255, 255, 255); + g_ReGameHookchains.m_PlayerBlind.callChain(PlayerBlind, pPlayer, pevInflictor, pevAttacker, fadeTime, fadeHold, alpha, color); + } + } +} + +float GetAmountOfPlayerVisible(Vector vecSrc, CBaseEntity *pEntity) +{ + float retval = 0.0f; + TraceResult tr; + + const float topOfHead = 25.0f; + const float standFeet = 34.0f; + const float crouchFeet = 14.0f; + const float edgeOffset = 13.0f; + + const float damagePercentageChest = 0.40f; + const float damagePercentageHead = 0.20f; + const float damagePercentageFeet = 0.20f; + const float damagePercentageRightSide = 0.10f; + const float damagePercentageLeftSide = 0.10f; + + if (!pEntity->IsPlayer()) + { + // the entity is not a player, so the damage is all or nothing. + UTIL_TraceLine(vecSrc, pEntity->pev->origin, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval = 1.0f; + + return retval; + } + + // check chest + Vector vecChest = pEntity->pev->origin; + UTIL_TraceLine(vecSrc, vecChest, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval += damagePercentageChest; + + // check top of head + Vector vecHead = pEntity->pev->origin + Vector(0, 0, topOfHead); + UTIL_TraceLine(vecSrc, vecHead, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval += damagePercentageHead; + + // check feet + Vector vecFeet = pEntity->pev->origin; + vecFeet.z -= (pEntity->pev->flags & FL_DUCKING) ? crouchFeet : standFeet; + + UTIL_TraceLine(vecSrc, vecFeet, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval += damagePercentageFeet; + + Vector2D dir = (pEntity->pev->origin - vecSrc).Make2D(); + dir.NormalizeInPlace(); + + Vector2D perp(-dir.y * edgeOffset, dir.x * edgeOffset); + Vector vecRightSide = pEntity->pev->origin + Vector(perp.x, perp.y, 0); + Vector vecLeftSide = pEntity->pev->origin - Vector(perp.x, perp.y, 0); + + // check right "edge" + UTIL_TraceLine(vecSrc, vecRightSide, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval += damagePercentageRightSide; + + // check left "edge" + UTIL_TraceLine(vecSrc, vecLeftSide, ignore_monsters, nullptr, &tr); + + if (tr.flFraction == 1.0f) + retval += damagePercentageLeftSide; + + return retval; +} + +void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType) +{ + CBaseEntity *pEntity = nullptr; + TraceResult tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + + if (flRadius) + falloff = flDamage / flRadius; + else + falloff = 1.0; + + int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); + + // in case grenade is lying on the ground + vecSrc.z += 1; + + if (!pevAttacker) + pevAttacker = pevInflictor; + + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, flRadius))) + { + if (pEntity->pev->takedamage != DAMAGE_NO) + { + // UNDONE: this should check a damage mask, not an ignore + if (iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore) + continue; + + // blast's don't tavel into or out of water + if (bInWater && pEntity->pev->waterlevel == 0) + continue; + + if (!bInWater && pEntity->pev->waterlevel == 3) + continue; + + bool useLOS = false; + float damageRatio = 1.0f; + + if ((bitsDamageType & DMG_EXPLOSION) && AreRunningCZero()) + { + useLOS = true; + damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); + } + + damageRatio = GetAmountOfPlayerVisible(vecSrc, pEntity); + + float length; +#ifdef REGAMEDLL_ADD + // allow to damage breakable objects + if (FClassnameIs(pEntity->pev, "func_breakable")) + length = (vecSrc - pEntity->Center()).Length(); + else +#endif + length = (vecSrc - pEntity->pev->origin).Length(); + + if (useLOS) + { + if (!flRadius) + flRadius = flDamage; + + if (!flDamage) + flRadius = 0; + + flAdjustedDamage = (flRadius - length) * (flRadius - length) * 1.25 / (flRadius * flRadius) * (damageRatio * flDamage) * 1.5; + } + else + { + flAdjustedDamage = flDamage - length * falloff; +#ifdef REGAMEDLL_ADD + // disable grenade damage through walls? + if (hegrenade_penetration.string[0] == '1' && (bitsDamageType & DMG_EXPLOSION)) + { + UTIL_TraceLine(vecSrc, pEntity->pev->origin, ignore_monsters, nullptr, &tr); + + if (tr.flFraction != 1.0f) + flAdjustedDamage = 0.0f; + } +#endif + } + +#ifdef REGAMEDLL_FIXES + // not allow inflict to the player damage is less than 1.0f and to entities not less than 0.0f + if ((pEntity->Classify() == CLASS_PLAYER && flAdjustedDamage < 1.0f) || flAdjustedDamage <= 0.0f) + continue; +#else + if (flAdjustedDamage < 0) + flAdjustedDamage = 0; +#endif + pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType); + } + } +} + +void RadiusDamage2(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType) +{ + CBaseEntity *pEntity = nullptr; + TraceResult tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + + if (flRadius) + falloff = flDamage / flRadius; + else + falloff = 1.0; + + int bInWater = (UTIL_PointContents(vecSrc) == CONTENTS_WATER); + + // in case grenade is lying on the ground + vecSrc.z += 1; + + if (!pevAttacker) + pevAttacker = pevInflictor; + + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere(pEntity, vecSrc, flRadius))) + { + if (pEntity->pev->takedamage != DAMAGE_NO) + { + // UNDONE: this should check a damage mask, not an ignore + if (iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore) + continue; + + // blast's don't tavel into or out of water + if (bInWater && pEntity->pev->waterlevel == 0) + continue; + + if (!bInWater && pEntity->pev->waterlevel == 3) + continue; + + vecSpot = pEntity->BodyTarget(vecSrc); + UTIL_TraceLine(vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr); + + if (tr.flFraction == 1.0f || tr.pHit == pEntity->edict()) + { + if (tr.fStartSolid) + { + tr.vecEndPos = vecSrc; + tr.flFraction = 0; + } + + flAdjustedDamage = flDamage - (vecSrc - pEntity->pev->origin).Length() * falloff; + + if (flAdjustedDamage < 0) + flAdjustedDamage = 0; + + else if (flAdjustedDamage > 75) + flAdjustedDamage = 75; + + if (tr.flFraction == 1.0f) + pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType); + + else + { + ClearMultiDamage(); + pEntity->TraceAttack(pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType); + ApplyMultiDamage(pevInflictor, pevAttacker); + } + } + } + } +} diff --git a/regamedll/dlls/extdll.h b/regamedll/dlls/extdll.h index 63baf4d3..1db8e3d0 100644 --- a/regamedll/dlls/extdll.h +++ b/regamedll/dlls/extdll.h @@ -1,85 +1,85 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#pragma warning(disable:4244) // int or float down-conversion -#pragma warning(disable:4305) // int or float data truncation -#pragma warning(disable:4201) // nameless struct/union -#pragma warning(disable:4514) // unreferenced inline function removed -#pragma warning(disable:4100) // unreferenced formal parameter - -#include "archtypes.h" -#include "maintypes.h" -#include "strtools.h" - -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - #define NOWINRES - #define NOSERVICE - #define NOMCX - #define NOIME - #include "winsani_in.h" - #include "windows.h" - #include "winsani_out.h" - #undef PlaySound -#else - #include - #include - #include -#endif // _WIN32 - -// Misc C-runtime library headers -#include "stdio.h" -#include "stdlib.h" -#include "math.h" - -// Header file containing definition of globalvars_t and entvars_t -typedef int EOFFSET; // More explicit than "int" -typedef unsigned int func_t; -typedef float vec_t; // needed before including progdefs.h - -// Vector class -#include "vector.h" - -// Defining it as a (bogus) struct helps enforce type-checking -#define vec3_t Vector - -// QString class -#include "qstring.h" - -// Shared engine/DLL constants -#include "const.h" -#include "edict.h" - -// Shared header describing protocol between engine and DLLs -#include "eiface.h" - -// Shared header between the client DLL and the game DLLs -#include "cdll_dll.h" -#include "extdef.h" +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#pragma warning(disable:4244) // int or float down-conversion +#pragma warning(disable:4305) // int or float data truncation +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4514) // unreferenced inline function removed +#pragma warning(disable:4100) // unreferenced formal parameter + +#include "archtypes.h" +#include "maintypes.h" +#include "strtools.h" + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + #define NOWINRES + #define NOSERVICE + #define NOMCX + #define NOIME + #include "winsani_in.h" + #include "windows.h" + #include "winsani_out.h" + #undef PlaySound +#else + #include + #include + #include +#endif // _WIN32 + +// Misc C-runtime library headers +#include "stdio.h" +#include "stdlib.h" +#include "math.h" + +// Header file containing definition of globalvars_t and entvars_t +typedef int EOFFSET; // More explicit than "int" +typedef unsigned int func_t; +typedef float vec_t; // needed before including progdefs.h + +// Vector class +#include "vector.h" + +// Defining it as a (bogus) struct helps enforce type-checking +#define vec3_t Vector + +// QString class +#include "qstring.h" + +// Shared engine/DLL constants +#include "const.h" +#include "edict.h" + +// Shared header describing protocol between engine and DLLs +#include "eiface.h" + +// Shared header between the client DLL and the game DLLs +#include "cdll_dll.h" +#include "extdef.h" diff --git a/regamedll/dlls/func_break.cpp b/regamedll/dlls/func_break.cpp index 5cc9cd40..3178daf4 100644 --- a/regamedll/dlls/func_break.cpp +++ b/regamedll/dlls/func_break.cpp @@ -1,1103 +1,1103 @@ -#include "precompiled.h" - -// Just add more items to the bottom of this array and they will automagically be supported -// This is done instead of just a classname in the FGD so we can control which entities can -// be spawned, and still remain fairly flexible -const char *CBreakable::m_pszSpawnObjects[] = -{ - nullptr, - "item_battery", - "item_healthkit", - "weapon_9mmhandgun", - "ammo_9mmclip", - "weapon_9mmAR", - "ammo_9mmAR", - "ammo_ARgrenades", - "weapon_shotgun", - "ammo_buckshot", - "weapon_crossbow", - "ammo_crossbow", - "weapon_357", - "ammo_357", - "weapon_rpg", - "ammo_rpgclip", - "ammo_gaussclip", - "weapon_handgrenade", - "weapon_tripmine", - "weapon_satchel", - "weapon_snark", - "weapon_hornetgun", - "weapon_usp", - "weapon_glock18", - "weapon_awp", - "weapon_mp5n", - "weapon_m249", - "weapon_m3", - "weapon_m4a1", - "weapon_tmp", - "weapon_g3sg1", - "weapon_flashbang" -}; - -const char *CBreakable::m_pszSoundsWood[] = -{ - "debris/wood1.wav", - "debris/wood2.wav", - "debris/wood3.wav" -}; - -const char *CBreakable::m_pszSoundsFlesh[] = -{ - "debris/flesh1.wav", - "debris/flesh2.wav", - "debris/flesh3.wav", - "debris/flesh5.wav", - "debris/flesh6.wav", - "debris/flesh7.wav" -}; - -const char *CBreakable::m_pszSoundsMetal[] = -{ - "debris/metal1.wav", - "debris/metal2.wav", - "debris/metal3.wav" -}; - -const char *CBreakable::m_pszSoundsConcrete[] = -{ - "debris/concrete1.wav", - "debris/concrete2.wav", - "debris/concrete3.wav" -}; - -const char *CBreakable::m_pszSoundsGlass[] = -{ - "debris/glass1.wav", - "debris/glass2.wav", - "debris/glass3.wav" -}; - -TYPEDESCRIPTION CBreakable::m_SaveData[] = -{ - DEFINE_FIELD(CBreakable, m_Material, FIELD_INTEGER), - DEFINE_FIELD(CBreakable, m_Explosion, FIELD_INTEGER), - DEFINE_FIELD(CBreakable, m_angle, FIELD_FLOAT), - DEFINE_FIELD(CBreakable, m_iszGibModel, FIELD_STRING), - DEFINE_FIELD(CBreakable, m_iszSpawnObject, FIELD_STRING), -}; - -LINK_ENTITY_TO_CLASS(func_breakable, CBreakable, CCSBreakable) -IMPLEMENT_SAVERESTORE(CBreakable, CBaseEntity) - -void CBreakable::Spawn() -{ - Precache(); - - if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) - pev->takedamage = DAMAGE_NO; - else - pev->takedamage = DAMAGE_YES; - - m_flHealth = pev->health; - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - m_angle = pev->angles.y; - pev->angles.y = 0; - - // HACK: matGlass can receive decals, we need the client to know about this - // so use class to store the material flag - if (m_Material == matGlass) - { - pev->playerclass = 1; - } - - // set size and link into world. - SET_MODEL(ENT(pev), STRING(pev->model)); - - SetTouch(&CBreakable::BreakTouch); - - // Only break on trigger - if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) - { - SetTouch(nullptr); - } - - // Flag unbreakable glass as "worldbrush" so it will block ALL tracelines - if (!IsBreakable() && pev->rendermode != kRenderNormal) - { - pev->flags |= FL_WORLDBRUSH; - } -} - -void CBreakable::Restart() -{ - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - pev->deadflag = DEAD_NO; - - if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) - pev->takedamage = DAMAGE_NO; - else - pev->takedamage = DAMAGE_YES; - - pev->health = m_flHealth; - pev->effects &= ~EF_NODRAW; - m_angle = pev->angles.y; - pev->angles.y = 0; - - SET_MODEL(ENT(pev), STRING(pev->model)); - SetTouch(&CBreakable::BreakTouch); - - if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) - { - SetTouch(nullptr); - } - - if (!IsBreakable() && pev->rendermode != kRenderNormal) - { - pev->flags |= FL_WORLDBRUSH; - } -} - -void CBreakable::KeyValue(KeyValueData *pkvd) -{ - // UNDONE_WC: explicitly ignoring these fields, but they shouldn't be in the map file! - if (FStrEq(pkvd->szKeyName, "explosion")) - { - if (!Q_stricmp(pkvd->szValue, "directed")) - m_Explosion = expDirected; - - else if (!Q_stricmp(pkvd->szValue, "random")) - m_Explosion = expRandom; - else - m_Explosion = expRandom; - - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "material")) - { - Materials type = (Materials)Q_atoi(pkvd->szValue); - - // 0:glass, 1:wood, 2:metal, 3:flesh etc - if (type < 0 || type >= matLastMaterial) - m_Material = matWood; - else - m_Material = type; - - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "deadmodel")) - { - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "shards")) - { - //m_iShards = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "gibmodel")) - { - m_iszGibModel = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spawnobject")) - { - int object = Q_atoi(pkvd->szValue); - if (object > 0 && object < ARRAYSIZE(m_pszSpawnObjects)) - { - m_iszSpawnObject = MAKE_STRING(m_pszSpawnObjects[object]); - } - - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "explodemagnitude")) - { - ExplosionSetMagnitude(Q_atoi(pkvd->szValue)); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "lip")) - { - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue(pkvd); - } -} - -const char **CBreakable::MaterialSoundList(Materials precacheMaterial, int &soundCount) -{ - const char **pSoundList; - - switch (precacheMaterial) - { - case matWood: - { - pSoundList = m_pszSoundsWood; - soundCount = ARRAYSIZE(m_pszSoundsWood); - break; - } - case matFlesh: - { - pSoundList = m_pszSoundsFlesh; - soundCount = ARRAYSIZE(m_pszSoundsFlesh); - break; - } - case matGlass: - case matComputer: - case matUnbreakableGlass: - { - pSoundList = m_pszSoundsGlass; - soundCount = ARRAYSIZE(m_pszSoundsGlass); - break; - } - case matMetal: - { - pSoundList = m_pszSoundsMetal; - soundCount = ARRAYSIZE(m_pszSoundsMetal); - break; - } - case matCinderBlock: - case matRocks: - { - pSoundList = m_pszSoundsConcrete; - soundCount = ARRAYSIZE(m_pszSoundsConcrete); - break; - } - case matCeilingTile: - case matNone: - default: - pSoundList = nullptr; - soundCount = 0; - break; - } - - return pSoundList; -} - -void CBreakable::MaterialSoundPrecache(Materials precacheMaterial) -{ - const char **pSoundList; - int i, soundCount = 0; - - pSoundList = MaterialSoundList(precacheMaterial, soundCount); - - for (i = 0; i < soundCount; i++) - { - PRECACHE_SOUND((char *)pSoundList[i]); - } -} - -void CBreakable::MaterialSoundRandom(edict_t *pEdict, Materials soundMaterial, float volume) -{ - int soundCount = 0; - const char **pSoundList = MaterialSoundList(soundMaterial, soundCount); - - if (soundCount) - { - EMIT_SOUND(pEdict, CHAN_BODY, pSoundList[RANDOM_LONG(0, soundCount - 1)], volume, 1.0); - } -} - -void CBreakable::Precache() -{ - const char *pGibName = nullptr; - - switch (m_Material) - { - case matWood: - pGibName = "models/woodgibs.mdl"; - - PRECACHE_SOUND("debris/bustcrate1.wav"); - PRECACHE_SOUND("debris/bustcrate2.wav"); - break; - case matFlesh: - pGibName = "models/fleshgibs.mdl"; - - PRECACHE_SOUND("debris/bustflesh1.wav"); - PRECACHE_SOUND("debris/bustflesh2.wav"); - break; - case matComputer: - PRECACHE_SOUND("buttons/spark5.wav"); - PRECACHE_SOUND("buttons/spark6.wav"); - pGibName = "models/computergibs.mdl"; - - PRECACHE_SOUND("debris/bustmetal1.wav"); - PRECACHE_SOUND("debris/bustmetal2.wav"); - break; - case matGlass: - case matUnbreakableGlass: - pGibName = "models/glassgibs.mdl"; - - PRECACHE_SOUND("debris/bustglass1.wav"); - PRECACHE_SOUND("debris/bustglass2.wav"); - break; - case matMetal: - pGibName = "models/metalplategibs.mdl"; - - PRECACHE_SOUND("debris/bustmetal1.wav"); - PRECACHE_SOUND("debris/bustmetal2.wav"); - break; - case matCinderBlock: - pGibName = "models/cindergibs.mdl"; - - PRECACHE_SOUND("debris/bustconcrete1.wav"); - PRECACHE_SOUND("debris/bustconcrete2.wav"); - break; - case matRocks: - pGibName = "models/rockgibs.mdl"; - - PRECACHE_SOUND("debris/bustconcrete1.wav"); - PRECACHE_SOUND("debris/bustconcrete2.wav"); - break; - case matCeilingTile: - pGibName = "models/ceilinggibs.mdl"; - - PRECACHE_SOUND("debris/bustceiling.wav"); - break; - } - - MaterialSoundPrecache(m_Material); - - if (m_iszGibModel) - { - pGibName = STRING(m_iszGibModel); - } - - if (pGibName) - { - m_idShard = PRECACHE_MODEL(pGibName); - } - - // Precache the spawn item's data - if (m_iszSpawnObject) - { - UTIL_PrecacheOther(STRING(m_iszSpawnObject)); - } -} - -void CBreakable::DamageSound() -{ - int pitch; - float fvol; - char *rgpsz[6]; - int i; - int material = m_Material; - - if (RANDOM_LONG(0, 2)) - pitch = PITCH_NORM; - else - pitch = 95 + RANDOM_LONG(0, 34); - - fvol = RANDOM_FLOAT(0.75, 1.0); - -#ifndef REGAMEDLL_FIXES - if (material == matComputer && RANDOM_LONG(0, 1)) - material = matMetal; -#endif - - switch (material) - { - case matGlass: - case matComputer: - case matUnbreakableGlass: - rgpsz[0] = "debris/glass1.wav"; - rgpsz[1] = "debris/glass2.wav"; - rgpsz[2] = "debris/glass3.wav"; - i = 3; - break; - - case matWood: - rgpsz[0] = "debris/wood1.wav"; - rgpsz[1] = "debris/wood2.wav"; - rgpsz[2] = "debris/wood3.wav"; - i = 3; - break; - - case matMetal: - rgpsz[0] = "debris/metal1.wav"; - rgpsz[1] = "debris/metal3.wav"; - rgpsz[2] = "debris/metal2.wav"; -#ifdef REGAMEDLL_FIXES - i = 3; -#else - i = 2; -#endif - break; - - case matFlesh: - rgpsz[0] = "debris/flesh1.wav"; - rgpsz[1] = "debris/flesh2.wav"; - rgpsz[2] = "debris/flesh3.wav"; - rgpsz[3] = "debris/flesh5.wav"; - rgpsz[4] = "debris/flesh6.wav"; - rgpsz[5] = "debris/flesh7.wav"; - i = 6; - break; - - case matRocks: - case matCinderBlock: - rgpsz[0] = "debris/concrete1.wav"; - rgpsz[1] = "debris/concrete2.wav"; - rgpsz[2] = "debris/concrete3.wav"; - i = 3; - break; - - case matCeilingTile: - // UNDONE: no ceiling tile shard sound yet - i = 0; - break; - } - - if (i) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, rgpsz[RANDOM_LONG(0, i - 1)], fvol, ATTN_NORM, 0, pitch); - } -} - -void CBreakable::BreakTouch(CBaseEntity *pOther) -{ - float flDamage; - entvars_t *pevToucher = pOther->pev; - - // only players can break these right now - if (!pOther->IsPlayer() || !IsBreakable()) - { - if (pev->rendermode == kRenderNormal || !FClassnameIs(pOther->pev, "grenade")) - return; - - pev->angles.y = m_angle; - UTIL_MakeVectors(pev->angles); - - g_vecAttackDir = gpGlobals->v_forward; - -#ifndef REGAMEDLL_FIXES - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - pev->effects = EF_NODRAW; -#endif - - Die(); - } - - // can be broken when run into - if (pev->spawnflags & SF_BREAK_TOUCH) - { - flDamage = pevToucher->velocity.Length() * 0.01f; - - if (flDamage >= pev->health) - { - SetTouch(nullptr); - TakeDamage(pevToucher, pevToucher, flDamage, DMG_CRUSH); - - // do a little damage to player if we broke glass or computer - pOther->TakeDamage(pev, pev, flDamage / 4, DMG_SLASH); - } - } - - // can be broken when stood upon - if ((pev->spawnflags & SF_BREAK_PRESSURE) && pevToucher->absmin.z >= pev->maxs.z - 2) - { - // play creaking sound here. - DamageSound(); - - SetThink(&CBreakable::Die); - SetTouch(nullptr); - - // BUGBUG: why doesn't zero delay work? - if (m_flDelay == 0.0f) - { - m_flDelay = 0.1f; - } - - pev->nextthink = pev->ltime + m_flDelay; - } -} - -// Smash the our breakable object -// Break when triggered -void CBreakable::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - if (IsBreakable()) - { - pev->angles.y = m_angle; - UTIL_MakeVectors(pev->angles); - g_vecAttackDir = gpGlobals->v_forward; - -#ifndef REGAMEDLL_FIXES - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - pev->effects = EF_NODRAW; -#endif - - Die(); - } -} - -void CBreakable::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - // random spark if this is a 'computer' object - if (RANDOM_LONG(0, 1)) - { - switch (m_Material) - { - case matComputer: - { - UTIL_Sparks(ptr->vecEndPos); - - //random volume range - float flVolume = RANDOM_FLOAT(0.7 , 1.0); - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break; - } - - break; - } - case matUnbreakableGlass: - { - UTIL_Ricochet(ptr->vecEndPos, RANDOM_FLOAT(0.5, 1.5)); - break; - } - } - } - - CBaseDelay::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); -} - -// Special takedamage for func_breakable. Allows us to make -// exceptions that are breakable-specific -// bitsDamageType indicates the type of damage sustained ie: DMG_CRUSH -BOOL CBreakable::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - Vector vecTemp; - - // if Attacker == Inflictor, the attack was a melee or other instant-hit attack. - // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). - if (pevAttacker == pevInflictor) - { - vecTemp = pevInflictor->origin - (pev->absmin + (pev->size * 0.5f)); - - // if a client hit the breakable with a crowbar, and breakable is crowbar-sensitive, break it now. - if ((pevAttacker->flags & FL_CLIENT) && (pev->spawnflags & SF_BREAK_CROWBAR) && (bitsDamageType & DMG_CLUB)) - { - flDamage = pev->health; - } - } - else - { - // an actual missile was involved. - vecTemp = pevInflictor->origin - (pev->absmin + (pev->size * 0.5f)); - } - - if (!IsBreakable()) - return FALSE; - - // Breakables take double damage from the crowbar - if (bitsDamageType & DMG_CLUB) - { - flDamage *= 2.0f; - } - - // Boxes / glass / etc. don't take much poison damage, just the impact of the dart - consider that 10% - if (bitsDamageType & DMG_POISON) - { - flDamage *= 0.1f; - } - - // this global is still used for glass and other non-monster killables, along with decals. - g_vecAttackDir = vecTemp.Normalize(); - - // do the damage - pev->health -= flDamage; - - if (pev->health <= 0) - { -#ifndef REGAMEDLL_FIXES - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - pev->effects = EF_NODRAW; -#endif - Die(); - - if (m_flDelay == 0.0f) - { - m_flDelay = 0.1f; - } - - pev->nextthink = pev->ltime + m_flDelay; - return FALSE; - } - - // Make a shard noise each time func breakable is hit. - // Don't play shard noise if cbreakable actually died. - DamageSound(); - return TRUE; -} - -void CBreakable::Die() -{ - Vector vecSpot; // shard origin - Vector vecVelocity; // shard velocity - CBaseEntity *pEntity = nullptr; - char cFlag = 0; - int pitch; - float fvol; - -#ifdef REGAMEDLL_FIXES - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - pev->effects = EF_NODRAW; -#endif - - pitch = 95 + RANDOM_LONG(0, 29); - - if (pitch > 97 && pitch < 103) - pitch = 100; - - // The more negative pev->health, the louder - // the sound should be. - -#ifdef REGAMEDLL_FIXES - fvol = RANDOM_FLOAT(0.85f, 1.0f) + (Q_abs(pev->health) / 100.0f); -#else - fvol = RANDOM_FLOAT(0.85f, 1.0f) + (Q_abs(int(pev->health)) / 100.0f); -#endif - - if (fvol > 1.0f) - fvol = 1.0f; - - switch (m_Material) - { - case matGlass: - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch); - break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - cFlag = BREAK_GLASS; - - if (TheBots) - { - TheBots->OnEvent(EVENT_BREAK_GLASS, this); - } - break; - case matWood: - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch); - break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - cFlag = BREAK_WOOD; - - if (TheBots) - { - TheBots->OnEvent(EVENT_BREAK_WOOD, this); - } - break; - - case matMetal: - case matComputer: - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch); - break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - cFlag = BREAK_METAL; - - if (TheBots) - { - TheBots->OnEvent(EVENT_BREAK_METAL, this); - } - break; - - case matFlesh: - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch); - break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - cFlag = BREAK_FLESH; - - if (TheBots) - { - TheBots->OnEvent(EVENT_BREAK_FLESH, this); - } - break; - - case matCinderBlock: - case matRocks: - switch (RANDOM_LONG(0, 1)) - { - case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch); - break; - case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - cFlag = BREAK_CONCRETE; - - if (TheBots) - { - TheBots->OnEvent(EVENT_BREAK_CONCRETE, this); - } - break; - - case matCeilingTile: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch); - break; - } - - if (m_Explosion == expDirected) - { - vecVelocity = g_vecAttackDir * 200.0f; - } - else - { - vecVelocity.x = 0; - vecVelocity.y = 0; - vecVelocity.z = 0; - } - - vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5f; - - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecSpot); - WRITE_BYTE(TE_BREAKMODEL); - WRITE_COORD(vecSpot.x); // position - WRITE_COORD(vecSpot.y); - WRITE_COORD(vecSpot.z); - WRITE_COORD(pev->size.x); // size - WRITE_COORD(pev->size.y); - WRITE_COORD(pev->size.z); - WRITE_COORD(vecVelocity.x); // velocity - WRITE_COORD(vecVelocity.y); - WRITE_COORD(vecVelocity.z); - WRITE_BYTE(10); // randomization - WRITE_SHORT(m_idShard); // model id# - WRITE_BYTE(0); // # of shards, let client decide - WRITE_BYTE(25); // duration, 2.5 seconds - WRITE_BYTE(cFlag); // flags - MESSAGE_END(); - - float size = pev->size.x; - - if (size < pev->size.y) - size = pev->size.y; - - if (size < pev->size.z) - size = pev->size.z; - - Vector mins = pev->absmin; - Vector maxs = pev->absmax; - mins.z = pev->absmax.z; - maxs.z += 8; - - CBaseEntity *pList[256]; - int count = UTIL_EntitiesInBox(pList, ARRAYSIZE(pList), mins, maxs, FL_ONGROUND); - - for (int i = 0; i < count; i++) - { - pList[i]->pev->flags &= ~FL_ONGROUND; - pList[i]->pev->groundentity = nullptr; - } - - pev->solid = SOLID_NOT; - SUB_UseTargets(nullptr, USE_TOGGLE, 0); - SetThink(nullptr); - - pev->nextthink = pev->ltime + 0.1f; - - if (m_iszSpawnObject) - { - // TODO: Implement a list of entities to remove them on restart round - auto pItem = CBaseEntity::Create((char *)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, edict()); - -#ifdef REGAMEDLL_FIXES - // FIX: entity leak! - if (pItem) - { - pItem->pev->spawnflags |= SF_NORESPAWN; - pItem->SetThink(&CBaseEntity::SUB_Remove); - pItem->pev->nextthink = gpGlobals->time + CGameRules::GetItemKillDelay(); - } -#endif - - } - - if (Explodable()) - { - ExplosionCreate(Center(), pev->angles, edict(), ExplosionMagnitude(), TRUE); - } -} - -BOOL CBreakable::IsBreakable() -{ - return m_Material != matUnbreakableGlass; -} - -int CBreakable::DamageDecal(int bitsDamageType) -{ - if (m_Material == matGlass) - return DECAL_GLASSBREAK1 + RANDOM_LONG(0, 2); - - if (m_Material == matUnbreakableGlass) - return DECAL_BPROOF1; - - return CBaseEntity::DamageDecal(bitsDamageType); -} - -TYPEDESCRIPTION CPushable::m_SaveData[] = -{ - DEFINE_FIELD(CPushable, m_maxSpeed, FIELD_FLOAT), - DEFINE_FIELD(CPushable, m_soundTime, FIELD_TIME), -}; - -const char *CPushable::m_soundNames[] = -{ - "debris/pushbox1.wav", - "debris/pushbox2.wav", - "debris/pushbox3.wav" -}; - -LINK_ENTITY_TO_CLASS(func_pushable, CPushable, CCSPushable) -IMPLEMENT_SAVERESTORE(CPushable, CBreakable) - -void CPushable::Spawn() -{ - if (pev->spawnflags & SF_PUSH_BREAKABLE) - CBreakable::Spawn(); - else - Precache(); - - pev->movetype = MOVETYPE_PUSHSTEP; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), STRING(pev->model)); - - if (pev->friction > 399) - { - pev->friction = 399; - } - - m_maxSpeed = 400 - pev->friction; - - pev->flags |= FL_FLOAT; - pev->friction = 0; - - // Pick up off of the floor - pev->origin.z += 1; - UTIL_SetOrigin(pev, pev->origin); - -#ifdef REGAMEDLL_FIXES - pev->oldorigin = pev->origin; -#endif - - // Multiply by area of the box's cross-section (assume 1000 units^3 standard volume) - pev->skin = int((pev->skin * (pev->maxs.x - pev->mins.x) * (pev->maxs.y - pev->mins.y)) * 0.0005); - m_soundTime = 0; -} - -void CPushable::Precache() -{ - for (int i = 0; i < ARRAYSIZE(m_soundNames); i++) - { - PRECACHE_SOUND(m_soundNames[i]); - } - - if (pev->spawnflags & SF_PUSH_BREAKABLE) - { - CBreakable::Precache(); - } -} - -#ifdef REGAMEDLL_FIXES -void CPushable::Restart() -{ - if (pev->spawnflags & SF_PUSH_BREAKABLE) - CBreakable::Restart(); - - pev->movetype = MOVETYPE_PUSHSTEP; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), STRING(pev->model)); - - if (pev->friction > 399) - pev->friction = 399; - - m_soundTime = 0; - m_maxSpeed = 400 - pev->friction; - - pev->flags |= FL_FLOAT; - pev->friction = 0; - - UTIL_SetOrigin(pev, pev->oldorigin); -} -#endif - -void CPushable::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "size")) - { - int bbox = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - - switch (bbox) - { - case 0: // Point - UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8)); - break; - - case 2: // TODO: Big Hull? BUGBUG: Figure out what this hull really is - UTIL_SetSize(pev, VEC_DUCK_HULL_MIN * 2, VEC_DUCK_HULL_MAX * 2); - break; - - case 3: // Player duck - UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); - break; - - default: - case 1: // Player - UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); - break; - } - } - else if (FStrEq(pkvd->szKeyName, "buoyancy")) - { - pev->skin = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBreakable::KeyValue(pkvd); - } -} - -// Pull the func_pushable -void CPushable::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - if (!pActivator || !pActivator->IsPlayer()) - { - if (pev->spawnflags & SF_PUSH_BREAKABLE) - { - this->CBreakable::Use(pActivator, pCaller, useType, value); - } - - return; - } - - if (pActivator->pev->velocity != g_vecZero) - { - Move(pActivator, 0); - } -} - -void CPushable::Touch(CBaseEntity *pOther) -{ - if (FClassnameIs(pOther->pev, "worldspawn")) - return; - - Move(pOther, 1); -} - -void CPushable::Move(CBaseEntity *pOther, int push) -{ - entvars_t *pevToucher = pOther->pev; - bool bPlayerTouch = false; - - // Is entity standing on this pushable ? - if ((pevToucher->flags & FL_ONGROUND) && pevToucher->groundentity && VARS(pevToucher->groundentity) == pev) - { - // Only push if floating - if (pev->waterlevel > 0) - { - pev->velocity.z += pevToucher->velocity.z * 0.1f; - } - - return; - } - - if (pOther->IsPlayer()) - { - // Don't push unless the player is pushing forward and NOT use (pull) - if (push && !(pevToucher->button & (IN_FORWARD | IN_USE))) - { - return; - } - - bPlayerTouch = true; - } - - real_t factor; - - if (bPlayerTouch) - { - // Don't push away from jumping/falling players unless in water - if (!(pevToucher->flags & FL_ONGROUND)) - { - if (pev->waterlevel < 1) - return; - else - factor = 0.1f; - } - else - factor = 1.0f; - } - else - { - factor = 0.25f; - } - - pev->velocity.x += pevToucher->velocity.x * factor; - pev->velocity.y += pevToucher->velocity.y * factor; - - real_t length = Q_sqrt(pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y); - - if (push && (length > MaxSpeed())) - { - pev->velocity.x = (pev->velocity.x * MaxSpeed() / length); - pev->velocity.y = (pev->velocity.y * MaxSpeed() / length); - } - - if (bPlayerTouch) - { -// do not push player along with entity -#ifndef REGAMEDLL_FIXES - pevToucher->velocity.x = pev->velocity.x; - pevToucher->velocity.y = pev->velocity.y; -#endif // REGAMEDLL_FIXES - - if ((gpGlobals->time - m_soundTime) > 0.7f) - { - m_soundTime = gpGlobals->time; - - if (length > 0 && (pev->flags & FL_ONGROUND)) - { - m_lastSound = RANDOM_LONG(0, 2); - EMIT_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5, ATTN_NORM); - - //SetThink(StopSound); - //pev->nextthink = pev->ltime + 0.1f; - } - else - STOP_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound]); - } - } -} - -BOOL CPushable::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - if (pev->spawnflags & SF_PUSH_BREAKABLE) - { - return CBreakable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - } - - return TRUE; -} +#include "precompiled.h" + +// Just add more items to the bottom of this array and they will automagically be supported +// This is done instead of just a classname in the FGD so we can control which entities can +// be spawned, and still remain fairly flexible +const char *CBreakable::m_pszSpawnObjects[] = +{ + nullptr, + "item_battery", + "item_healthkit", + "weapon_9mmhandgun", + "ammo_9mmclip", + "weapon_9mmAR", + "ammo_9mmAR", + "ammo_ARgrenades", + "weapon_shotgun", + "ammo_buckshot", + "weapon_crossbow", + "ammo_crossbow", + "weapon_357", + "ammo_357", + "weapon_rpg", + "ammo_rpgclip", + "ammo_gaussclip", + "weapon_handgrenade", + "weapon_tripmine", + "weapon_satchel", + "weapon_snark", + "weapon_hornetgun", + "weapon_usp", + "weapon_glock18", + "weapon_awp", + "weapon_mp5n", + "weapon_m249", + "weapon_m3", + "weapon_m4a1", + "weapon_tmp", + "weapon_g3sg1", + "weapon_flashbang" +}; + +const char *CBreakable::m_pszSoundsWood[] = +{ + "debris/wood1.wav", + "debris/wood2.wav", + "debris/wood3.wav" +}; + +const char *CBreakable::m_pszSoundsFlesh[] = +{ + "debris/flesh1.wav", + "debris/flesh2.wav", + "debris/flesh3.wav", + "debris/flesh5.wav", + "debris/flesh6.wav", + "debris/flesh7.wav" +}; + +const char *CBreakable::m_pszSoundsMetal[] = +{ + "debris/metal1.wav", + "debris/metal2.wav", + "debris/metal3.wav" +}; + +const char *CBreakable::m_pszSoundsConcrete[] = +{ + "debris/concrete1.wav", + "debris/concrete2.wav", + "debris/concrete3.wav" +}; + +const char *CBreakable::m_pszSoundsGlass[] = +{ + "debris/glass1.wav", + "debris/glass2.wav", + "debris/glass3.wav" +}; + +TYPEDESCRIPTION CBreakable::m_SaveData[] = +{ + DEFINE_FIELD(CBreakable, m_Material, FIELD_INTEGER), + DEFINE_FIELD(CBreakable, m_Explosion, FIELD_INTEGER), + DEFINE_FIELD(CBreakable, m_angle, FIELD_FLOAT), + DEFINE_FIELD(CBreakable, m_iszGibModel, FIELD_STRING), + DEFINE_FIELD(CBreakable, m_iszSpawnObject, FIELD_STRING), +}; + +LINK_ENTITY_TO_CLASS(func_breakable, CBreakable, CCSBreakable) +IMPLEMENT_SAVERESTORE(CBreakable, CBaseEntity) + +void CBreakable::Spawn() +{ + Precache(); + + if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) + pev->takedamage = DAMAGE_NO; + else + pev->takedamage = DAMAGE_YES; + + m_flHealth = pev->health; + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + m_angle = pev->angles.y; + pev->angles.y = 0; + + // HACK: matGlass can receive decals, we need the client to know about this + // so use class to store the material flag + if (m_Material == matGlass) + { + pev->playerclass = 1; + } + + // set size and link into world. + SET_MODEL(ENT(pev), STRING(pev->model)); + + SetTouch(&CBreakable::BreakTouch); + + // Only break on trigger + if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) + { + SetTouch(nullptr); + } + + // Flag unbreakable glass as "worldbrush" so it will block ALL tracelines + if (!IsBreakable() && pev->rendermode != kRenderNormal) + { + pev->flags |= FL_WORLDBRUSH; + } +} + +void CBreakable::Restart() +{ + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + pev->deadflag = DEAD_NO; + + if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) + pev->takedamage = DAMAGE_NO; + else + pev->takedamage = DAMAGE_YES; + + pev->health = m_flHealth; + pev->effects &= ~EF_NODRAW; + m_angle = pev->angles.y; + pev->angles.y = 0; + + SET_MODEL(ENT(pev), STRING(pev->model)); + SetTouch(&CBreakable::BreakTouch); + + if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) + { + SetTouch(nullptr); + } + + if (!IsBreakable() && pev->rendermode != kRenderNormal) + { + pev->flags |= FL_WORLDBRUSH; + } +} + +void CBreakable::KeyValue(KeyValueData *pkvd) +{ + // UNDONE_WC: explicitly ignoring these fields, but they shouldn't be in the map file! + if (FStrEq(pkvd->szKeyName, "explosion")) + { + if (!Q_stricmp(pkvd->szValue, "directed")) + m_Explosion = expDirected; + + else if (!Q_stricmp(pkvd->szValue, "random")) + m_Explosion = expRandom; + else + m_Explosion = expRandom; + + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "material")) + { + Materials type = (Materials)Q_atoi(pkvd->szValue); + + // 0:glass, 1:wood, 2:metal, 3:flesh etc + if (type < 0 || type >= matLastMaterial) + m_Material = matWood; + else + m_Material = type; + + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "deadmodel")) + { + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "shards")) + { + //m_iShards = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "gibmodel")) + { + m_iszGibModel = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spawnobject")) + { + int object = Q_atoi(pkvd->szValue); + if (object > 0 && object < ARRAYSIZE(m_pszSpawnObjects)) + { + m_iszSpawnObject = MAKE_STRING(m_pszSpawnObjects[object]); + } + + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "explodemagnitude")) + { + ExplosionSetMagnitude(Q_atoi(pkvd->szValue)); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "lip")) + { + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue(pkvd); + } +} + +const char **CBreakable::MaterialSoundList(Materials precacheMaterial, int &soundCount) +{ + const char **pSoundList; + + switch (precacheMaterial) + { + case matWood: + { + pSoundList = m_pszSoundsWood; + soundCount = ARRAYSIZE(m_pszSoundsWood); + break; + } + case matFlesh: + { + pSoundList = m_pszSoundsFlesh; + soundCount = ARRAYSIZE(m_pszSoundsFlesh); + break; + } + case matGlass: + case matComputer: + case matUnbreakableGlass: + { + pSoundList = m_pszSoundsGlass; + soundCount = ARRAYSIZE(m_pszSoundsGlass); + break; + } + case matMetal: + { + pSoundList = m_pszSoundsMetal; + soundCount = ARRAYSIZE(m_pszSoundsMetal); + break; + } + case matCinderBlock: + case matRocks: + { + pSoundList = m_pszSoundsConcrete; + soundCount = ARRAYSIZE(m_pszSoundsConcrete); + break; + } + case matCeilingTile: + case matNone: + default: + pSoundList = nullptr; + soundCount = 0; + break; + } + + return pSoundList; +} + +void CBreakable::MaterialSoundPrecache(Materials precacheMaterial) +{ + const char **pSoundList; + int i, soundCount = 0; + + pSoundList = MaterialSoundList(precacheMaterial, soundCount); + + for (i = 0; i < soundCount; i++) + { + PRECACHE_SOUND((char *)pSoundList[i]); + } +} + +void CBreakable::MaterialSoundRandom(edict_t *pEdict, Materials soundMaterial, float volume) +{ + int soundCount = 0; + const char **pSoundList = MaterialSoundList(soundMaterial, soundCount); + + if (soundCount) + { + EMIT_SOUND(pEdict, CHAN_BODY, pSoundList[RANDOM_LONG(0, soundCount - 1)], volume, 1.0); + } +} + +void CBreakable::Precache() +{ + const char *pGibName = nullptr; + + switch (m_Material) + { + case matWood: + pGibName = "models/woodgibs.mdl"; + + PRECACHE_SOUND("debris/bustcrate1.wav"); + PRECACHE_SOUND("debris/bustcrate2.wav"); + break; + case matFlesh: + pGibName = "models/fleshgibs.mdl"; + + PRECACHE_SOUND("debris/bustflesh1.wav"); + PRECACHE_SOUND("debris/bustflesh2.wav"); + break; + case matComputer: + PRECACHE_SOUND("buttons/spark5.wav"); + PRECACHE_SOUND("buttons/spark6.wav"); + pGibName = "models/computergibs.mdl"; + + PRECACHE_SOUND("debris/bustmetal1.wav"); + PRECACHE_SOUND("debris/bustmetal2.wav"); + break; + case matGlass: + case matUnbreakableGlass: + pGibName = "models/glassgibs.mdl"; + + PRECACHE_SOUND("debris/bustglass1.wav"); + PRECACHE_SOUND("debris/bustglass2.wav"); + break; + case matMetal: + pGibName = "models/metalplategibs.mdl"; + + PRECACHE_SOUND("debris/bustmetal1.wav"); + PRECACHE_SOUND("debris/bustmetal2.wav"); + break; + case matCinderBlock: + pGibName = "models/cindergibs.mdl"; + + PRECACHE_SOUND("debris/bustconcrete1.wav"); + PRECACHE_SOUND("debris/bustconcrete2.wav"); + break; + case matRocks: + pGibName = "models/rockgibs.mdl"; + + PRECACHE_SOUND("debris/bustconcrete1.wav"); + PRECACHE_SOUND("debris/bustconcrete2.wav"); + break; + case matCeilingTile: + pGibName = "models/ceilinggibs.mdl"; + + PRECACHE_SOUND("debris/bustceiling.wav"); + break; + } + + MaterialSoundPrecache(m_Material); + + if (m_iszGibModel) + { + pGibName = STRING(m_iszGibModel); + } + + if (pGibName) + { + m_idShard = PRECACHE_MODEL(pGibName); + } + + // Precache the spawn item's data + if (m_iszSpawnObject) + { + UTIL_PrecacheOther(STRING(m_iszSpawnObject)); + } +} + +void CBreakable::DamageSound() +{ + int pitch; + float fvol; + char *rgpsz[6]; + int i; + int material = m_Material; + + if (RANDOM_LONG(0, 2)) + pitch = PITCH_NORM; + else + pitch = 95 + RANDOM_LONG(0, 34); + + fvol = RANDOM_FLOAT(0.75, 1.0); + +#ifndef REGAMEDLL_FIXES + if (material == matComputer && RANDOM_LONG(0, 1)) + material = matMetal; +#endif + + switch (material) + { + case matGlass: + case matComputer: + case matUnbreakableGlass: + rgpsz[0] = "debris/glass1.wav"; + rgpsz[1] = "debris/glass2.wav"; + rgpsz[2] = "debris/glass3.wav"; + i = 3; + break; + + case matWood: + rgpsz[0] = "debris/wood1.wav"; + rgpsz[1] = "debris/wood2.wav"; + rgpsz[2] = "debris/wood3.wav"; + i = 3; + break; + + case matMetal: + rgpsz[0] = "debris/metal1.wav"; + rgpsz[1] = "debris/metal3.wav"; + rgpsz[2] = "debris/metal2.wav"; +#ifdef REGAMEDLL_FIXES + i = 3; +#else + i = 2; +#endif + break; + + case matFlesh: + rgpsz[0] = "debris/flesh1.wav"; + rgpsz[1] = "debris/flesh2.wav"; + rgpsz[2] = "debris/flesh3.wav"; + rgpsz[3] = "debris/flesh5.wav"; + rgpsz[4] = "debris/flesh6.wav"; + rgpsz[5] = "debris/flesh7.wav"; + i = 6; + break; + + case matRocks: + case matCinderBlock: + rgpsz[0] = "debris/concrete1.wav"; + rgpsz[1] = "debris/concrete2.wav"; + rgpsz[2] = "debris/concrete3.wav"; + i = 3; + break; + + case matCeilingTile: + // UNDONE: no ceiling tile shard sound yet + i = 0; + break; + } + + if (i) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, rgpsz[RANDOM_LONG(0, i - 1)], fvol, ATTN_NORM, 0, pitch); + } +} + +void CBreakable::BreakTouch(CBaseEntity *pOther) +{ + float flDamage; + entvars_t *pevToucher = pOther->pev; + + // only players can break these right now + if (!pOther->IsPlayer() || !IsBreakable()) + { + if (pev->rendermode == kRenderNormal || !FClassnameIs(pOther->pev, "grenade")) + return; + + pev->angles.y = m_angle; + UTIL_MakeVectors(pev->angles); + + g_vecAttackDir = gpGlobals->v_forward; + +#ifndef REGAMEDLL_FIXES + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + pev->effects = EF_NODRAW; +#endif + + Die(); + } + + // can be broken when run into + if (pev->spawnflags & SF_BREAK_TOUCH) + { + flDamage = pevToucher->velocity.Length() * 0.01f; + + if (flDamage >= pev->health) + { + SetTouch(nullptr); + TakeDamage(pevToucher, pevToucher, flDamage, DMG_CRUSH); + + // do a little damage to player if we broke glass or computer + pOther->TakeDamage(pev, pev, flDamage / 4, DMG_SLASH); + } + } + + // can be broken when stood upon + if ((pev->spawnflags & SF_BREAK_PRESSURE) && pevToucher->absmin.z >= pev->maxs.z - 2) + { + // play creaking sound here. + DamageSound(); + + SetThink(&CBreakable::Die); + SetTouch(nullptr); + + // BUGBUG: why doesn't zero delay work? + if (m_flDelay == 0.0f) + { + m_flDelay = 0.1f; + } + + pev->nextthink = pev->ltime + m_flDelay; + } +} + +// Smash the our breakable object +// Break when triggered +void CBreakable::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + if (IsBreakable()) + { + pev->angles.y = m_angle; + UTIL_MakeVectors(pev->angles); + g_vecAttackDir = gpGlobals->v_forward; + +#ifndef REGAMEDLL_FIXES + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + pev->effects = EF_NODRAW; +#endif + + Die(); + } +} + +void CBreakable::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + // random spark if this is a 'computer' object + if (RANDOM_LONG(0, 1)) + { + switch (m_Material) + { + case matComputer: + { + UTIL_Sparks(ptr->vecEndPos); + + //random volume range + float flVolume = RANDOM_FLOAT(0.7 , 1.0); + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break; + } + + break; + } + case matUnbreakableGlass: + { + UTIL_Ricochet(ptr->vecEndPos, RANDOM_FLOAT(0.5, 1.5)); + break; + } + } + } + + CBaseDelay::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); +} + +// Special takedamage for func_breakable. Allows us to make +// exceptions that are breakable-specific +// bitsDamageType indicates the type of damage sustained ie: DMG_CRUSH +BOOL CBreakable::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + Vector vecTemp; + + // if Attacker == Inflictor, the attack was a melee or other instant-hit attack. + // (that is, no actual entity projectile was involved in the attack so use the shooter's origin). + if (pevAttacker == pevInflictor) + { + vecTemp = pevInflictor->origin - (pev->absmin + (pev->size * 0.5f)); + + // if a client hit the breakable with a crowbar, and breakable is crowbar-sensitive, break it now. + if ((pevAttacker->flags & FL_CLIENT) && (pev->spawnflags & SF_BREAK_CROWBAR) && (bitsDamageType & DMG_CLUB)) + { + flDamage = pev->health; + } + } + else + { + // an actual missile was involved. + vecTemp = pevInflictor->origin - (pev->absmin + (pev->size * 0.5f)); + } + + if (!IsBreakable()) + return FALSE; + + // Breakables take double damage from the crowbar + if (bitsDamageType & DMG_CLUB) + { + flDamage *= 2.0f; + } + + // Boxes / glass / etc. don't take much poison damage, just the impact of the dart - consider that 10% + if (bitsDamageType & DMG_POISON) + { + flDamage *= 0.1f; + } + + // this global is still used for glass and other non-monster killables, along with decals. + g_vecAttackDir = vecTemp.Normalize(); + + // do the damage + pev->health -= flDamage; + + if (pev->health <= 0) + { +#ifndef REGAMEDLL_FIXES + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + pev->effects = EF_NODRAW; +#endif + Die(); + + if (m_flDelay == 0.0f) + { + m_flDelay = 0.1f; + } + + pev->nextthink = pev->ltime + m_flDelay; + return FALSE; + } + + // Make a shard noise each time func breakable is hit. + // Don't play shard noise if cbreakable actually died. + DamageSound(); + return TRUE; +} + +void CBreakable::Die() +{ + Vector vecSpot; // shard origin + Vector vecVelocity; // shard velocity + CBaseEntity *pEntity = nullptr; + char cFlag = 0; + int pitch; + float fvol; + +#ifdef REGAMEDLL_FIXES + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + pev->effects = EF_NODRAW; +#endif + + pitch = 95 + RANDOM_LONG(0, 29); + + if (pitch > 97 && pitch < 103) + pitch = 100; + + // The more negative pev->health, the louder + // the sound should be. + +#ifdef REGAMEDLL_FIXES + fvol = RANDOM_FLOAT(0.85f, 1.0f) + (Q_abs(pev->health) / 100.0f); +#else + fvol = RANDOM_FLOAT(0.85f, 1.0f) + (Q_abs(int(pev->health)) / 100.0f); +#endif + + if (fvol > 1.0f) + fvol = 1.0f; + + switch (m_Material) + { + case matGlass: + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass1.wav", fvol, ATTN_NORM, 0, pitch); + break; + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustglass2.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + cFlag = BREAK_GLASS; + + if (TheBots) + { + TheBots->OnEvent(EVENT_BREAK_GLASS, this); + } + break; + case matWood: + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch); + break; + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + cFlag = BREAK_WOOD; + + if (TheBots) + { + TheBots->OnEvent(EVENT_BREAK_WOOD, this); + } + break; + + case matMetal: + case matComputer: + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch); + break; + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + cFlag = BREAK_METAL; + + if (TheBots) + { + TheBots->OnEvent(EVENT_BREAK_METAL, this); + } + break; + + case matFlesh: + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch); + break; + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + cFlag = BREAK_FLESH; + + if (TheBots) + { + TheBots->OnEvent(EVENT_BREAK_FLESH, this); + } + break; + + case matCinderBlock: + case matRocks: + switch (RANDOM_LONG(0, 1)) + { + case 0: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete1.wav", fvol, ATTN_NORM, 0, pitch); + break; + case 1: EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustconcrete2.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + cFlag = BREAK_CONCRETE; + + if (TheBots) + { + TheBots->OnEvent(EVENT_BREAK_CONCRETE, this); + } + break; + + case matCeilingTile: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "debris/bustceiling.wav", fvol, ATTN_NORM, 0, pitch); + break; + } + + if (m_Explosion == expDirected) + { + vecVelocity = g_vecAttackDir * 200.0f; + } + else + { + vecVelocity.x = 0; + vecVelocity.y = 0; + vecVelocity.z = 0; + } + + vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5f; + + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecSpot); + WRITE_BYTE(TE_BREAKMODEL); + WRITE_COORD(vecSpot.x); // position + WRITE_COORD(vecSpot.y); + WRITE_COORD(vecSpot.z); + WRITE_COORD(pev->size.x); // size + WRITE_COORD(pev->size.y); + WRITE_COORD(pev->size.z); + WRITE_COORD(vecVelocity.x); // velocity + WRITE_COORD(vecVelocity.y); + WRITE_COORD(vecVelocity.z); + WRITE_BYTE(10); // randomization + WRITE_SHORT(m_idShard); // model id# + WRITE_BYTE(0); // # of shards, let client decide + WRITE_BYTE(25); // duration, 2.5 seconds + WRITE_BYTE(cFlag); // flags + MESSAGE_END(); + + float size = pev->size.x; + + if (size < pev->size.y) + size = pev->size.y; + + if (size < pev->size.z) + size = pev->size.z; + + Vector mins = pev->absmin; + Vector maxs = pev->absmax; + mins.z = pev->absmax.z; + maxs.z += 8; + + CBaseEntity *pList[256]; + int count = UTIL_EntitiesInBox(pList, ARRAYSIZE(pList), mins, maxs, FL_ONGROUND); + + for (int i = 0; i < count; i++) + { + pList[i]->pev->flags &= ~FL_ONGROUND; + pList[i]->pev->groundentity = nullptr; + } + + pev->solid = SOLID_NOT; + SUB_UseTargets(nullptr, USE_TOGGLE, 0); + SetThink(nullptr); + + pev->nextthink = pev->ltime + 0.1f; + + if (m_iszSpawnObject) + { + // TODO: Implement a list of entities to remove them on restart round + auto pItem = CBaseEntity::Create((char *)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, edict()); + +#ifdef REGAMEDLL_FIXES + // FIX: entity leak! + if (pItem) + { + pItem->pev->spawnflags |= SF_NORESPAWN; + pItem->SetThink(&CBaseEntity::SUB_Remove); + pItem->pev->nextthink = gpGlobals->time + CGameRules::GetItemKillDelay(); + } +#endif + + } + + if (Explodable()) + { + ExplosionCreate(Center(), pev->angles, edict(), ExplosionMagnitude(), TRUE); + } +} + +BOOL CBreakable::IsBreakable() +{ + return m_Material != matUnbreakableGlass; +} + +int CBreakable::DamageDecal(int bitsDamageType) +{ + if (m_Material == matGlass) + return DECAL_GLASSBREAK1 + RANDOM_LONG(0, 2); + + if (m_Material == matUnbreakableGlass) + return DECAL_BPROOF1; + + return CBaseEntity::DamageDecal(bitsDamageType); +} + +TYPEDESCRIPTION CPushable::m_SaveData[] = +{ + DEFINE_FIELD(CPushable, m_maxSpeed, FIELD_FLOAT), + DEFINE_FIELD(CPushable, m_soundTime, FIELD_TIME), +}; + +const char *CPushable::m_soundNames[] = +{ + "debris/pushbox1.wav", + "debris/pushbox2.wav", + "debris/pushbox3.wav" +}; + +LINK_ENTITY_TO_CLASS(func_pushable, CPushable, CCSPushable) +IMPLEMENT_SAVERESTORE(CPushable, CBreakable) + +void CPushable::Spawn() +{ + if (pev->spawnflags & SF_PUSH_BREAKABLE) + CBreakable::Spawn(); + else + Precache(); + + pev->movetype = MOVETYPE_PUSHSTEP; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), STRING(pev->model)); + + if (pev->friction > 399) + { + pev->friction = 399; + } + + m_maxSpeed = 400 - pev->friction; + + pev->flags |= FL_FLOAT; + pev->friction = 0; + + // Pick up off of the floor + pev->origin.z += 1; + UTIL_SetOrigin(pev, pev->origin); + +#ifdef REGAMEDLL_FIXES + pev->oldorigin = pev->origin; +#endif + + // Multiply by area of the box's cross-section (assume 1000 units^3 standard volume) + pev->skin = int((pev->skin * (pev->maxs.x - pev->mins.x) * (pev->maxs.y - pev->mins.y)) * 0.0005); + m_soundTime = 0; +} + +void CPushable::Precache() +{ + for (int i = 0; i < ARRAYSIZE(m_soundNames); i++) + { + PRECACHE_SOUND(m_soundNames[i]); + } + + if (pev->spawnflags & SF_PUSH_BREAKABLE) + { + CBreakable::Precache(); + } +} + +#ifdef REGAMEDLL_FIXES +void CPushable::Restart() +{ + if (pev->spawnflags & SF_PUSH_BREAKABLE) + CBreakable::Restart(); + + pev->movetype = MOVETYPE_PUSHSTEP; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), STRING(pev->model)); + + if (pev->friction > 399) + pev->friction = 399; + + m_soundTime = 0; + m_maxSpeed = 400 - pev->friction; + + pev->flags |= FL_FLOAT; + pev->friction = 0; + + UTIL_SetOrigin(pev, pev->oldorigin); +} +#endif + +void CPushable::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "size")) + { + int bbox = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + + switch (bbox) + { + case 0: // Point + UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8)); + break; + + case 2: // TODO: Big Hull? BUGBUG: Figure out what this hull really is + UTIL_SetSize(pev, VEC_DUCK_HULL_MIN * 2, VEC_DUCK_HULL_MAX * 2); + break; + + case 3: // Player duck + UTIL_SetSize(pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX); + break; + + default: + case 1: // Player + UTIL_SetSize(pev, VEC_HULL_MIN, VEC_HULL_MAX); + break; + } + } + else if (FStrEq(pkvd->szKeyName, "buoyancy")) + { + pev->skin = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBreakable::KeyValue(pkvd); + } +} + +// Pull the func_pushable +void CPushable::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + if (!pActivator || !pActivator->IsPlayer()) + { + if (pev->spawnflags & SF_PUSH_BREAKABLE) + { + this->CBreakable::Use(pActivator, pCaller, useType, value); + } + + return; + } + + if (pActivator->pev->velocity != g_vecZero) + { + Move(pActivator, 0); + } +} + +void CPushable::Touch(CBaseEntity *pOther) +{ + if (FClassnameIs(pOther->pev, "worldspawn")) + return; + + Move(pOther, 1); +} + +void CPushable::Move(CBaseEntity *pOther, int push) +{ + entvars_t *pevToucher = pOther->pev; + bool bPlayerTouch = false; + + // Is entity standing on this pushable ? + if ((pevToucher->flags & FL_ONGROUND) && pevToucher->groundentity && VARS(pevToucher->groundentity) == pev) + { + // Only push if floating + if (pev->waterlevel > 0) + { + pev->velocity.z += pevToucher->velocity.z * 0.1f; + } + + return; + } + + if (pOther->IsPlayer()) + { + // Don't push unless the player is pushing forward and NOT use (pull) + if (push && !(pevToucher->button & (IN_FORWARD | IN_USE))) + { + return; + } + + bPlayerTouch = true; + } + + real_t factor; + + if (bPlayerTouch) + { + // Don't push away from jumping/falling players unless in water + if (!(pevToucher->flags & FL_ONGROUND)) + { + if (pev->waterlevel < 1) + return; + else + factor = 0.1f; + } + else + factor = 1.0f; + } + else + { + factor = 0.25f; + } + + pev->velocity.x += pevToucher->velocity.x * factor; + pev->velocity.y += pevToucher->velocity.y * factor; + + real_t length = Q_sqrt(pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y); + + if (push && (length > MaxSpeed())) + { + pev->velocity.x = (pev->velocity.x * MaxSpeed() / length); + pev->velocity.y = (pev->velocity.y * MaxSpeed() / length); + } + + if (bPlayerTouch) + { +// do not push player along with entity +#ifndef REGAMEDLL_FIXES + pevToucher->velocity.x = pev->velocity.x; + pevToucher->velocity.y = pev->velocity.y; +#endif // REGAMEDLL_FIXES + + if ((gpGlobals->time - m_soundTime) > 0.7f) + { + m_soundTime = gpGlobals->time; + + if (length > 0 && (pev->flags & FL_ONGROUND)) + { + m_lastSound = RANDOM_LONG(0, 2); + EMIT_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5, ATTN_NORM); + + //SetThink(StopSound); + //pev->nextthink = pev->ltime + 0.1f; + } + else + STOP_SOUND(ENT(pev), CHAN_WEAPON, m_soundNames[m_lastSound]); + } + } +} + +BOOL CPushable::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + if (pev->spawnflags & SF_PUSH_BREAKABLE) + { + return CBreakable::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + } + + return TRUE; +} diff --git a/regamedll/dlls/func_tank.cpp b/regamedll/dlls/func_tank.cpp index 4f278fc5..e6eace95 100644 --- a/regamedll/dlls/func_tank.cpp +++ b/regamedll/dlls/func_tank.cpp @@ -1,913 +1,913 @@ -#include "precompiled.h" - -TYPEDESCRIPTION CFuncTank::m_SaveData[] = -{ - DEFINE_FIELD(CFuncTank, m_yawCenter, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_yawRate, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_yawRange, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_yawTolerance, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_pitchCenter, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_pitchRate, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_pitchRange, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_pitchTolerance, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_fireLast, FIELD_TIME), - DEFINE_FIELD(CFuncTank, m_fireRate, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_lastSightTime, FIELD_TIME), - DEFINE_FIELD(CFuncTank, m_persist, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_minRange, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_maxRange, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_barrelPos, FIELD_VECTOR), - DEFINE_FIELD(CFuncTank, m_spriteScale, FIELD_FLOAT), - DEFINE_FIELD(CFuncTank, m_iszSpriteSmoke, FIELD_STRING), - DEFINE_FIELD(CFuncTank, m_iszSpriteFlash, FIELD_STRING), - DEFINE_FIELD(CFuncTank, m_bulletType, FIELD_INTEGER), - DEFINE_FIELD(CFuncTank, m_sightOrigin, FIELD_VECTOR), - DEFINE_FIELD(CFuncTank, m_spread, FIELD_INTEGER), - DEFINE_FIELD(CFuncTank, m_pController, FIELD_CLASSPTR), - DEFINE_FIELD(CFuncTank, m_vecControllerUsePos, FIELD_VECTOR), - DEFINE_FIELD(CFuncTank, m_flNextAttack, FIELD_TIME), - DEFINE_FIELD(CFuncTank, m_iBulletDamage, FIELD_INTEGER), - DEFINE_FIELD(CFuncTank, m_iszMaster, FIELD_STRING), -}; - -Vector CFuncTank::m_TankSpread[] = -{ - Vector(0, 0, 0), // perfect - Vector(0.025, 0.025, 0.025), // small cone - Vector(0.05, 0.05, 0.05), // medium cone - Vector(0.1, 0.1, 0.1), // large cone - Vector(0.25, 0.25, 0.25), // extra-large cone -}; - -constexpr int MAX_FIRING_SPREADS = ARRAYSIZE(CFuncTank::m_TankSpread); - -IMPLEMENT_SAVERESTORE(CFuncTank, CBaseEntity) - -void CFuncTank::Spawn() -{ - Precache(); - - // so it doesn't get pushed by anything - pev->movetype = MOVETYPE_PUSH; - pev->solid = SOLID_BSP; - SET_MODEL(ENT(pev), STRING(pev->model)); - - m_yawCenter = pev->angles.y; - m_pitchCenter = pev->angles.x; - - if (IsActive()) - { - pev->nextthink = pev->ltime + 1.0f; - } - - // Point at the end of the barrel - m_sightOrigin = BarrelPosition(); - - if (m_fireRate <= 0.0f) - m_fireRate = 1.0f; - - if (m_spread > MAX_FIRING_SPREADS) - m_spread = 0; - - pev->oldorigin = pev->origin; -} - -void CFuncTank::Precache() -{ - if (m_iszSpriteSmoke) - { - PRECACHE_MODEL((char *)STRING(m_iszSpriteSmoke)); - } - - if (m_iszSpriteFlash) - { - PRECACHE_MODEL((char *)STRING(m_iszSpriteFlash)); - } - - if (pev->noise) - { - PRECACHE_SOUND((char *)STRING(pev->noise)); - } -} - -void CFuncTank::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "yawrate")) - { - m_yawRate = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "yawrange")) - { - m_yawRange = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "yawtolerance")) - { - m_yawTolerance = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchrange")) - { - m_pitchRange = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchrate")) - { - m_pitchRate = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "pitchtolerance")) - { - m_pitchTolerance = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firerate")) - { - m_fireRate = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrel")) - { - m_barrelPos.x = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrely")) - { - m_barrelPos.y = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "barrelz")) - { - m_barrelPos.z = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spritescale")) - { - m_spriteScale = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spritesmoke")) - { - m_iszSpriteSmoke = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "spriteflash")) - { - m_iszSpriteFlash = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "rotatesound")) - { - pev->noise = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "persistence")) - { - m_persist = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "bullet")) - { - m_bulletType = (TANKBULLET)Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "bullet_damage")) - { - m_iBulletDamage = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "firespread")) - { - m_spread = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "minRange")) - { - m_minRange = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "maxRange")) - { - m_maxRange = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) - { - m_iszMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue(pkvd); - } -} - -BOOL CFuncTank::OnControls(entvars_t *pevTest) -{ - if (!(pev->spawnflags & SF_TANK_CANCONTROL)) - return FALSE; - - Vector offset = pevTest->origin - pev->origin; - if ((m_vecControllerUsePos - pevTest->origin).Length() < 30.0f) - { - return TRUE; - } - - return FALSE; -} - -BOOL CFuncTank::StartControl(CBasePlayer *pController) -{ - if (m_pController) - return FALSE; - - // Team only or disabled? - if (m_iszMaster) - { - if (!UTIL_IsMasterTriggered(m_iszMaster, pController)) - { - return FALSE; - } - } - - ALERT(at_console, "using TANK!\n"); - - m_pController = pController; - - if (m_pController->m_pActiveItem) - { - m_pController->m_pActiveItem->Holster(); - m_pController->pev->weaponmodel = 0; - -#ifdef BUILD_LATEST_FIXES - m_pController->pev->viewmodel = 0; -#endif - -#ifdef REGAMEDLL_FIXES - m_pController->m_iFOV = DEFAULT_FOV; -#endif - } - - m_pController->m_iHideHUD |= HIDEHUD_WEAPONS; - m_vecControllerUsePos = m_pController->pev->origin; - - pev->nextthink = pev->ltime + 0.1f; - - return TRUE; -} - -void CFuncTank::StopControl() -{ - // TODO: bring back the controllers current weapon - if (!m_pController) - return; - - if (m_pController->m_pActiveItem) - { - m_pController->m_pActiveItem->Deploy(); - - if (m_pController->IsPlayer()) - { - m_pController->ResetMaxSpeed(); - } - } - - ALERT(at_console, "stopped using TANK\n"); - -#ifdef REGAMEDLL_FIXES - if (m_pController->m_pActiveItem) -#endif - { - m_pController->m_iHideHUD &= ~HIDEHUD_WEAPONS; - } - - pev->nextthink = 0; - m_pController = nullptr; - - if (IsActive()) - { - pev->nextthink = pev->ltime + 1.0f; - } -} - -void CFuncTank::ControllerPostFrame() -{ - assert(m_pController != nullptr); - - if (gpGlobals->time < m_flNextAttack) - return; - - if (m_pController->pev->button & IN_ATTACK) - { - Vector vecForward; - UTIL_MakeVectorsPrivate(pev->angles, vecForward, nullptr, nullptr); - - m_fireLast = gpGlobals->time - (1.0f / m_fireRate) - 0.01f; - Fire(BarrelPosition(), vecForward, m_pController->pev); - - if (m_pController && m_pController->IsPlayer()) - { - m_pController->m_iWeaponVolume = LOUD_GUN_VOLUME; - } - - m_flNextAttack = gpGlobals->time + (1.0f / m_fireRate); - } -} - -void CFuncTank::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - // player controlled turret - if (pev->spawnflags & SF_TANK_CANCONTROL) - { - if (pActivator->Classify() != CLASS_PLAYER) - return; - - if (value == 2 && useType == USE_SET) - { - ControllerPostFrame(); - } - else if (!m_pController && useType != USE_OFF) - { - ((CBasePlayer *)pActivator)->m_pTank = this; - StartControl((CBasePlayer*)pActivator); - } - else - { - StopControl(); - } - } - else - { - if (!ShouldToggle(useType, IsActive())) - return; - - if (IsActive()) - TankDeactivate(); - else - TankActivate(); - } -} - -edict_t *CFuncTank::FindTarget(edict_t *pPlayer) -{ - return pPlayer; -} - -BOOL CFuncTank::InRange(float range) -{ - if (range < m_minRange) - return FALSE; - - if (m_maxRange > 0 && range > m_maxRange) - return FALSE; - - return TRUE; -} - -void CFuncTank::Think() -{ - pev->avelocity = g_vecZero; - TrackTarget(); - - if (Q_fabs(real_t(pev->avelocity.x)) > 1 || Q_fabs(real_t(pev->avelocity.y)) > 1) - StartRotSound(); - else - StopRotSound(); -} - -void CFuncTank::TrackTarget() -{ - TraceResult tr; - edict_t *pPlayer = FIND_CLIENT_IN_PVS(edict()); - bool updateTime = false, lineOfSight = false; - Vector angles, direction, targetPosition, barrelEnd; - edict_t *pTarget = nullptr; - - // Get a position to aim for - if (m_pController) - { - // Tanks attempt to mirror the player's angles - angles = m_pController->pev->v_angle; - angles.x = 0 - angles.x; - pev->nextthink = pev->ltime + 0.05f; - } - else - { - if (IsActive()) - pev->nextthink = pev->ltime + 0.1f; - else - return; - - if (FNullEnt(pPlayer)) - { - if (IsActive()) - { - // Wait 2 secs - pev->nextthink = pev->ltime + 2.0f; - } - - return; - } - - pTarget = FindTarget(pPlayer); - if (!pTarget) - { - return; - } - - // Calculate angle needed to aim at target - barrelEnd = BarrelPosition(); - targetPosition = pTarget->v.origin + pTarget->v.view_ofs; - float range = (targetPosition - barrelEnd).Length(); - - if (!InRange(range)) - return; - - UTIL_TraceLine(barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr); - - // No line of sight, don't track - if (tr.flFraction == 1.0f || tr.pHit == pTarget) - { - lineOfSight = true; - - CBaseEntity *pInstance = CBaseEntity::Instance(pTarget); - if (InRange(range) && pInstance && pInstance->IsAlive()) - { - updateTime = true; - m_sightOrigin = UpdateTargetPosition(pInstance); - } - } - - // Track sight origin - // TODO: I'm not sure what i changed - direction = m_sightOrigin - pev->origin; - //direction = m_sightOrigin - barrelEnd; - angles = UTIL_VecToAngles(direction); - - // Calculate the additional rotation to point the end of the barrel at the target (not the gun's center) - AdjustAnglesForBarrel(angles, direction.Length()); - } - - angles.x = -angles.x; - - // Force the angles to be relative to the center position - angles.y = m_yawCenter + UTIL_AngleDistance(angles.y, m_yawCenter); - angles.x = m_pitchCenter + UTIL_AngleDistance(angles.x, m_pitchCenter); - - // Limit against range in y - if (angles.y > m_yawCenter + m_yawRange) - { - angles.y = m_yawCenter + m_yawRange; - - // Don't update if you saw the player, but out of range - updateTime = false; - } - else if (angles.y < (m_yawCenter - m_yawRange)) - { - angles.y = (m_yawCenter - m_yawRange); - - // Don't update if you saw the player, but out of range - updateTime = false; - } - - if (updateTime) - { - m_lastSightTime = gpGlobals->time; - } - - // Move toward target at rate or less - real_t distY = UTIL_AngleDistance(angles.y, pev->angles.y); - pev->avelocity.y = distY * 10.0f; - - if (pev->avelocity.y > m_yawRate) - { - pev->avelocity.y = m_yawRate; - } - else if (pev->avelocity.y < -m_yawRate) - { - pev->avelocity.y = -m_yawRate; - } - - // Limit against range in x - if (angles.x > m_pitchCenter + m_pitchRange) - { - angles.x = m_pitchCenter + m_pitchRange; - } - else if (angles.x < m_pitchCenter - m_pitchRange) - { - angles.x = m_pitchCenter - m_pitchRange; - } - - // Move toward target at rate or less - real_t distX = UTIL_AngleDistance(angles.x, pev->angles.x); - pev->avelocity.x = distX * 10.0f; - - if (pev->avelocity.x > m_pitchRate) - { - pev->avelocity.x = m_pitchRate; - } - else if (pev->avelocity.x < -m_pitchRate) - { - pev->avelocity.x = -m_pitchRate; - } - - if (m_pController) - { - return; - } - - if (CanFire() && ((Q_fabs(distX) < m_pitchTolerance && Q_fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT))) - { - bool fire = false; - Vector forward; - UTIL_MakeVectorsPrivate(pev->angles, forward, nullptr, nullptr); - - if (pev->spawnflags & SF_TANK_LINEOFSIGHT) - { - float length = direction.Length(); - UTIL_TraceLine(barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr); - - if (tr.pHit == pTarget) - { - fire = true; - } - } - else - fire = true; - - if (fire) - { - Fire(BarrelPosition(), forward, pev); - } - else - m_fireLast = 0; - } - else - m_fireLast = 0; -} - -// If barrel is offset, add in additional rotation -void CFuncTank::AdjustAnglesForBarrel(Vector &angles, float distance) -{ - real_t r2, d2; - - if (m_barrelPos.y != 0.0f || m_barrelPos.z != 0.0f) - { - distance -= m_barrelPos.z; - d2 = distance * distance; - - if (m_barrelPos.y) - { - r2 = m_barrelPos.y * m_barrelPos.y; - angles.y += (180.0f / M_PI) * atan2(m_barrelPos.y, Q_sqrt(d2 - r2)); - } - - if (m_barrelPos.z) - { - r2 = m_barrelPos.z * m_barrelPos.z; - angles.x += (180.0f / M_PI) * atan2(-m_barrelPos.z, Q_sqrt(d2 - r2)); - } - } -} - -// Fire targets and spawn sprites -void CFuncTank::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) -{ - if (m_fireLast != 0.0f) - { - if (m_iszSpriteSmoke) - { - CSprite *pSprite = CSprite::SpriteCreate(STRING(m_iszSpriteSmoke), barrelEnd, TRUE); - - pSprite->AnimateAndDie(RANDOM_FLOAT(15, 20)); - pSprite->SetTransparency(kRenderTransAlpha, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, 255, kRenderFxNone); - pSprite->pev->velocity.z = RANDOM_FLOAT(40, 80); - pSprite->SetScale(m_spriteScale); - } - - if (m_iszSpriteFlash) - { - CSprite *pSprite = CSprite::SpriteCreate(STRING(m_iszSpriteFlash), barrelEnd, TRUE); - - pSprite->AnimateAndDie(60); - pSprite->SetTransparency(kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation); - pSprite->SetScale(m_spriteScale); - - // Hack Hack, make it stick around for at least 100 ms. - pSprite->pev->nextthink += 0.1f; - } - - SUB_UseTargets(this, USE_TOGGLE, 0); - } - - m_fireLast = gpGlobals->time; -} - -void CFuncTank::TankTrace(const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr) -{ - // get circular gaussian spread - float x, z; - real_t y; - - do - { - x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); - y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); - z = x * x + y * y; - } - while (z > 1); - - Vector vecDir = vecForward + x * vecSpread.x * gpGlobals->v_right + y * vecSpread.y * gpGlobals->v_up; - Vector vecEnd; - - vecEnd = vecStart + vecDir * 4096.0f; - UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, edict(), &tr); -} - -void CFuncTank::StartRotSound() -{ - if (!pev->noise || (pev->spawnflags & SF_TANK_SOUNDON)) - { - return; - } - - pev->spawnflags |= SF_TANK_SOUNDON; - EMIT_SOUND(edict(), CHAN_STATIC, (char *)STRING(pev->noise), 0.85, ATTN_NORM); -} - -void CFuncTank::StopRotSound() -{ - if (pev->spawnflags & SF_TANK_SOUNDON) - { - STOP_SOUND(edict(), CHAN_STATIC, (char *)STRING(pev->noise)); - } - - pev->spawnflags &= ~SF_TANK_SOUNDON; -} - -LINK_ENTITY_TO_CLASS(func_tank, CFuncTankGun, CCSFuncTankGun) - -void CFuncTankGun::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) -{ - if (m_fireLast != 0.0f) - { - // FireBullets needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); - if (bulletCount > 0) - { - for (int i = 0; i < bulletCount; i++) - { - switch (m_bulletType) - { - case TANK_BULLET_9MM: - FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_9MM, 1, m_iBulletDamage, pevAttacker); - break; - case TANK_BULLET_MP5: - FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_MP5, 1, m_iBulletDamage, pevAttacker); - break; - case TANK_BULLET_12MM: - FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_12MM, 1, m_iBulletDamage, pevAttacker); - break; - default: - case TANK_BULLET_NONE: - break; - } - } - - CFuncTank::Fire(barrelEnd, forward, pevAttacker); - } - } - else - { - CFuncTank::Fire(barrelEnd, forward, pevAttacker); - } -} - -TYPEDESCRIPTION CFuncTankLaser::m_SaveData[] = -{ - DEFINE_FIELD(CFuncTankLaser, m_pLaser, FIELD_CLASSPTR), - DEFINE_FIELD(CFuncTankLaser, m_laserTime, FIELD_TIME), -}; - -LINK_ENTITY_TO_CLASS(func_tanklaser, CFuncTankLaser, CCSFuncTankLaser) -IMPLEMENT_SAVERESTORE(CFuncTankLaser, CFuncTank) - -void CFuncTankLaser::Activate() -{ - if (!GetLaser()) - { - UTIL_Remove(this); - ALERT(at_error, "Laser tank with no env_laser!\n"); - } - else - { - m_pLaser->TurnOff(); - } -} - -void CFuncTankLaser::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "laserentity")) - { - pev->message = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CFuncTank::KeyValue(pkvd); - } -} - -CLaser *CFuncTankLaser::GetLaser() -{ - if (m_pLaser) - { - return m_pLaser; - } - - edict_t *pentLaser = FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(pev->message)); - - while (!FNullEnt(pentLaser)) - { - // Found the landmark - if (FClassnameIs(pentLaser, "env_laser")) - { - m_pLaser = (CLaser *)CBaseEntity::Instance(pentLaser); - break; - } - else - pentLaser = FIND_ENTITY_BY_TARGETNAME(pentLaser, STRING(pev->message)); - } - - return m_pLaser; -} - -void CFuncTankLaser::Think() -{ - if (m_pLaser && gpGlobals->time > m_laserTime) - { - m_pLaser->TurnOff(); - } - - CFuncTank::Think(); -} - -void CFuncTankLaser::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) -{ - if (m_fireLast != 0.0f && GetLaser()) - { - // TankTrace needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); - if (bulletCount) - { - TraceResult tr; - for (int i = 0; i < bulletCount; i++) - { - m_pLaser->pev->origin = barrelEnd; - TankTrace(barrelEnd, forward, m_TankSpread[m_spread], tr); - - m_laserTime = gpGlobals->time; - m_pLaser->TurnOn(); - m_pLaser->pev->dmgtime = gpGlobals->time - 1.0f; - m_pLaser->FireAtPoint(tr); - m_pLaser->pev->nextthink = 0; - } - - CFuncTank::Fire(barrelEnd, forward, pev); - } - } - else - { - CFuncTank::Fire(barrelEnd, forward, pev); - } -} - -LINK_ENTITY_TO_CLASS(func_tankrocket, CFuncTankRocket, CCSFuncTankRocket) - -void CFuncTankRocket::Precache() -{ - UTIL_PrecacheOther("rpg_rocket"); - CFuncTank::Precache(); -} - -void CFuncTankRocket::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) -{ - if (m_fireLast != 0.0f) - { - int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); - if (bulletCount > 0) - { - for (int i = 0; i < bulletCount; i++) - { - CBaseEntity *pRocket = CBaseEntity::Create("rpg_rocket", barrelEnd, pev->angles, edict()); - } - - CFuncTank::Fire(barrelEnd, forward, pev); - } - } - else - { - CFuncTank::Fire(barrelEnd, forward, pev); - } -} - -LINK_ENTITY_TO_CLASS(func_tankmortar, CFuncTankMortar, CCSFuncTankMortar) - -void CFuncTankMortar::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "iMagnitude")) - { - pev->impulse = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CFuncTank::KeyValue(pkvd); - } -} - -void CFuncTankMortar::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) -{ - if (m_fireLast != 0.0f) - { - int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); - - // Only create 1 explosion - if (bulletCount > 0) - { - TraceResult tr; - - // TankTrace needs gpGlobals->v_up, etc. - UTIL_MakeAimVectors(pev->angles); - - TankTrace(barrelEnd, forward, m_TankSpread[m_spread], tr); - ExplosionCreate(tr.vecEndPos, pev->angles, edict(), pev->impulse, TRUE); - CFuncTank::Fire(barrelEnd, forward, pev); - } - } - else - { - CFuncTank::Fire(barrelEnd, forward, pev); - } -} - -TYPEDESCRIPTION CFuncTankControls::m_SaveData[] = -{ - DEFINE_FIELD(CFuncTankControls, m_pTank, FIELD_CLASSPTR), -}; - -LINK_ENTITY_TO_CLASS(func_tankcontrols, CFuncTankControls, CCSFuncTankControls) -IMPLEMENT_SAVERESTORE(CFuncTankControls, CBaseEntity) - -void CFuncTankControls::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - // pass the Use command onto the controls - if (m_pTank) - { - m_pTank->Use(pActivator, pCaller, useType, value); - } - - // if this fails, most likely means save/restore hasn't worked properly - assert(m_pTank != nullptr); -} - -void CFuncTankControls::Think() -{ - edict_t *pTarget = nullptr; - - do - { - pTarget = FIND_ENTITY_BY_TARGETNAME(pTarget, STRING(pev->target)); - } - while (!FNullEnt(pTarget) && Q_strncmp(STRING(pTarget->v.classname), "func_tank", 9) != 0); - - if (FNullEnt(pTarget)) - { - ALERT(at_console, "No tank %s\n", STRING(pev->target)); - return; - } - - m_pTank = static_cast(Instance(pTarget)); -} - -void CFuncTankControls::Spawn() -{ - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - pev->effects |= EF_NODRAW; - - SET_MODEL(ENT(pev), STRING(pev->model)); - - UTIL_SetSize(pev, pev->mins, pev->maxs); - UTIL_SetOrigin(pev, pev->origin); - - // After all the func_tank's have spawned - pev->nextthink = gpGlobals->time + 0.3f; - - CBaseEntity::Spawn(); -} +#include "precompiled.h" + +TYPEDESCRIPTION CFuncTank::m_SaveData[] = +{ + DEFINE_FIELD(CFuncTank, m_yawCenter, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_yawRate, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_yawRange, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_yawTolerance, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_pitchCenter, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_pitchRate, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_pitchRange, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_pitchTolerance, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_fireLast, FIELD_TIME), + DEFINE_FIELD(CFuncTank, m_fireRate, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_lastSightTime, FIELD_TIME), + DEFINE_FIELD(CFuncTank, m_persist, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_minRange, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_maxRange, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_barrelPos, FIELD_VECTOR), + DEFINE_FIELD(CFuncTank, m_spriteScale, FIELD_FLOAT), + DEFINE_FIELD(CFuncTank, m_iszSpriteSmoke, FIELD_STRING), + DEFINE_FIELD(CFuncTank, m_iszSpriteFlash, FIELD_STRING), + DEFINE_FIELD(CFuncTank, m_bulletType, FIELD_INTEGER), + DEFINE_FIELD(CFuncTank, m_sightOrigin, FIELD_VECTOR), + DEFINE_FIELD(CFuncTank, m_spread, FIELD_INTEGER), + DEFINE_FIELD(CFuncTank, m_pController, FIELD_CLASSPTR), + DEFINE_FIELD(CFuncTank, m_vecControllerUsePos, FIELD_VECTOR), + DEFINE_FIELD(CFuncTank, m_flNextAttack, FIELD_TIME), + DEFINE_FIELD(CFuncTank, m_iBulletDamage, FIELD_INTEGER), + DEFINE_FIELD(CFuncTank, m_iszMaster, FIELD_STRING), +}; + +Vector CFuncTank::m_TankSpread[] = +{ + Vector(0, 0, 0), // perfect + Vector(0.025, 0.025, 0.025), // small cone + Vector(0.05, 0.05, 0.05), // medium cone + Vector(0.1, 0.1, 0.1), // large cone + Vector(0.25, 0.25, 0.25), // extra-large cone +}; + +constexpr int MAX_FIRING_SPREADS = ARRAYSIZE(CFuncTank::m_TankSpread); + +IMPLEMENT_SAVERESTORE(CFuncTank, CBaseEntity) + +void CFuncTank::Spawn() +{ + Precache(); + + // so it doesn't get pushed by anything + pev->movetype = MOVETYPE_PUSH; + pev->solid = SOLID_BSP; + SET_MODEL(ENT(pev), STRING(pev->model)); + + m_yawCenter = pev->angles.y; + m_pitchCenter = pev->angles.x; + + if (IsActive()) + { + pev->nextthink = pev->ltime + 1.0f; + } + + // Point at the end of the barrel + m_sightOrigin = BarrelPosition(); + + if (m_fireRate <= 0.0f) + m_fireRate = 1.0f; + + if (m_spread > MAX_FIRING_SPREADS) + m_spread = 0; + + pev->oldorigin = pev->origin; +} + +void CFuncTank::Precache() +{ + if (m_iszSpriteSmoke) + { + PRECACHE_MODEL((char *)STRING(m_iszSpriteSmoke)); + } + + if (m_iszSpriteFlash) + { + PRECACHE_MODEL((char *)STRING(m_iszSpriteFlash)); + } + + if (pev->noise) + { + PRECACHE_SOUND((char *)STRING(pev->noise)); + } +} + +void CFuncTank::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "yawrate")) + { + m_yawRate = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "yawrange")) + { + m_yawRange = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "yawtolerance")) + { + m_yawTolerance = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchrange")) + { + m_pitchRange = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchrate")) + { + m_pitchRate = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "pitchtolerance")) + { + m_pitchTolerance = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "firerate")) + { + m_fireRate = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrel")) + { + m_barrelPos.x = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrely")) + { + m_barrelPos.y = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "barrelz")) + { + m_barrelPos.z = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spritescale")) + { + m_spriteScale = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spritesmoke")) + { + m_iszSpriteSmoke = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "spriteflash")) + { + m_iszSpriteFlash = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "rotatesound")) + { + pev->noise = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "persistence")) + { + m_persist = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "bullet")) + { + m_bulletType = (TANKBULLET)Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "bullet_damage")) + { + m_iBulletDamage = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "firespread")) + { + m_spread = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "minRange")) + { + m_minRange = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "maxRange")) + { + m_maxRange = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "master")) + { + m_iszMaster = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseEntity::KeyValue(pkvd); + } +} + +BOOL CFuncTank::OnControls(entvars_t *pevTest) +{ + if (!(pev->spawnflags & SF_TANK_CANCONTROL)) + return FALSE; + + Vector offset = pevTest->origin - pev->origin; + if ((m_vecControllerUsePos - pevTest->origin).Length() < 30.0f) + { + return TRUE; + } + + return FALSE; +} + +BOOL CFuncTank::StartControl(CBasePlayer *pController) +{ + if (m_pController) + return FALSE; + + // Team only or disabled? + if (m_iszMaster) + { + if (!UTIL_IsMasterTriggered(m_iszMaster, pController)) + { + return FALSE; + } + } + + ALERT(at_console, "using TANK!\n"); + + m_pController = pController; + + if (m_pController->m_pActiveItem) + { + m_pController->m_pActiveItem->Holster(); + m_pController->pev->weaponmodel = 0; + +#ifdef BUILD_LATEST_FIXES + m_pController->pev->viewmodel = 0; +#endif + +#ifdef REGAMEDLL_FIXES + m_pController->m_iFOV = DEFAULT_FOV; +#endif + } + + m_pController->m_iHideHUD |= HIDEHUD_WEAPONS; + m_vecControllerUsePos = m_pController->pev->origin; + + pev->nextthink = pev->ltime + 0.1f; + + return TRUE; +} + +void CFuncTank::StopControl() +{ + // TODO: bring back the controllers current weapon + if (!m_pController) + return; + + if (m_pController->m_pActiveItem) + { + m_pController->m_pActiveItem->Deploy(); + + if (m_pController->IsPlayer()) + { + m_pController->ResetMaxSpeed(); + } + } + + ALERT(at_console, "stopped using TANK\n"); + +#ifdef REGAMEDLL_FIXES + if (m_pController->m_pActiveItem) +#endif + { + m_pController->m_iHideHUD &= ~HIDEHUD_WEAPONS; + } + + pev->nextthink = 0; + m_pController = nullptr; + + if (IsActive()) + { + pev->nextthink = pev->ltime + 1.0f; + } +} + +void CFuncTank::ControllerPostFrame() +{ + assert(m_pController != nullptr); + + if (gpGlobals->time < m_flNextAttack) + return; + + if (m_pController->pev->button & IN_ATTACK) + { + Vector vecForward; + UTIL_MakeVectorsPrivate(pev->angles, vecForward, nullptr, nullptr); + + m_fireLast = gpGlobals->time - (1.0f / m_fireRate) - 0.01f; + Fire(BarrelPosition(), vecForward, m_pController->pev); + + if (m_pController && m_pController->IsPlayer()) + { + m_pController->m_iWeaponVolume = LOUD_GUN_VOLUME; + } + + m_flNextAttack = gpGlobals->time + (1.0f / m_fireRate); + } +} + +void CFuncTank::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + // player controlled turret + if (pev->spawnflags & SF_TANK_CANCONTROL) + { + if (pActivator->Classify() != CLASS_PLAYER) + return; + + if (value == 2 && useType == USE_SET) + { + ControllerPostFrame(); + } + else if (!m_pController && useType != USE_OFF) + { + ((CBasePlayer *)pActivator)->m_pTank = this; + StartControl((CBasePlayer*)pActivator); + } + else + { + StopControl(); + } + } + else + { + if (!ShouldToggle(useType, IsActive())) + return; + + if (IsActive()) + TankDeactivate(); + else + TankActivate(); + } +} + +edict_t *CFuncTank::FindTarget(edict_t *pPlayer) +{ + return pPlayer; +} + +BOOL CFuncTank::InRange(float range) +{ + if (range < m_minRange) + return FALSE; + + if (m_maxRange > 0 && range > m_maxRange) + return FALSE; + + return TRUE; +} + +void CFuncTank::Think() +{ + pev->avelocity = g_vecZero; + TrackTarget(); + + if (Q_fabs(real_t(pev->avelocity.x)) > 1 || Q_fabs(real_t(pev->avelocity.y)) > 1) + StartRotSound(); + else + StopRotSound(); +} + +void CFuncTank::TrackTarget() +{ + TraceResult tr; + edict_t *pPlayer = FIND_CLIENT_IN_PVS(edict()); + bool updateTime = false, lineOfSight = false; + Vector angles, direction, targetPosition, barrelEnd; + edict_t *pTarget = nullptr; + + // Get a position to aim for + if (m_pController) + { + // Tanks attempt to mirror the player's angles + angles = m_pController->pev->v_angle; + angles.x = 0 - angles.x; + pev->nextthink = pev->ltime + 0.05f; + } + else + { + if (IsActive()) + pev->nextthink = pev->ltime + 0.1f; + else + return; + + if (FNullEnt(pPlayer)) + { + if (IsActive()) + { + // Wait 2 secs + pev->nextthink = pev->ltime + 2.0f; + } + + return; + } + + pTarget = FindTarget(pPlayer); + if (!pTarget) + { + return; + } + + // Calculate angle needed to aim at target + barrelEnd = BarrelPosition(); + targetPosition = pTarget->v.origin + pTarget->v.view_ofs; + float range = (targetPosition - barrelEnd).Length(); + + if (!InRange(range)) + return; + + UTIL_TraceLine(barrelEnd, targetPosition, dont_ignore_monsters, edict(), &tr); + + // No line of sight, don't track + if (tr.flFraction == 1.0f || tr.pHit == pTarget) + { + lineOfSight = true; + + CBaseEntity *pInstance = CBaseEntity::Instance(pTarget); + if (InRange(range) && pInstance && pInstance->IsAlive()) + { + updateTime = true; + m_sightOrigin = UpdateTargetPosition(pInstance); + } + } + + // Track sight origin + // TODO: I'm not sure what i changed + direction = m_sightOrigin - pev->origin; + //direction = m_sightOrigin - barrelEnd; + angles = UTIL_VecToAngles(direction); + + // Calculate the additional rotation to point the end of the barrel at the target (not the gun's center) + AdjustAnglesForBarrel(angles, direction.Length()); + } + + angles.x = -angles.x; + + // Force the angles to be relative to the center position + angles.y = m_yawCenter + UTIL_AngleDistance(angles.y, m_yawCenter); + angles.x = m_pitchCenter + UTIL_AngleDistance(angles.x, m_pitchCenter); + + // Limit against range in y + if (angles.y > m_yawCenter + m_yawRange) + { + angles.y = m_yawCenter + m_yawRange; + + // Don't update if you saw the player, but out of range + updateTime = false; + } + else if (angles.y < (m_yawCenter - m_yawRange)) + { + angles.y = (m_yawCenter - m_yawRange); + + // Don't update if you saw the player, but out of range + updateTime = false; + } + + if (updateTime) + { + m_lastSightTime = gpGlobals->time; + } + + // Move toward target at rate or less + real_t distY = UTIL_AngleDistance(angles.y, pev->angles.y); + pev->avelocity.y = distY * 10.0f; + + if (pev->avelocity.y > m_yawRate) + { + pev->avelocity.y = m_yawRate; + } + else if (pev->avelocity.y < -m_yawRate) + { + pev->avelocity.y = -m_yawRate; + } + + // Limit against range in x + if (angles.x > m_pitchCenter + m_pitchRange) + { + angles.x = m_pitchCenter + m_pitchRange; + } + else if (angles.x < m_pitchCenter - m_pitchRange) + { + angles.x = m_pitchCenter - m_pitchRange; + } + + // Move toward target at rate or less + real_t distX = UTIL_AngleDistance(angles.x, pev->angles.x); + pev->avelocity.x = distX * 10.0f; + + if (pev->avelocity.x > m_pitchRate) + { + pev->avelocity.x = m_pitchRate; + } + else if (pev->avelocity.x < -m_pitchRate) + { + pev->avelocity.x = -m_pitchRate; + } + + if (m_pController) + { + return; + } + + if (CanFire() && ((Q_fabs(distX) < m_pitchTolerance && Q_fabs(distY) < m_yawTolerance) || (pev->spawnflags & SF_TANK_LINEOFSIGHT))) + { + bool fire = false; + Vector forward; + UTIL_MakeVectorsPrivate(pev->angles, forward, nullptr, nullptr); + + if (pev->spawnflags & SF_TANK_LINEOFSIGHT) + { + float length = direction.Length(); + UTIL_TraceLine(barrelEnd, barrelEnd + forward * length, dont_ignore_monsters, edict(), &tr); + + if (tr.pHit == pTarget) + { + fire = true; + } + } + else + fire = true; + + if (fire) + { + Fire(BarrelPosition(), forward, pev); + } + else + m_fireLast = 0; + } + else + m_fireLast = 0; +} + +// If barrel is offset, add in additional rotation +void CFuncTank::AdjustAnglesForBarrel(Vector &angles, float distance) +{ + real_t r2, d2; + + if (m_barrelPos.y != 0.0f || m_barrelPos.z != 0.0f) + { + distance -= m_barrelPos.z; + d2 = distance * distance; + + if (m_barrelPos.y) + { + r2 = m_barrelPos.y * m_barrelPos.y; + angles.y += (180.0f / M_PI) * atan2(m_barrelPos.y, Q_sqrt(d2 - r2)); + } + + if (m_barrelPos.z) + { + r2 = m_barrelPos.z * m_barrelPos.z; + angles.x += (180.0f / M_PI) * atan2(-m_barrelPos.z, Q_sqrt(d2 - r2)); + } + } +} + +// Fire targets and spawn sprites +void CFuncTank::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) +{ + if (m_fireLast != 0.0f) + { + if (m_iszSpriteSmoke) + { + CSprite *pSprite = CSprite::SpriteCreate(STRING(m_iszSpriteSmoke), barrelEnd, TRUE); + + pSprite->AnimateAndDie(RANDOM_FLOAT(15, 20)); + pSprite->SetTransparency(kRenderTransAlpha, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, 255, kRenderFxNone); + pSprite->pev->velocity.z = RANDOM_FLOAT(40, 80); + pSprite->SetScale(m_spriteScale); + } + + if (m_iszSpriteFlash) + { + CSprite *pSprite = CSprite::SpriteCreate(STRING(m_iszSpriteFlash), barrelEnd, TRUE); + + pSprite->AnimateAndDie(60); + pSprite->SetTransparency(kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation); + pSprite->SetScale(m_spriteScale); + + // Hack Hack, make it stick around for at least 100 ms. + pSprite->pev->nextthink += 0.1f; + } + + SUB_UseTargets(this, USE_TOGGLE, 0); + } + + m_fireLast = gpGlobals->time; +} + +void CFuncTank::TankTrace(const Vector &vecStart, const Vector &vecForward, const Vector &vecSpread, TraceResult &tr) +{ + // get circular gaussian spread + float x, z; + real_t y; + + do + { + x = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); + y = RANDOM_FLOAT(-0.5, 0.5) + RANDOM_FLOAT(-0.5, 0.5); + z = x * x + y * y; + } + while (z > 1); + + Vector vecDir = vecForward + x * vecSpread.x * gpGlobals->v_right + y * vecSpread.y * gpGlobals->v_up; + Vector vecEnd; + + vecEnd = vecStart + vecDir * 4096.0f; + UTIL_TraceLine(vecStart, vecEnd, dont_ignore_monsters, edict(), &tr); +} + +void CFuncTank::StartRotSound() +{ + if (!pev->noise || (pev->spawnflags & SF_TANK_SOUNDON)) + { + return; + } + + pev->spawnflags |= SF_TANK_SOUNDON; + EMIT_SOUND(edict(), CHAN_STATIC, (char *)STRING(pev->noise), 0.85, ATTN_NORM); +} + +void CFuncTank::StopRotSound() +{ + if (pev->spawnflags & SF_TANK_SOUNDON) + { + STOP_SOUND(edict(), CHAN_STATIC, (char *)STRING(pev->noise)); + } + + pev->spawnflags &= ~SF_TANK_SOUNDON; +} + +LINK_ENTITY_TO_CLASS(func_tank, CFuncTankGun, CCSFuncTankGun) + +void CFuncTankGun::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) +{ + if (m_fireLast != 0.0f) + { + // FireBullets needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); + if (bulletCount > 0) + { + for (int i = 0; i < bulletCount; i++) + { + switch (m_bulletType) + { + case TANK_BULLET_9MM: + FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_9MM, 1, m_iBulletDamage, pevAttacker); + break; + case TANK_BULLET_MP5: + FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_MP5, 1, m_iBulletDamage, pevAttacker); + break; + case TANK_BULLET_12MM: + FireBullets(1, barrelEnd, forward, m_TankSpread[m_spread], 4096, BULLET_MONSTER_12MM, 1, m_iBulletDamage, pevAttacker); + break; + default: + case TANK_BULLET_NONE: + break; + } + } + + CFuncTank::Fire(barrelEnd, forward, pevAttacker); + } + } + else + { + CFuncTank::Fire(barrelEnd, forward, pevAttacker); + } +} + +TYPEDESCRIPTION CFuncTankLaser::m_SaveData[] = +{ + DEFINE_FIELD(CFuncTankLaser, m_pLaser, FIELD_CLASSPTR), + DEFINE_FIELD(CFuncTankLaser, m_laserTime, FIELD_TIME), +}; + +LINK_ENTITY_TO_CLASS(func_tanklaser, CFuncTankLaser, CCSFuncTankLaser) +IMPLEMENT_SAVERESTORE(CFuncTankLaser, CFuncTank) + +void CFuncTankLaser::Activate() +{ + if (!GetLaser()) + { + UTIL_Remove(this); + ALERT(at_error, "Laser tank with no env_laser!\n"); + } + else + { + m_pLaser->TurnOff(); + } +} + +void CFuncTankLaser::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "laserentity")) + { + pev->message = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CFuncTank::KeyValue(pkvd); + } +} + +CLaser *CFuncTankLaser::GetLaser() +{ + if (m_pLaser) + { + return m_pLaser; + } + + edict_t *pentLaser = FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(pev->message)); + + while (!FNullEnt(pentLaser)) + { + // Found the landmark + if (FClassnameIs(pentLaser, "env_laser")) + { + m_pLaser = (CLaser *)CBaseEntity::Instance(pentLaser); + break; + } + else + pentLaser = FIND_ENTITY_BY_TARGETNAME(pentLaser, STRING(pev->message)); + } + + return m_pLaser; +} + +void CFuncTankLaser::Think() +{ + if (m_pLaser && gpGlobals->time > m_laserTime) + { + m_pLaser->TurnOff(); + } + + CFuncTank::Think(); +} + +void CFuncTankLaser::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) +{ + if (m_fireLast != 0.0f && GetLaser()) + { + // TankTrace needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); + if (bulletCount) + { + TraceResult tr; + for (int i = 0; i < bulletCount; i++) + { + m_pLaser->pev->origin = barrelEnd; + TankTrace(barrelEnd, forward, m_TankSpread[m_spread], tr); + + m_laserTime = gpGlobals->time; + m_pLaser->TurnOn(); + m_pLaser->pev->dmgtime = gpGlobals->time - 1.0f; + m_pLaser->FireAtPoint(tr); + m_pLaser->pev->nextthink = 0; + } + + CFuncTank::Fire(barrelEnd, forward, pev); + } + } + else + { + CFuncTank::Fire(barrelEnd, forward, pev); + } +} + +LINK_ENTITY_TO_CLASS(func_tankrocket, CFuncTankRocket, CCSFuncTankRocket) + +void CFuncTankRocket::Precache() +{ + UTIL_PrecacheOther("rpg_rocket"); + CFuncTank::Precache(); +} + +void CFuncTankRocket::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) +{ + if (m_fireLast != 0.0f) + { + int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); + if (bulletCount > 0) + { + for (int i = 0; i < bulletCount; i++) + { + CBaseEntity *pRocket = CBaseEntity::Create("rpg_rocket", barrelEnd, pev->angles, edict()); + } + + CFuncTank::Fire(barrelEnd, forward, pev); + } + } + else + { + CFuncTank::Fire(barrelEnd, forward, pev); + } +} + +LINK_ENTITY_TO_CLASS(func_tankmortar, CFuncTankMortar, CCSFuncTankMortar) + +void CFuncTankMortar::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "iMagnitude")) + { + pev->impulse = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CFuncTank::KeyValue(pkvd); + } +} + +void CFuncTankMortar::Fire(const Vector &barrelEnd, const Vector &forward, entvars_t *pevAttacker) +{ + if (m_fireLast != 0.0f) + { + int bulletCount = int((gpGlobals->time - m_fireLast) * m_fireRate); + + // Only create 1 explosion + if (bulletCount > 0) + { + TraceResult tr; + + // TankTrace needs gpGlobals->v_up, etc. + UTIL_MakeAimVectors(pev->angles); + + TankTrace(barrelEnd, forward, m_TankSpread[m_spread], tr); + ExplosionCreate(tr.vecEndPos, pev->angles, edict(), pev->impulse, TRUE); + CFuncTank::Fire(barrelEnd, forward, pev); + } + } + else + { + CFuncTank::Fire(barrelEnd, forward, pev); + } +} + +TYPEDESCRIPTION CFuncTankControls::m_SaveData[] = +{ + DEFINE_FIELD(CFuncTankControls, m_pTank, FIELD_CLASSPTR), +}; + +LINK_ENTITY_TO_CLASS(func_tankcontrols, CFuncTankControls, CCSFuncTankControls) +IMPLEMENT_SAVERESTORE(CFuncTankControls, CBaseEntity) + +void CFuncTankControls::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + // pass the Use command onto the controls + if (m_pTank) + { + m_pTank->Use(pActivator, pCaller, useType, value); + } + + // if this fails, most likely means save/restore hasn't worked properly + assert(m_pTank != nullptr); +} + +void CFuncTankControls::Think() +{ + edict_t *pTarget = nullptr; + + do + { + pTarget = FIND_ENTITY_BY_TARGETNAME(pTarget, STRING(pev->target)); + } + while (!FNullEnt(pTarget) && Q_strncmp(STRING(pTarget->v.classname), "func_tank", 9) != 0); + + if (FNullEnt(pTarget)) + { + ALERT(at_console, "No tank %s\n", STRING(pev->target)); + return; + } + + m_pTank = static_cast(Instance(pTarget)); +} + +void CFuncTankControls::Spawn() +{ + pev->solid = SOLID_TRIGGER; + pev->movetype = MOVETYPE_NONE; + pev->effects |= EF_NODRAW; + + SET_MODEL(ENT(pev), STRING(pev->model)); + + UTIL_SetSize(pev, pev->mins, pev->maxs); + UTIL_SetOrigin(pev, pev->origin); + + // After all the func_tank's have spawned + pev->nextthink = gpGlobals->time + 0.3f; + + CBaseEntity::Spawn(); +} diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 5b06ebbe..162fea26 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -1,366 +1,366 @@ -#include "precompiled.h" - -cvar_t *g_pskill = nullptr; -cvar_t *g_psv_gravity = nullptr; -cvar_t *g_psv_aim = nullptr; -cvar_t *g_footsteps = nullptr; -cvar_t *g_psv_accelerate = nullptr; -cvar_t *g_psv_friction = nullptr; -cvar_t *g_psv_stopspeed = nullptr; -cvar_t *g_psv_stepsize = nullptr; -cvar_t *g_psv_clienttrace = nullptr; - -cvar_t displaysoundlist = { "displaysoundlist", "0", 0, 0.0f, nullptr }; -cvar_t timelimit = { "mp_timelimit", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t flashlight = { "mp_flashlight", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t decalfrequency = { "decalfrequency", "30", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t fadetoblack = { "mp_fadetoblack", "0", FCVAR_SERVER, 0.0f, nullptr }; - -cvar_t fragsleft = { "mp_fragsleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, 0.0f, nullptr }; // Don't spam console/log files/users with this changing -cvar_t timeleft = { "mp_timeleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, 0.0f, nullptr }; - -cvar_t friendlyfire = { "mp_friendlyfire", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t infiniteAmmo = { "mp_infinite_ammo", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t infiniteGrenades = { "mp_infinite_grenades", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t allowmonsters = { "mp_allowmonsters", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t roundtime = { "mp_roundtime", "5", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t buytime = { "mp_buytime", "1.5", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t freezetime = { "mp_freezetime", "6", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t c4timer = { "mp_c4timer", "45", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t ghostfrequency = { "mp_ghostfrequency", "0.1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t autokick = { "mp_autokick", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t autokick_timeout = { "mp_autokick_timeout", "-1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t restartround = { "sv_restartround", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t sv_restart = { "sv_restart", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t limitteams = { "mp_limitteams", "2", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t autoteambalance = { "mp_autoteambalance", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t tkpunish = { "mp_tkpunish", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t hostagepenalty = { "mp_hostagepenalty", "13", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t mirrordamage = { "mp_mirrordamage", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t logmessages = { "mp_logmessages", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t forcecamera = { "mp_forcecamera", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t forcechasecam = { "mp_forcechasecam", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t mapvoteratio = { "mp_mapvoteratio", "0.66", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t logdetail = { "mp_logdetail", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t startmoney = { "mp_startmoney", "800", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t maxrounds = { "mp_maxrounds", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t winlimit = { "mp_winlimit", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t windifference = { "mp_windifference", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t playerid = { "mp_playerid", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t allow_spectators = { "allow_spectators", "1.0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t mp_chattime = { "mp_chattime", "10", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t kick_percent = { "mp_kickpercent", "0.66", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t humans_join_team = { "humans_join_team", "any", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t sk_plr_9mm_bullet1 = { "sk_plr_9mm_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mm_bullet2 = { "sk_plr_9mm_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mm_bullet3 = { "sk_plr_9mm_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_357_bullet1 = { "sk_plr_357_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_357_bullet2 = { "sk_plr_357_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_357_bullet3 = { "sk_plr_357_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_bullet1 = { "sk_plr_9mmAR_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_bullet2 = { "sk_plr_9mmAR_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_bullet3 = { "sk_plr_9mmAR_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_grenade1 = { "sk_plr_9mmAR_grenade1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_grenade2 = { "sk_plr_9mmAR_grenade2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_9mmAR_grenade3 = { "sk_plr_9mmAR_grenade3", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_buckshot1 = { "sk_plr_buckshot1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_buckshot2 = { "sk_plr_buckshot2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_buckshot3 = { "sk_plr_buckshot3", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_rpg1 = { "sk_plr_rpg1", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_rpg2 = { "sk_plr_rpg2", "0", 0, 0.0f, nullptr }; -cvar_t sk_plr_rpg3 = { "sk_plr_rpg3", "0", 0, 0.0f, nullptr }; -cvar_t sk_12mm_bullet1 = { "sk_12mm_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_12mm_bullet2 = { "sk_12mm_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_12mm_bullet3 = { "sk_12mm_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mmAR_bullet1 = { "sk_9mmAR_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mmAR_bullet2 = { "sk_9mmAR_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mmAR_bullet3 = { "sk_9mmAR_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mm_bullet1 = { "sk_9mm_bullet1", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mm_bullet2 = { "sk_9mm_bullet2", "0", 0, 0.0f, nullptr }; -cvar_t sk_9mm_bullet3 = { "sk_9mm_bullet3", "0", 0, 0.0f, nullptr }; -cvar_t sk_suitcharger1 = { "sk_suitcharger1", "0", 0, 0.0f, nullptr }; -cvar_t sk_suitcharger2 = { "sk_suitcharger2", "0", 0, 0.0f, nullptr }; -cvar_t sk_suitcharger3 = { "sk_suitcharger3", "0", 0, 0.0f, nullptr }; -cvar_t sk_battery1 = { "sk_battery1", "0", 0, 0.0f, nullptr }; -cvar_t sk_battery2 = { "sk_battery2", "0", 0, 0.0f, nullptr }; -cvar_t sk_battery3 = { "sk_battery3", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthcharger1 = { "sk_healthcharger1", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthcharger2 = { "sk_healthcharger2", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthcharger3 = { "sk_healthcharger3", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthkit1 = { "sk_healthkit1", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthkit2 = { "sk_healthkit2", "0", 0, 0.0f, nullptr }; -cvar_t sk_healthkit3 = { "sk_healthkit3", "0", 0, 0.0f, nullptr }; -cvar_t sk_scientist_heal1 = { "sk_scientist_heal1", "0", 0, 0.0f, nullptr }; -cvar_t sk_scientist_heal2 = { "sk_scientist_heal2", "0", 0, 0.0f, nullptr }; -cvar_t sk_scientist_heal3 = { "sk_scientist_heal3", "0", 0, 0.0f, nullptr }; - -#ifdef REGAMEDLL_ADD - -cvar_t game_version = { "game_version", APP_VERSION, FCVAR_SERVER, 0.0f, nullptr }; -cvar_t maxmoney = { "mp_maxmoney", "16000", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t round_infinite = { "mp_round_infinite", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t hegrenade_penetration = { "mp_hegrenade_penetration", "0", 0, 0.0f, nullptr }; -cvar_t nadedrops = { "mp_nadedrops", "0", 0, 0.0f, nullptr }; -cvar_t roundrespawn_time = { "mp_roundrespawn_time", "20", 0, 20.0f, nullptr }; -cvar_t auto_reload_weapons = { "mp_auto_reload_weapons", "0", 0, 0.0f, nullptr }; -cvar_t refill_bpammo_weapons = { "mp_refill_bpammo_weapons", "0", 0, 0.0f, nullptr }; // Useful for mods like DeathMatch, GunGame, ZombieMod etc -cvar_t freeforall = { "mp_freeforall", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t auto_join_team = { "mp_auto_join_team", "0", 0, 0.0f, nullptr }; -cvar_t max_teamkills = { "mp_max_teamkills", "3", 0, 3.0f, nullptr }; -cvar_t fraglimit = { "mp_fraglimit", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t round_restart_delay = { "mp_round_restart_delay", "5", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t showtriggers = { "showtriggers", "0", 0, 0.0f, nullptr }; // debug cvar shows triggers - // TODO: Maybe it's better to register in the engine? -cvar_t hostagehurtable = { "mp_hostage_hurtable", "1", FCVAR_SERVER, 1.0f, nullptr }; -cvar_t roundover = { "mp_roundover", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t forcerespawn = { "mp_forcerespawn", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t show_radioicon = { "mp_show_radioicon", "1", 0, 1.0f, nullptr }; -cvar_t show_scenarioicon = { "mp_show_scenarioicon", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t old_bomb_defused_sound = { "mp_old_bomb_defused_sound", "1", 0, 1.0f, nullptr }; -cvar_t item_staytime = { "mp_item_staytime", "300", FCVAR_SERVER, 300.0f, nullptr }; -cvar_t legacy_bombtarget_touch = { "mp_legacy_bombtarget_touch", "1", 0, 1.0f, nullptr }; -cvar_t respawn_immunitytime = { "mp_respawn_immunitytime", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t respawn_immunity_effects = { "mp_respawn_immunity_effects", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t kill_filled_spawn = { "mp_kill_filled_spawn", "1", 0, 0.0f, nullptr }; -cvar_t afk_bomb_drop_time = { "mp_afk_bomb_drop_time", "0", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t buy_anywhere = { "mp_buy_anywhere", "0", FCVAR_SERVER, 0.0f, nullptr }; - -cvar_t allow_point_servercommand = { "mp_allow_point_servercommand", "0", 0, 0.0f, nullptr }; -cvar_t hullbounds_sets = { "mp_hullbounds_sets", "1", 0, 0.0f, nullptr }; -cvar_t unduck_method = { "mp_unduck_method", "0", 0, 0.0f, nullptr }; - -cvar_t scoreboard_showmoney = { "mp_scoreboard_showmoney", "3", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t scoreboard_showhealth = { "mp_scoreboard_showhealth", "3", FCVAR_SERVER, 0.0f, nullptr }; - -cvar_t ff_damage_reduction_bullets = { "ff_damage_reduction_bullets", "0.35", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t ff_damage_reduction_grenade = { "ff_damage_reduction_grenade", "0.25", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t ff_damage_reduction_grenade_self = { "ff_damage_reduction_grenade_self", "1", FCVAR_SERVER, 0.0f, nullptr }; -cvar_t ff_damage_reduction_other = { "ff_damage_reduction_other", "0.25", FCVAR_SERVER, 0.0f, nullptr }; - -cvar_t radio_timeout = { "mp_radio_timeout", "1.5", FCVAR_SERVER, 1.5f, nullptr }; -cvar_t radio_maxinround = { "mp_radio_maxinround", "60", FCVAR_SERVER, 60.0f, nullptr }; - -void GameDLL_Version_f() -{ - if (Q_stricmp(CMD_ARGV(1), "version") != 0) - return; - - // print version - CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); - CONSOLE_ECHO("Build date: " APP_COMMIT_TIME " " APP_COMMIT_DATE "\n"); - CONSOLE_ECHO("Build from: " APP_COMMIT_URL APP_COMMIT_SHA "\n"); -} - -void GameDLL_EndRound_f() -{ - if (CMD_ARGC() == 2) - { - const char *pCmd = CMD_ARGV(1); - - if (pCmd[0] == '1' || !Q_stricmp(pCmd, "T")) - { - CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_TERRORISTS, ROUND_TERRORISTS_WIN, CSGameRules()->GetRoundRestartDelay()); - return; - } - else if (pCmd[0] == '2' || !Q_stricmp(pCmd, "CT")) - { - CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_CTS, ROUND_CTS_WIN, CSGameRules()->GetRoundRestartDelay()); - return; - } - } - - CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_DRAW, ROUND_END_DRAW, CSGameRules()->GetRoundRestartDelay()); -} - -#endif // REGAMEDLL_ADD - -void EXT_FUNC GameDLLInit() -{ - g_pskill = CVAR_GET_POINTER("skill"); - g_psv_gravity = CVAR_GET_POINTER("sv_gravity"); - g_psv_aim = CVAR_GET_POINTER("sv_aim"); - g_footsteps = CVAR_GET_POINTER("mp_footsteps"); - g_psv_accelerate = CVAR_GET_POINTER("sv_accelerate"); - g_psv_friction = CVAR_GET_POINTER("sv_friction"); - g_psv_stopspeed = CVAR_GET_POINTER("sv_stopspeed"); - g_psv_stepsize = CVAR_GET_POINTER("sv_stepsize"); - g_psv_clienttrace = CVAR_GET_POINTER("sv_clienttrace"); - - CVAR_REGISTER(&displaysoundlist); - CVAR_REGISTER(&timelimit); - CVAR_REGISTER(&friendlyfire); - -#ifdef BUILD_LATEST - CVAR_REGISTER(&infiniteAmmo); - CVAR_REGISTER(&infiniteGrenades); -#endif - - CVAR_REGISTER(&flashlight); - CVAR_REGISTER(&decalfrequency); - -#ifndef REGAMEDLL_FIXES - CVAR_REGISTER(&allowmonsters); -#endif - - CVAR_REGISTER(&roundtime); - CVAR_REGISTER(&buytime); - CVAR_REGISTER(&freezetime); - CVAR_REGISTER(&c4timer); - -#ifndef REGAMEDLL_FIXES - CVAR_REGISTER(&ghostfrequency); -#endif - - CVAR_REGISTER(&autokick); - CVAR_REGISTER(&autokick_timeout); - CVAR_REGISTER(&restartround); - CVAR_REGISTER(&sv_restart); - CVAR_REGISTER(&limitteams); - CVAR_REGISTER(&autoteambalance); - CVAR_REGISTER(&tkpunish); - CVAR_REGISTER(&hostagepenalty); - CVAR_REGISTER(&mirrordamage); - CVAR_REGISTER(&logmessages); - CVAR_REGISTER(&forcecamera); - CVAR_REGISTER(&forcechasecam); - CVAR_REGISTER(&mapvoteratio); - CVAR_REGISTER(&maxrounds); - CVAR_REGISTER(&winlimit); - CVAR_REGISTER(&windifference); - CVAR_REGISTER(&fadetoblack); - CVAR_REGISTER(&logdetail); - CVAR_REGISTER(&startmoney); - CVAR_REGISTER(&playerid); - CVAR_REGISTER(&allow_spectators); - CVAR_REGISTER(&mp_chattime); - CVAR_REGISTER(&kick_percent); - CVAR_REGISTER(&fragsleft); - CVAR_REGISTER(&timeleft); - CVAR_REGISTER(&humans_join_team); - -#ifdef BUILD_LATEST - if (AreRunningBeta()) - { - CVAR_REGISTER(&scoreboard_showhealth); - CVAR_REGISTER(&scoreboard_showmoney); - } -#endif - -// Remove unused cvars -#ifndef REGAMEDLL_FIXES - - CVAR_REGISTER(&sk_plr_9mm_bullet1); - CVAR_REGISTER(&sk_plr_9mm_bullet2); - CVAR_REGISTER(&sk_plr_9mm_bullet3); - CVAR_REGISTER(&sk_plr_357_bullet1); - CVAR_REGISTER(&sk_plr_357_bullet2); - CVAR_REGISTER(&sk_plr_357_bullet3); - CVAR_REGISTER(&sk_plr_9mmAR_bullet1); - CVAR_REGISTER(&sk_plr_9mmAR_bullet2); - CVAR_REGISTER(&sk_plr_9mmAR_bullet3); - CVAR_REGISTER(&sk_plr_9mmAR_grenade1); - CVAR_REGISTER(&sk_plr_9mmAR_grenade2); - CVAR_REGISTER(&sk_plr_9mmAR_grenade3); - CVAR_REGISTER(&sk_plr_buckshot1); - CVAR_REGISTER(&sk_plr_buckshot2); - CVAR_REGISTER(&sk_plr_buckshot3); - CVAR_REGISTER(&sk_plr_rpg1); - CVAR_REGISTER(&sk_plr_rpg2); - CVAR_REGISTER(&sk_plr_rpg3); - CVAR_REGISTER(&sk_12mm_bullet1); - CVAR_REGISTER(&sk_12mm_bullet2); - CVAR_REGISTER(&sk_12mm_bullet3); - CVAR_REGISTER(&sk_9mmAR_bullet1); - CVAR_REGISTER(&sk_9mmAR_bullet2); - CVAR_REGISTER(&sk_9mmAR_bullet3); - CVAR_REGISTER(&sk_9mm_bullet1); - CVAR_REGISTER(&sk_9mm_bullet2); - CVAR_REGISTER(&sk_9mm_bullet3); - CVAR_REGISTER(&sk_suitcharger1); - CVAR_REGISTER(&sk_suitcharger2); - CVAR_REGISTER(&sk_suitcharger3); - CVAR_REGISTER(&sk_battery1); - CVAR_REGISTER(&sk_battery2); - CVAR_REGISTER(&sk_battery3); - CVAR_REGISTER(&sk_healthcharger1); - CVAR_REGISTER(&sk_healthcharger2); - CVAR_REGISTER(&sk_healthcharger3); - CVAR_REGISTER(&sk_healthkit1); - CVAR_REGISTER(&sk_healthkit2); - CVAR_REGISTER(&sk_healthkit3); - CVAR_REGISTER(&sk_scientist_heal1); - CVAR_REGISTER(&sk_scientist_heal2); - CVAR_REGISTER(&sk_scientist_heal3); - -#endif // REGAMEDLL_FIXES - -#ifdef REGAMEDLL_ADD - - ADD_SERVER_COMMAND("game", GameDLL_Version_f); - ADD_SERVER_COMMAND("endround", GameDLL_EndRound_f); - - CVAR_REGISTER(&game_version); - CVAR_REGISTER(&maxmoney); - CVAR_REGISTER(&round_infinite); - CVAR_REGISTER(&hegrenade_penetration); - CVAR_REGISTER(&nadedrops); - CVAR_REGISTER(&roundrespawn_time); - CVAR_REGISTER(&auto_reload_weapons); - CVAR_REGISTER(&refill_bpammo_weapons); - CVAR_REGISTER(&freeforall); - CVAR_REGISTER(&auto_join_team); - CVAR_REGISTER(&max_teamkills); - CVAR_REGISTER(&fraglimit); - CVAR_REGISTER(&round_restart_delay); - - CVAR_REGISTER(&showtriggers); - CVAR_REGISTER(&hostagehurtable); - CVAR_REGISTER(&roundover); - CVAR_REGISTER(&forcerespawn); - CVAR_REGISTER(&show_radioicon); - - if (!AreRunningCZero()) - { - CVAR_REGISTER(&show_scenarioicon); - } - - CVAR_REGISTER(&old_bomb_defused_sound); - CVAR_REGISTER(&item_staytime); - CVAR_REGISTER(&legacy_bombtarget_touch); - CVAR_REGISTER(&respawn_immunitytime); - CVAR_REGISTER(&respawn_immunity_effects); - CVAR_REGISTER(&kill_filled_spawn); - CVAR_REGISTER(&afk_bomb_drop_time); - CVAR_REGISTER(&buy_anywhere); - CVAR_REGISTER(&allow_point_servercommand); - CVAR_REGISTER(&hullbounds_sets); - CVAR_REGISTER(&unduck_method); - - CVAR_REGISTER(&ff_damage_reduction_bullets); - CVAR_REGISTER(&ff_damage_reduction_grenade); - CVAR_REGISTER(&ff_damage_reduction_grenade_self); - CVAR_REGISTER(&ff_damage_reduction_other); - - CVAR_REGISTER(&radio_timeout); - CVAR_REGISTER(&radio_maxinround); - - // print version - CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); - -#endif // REGAMEDLL_ADD - - Bot_RegisterCVars(); - Tutor_RegisterCVars(); - Hostage_RegisterCVars(); - -#ifdef REGAMEDLL_FIXES - VoiceGameMgr_RegisterCVars(); -#endif - -#ifdef REGAMEDLL_ADD - // execute initial pre-configurations - SERVER_COMMAND("exec game_init.cfg\n"); - SERVER_EXECUTE(); -#endif - -} +#include "precompiled.h" + +cvar_t *g_pskill = nullptr; +cvar_t *g_psv_gravity = nullptr; +cvar_t *g_psv_aim = nullptr; +cvar_t *g_footsteps = nullptr; +cvar_t *g_psv_accelerate = nullptr; +cvar_t *g_psv_friction = nullptr; +cvar_t *g_psv_stopspeed = nullptr; +cvar_t *g_psv_stepsize = nullptr; +cvar_t *g_psv_clienttrace = nullptr; + +cvar_t displaysoundlist = { "displaysoundlist", "0", 0, 0.0f, nullptr }; +cvar_t timelimit = { "mp_timelimit", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t flashlight = { "mp_flashlight", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t decalfrequency = { "decalfrequency", "30", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t fadetoblack = { "mp_fadetoblack", "0", FCVAR_SERVER, 0.0f, nullptr }; + +cvar_t fragsleft = { "mp_fragsleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, 0.0f, nullptr }; // Don't spam console/log files/users with this changing +cvar_t timeleft = { "mp_timeleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, 0.0f, nullptr }; + +cvar_t friendlyfire = { "mp_friendlyfire", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t infiniteAmmo = { "mp_infinite_ammo", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t infiniteGrenades = { "mp_infinite_grenades", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t allowmonsters = { "mp_allowmonsters", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t roundtime = { "mp_roundtime", "5", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t buytime = { "mp_buytime", "1.5", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t freezetime = { "mp_freezetime", "6", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t c4timer = { "mp_c4timer", "45", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t ghostfrequency = { "mp_ghostfrequency", "0.1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t autokick = { "mp_autokick", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t autokick_timeout = { "mp_autokick_timeout", "-1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t restartround = { "sv_restartround", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t sv_restart = { "sv_restart", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t limitteams = { "mp_limitteams", "2", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t autoteambalance = { "mp_autoteambalance", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t tkpunish = { "mp_tkpunish", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t hostagepenalty = { "mp_hostagepenalty", "13", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t mirrordamage = { "mp_mirrordamage", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t logmessages = { "mp_logmessages", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t forcecamera = { "mp_forcecamera", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t forcechasecam = { "mp_forcechasecam", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t mapvoteratio = { "mp_mapvoteratio", "0.66", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t logdetail = { "mp_logdetail", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t startmoney = { "mp_startmoney", "800", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t maxrounds = { "mp_maxrounds", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t winlimit = { "mp_winlimit", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t windifference = { "mp_windifference", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t playerid = { "mp_playerid", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t allow_spectators = { "allow_spectators", "1.0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t mp_chattime = { "mp_chattime", "10", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t kick_percent = { "mp_kickpercent", "0.66", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t humans_join_team = { "humans_join_team", "any", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t sk_plr_9mm_bullet1 = { "sk_plr_9mm_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mm_bullet2 = { "sk_plr_9mm_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mm_bullet3 = { "sk_plr_9mm_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_357_bullet1 = { "sk_plr_357_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_357_bullet2 = { "sk_plr_357_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_357_bullet3 = { "sk_plr_357_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_bullet1 = { "sk_plr_9mmAR_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_bullet2 = { "sk_plr_9mmAR_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_bullet3 = { "sk_plr_9mmAR_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_grenade1 = { "sk_plr_9mmAR_grenade1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_grenade2 = { "sk_plr_9mmAR_grenade2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_9mmAR_grenade3 = { "sk_plr_9mmAR_grenade3", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_buckshot1 = { "sk_plr_buckshot1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_buckshot2 = { "sk_plr_buckshot2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_buckshot3 = { "sk_plr_buckshot3", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_rpg1 = { "sk_plr_rpg1", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_rpg2 = { "sk_plr_rpg2", "0", 0, 0.0f, nullptr }; +cvar_t sk_plr_rpg3 = { "sk_plr_rpg3", "0", 0, 0.0f, nullptr }; +cvar_t sk_12mm_bullet1 = { "sk_12mm_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_12mm_bullet2 = { "sk_12mm_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_12mm_bullet3 = { "sk_12mm_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mmAR_bullet1 = { "sk_9mmAR_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mmAR_bullet2 = { "sk_9mmAR_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mmAR_bullet3 = { "sk_9mmAR_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mm_bullet1 = { "sk_9mm_bullet1", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mm_bullet2 = { "sk_9mm_bullet2", "0", 0, 0.0f, nullptr }; +cvar_t sk_9mm_bullet3 = { "sk_9mm_bullet3", "0", 0, 0.0f, nullptr }; +cvar_t sk_suitcharger1 = { "sk_suitcharger1", "0", 0, 0.0f, nullptr }; +cvar_t sk_suitcharger2 = { "sk_suitcharger2", "0", 0, 0.0f, nullptr }; +cvar_t sk_suitcharger3 = { "sk_suitcharger3", "0", 0, 0.0f, nullptr }; +cvar_t sk_battery1 = { "sk_battery1", "0", 0, 0.0f, nullptr }; +cvar_t sk_battery2 = { "sk_battery2", "0", 0, 0.0f, nullptr }; +cvar_t sk_battery3 = { "sk_battery3", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthcharger1 = { "sk_healthcharger1", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthcharger2 = { "sk_healthcharger2", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthcharger3 = { "sk_healthcharger3", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthkit1 = { "sk_healthkit1", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthkit2 = { "sk_healthkit2", "0", 0, 0.0f, nullptr }; +cvar_t sk_healthkit3 = { "sk_healthkit3", "0", 0, 0.0f, nullptr }; +cvar_t sk_scientist_heal1 = { "sk_scientist_heal1", "0", 0, 0.0f, nullptr }; +cvar_t sk_scientist_heal2 = { "sk_scientist_heal2", "0", 0, 0.0f, nullptr }; +cvar_t sk_scientist_heal3 = { "sk_scientist_heal3", "0", 0, 0.0f, nullptr }; + +#ifdef REGAMEDLL_ADD + +cvar_t game_version = { "game_version", APP_VERSION, FCVAR_SERVER, 0.0f, nullptr }; +cvar_t maxmoney = { "mp_maxmoney", "16000", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t round_infinite = { "mp_round_infinite", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t hegrenade_penetration = { "mp_hegrenade_penetration", "0", 0, 0.0f, nullptr }; +cvar_t nadedrops = { "mp_nadedrops", "0", 0, 0.0f, nullptr }; +cvar_t roundrespawn_time = { "mp_roundrespawn_time", "20", 0, 20.0f, nullptr }; +cvar_t auto_reload_weapons = { "mp_auto_reload_weapons", "0", 0, 0.0f, nullptr }; +cvar_t refill_bpammo_weapons = { "mp_refill_bpammo_weapons", "0", 0, 0.0f, nullptr }; // Useful for mods like DeathMatch, GunGame, ZombieMod etc +cvar_t freeforall = { "mp_freeforall", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t auto_join_team = { "mp_auto_join_team", "0", 0, 0.0f, nullptr }; +cvar_t max_teamkills = { "mp_max_teamkills", "3", 0, 3.0f, nullptr }; +cvar_t fraglimit = { "mp_fraglimit", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t round_restart_delay = { "mp_round_restart_delay", "5", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t showtriggers = { "showtriggers", "0", 0, 0.0f, nullptr }; // debug cvar shows triggers + // TODO: Maybe it's better to register in the engine? +cvar_t hostagehurtable = { "mp_hostage_hurtable", "1", FCVAR_SERVER, 1.0f, nullptr }; +cvar_t roundover = { "mp_roundover", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t forcerespawn = { "mp_forcerespawn", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t show_radioicon = { "mp_show_radioicon", "1", 0, 1.0f, nullptr }; +cvar_t show_scenarioicon = { "mp_show_scenarioicon", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t old_bomb_defused_sound = { "mp_old_bomb_defused_sound", "1", 0, 1.0f, nullptr }; +cvar_t item_staytime = { "mp_item_staytime", "300", FCVAR_SERVER, 300.0f, nullptr }; +cvar_t legacy_bombtarget_touch = { "mp_legacy_bombtarget_touch", "1", 0, 1.0f, nullptr }; +cvar_t respawn_immunitytime = { "mp_respawn_immunitytime", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t respawn_immunity_effects = { "mp_respawn_immunity_effects", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t kill_filled_spawn = { "mp_kill_filled_spawn", "1", 0, 0.0f, nullptr }; +cvar_t afk_bomb_drop_time = { "mp_afk_bomb_drop_time", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t buy_anywhere = { "mp_buy_anywhere", "0", FCVAR_SERVER, 0.0f, nullptr }; + +cvar_t allow_point_servercommand = { "mp_allow_point_servercommand", "0", 0, 0.0f, nullptr }; +cvar_t hullbounds_sets = { "mp_hullbounds_sets", "1", 0, 0.0f, nullptr }; +cvar_t unduck_method = { "mp_unduck_method", "0", 0, 0.0f, nullptr }; + +cvar_t scoreboard_showmoney = { "mp_scoreboard_showmoney", "3", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t scoreboard_showhealth = { "mp_scoreboard_showhealth", "3", FCVAR_SERVER, 0.0f, nullptr }; + +cvar_t ff_damage_reduction_bullets = { "ff_damage_reduction_bullets", "0.35", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t ff_damage_reduction_grenade = { "ff_damage_reduction_grenade", "0.25", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t ff_damage_reduction_grenade_self = { "ff_damage_reduction_grenade_self", "1", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t ff_damage_reduction_other = { "ff_damage_reduction_other", "0.25", FCVAR_SERVER, 0.0f, nullptr }; + +cvar_t radio_timeout = { "mp_radio_timeout", "1.5", FCVAR_SERVER, 1.5f, nullptr }; +cvar_t radio_maxinround = { "mp_radio_maxinround", "60", FCVAR_SERVER, 60.0f, nullptr }; + +void GameDLL_Version_f() +{ + if (Q_stricmp(CMD_ARGV(1), "version") != 0) + return; + + // print version + CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); + CONSOLE_ECHO("Build date: " APP_COMMIT_TIME " " APP_COMMIT_DATE "\n"); + CONSOLE_ECHO("Build from: " APP_COMMIT_URL APP_COMMIT_SHA "\n"); +} + +void GameDLL_EndRound_f() +{ + if (CMD_ARGC() == 2) + { + const char *pCmd = CMD_ARGV(1); + + if (pCmd[0] == '1' || !Q_stricmp(pCmd, "T")) + { + CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_TERRORISTS, ROUND_TERRORISTS_WIN, CSGameRules()->GetRoundRestartDelay()); + return; + } + else if (pCmd[0] == '2' || !Q_stricmp(pCmd, "CT")) + { + CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_CTS, ROUND_CTS_WIN, CSGameRules()->GetRoundRestartDelay()); + return; + } + } + + CSGameRules()->OnRoundEnd_Intercept(WINSTATUS_DRAW, ROUND_END_DRAW, CSGameRules()->GetRoundRestartDelay()); +} + +#endif // REGAMEDLL_ADD + +void EXT_FUNC GameDLLInit() +{ + g_pskill = CVAR_GET_POINTER("skill"); + g_psv_gravity = CVAR_GET_POINTER("sv_gravity"); + g_psv_aim = CVAR_GET_POINTER("sv_aim"); + g_footsteps = CVAR_GET_POINTER("mp_footsteps"); + g_psv_accelerate = CVAR_GET_POINTER("sv_accelerate"); + g_psv_friction = CVAR_GET_POINTER("sv_friction"); + g_psv_stopspeed = CVAR_GET_POINTER("sv_stopspeed"); + g_psv_stepsize = CVAR_GET_POINTER("sv_stepsize"); + g_psv_clienttrace = CVAR_GET_POINTER("sv_clienttrace"); + + CVAR_REGISTER(&displaysoundlist); + CVAR_REGISTER(&timelimit); + CVAR_REGISTER(&friendlyfire); + +#ifdef BUILD_LATEST + CVAR_REGISTER(&infiniteAmmo); + CVAR_REGISTER(&infiniteGrenades); +#endif + + CVAR_REGISTER(&flashlight); + CVAR_REGISTER(&decalfrequency); + +#ifndef REGAMEDLL_FIXES + CVAR_REGISTER(&allowmonsters); +#endif + + CVAR_REGISTER(&roundtime); + CVAR_REGISTER(&buytime); + CVAR_REGISTER(&freezetime); + CVAR_REGISTER(&c4timer); + +#ifndef REGAMEDLL_FIXES + CVAR_REGISTER(&ghostfrequency); +#endif + + CVAR_REGISTER(&autokick); + CVAR_REGISTER(&autokick_timeout); + CVAR_REGISTER(&restartround); + CVAR_REGISTER(&sv_restart); + CVAR_REGISTER(&limitteams); + CVAR_REGISTER(&autoteambalance); + CVAR_REGISTER(&tkpunish); + CVAR_REGISTER(&hostagepenalty); + CVAR_REGISTER(&mirrordamage); + CVAR_REGISTER(&logmessages); + CVAR_REGISTER(&forcecamera); + CVAR_REGISTER(&forcechasecam); + CVAR_REGISTER(&mapvoteratio); + CVAR_REGISTER(&maxrounds); + CVAR_REGISTER(&winlimit); + CVAR_REGISTER(&windifference); + CVAR_REGISTER(&fadetoblack); + CVAR_REGISTER(&logdetail); + CVAR_REGISTER(&startmoney); + CVAR_REGISTER(&playerid); + CVAR_REGISTER(&allow_spectators); + CVAR_REGISTER(&mp_chattime); + CVAR_REGISTER(&kick_percent); + CVAR_REGISTER(&fragsleft); + CVAR_REGISTER(&timeleft); + CVAR_REGISTER(&humans_join_team); + +#ifdef BUILD_LATEST + if (AreRunningBeta()) + { + CVAR_REGISTER(&scoreboard_showhealth); + CVAR_REGISTER(&scoreboard_showmoney); + } +#endif + +// Remove unused cvars +#ifndef REGAMEDLL_FIXES + + CVAR_REGISTER(&sk_plr_9mm_bullet1); + CVAR_REGISTER(&sk_plr_9mm_bullet2); + CVAR_REGISTER(&sk_plr_9mm_bullet3); + CVAR_REGISTER(&sk_plr_357_bullet1); + CVAR_REGISTER(&sk_plr_357_bullet2); + CVAR_REGISTER(&sk_plr_357_bullet3); + CVAR_REGISTER(&sk_plr_9mmAR_bullet1); + CVAR_REGISTER(&sk_plr_9mmAR_bullet2); + CVAR_REGISTER(&sk_plr_9mmAR_bullet3); + CVAR_REGISTER(&sk_plr_9mmAR_grenade1); + CVAR_REGISTER(&sk_plr_9mmAR_grenade2); + CVAR_REGISTER(&sk_plr_9mmAR_grenade3); + CVAR_REGISTER(&sk_plr_buckshot1); + CVAR_REGISTER(&sk_plr_buckshot2); + CVAR_REGISTER(&sk_plr_buckshot3); + CVAR_REGISTER(&sk_plr_rpg1); + CVAR_REGISTER(&sk_plr_rpg2); + CVAR_REGISTER(&sk_plr_rpg3); + CVAR_REGISTER(&sk_12mm_bullet1); + CVAR_REGISTER(&sk_12mm_bullet2); + CVAR_REGISTER(&sk_12mm_bullet3); + CVAR_REGISTER(&sk_9mmAR_bullet1); + CVAR_REGISTER(&sk_9mmAR_bullet2); + CVAR_REGISTER(&sk_9mmAR_bullet3); + CVAR_REGISTER(&sk_9mm_bullet1); + CVAR_REGISTER(&sk_9mm_bullet2); + CVAR_REGISTER(&sk_9mm_bullet3); + CVAR_REGISTER(&sk_suitcharger1); + CVAR_REGISTER(&sk_suitcharger2); + CVAR_REGISTER(&sk_suitcharger3); + CVAR_REGISTER(&sk_battery1); + CVAR_REGISTER(&sk_battery2); + CVAR_REGISTER(&sk_battery3); + CVAR_REGISTER(&sk_healthcharger1); + CVAR_REGISTER(&sk_healthcharger2); + CVAR_REGISTER(&sk_healthcharger3); + CVAR_REGISTER(&sk_healthkit1); + CVAR_REGISTER(&sk_healthkit2); + CVAR_REGISTER(&sk_healthkit3); + CVAR_REGISTER(&sk_scientist_heal1); + CVAR_REGISTER(&sk_scientist_heal2); + CVAR_REGISTER(&sk_scientist_heal3); + +#endif // REGAMEDLL_FIXES + +#ifdef REGAMEDLL_ADD + + ADD_SERVER_COMMAND("game", GameDLL_Version_f); + ADD_SERVER_COMMAND("endround", GameDLL_EndRound_f); + + CVAR_REGISTER(&game_version); + CVAR_REGISTER(&maxmoney); + CVAR_REGISTER(&round_infinite); + CVAR_REGISTER(&hegrenade_penetration); + CVAR_REGISTER(&nadedrops); + CVAR_REGISTER(&roundrespawn_time); + CVAR_REGISTER(&auto_reload_weapons); + CVAR_REGISTER(&refill_bpammo_weapons); + CVAR_REGISTER(&freeforall); + CVAR_REGISTER(&auto_join_team); + CVAR_REGISTER(&max_teamkills); + CVAR_REGISTER(&fraglimit); + CVAR_REGISTER(&round_restart_delay); + + CVAR_REGISTER(&showtriggers); + CVAR_REGISTER(&hostagehurtable); + CVAR_REGISTER(&roundover); + CVAR_REGISTER(&forcerespawn); + CVAR_REGISTER(&show_radioicon); + + if (!AreRunningCZero()) + { + CVAR_REGISTER(&show_scenarioicon); + } + + CVAR_REGISTER(&old_bomb_defused_sound); + CVAR_REGISTER(&item_staytime); + CVAR_REGISTER(&legacy_bombtarget_touch); + CVAR_REGISTER(&respawn_immunitytime); + CVAR_REGISTER(&respawn_immunity_effects); + CVAR_REGISTER(&kill_filled_spawn); + CVAR_REGISTER(&afk_bomb_drop_time); + CVAR_REGISTER(&buy_anywhere); + CVAR_REGISTER(&allow_point_servercommand); + CVAR_REGISTER(&hullbounds_sets); + CVAR_REGISTER(&unduck_method); + + CVAR_REGISTER(&ff_damage_reduction_bullets); + CVAR_REGISTER(&ff_damage_reduction_grenade); + CVAR_REGISTER(&ff_damage_reduction_grenade_self); + CVAR_REGISTER(&ff_damage_reduction_other); + + CVAR_REGISTER(&radio_timeout); + CVAR_REGISTER(&radio_maxinround); + + // print version + CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); + +#endif // REGAMEDLL_ADD + + Bot_RegisterCVars(); + Tutor_RegisterCVars(); + Hostage_RegisterCVars(); + +#ifdef REGAMEDLL_FIXES + VoiceGameMgr_RegisterCVars(); +#endif + +#ifdef REGAMEDLL_ADD + // execute initial pre-configurations + SERVER_COMMAND("exec game_init.cfg\n"); + SERVER_EXECUTE(); +#endif + +} diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index e03f3b08..3bf50ab0 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -1,179 +1,179 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#define LOG_ENEMYATTACK 1 -#define LOG_TEAMMATEATTACK 2 - -// playerid -#define PLAYERID_MODE_EVERYONE 0 -#define PLAYERID_MODE_TEAMONLY 1 -#define PLAYERID_MODE_OFF 2 - -#define PLAYERID_EVERYONE 0 -#define PLAYERID_TEAMONLY 1 -#define PLAYERID_OFF 2 - -extern cvar_t *g_pskill; -extern cvar_t *g_psv_gravity; -extern cvar_t *g_psv_aim; -extern cvar_t *g_psv_accelerate; -extern cvar_t *g_psv_friction; -extern cvar_t *g_psv_stopspeed; -extern cvar_t *g_psv_stepsize; -extern cvar_t *g_psv_clienttrace; -extern cvar_t *g_footsteps; - -extern cvar_t displaysoundlist; -extern cvar_t timelimit; -extern cvar_t flashlight; -extern cvar_t decalfrequency; -extern cvar_t fadetoblack; -extern cvar_t fragsleft; -extern cvar_t timeleft; -extern cvar_t friendlyfire; -extern cvar_t infiniteAmmo; -extern cvar_t infiniteGrenades; -extern cvar_t allowmonsters; -extern cvar_t roundtime; -extern cvar_t buytime; -extern cvar_t freezetime; -extern cvar_t c4timer; -extern cvar_t ghostfrequency; -extern cvar_t autokick; -extern cvar_t autokick_timeout; -extern cvar_t restartround; -extern cvar_t sv_restart; -extern cvar_t limitteams; -extern cvar_t autoteambalance; -extern cvar_t tkpunish; -extern cvar_t hostagepenalty; -extern cvar_t mirrordamage; -extern cvar_t logmessages; -extern cvar_t forcecamera; -extern cvar_t forcechasecam; -extern cvar_t mapvoteratio; -extern cvar_t logdetail; -extern cvar_t startmoney; -extern cvar_t maxrounds; -extern cvar_t winlimit; -extern cvar_t windifference; -extern cvar_t playerid; -extern cvar_t allow_spectators; -extern cvar_t mp_chattime; -extern cvar_t kick_percent; -extern cvar_t humans_join_team; -extern cvar_t sk_plr_9mm_bullet1; -extern cvar_t sk_plr_9mm_bullet2; -extern cvar_t sk_plr_9mm_bullet3; -extern cvar_t sk_plr_357_bullet1; -extern cvar_t sk_plr_357_bullet2; -extern cvar_t sk_plr_357_bullet3; -extern cvar_t sk_plr_9mmAR_bullet1; -extern cvar_t sk_plr_9mmAR_bullet2; -extern cvar_t sk_plr_9mmAR_bullet3; -extern cvar_t sk_plr_9mmAR_grenade1; -extern cvar_t sk_plr_9mmAR_grenade2; -extern cvar_t sk_plr_9mmAR_grenade3; -extern cvar_t sk_plr_buckshot1; -extern cvar_t sk_plr_buckshot2; -extern cvar_t sk_plr_buckshot3; -extern cvar_t sk_plr_rpg1; -extern cvar_t sk_plr_rpg2; -extern cvar_t sk_plr_rpg3; -extern cvar_t sk_12mm_bullet1; -extern cvar_t sk_12mm_bullet2; -extern cvar_t sk_12mm_bullet3; -extern cvar_t sk_9mmAR_bullet1; -extern cvar_t sk_9mmAR_bullet2; -extern cvar_t sk_9mmAR_bullet3; -extern cvar_t sk_9mm_bullet1; -extern cvar_t sk_9mm_bullet2; -extern cvar_t sk_9mm_bullet3; -extern cvar_t sk_suitcharger1; -extern cvar_t sk_suitcharger2; -extern cvar_t sk_suitcharger3; -extern cvar_t sk_battery1; -extern cvar_t sk_battery2; -extern cvar_t sk_battery3; -extern cvar_t sk_healthcharger1; -extern cvar_t sk_healthcharger2; -extern cvar_t sk_healthcharger3; -extern cvar_t sk_healthkit1; -extern cvar_t sk_healthkit2; -extern cvar_t sk_healthkit3; -extern cvar_t sk_scientist_heal1; -extern cvar_t sk_scientist_heal2; -extern cvar_t sk_scientist_heal3; - -#ifdef REGAMEDLL_ADD - -extern cvar_t maxmoney; -extern cvar_t round_infinite; -extern cvar_t hegrenade_penetration; -extern cvar_t nadedrops; -extern cvar_t roundrespawn_time; -extern cvar_t auto_reload_weapons; -extern cvar_t refill_bpammo_weapons; -extern cvar_t freeforall; -extern cvar_t auto_join_team; -extern cvar_t max_teamkills; -extern cvar_t fraglimit; -extern cvar_t round_restart_delay; - -extern cvar_t showtriggers; -extern cvar_t hostagehurtable; -extern cvar_t roundover; -extern cvar_t forcerespawn; -extern cvar_t show_radioicon; -extern cvar_t show_scenarioicon; -extern cvar_t old_bomb_defused_sound; -extern cvar_t item_staytime; -extern cvar_t legacy_bombtarget_touch; -extern cvar_t respawn_immunitytime; -extern cvar_t respawn_immunity_effects; -extern cvar_t kill_filled_spawn; -extern cvar_t afk_bomb_drop_time; -extern cvar_t buy_anywhere; -extern cvar_t allow_point_servercommand; -extern cvar_t hullbounds_sets; -extern cvar_t unduck_method; -extern cvar_t ff_damage_reduction_bullets; -extern cvar_t ff_damage_reduction_grenade; -extern cvar_t ff_damage_reduction_grenade_self; -extern cvar_t ff_damage_reduction_other; -extern cvar_t radio_timeout; -extern cvar_t radio_maxinround; - -#endif - -extern cvar_t scoreboard_showmoney; -extern cvar_t scoreboard_showhealth; - -void GameDLLInit(); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#define LOG_ENEMYATTACK 1 +#define LOG_TEAMMATEATTACK 2 + +// playerid +#define PLAYERID_MODE_EVERYONE 0 +#define PLAYERID_MODE_TEAMONLY 1 +#define PLAYERID_MODE_OFF 2 + +#define PLAYERID_EVERYONE 0 +#define PLAYERID_TEAMONLY 1 +#define PLAYERID_OFF 2 + +extern cvar_t *g_pskill; +extern cvar_t *g_psv_gravity; +extern cvar_t *g_psv_aim; +extern cvar_t *g_psv_accelerate; +extern cvar_t *g_psv_friction; +extern cvar_t *g_psv_stopspeed; +extern cvar_t *g_psv_stepsize; +extern cvar_t *g_psv_clienttrace; +extern cvar_t *g_footsteps; + +extern cvar_t displaysoundlist; +extern cvar_t timelimit; +extern cvar_t flashlight; +extern cvar_t decalfrequency; +extern cvar_t fadetoblack; +extern cvar_t fragsleft; +extern cvar_t timeleft; +extern cvar_t friendlyfire; +extern cvar_t infiniteAmmo; +extern cvar_t infiniteGrenades; +extern cvar_t allowmonsters; +extern cvar_t roundtime; +extern cvar_t buytime; +extern cvar_t freezetime; +extern cvar_t c4timer; +extern cvar_t ghostfrequency; +extern cvar_t autokick; +extern cvar_t autokick_timeout; +extern cvar_t restartround; +extern cvar_t sv_restart; +extern cvar_t limitteams; +extern cvar_t autoteambalance; +extern cvar_t tkpunish; +extern cvar_t hostagepenalty; +extern cvar_t mirrordamage; +extern cvar_t logmessages; +extern cvar_t forcecamera; +extern cvar_t forcechasecam; +extern cvar_t mapvoteratio; +extern cvar_t logdetail; +extern cvar_t startmoney; +extern cvar_t maxrounds; +extern cvar_t winlimit; +extern cvar_t windifference; +extern cvar_t playerid; +extern cvar_t allow_spectators; +extern cvar_t mp_chattime; +extern cvar_t kick_percent; +extern cvar_t humans_join_team; +extern cvar_t sk_plr_9mm_bullet1; +extern cvar_t sk_plr_9mm_bullet2; +extern cvar_t sk_plr_9mm_bullet3; +extern cvar_t sk_plr_357_bullet1; +extern cvar_t sk_plr_357_bullet2; +extern cvar_t sk_plr_357_bullet3; +extern cvar_t sk_plr_9mmAR_bullet1; +extern cvar_t sk_plr_9mmAR_bullet2; +extern cvar_t sk_plr_9mmAR_bullet3; +extern cvar_t sk_plr_9mmAR_grenade1; +extern cvar_t sk_plr_9mmAR_grenade2; +extern cvar_t sk_plr_9mmAR_grenade3; +extern cvar_t sk_plr_buckshot1; +extern cvar_t sk_plr_buckshot2; +extern cvar_t sk_plr_buckshot3; +extern cvar_t sk_plr_rpg1; +extern cvar_t sk_plr_rpg2; +extern cvar_t sk_plr_rpg3; +extern cvar_t sk_12mm_bullet1; +extern cvar_t sk_12mm_bullet2; +extern cvar_t sk_12mm_bullet3; +extern cvar_t sk_9mmAR_bullet1; +extern cvar_t sk_9mmAR_bullet2; +extern cvar_t sk_9mmAR_bullet3; +extern cvar_t sk_9mm_bullet1; +extern cvar_t sk_9mm_bullet2; +extern cvar_t sk_9mm_bullet3; +extern cvar_t sk_suitcharger1; +extern cvar_t sk_suitcharger2; +extern cvar_t sk_suitcharger3; +extern cvar_t sk_battery1; +extern cvar_t sk_battery2; +extern cvar_t sk_battery3; +extern cvar_t sk_healthcharger1; +extern cvar_t sk_healthcharger2; +extern cvar_t sk_healthcharger3; +extern cvar_t sk_healthkit1; +extern cvar_t sk_healthkit2; +extern cvar_t sk_healthkit3; +extern cvar_t sk_scientist_heal1; +extern cvar_t sk_scientist_heal2; +extern cvar_t sk_scientist_heal3; + +#ifdef REGAMEDLL_ADD + +extern cvar_t maxmoney; +extern cvar_t round_infinite; +extern cvar_t hegrenade_penetration; +extern cvar_t nadedrops; +extern cvar_t roundrespawn_time; +extern cvar_t auto_reload_weapons; +extern cvar_t refill_bpammo_weapons; +extern cvar_t freeforall; +extern cvar_t auto_join_team; +extern cvar_t max_teamkills; +extern cvar_t fraglimit; +extern cvar_t round_restart_delay; + +extern cvar_t showtriggers; +extern cvar_t hostagehurtable; +extern cvar_t roundover; +extern cvar_t forcerespawn; +extern cvar_t show_radioicon; +extern cvar_t show_scenarioicon; +extern cvar_t old_bomb_defused_sound; +extern cvar_t item_staytime; +extern cvar_t legacy_bombtarget_touch; +extern cvar_t respawn_immunitytime; +extern cvar_t respawn_immunity_effects; +extern cvar_t kill_filled_spawn; +extern cvar_t afk_bomb_drop_time; +extern cvar_t buy_anywhere; +extern cvar_t allow_point_servercommand; +extern cvar_t hullbounds_sets; +extern cvar_t unduck_method; +extern cvar_t ff_damage_reduction_bullets; +extern cvar_t ff_damage_reduction_grenade; +extern cvar_t ff_damage_reduction_grenade_self; +extern cvar_t ff_damage_reduction_other; +extern cvar_t radio_timeout; +extern cvar_t radio_maxinround; + +#endif + +extern cvar_t scoreboard_showmoney; +extern cvar_t scoreboard_showhealth; + +void GameDLLInit(); diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index 548dd08f..dd17b8f9 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -1,915 +1,915 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#include "game_shared/voice_gamemgr.h" -#include "cmdhandler.h" - -const int MAX_RULE_BUFFER = 1024; -const int MAX_VOTE_MAPS = 100; -const int MAX_VIP_QUEUES = 5; -const int MAX_MONEY_THRESHOLD = 999999; // allowable money limit in the game that can be drawn on the HUD - -const int MAX_MOTD_CHUNK = 60; -const int MAX_MOTD_LENGTH = 1536; // (MAX_MOTD_CHUNK * 4) - -const float ITEM_RESPAWN_TIME = 30.0f; -const float WEAPON_RESPAWN_TIME = 20.0f; -const float AMMO_RESPAWN_TIME = 20.0f; -const float ROUND_RESPAWN_TIME = 20.0f; -const float ROUND_BEGIN_DELAY = 5.0f; // delay before beginning new round -const float ITEM_KILL_DELAY = 300.0f; -const float RADIO_TIMEOUT = 1.5f; - -const int MAX_INTERMISSION_TIME = 120; // longest the intermission can last, in seconds - -// when we are within this close to running out of entities, items -// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn -const int ENTITY_INTOLERANCE = 100; - -enum -{ - WINSTATUS_NONE = 0, - WINSTATUS_CTS, - WINSTATUS_TERRORISTS, - WINSTATUS_DRAW, -}; - -// Custom enum -// Used for EndRoundMessage() logged messages -enum ScenarioEventEndRound -{ - ROUND_NONE, - ROUND_TARGET_BOMB, - ROUND_VIP_ESCAPED, - ROUND_VIP_ASSASSINATED, - ROUND_TERRORISTS_ESCAPED, - ROUND_CTS_PREVENT_ESCAPE, - ROUND_ESCAPING_TERRORISTS_NEUTRALIZED, - ROUND_BOMB_DEFUSED, - ROUND_CTS_WIN, - ROUND_TERRORISTS_WIN, - ROUND_END_DRAW, - ROUND_ALL_HOSTAGES_RESCUED, - ROUND_TARGET_SAVED, - ROUND_HOSTAGE_NOT_RESCUED, - ROUND_TERRORISTS_NOT_ESCAPED, - ROUND_VIP_NOT_ESCAPED, - ROUND_GAME_COMMENCE, - ROUND_GAME_RESTART, - ROUND_GAME_OVER -}; - -enum RewardRules -{ - RR_CTS_WIN, - RR_TERRORISTS_WIN, - RR_TARGET_BOMB, - RR_VIP_ESCAPED, - RR_VIP_ASSASSINATED, - RR_TERRORISTS_ESCAPED, - RR_CTS_PREVENT_ESCAPE, - RR_ESCAPING_TERRORISTS_NEUTRALIZED, - RR_BOMB_DEFUSED, - RR_BOMB_PLANTED, - RR_BOMB_EXPLODED, - RR_ALL_HOSTAGES_RESCUED, - RR_TARGET_BOMB_SAVED, - RR_HOSTAGE_NOT_RESCUED, - RR_VIP_NOT_ESCAPED, - RR_LOSER_BONUS_DEFAULT, - RR_LOSER_BONUS_MIN, - RR_LOSER_BONUS_MAX, - RR_LOSER_BONUS_ADD, - RR_RESCUED_HOSTAGE, - RR_TOOK_HOSTAGE_ACC, - RR_TOOK_HOSTAGE, - RR_END -}; - -// custom enum -enum RewardAccount -{ - REWARD_TARGET_BOMB = 3500, - REWARD_VIP_ESCAPED = 3500, - REWARD_VIP_ASSASSINATED = 3250, - REWARD_TERRORISTS_ESCAPED = 3150, - REWARD_CTS_PREVENT_ESCAPE = 3500, - REWARD_ESCAPING_TERRORISTS_NEUTRALIZED = 3250, - REWARD_BOMB_DEFUSED = 3250, - REWARD_BOMB_PLANTED = 800, - REWARD_BOMB_EXPLODED = 3250, - REWARD_CTS_WIN = 3000, - REWARD_TERRORISTS_WIN = 3000, - REWARD_ALL_HOSTAGES_RESCUED = 2500, - - // the end round was by the expiration time - REWARD_TARGET_BOMB_SAVED = 3250, - REWARD_HOSTAGE_NOT_RESCUED = 3250, - REWARD_VIP_NOT_ESCAPED = 3250, - - // loser bonus - REWARD_LOSER_BONUS_DEFAULT = 1400, - REWARD_LOSER_BONUS_MIN = 1500, - REWARD_LOSER_BONUS_MAX = 3000, - REWARD_LOSER_BONUS_ADD = 500, - - REWARD_RESCUED_HOSTAGE = 750, - REWARD_KILLED_ENEMY = 300, - REWARD_KILLED_VIP = 2500, - REWARD_VIP_HAVE_SELF_RESCUED = 2500, - - REWARD_TAKEN_HOSTAGE = 1000, - REWARD_TOOK_HOSTAGE_ACC = 100, - REWARD_TOOK_HOSTAGE = 150, -}; - -// custom enum -enum PaybackForBadThing -{ - PAYBACK_FOR_KILLED_TEAMMATES = -3300, -}; - -// custom enum -enum InfoMapBuyParam -{ - BUYING_EVERYONE = 0, - BUYING_ONLY_CTS, - BUYING_ONLY_TERRORISTS, - BUYING_NO_ONE, -}; - -// weapon respawning return codes -enum -{ - GR_NONE = 0, - - GR_WEAPON_RESPAWN_YES, - GR_WEAPON_RESPAWN_NO, - - GR_AMMO_RESPAWN_YES, - GR_AMMO_RESPAWN_NO, - - GR_ITEM_RESPAWN_YES, - GR_ITEM_RESPAWN_NO, - - GR_PLR_DROP_GUN_ALL, - GR_PLR_DROP_GUN_ACTIVE, - GR_PLR_DROP_GUN_NO, - - GR_PLR_DROP_AMMO_ALL, - GR_PLR_DROP_AMMO_ACTIVE, - GR_PLR_DROP_AMMO_NO, -}; - -// custom enum -enum -{ - SCENARIO_BLOCK_TIME_EXPRIRED = BIT(0), // flag "a" - SCENARIO_BLOCK_NEED_PLAYERS = BIT(1), // flag "b" - SCENARIO_BLOCK_VIP_ESCAPE = BIT(2), // flag "c" - SCENARIO_BLOCK_PRISON_ESCAPE = BIT(3), // flag "d" - SCENARIO_BLOCK_BOMB = BIT(4), // flag "e" - SCENARIO_BLOCK_TEAM_EXTERMINATION = BIT(5), // flag "f" - SCENARIO_BLOCK_HOSTAGE_RESCUE = BIT(6), // flag "g" -}; - -// Player relationship return codes -enum -{ - GR_NOTTEAMMATE = 0, - GR_TEAMMATE, - GR_ENEMY, - GR_ALLY, - GR_NEUTRAL, -}; - -class CItem; - -class CGameRules -{ -public: - CGameRules(); - virtual ~CGameRules(); - - virtual void RefreshSkillData(); // fill skill data struct with proper values - virtual void Think() = 0; // runs every server frame, should handle any timer tasks, periodic events, etc. - virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity) = 0; // Can this item spawn (eg monsters don't spawn in deathmatch). - - virtual BOOL FAllowFlashlight() = 0; // Are players allowed to switch on their flashlight? - virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) = 0; // should the player switch to this weapon? - virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon) = 0; // I can't use this weapon anymore, get me the next best one. - - // Functions to verify the single/multiplayer status of a game - virtual BOOL IsMultiplayer() = 0; // is this a multiplayer game? (either coop or deathmatch) - virtual BOOL IsDeathmatch() = 0; // is this a deathmatch game? - virtual BOOL IsTeamplay() { return FALSE; } // is this deathmatch game being played with team rules? - virtual BOOL IsCoOp() = 0; // is this a coop game? - virtual const char *GetGameDescription(); // this is the game name that gets seen in the server browser - - // Client connection/disconnection - virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char *szRejectReason) = 0; // a client just connected to the server (player hasn't spawned yet) - virtual void InitHUD(CBasePlayer *pl) = 0; // the client dll is ready for updating - virtual void ClientDisconnected(edict_t *pClient) = 0; // a client just disconnected from the server - virtual void UpdateGameMode(CBasePlayer *pPlayer) {}; // the client needs to be informed of the current game mode - - // Client damage rules - virtual float FlPlayerFallDamage(CBasePlayer *pPlayer) = 0; - virtual BOOL FPlayerCanTakeDamage(CBasePlayer *pPlayer, CBaseEntity *pAttacker) { return TRUE; } // can this player take damage from this attacker? - virtual BOOL ShouldAutoAim(CBasePlayer *pPlayer, edict_t *target) { return TRUE; } - - // Client spawn/respawn control - virtual void PlayerSpawn(CBasePlayer *pPlayer) = 0; // called by CBasePlayer::Spawn just before releasing player into the game - virtual void PlayerThink(CBasePlayer *pPlayer) = 0; // called by CBasePlayer::PreThink every frame, before physics are run and after keys are accepted - virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer) = 0; // is this player allowed to respawn now? - virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer) = 0; // When in the future will this player be able to spawn? - virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); // Place this player on their spawnspot and face them the proper direction. - - virtual BOOL AllowAutoTargetCrosshair() { return TRUE; } - virtual BOOL ClientCommand_DeadOrAlive(CBasePlayer *pPlayer, const char *pcmd) { return FALSE; } - virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd) { return FALSE; } // handles the user commands; returns TRUE if command handled properly - virtual void ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer) {}; // the player has changed userinfo; can change it now - - // Client kills/scoring - virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled) = 0; // how many points do I award whoever kills this player? - virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor) = 0; // Called each time a player dies - virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor) = 0; // Call this from within a GameRules class to report an obituary. - - // Weapon retrieval - virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem); // The player is touching an CBasePlayerItem, do I give it to him? - virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) = 0; // Called each time a player picks up a weapon from the ground - - // Weapon spawn/respawn control - virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon) = 0; // should this weapon respawn? - virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon) = 0; // when may this weapon respawn? - virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon) = 0; // can i respawn now, and if not, when should i try again? - virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon) = 0; // where in the world should this weapon respawn? - - // Item retrieval - virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem) = 0; // is this player allowed to take this item? - virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem) = 0; // call each time a player picks up an item (battery, healthkit, longjump) - - // Item spawn/respawn control - virtual int ItemShouldRespawn(CItem *pItem) = 0; // Should this item respawn? - virtual float FlItemRespawnTime(CItem *pItem) = 0; // when may this item respawn? - virtual Vector VecItemRespawnSpot(CItem *pItem) = 0; // where in the world should this item respawn? - - // Ammo retrieval - virtual BOOL CanHaveAmmo(CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry); // can this player take more of this ammo? - virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount) = 0; // called each time a player picks up some ammo in the world - - // Ammo spawn/respawn control - virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo) = 0; // should this ammo item respawn? - virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo) = 0; // when should this ammo item respawn? - virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo) = 0; // where in the world should this ammo item respawn? - - // Healthcharger respawn control - virtual float FlHealthChargerRechargeTime() = 0; // how long until a depleted HealthCharger recharges itself? - virtual float FlHEVChargerRechargeTime() { return 0.0f; } // how long until a depleted HealthCharger recharges itself? - - // What happens to a dead player's weapons - virtual int DeadPlayerWeapons(CBasePlayer *pPlayer) = 0; // what do I do with a player's weapons when he's killed? - - // What happens to a dead player's ammo - virtual int DeadPlayerAmmo(CBasePlayer *pPlayer) = 0; // Do I drop ammo when the player dies? How much? - - // Teamplay stuff - virtual const char *GetTeamID(CBaseEntity *pEntity) = 0; // what team is this entity on? - virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget) = 0; // What is the player's relationship with this entity? - virtual int GetTeamIndex(const char *pTeamName) { return -1; } - virtual const char *GetIndexedTeamName(int teamIndex) { return ""; } - virtual BOOL IsValidTeam(const char *pTeamName) { return TRUE; } - virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib) {}; - virtual const char *SetDefaultPlayerTeam(CBasePlayer *pPlayer) { return ""; } - - // Sounds - virtual BOOL PlayTextureSounds() { return TRUE; } - - // Monsters - virtual BOOL FAllowMonsters() = 0; // are monsters allowed - - // Immediately end a multiplayer game - virtual void EndMultiplayerGame() {}; - - // Stuff that is shared between client and server. - virtual BOOL IsFreezePeriod() { return m_bFreezePeriod; } - virtual void ServerDeactivate() {}; - virtual void CheckMapConditions() {}; - - // inline function's - inline bool IsGameOver() const { return m_bGameOver; } - inline void SetGameOver() { m_bGameOver = true; } - static float GetItemKillDelay(); - static float GetRadioTimeout(); - -public: - BOOL m_bFreezePeriod; // TRUE at beginning of round, set to FALSE when the period expires - BOOL m_bBombDropped; - - // custom - char *m_GameDesc; - bool m_bGameOver; // intermission or finale (deprecated name g_fGameOver) -}; - -#define GAMERULES_API_INTERFACE_VERSION "GAMERULES_API_INTERFACE_VERSION001" - -// CHalfLifeRules - rules for the single player Half-Life game. -class CHalfLifeRules: public CGameRules -{ -public: - CHalfLifeRules(); - virtual ~CHalfLifeRules() {}; - - virtual void Think(); - virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity); - virtual BOOL FAllowFlashlight() { return TRUE; } - - virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); - - // Functions to verify the single/multiplayer status of a game - virtual BOOL IsMultiplayer(); - virtual BOOL IsDeathmatch(); - virtual BOOL IsCoOp(); - - // Client connection/disconnection - virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); - virtual void InitHUD(CBasePlayer *pl); // the client dll is ready for updating - virtual void ClientDisconnected(edict_t *pClient); - - // Client damage rules - virtual float FlPlayerFallDamage(CBasePlayer *pPlayer); - - // Client spawn/respawn control - virtual void PlayerSpawn(CBasePlayer *pPlayer); - virtual void PlayerThink(CBasePlayer *pPlayer); - virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer); - virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer); - virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); - - virtual BOOL AllowAutoTargetCrosshair(); - - // Client kills/scoring - virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); - virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - - // Weapon retrieval - virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - - // Weapon spawn/respawn control - virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon); - virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon); - virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon); - virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon); - - // Item retrieval - virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem); - virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem); - - // Item spawn/respawn control - virtual int ItemShouldRespawn(CItem *pItem); - virtual float FlItemRespawnTime(CItem *pItem); - virtual Vector VecItemRespawnSpot(CItem *pItem); - - // Ammo retrieval - virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount); - - // Ammo spawn/respawn control - virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo); - virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo); - virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo); - - // Healthcharger respawn control - virtual float FlHealthChargerRechargeTime(); - - // What happens to a dead player's weapons - virtual int DeadPlayerWeapons(CBasePlayer *pPlayer); - - // What happens to a dead player's ammo - virtual int DeadPlayerAmmo(CBasePlayer *pPlayer); - - // Teamplay stuff - virtual const char *GetTeamID(CBaseEntity *pEntity) { return ""; }; - virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget); - - // Monsters - virtual BOOL FAllowMonsters(); -}; - -// CHalfLifeMultiplay - rules for the basic half life multiplayer competition -class CHalfLifeMultiplay: public CGameRules -{ -public: - CHalfLifeMultiplay(); - virtual ~CHalfLifeMultiplay() {}; - - virtual void RefreshSkillData(); - virtual void Think(); - virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity); - virtual BOOL FAllowFlashlight(); - - virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); - - virtual BOOL IsMultiplayer(); - virtual BOOL IsDeathmatch(); - virtual BOOL IsCoOp(); - - // Client connection/disconnection - // If ClientConnected returns FALSE, the connection is rejected and the user is provided the reason specified in szRejectReason - // Only the client's name and remote address are provided to the dll for verification. - virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); - virtual void InitHUD(CBasePlayer *pl); // the client dll is ready for updating - virtual void ClientDisconnected(edict_t *pClient); - virtual void UpdateGameMode(CBasePlayer *pPlayer); // the client needs to be informed of the current game mode - - // Client damage rules - virtual float FlPlayerFallDamage(CBasePlayer *pPlayer); - virtual BOOL FPlayerCanTakeDamage(CBasePlayer *pPlayer, CBaseEntity *pAttacker); - - // Client spawn/respawn control - virtual void PlayerSpawn(CBasePlayer *pPlayer); - virtual void PlayerThink(CBasePlayer *pPlayer); - virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer); - virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer); - virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); - - virtual BOOL AllowAutoTargetCrosshair(); - virtual BOOL ClientCommand_DeadOrAlive(CBasePlayer *pPlayer, const char *pcmd); - virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd); - virtual void ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer); - - // Client kills/scoring - virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); - virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - - // Weapon retrieval - virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); // The player is touching an CBasePlayerItem, do I give it to him? - virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - - // Weapon spawn/respawn control - virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon); - virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon); - virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon); - virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon); - - // Item retrieval - virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem); - virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem); - - // Item spawn/respawn control - virtual int ItemShouldRespawn(CItem *pItem); - virtual float FlItemRespawnTime(CItem *pItem); - virtual Vector VecItemRespawnSpot(CItem *pItem); - - // Ammo retrieval - virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount); - - // Ammo spawn/respawn control - virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo); - virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo); - virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo); - - // Healthcharger respawn control - virtual float FlHealthChargerRechargeTime(); - virtual float FlHEVChargerRechargeTime(); - - // What happens to a dead player's weapons - virtual int DeadPlayerWeapons(CBasePlayer *pPlayer); - - // What happens to a dead player's ammo - virtual int DeadPlayerAmmo(CBasePlayer *pPlayer); - - // Teamplay stuff - virtual const char *GetTeamID(CBaseEntity *pEntity) { return ""; } - virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget); - virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib); - - virtual BOOL PlayTextureSounds() { return FALSE; } - - // Monsters - virtual BOOL FAllowMonsters(); - - // Immediately end a multiplayer game - virtual void EndMultiplayerGame() { GoToIntermission(); } - virtual void ServerDeactivate(); - virtual void CheckMapConditions(); - - // Recreate all the map entities from the map data (preserving their indices), - // then remove everything else except the players. - // Also get rid of all world decals. - virtual void CleanUpMap(); - virtual void RestartRound(); - - // check if the scenario has been won/lost - virtual void CheckWinConditions(); - virtual void RemoveGuns(); - virtual void GiveC4(); - virtual void ChangeLevel(); - virtual void GoToIntermission(); - -#ifdef REGAMEDLL_API - BOOL FShouldSwitchWeapon_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - BOOL GetNextBestWeapon_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); - float FlPlayerFallDamage_OrigFunc(CBasePlayer *pPlayer); - BOOL FPlayerCanTakeDamage_OrigFunc(CBasePlayer *pPlayer, CBaseEntity *pAttacker); - void PlayerSpawn_OrigFunc(CBasePlayer *pPlayer); - BOOL FPlayerCanRespawn_OrigFunc(CBasePlayer *pPlayer); - edict_t *GetPlayerSpawnSpot_OrigFunc(CBasePlayer *pPlayer); - void ClientUserInfoChanged_OrigFunc(CBasePlayer *pPlayer, char *infobuffer); - void PlayerKilled_OrigFunc(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - void DeathNotice_OrigFunc(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); - BOOL CanHavePlayerItem_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); - int DeadPlayerWeapons_OrigFunc(CBasePlayer *pPlayer); - void ServerDeactivate_OrigFunc(); - void CheckMapConditions_OrigFunc(); - void CleanUpMap_OrigFunc(); - void RestartRound_OrigFunc(); - void CheckWinConditions_OrigFunc(); - void RemoveGuns_OrigFunc(); - void GiveC4_OrigFunc(); - void ChangeLevel_OrigFunc(); - void GoToIntermission_OrigFunc(); - void BalanceTeams_OrigFunc(); -#endif - -public: - void ServerActivate(); - void ReadMultiplayCvars(); - - // Checks if it still needs players to start a round, or if it has enough players to start rounds. - // Starts a round and returns true if there are enough players. - bool NeededPlayersCheck(); - - // Setup counts for m_iNumTerrorist, m_iNumCT, m_iNumSpawnableTerrorist, m_iNumSpawnableCT, etc. - VFUNC void InitializePlayerCounts(int &NumAliveTerrorist, int &NumAliveCT, int &NumDeadTerrorist, int &NumDeadCT); - - // Check to see if the round is over for the various game types. Terminates the round - // and returns true if the round should end. - bool PrisonRoundEndCheck(int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT); - bool BombRoundEndCheck(); - bool HostageRescueRoundEndCheck(); - bool VIPRoundEndCheck(); - - // Check to see if the teams exterminated each other. Ends the round and returns true if so. - bool TeamExterminationCheck(int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT); - - // for internal functions API - void OnRoundFreezeEnd(); - bool OnRoundEnd(int winStatus, ScenarioEventEndRound event, float tmDelay); - bool OnRoundEnd_Intercept(int winStatus, ScenarioEventEndRound event, float tmDelay); - - bool RoundOver(float tmDelay); - bool NeededPlayersCheck(float tmDelay); - bool RestartRoundCheck(float tmDelay); - - bool VIP_Escaped(float tmDelay); - bool VIP_Died(float tmDelay); - bool VIP_NotEscaped(float tmDelay); - - bool Prison_Escaped(float tmDelay); - bool Prison_PreventEscape(float tmDelay); - bool Prison_NotEscaped(float tmDelay); - bool Prison_Neutralized(float tmDelay); - - bool Target_Bombed(float tmDelay); - bool Target_Saved(float tmDelay); - bool Target_Defused(float tmDelay); - - // Team extermination - bool Round_Cts(float tmDelay); - bool Round_Ts(float tmDelay); - bool Round_Draw(float tmDelay); - - bool Hostage_Rescue(float tmDelay); - bool Hostage_NotRescued(float tmDelay); - - // Check various conditions to end the map. - bool CheckGameOver(); - bool CheckTimeLimit(); - bool CheckFragLimit(); - bool CheckMaxRounds(); - bool CheckWinLimit(); - - void CheckFreezePeriodExpired(); - void CheckRoundTimeExpired(); - - void CheckLevelInitialized(); - void CheckRestartRound(); - - BOOL IsCareer(); - void QueueCareerRoundEndMenu(float tmDelay, int iWinStatus); - void SetCareerMatchLimit(int minWins, int winDifference); - bool IsInCareerRound(); - void CareerRestart(); - bool ShouldSkipShowMenu() const { return m_bSkipShowMenu; } - void MarkShowMenuSkipped() { m_bSkipShowMenu = false; } - bool ShouldSkipSpawn() const { return m_bSkipSpawn; } - void MarkSpawnSkipped() { m_bSkipSpawn = false; } - void PlayerJoinedTeam(CBasePlayer *pPlayer) { } - float GetRoundRemainingTime() const { return m_iRoundTimeSecs - gpGlobals->time + m_fRoundStartTime; } - float GetRoundRemainingTimeReal() const; - float GetTimeLeft() const { return m_flTimeLimit - gpGlobals->time; } - float GetRoundElapsedTime() const { return gpGlobals->time - m_fRoundStartTime; } - float GetMapElapsedTime() const { return gpGlobals->time; } - - BOOL TeamFull(int team_id); - BOOL TeamStacked(int newTeam_id, int curTeam_id); - bool IsVIPQueueEmpty(); - bool AddToVIPQueue(CBasePlayer *toAdd); - - // VIP FUNCTIONS - void PickNextVIP(); - void StackVIPQueue(); - void ResetCurrentVIP(); - - VFUNC void BalanceTeams(); - VFUNC void SwapAllPlayers(); - VFUNC void UpdateTeamScores(); - VFUNC void EndRoundMessage(const char *sentence, ScenarioEventEndRound event); - VFUNC void SetAccountRules(RewardRules rules, int amount) { m_rgRewardAccountRules[rules] = static_cast(amount); } - VFUNC RewardAccount GetAccountRules(RewardRules rules) const { return m_rgRewardAccountRules[rules]; } - - void DisplayMaps(CBasePlayer *pPlayer, int iVote); - void ResetAllMapVotes(); - void ProcessMapVote(CBasePlayer *pPlayer, int iVote); - - // BOMB MAP FUNCTIONS - VFUNC BOOL IsThereABomber(); - VFUNC BOOL IsThereABomb(); - VFUNC TeamName SelectDefaultTeam(); - - bool IsMatchStarted() { return (m_flRestartRoundTime != 0.0f || m_fCareerRoundMenuTime != 0.0f || m_fCareerMatchMenuTime != 0.0f); } - void SendMOTDToClient(edict_t *client); - - void TerminateRound(float tmDelay, int iWinStatus); - float GetRoundRespawnTime() const; - float GetRoundRestartDelay() const; - - bool IsGameStarted() const { return m_bGameStarted; } - - // has a style of gameplay when aren't any teams - bool IsFreeForAll() const; - bool CanPlayerBuy(CBasePlayer *pPlayer) const; - - VFUNC bool HasRoundTimeExpired(); - VFUNC bool IsBombPlanted(); - -private: - void MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int iTeam); - -public: - static RewardAccount m_rgRewardAccountRules[RR_END]; - static RewardAccount m_rgRewardAccountRules_default[RR_END]; - - CVoiceGameMgr m_VoiceGameMgr; - float m_flRestartRoundTime; // The global time when the round is supposed to end, if this is not 0 (deprecated name m_fTeamCount) - float m_flCheckWinConditions; - float m_fRoundStartTime; // Time round has started (deprecated name m_fRoundCount) - int m_iRoundTime; // (From mp_roundtime) - How many seconds long this round is. - int m_iRoundTimeSecs; - int m_iIntroRoundTime; // (From mp_freezetime) - How many seconds long the intro round (when players are frozen) is. - float m_fRoundStartTimeReal; // The global time when the intro round ends and the real one starts - // wrote the original "m_flRoundTime" comment for this variable). - int m_iAccountTerrorist; - int m_iAccountCT; - int m_iNumTerrorist; // The number of terrorists on the team (this is generated at the end of a round) - int m_iNumCT; // The number of CTs on the team (this is generated at the end of a round) - int m_iNumSpawnableTerrorist; - int m_iNumSpawnableCT; - int m_iSpawnPointCount_Terrorist; // Number of Terrorist spawn points - int m_iSpawnPointCount_CT; // Number of CT spawn points - int m_iHostagesRescued; - int m_iHostagesTouched; - int m_iRoundWinStatus; // 1 == CT's won last round, 2 == Terrorists did, 3 == Draw, no winner - - short m_iNumCTWins; - short m_iNumTerroristWins; - - bool m_bTargetBombed; // whether or not the bomb has been bombed - bool m_bBombDefused; // whether or not the bomb has been defused - - bool m_bMapHasBombTarget; - bool m_bMapHasBombZone; - bool m_bMapHasBuyZone; - bool m_bMapHasRescueZone; - bool m_bMapHasEscapeZone; - - BOOL m_bMapHasVIPSafetyZone; // TRUE = has VIP safety zone, FALSE = does not have VIP safetyzone - BOOL m_bMapHasCameras; - int m_iC4Timer; - int m_iC4Guy; // The current Terrorist who has the C4. - int m_iLoserBonus; // the amount of money the losing team gets. This scales up as they lose more rounds in a row - int m_iNumConsecutiveCTLoses; // the number of rounds the CTs have lost in a row. - int m_iNumConsecutiveTerroristLoses; // the number of rounds the Terrorists have lost in a row. - - float m_fMaxIdlePeriod; // For the idle kick functionality. This is tha max amount of time that the player has to be idle before being kicked - - int m_iLimitTeams; - bool m_bLevelInitialized; - bool m_bRoundTerminating; - bool m_bCompleteReset; // Set to TRUE to have the scores reset next time round restarts - float m_flRequiredEscapeRatio; - int m_iNumEscapers; - int m_iHaveEscaped; - bool m_bCTCantBuy; - bool m_bTCantBuy; // Who can and can't buy. - float m_flBombRadius; - int m_iConsecutiveVIP; - int m_iTotalGunCount; - int m_iTotalGrenadeCount; - int m_iTotalArmourCount; - int m_iUnBalancedRounds; // keeps track of the # of consecutive rounds that have gone by where one team outnumbers the other team by more than 2 - int m_iNumEscapeRounds; // keeps track of the # of consecutive rounds of escape played.. Teams will be swapped after 8 rounds - int m_iMapVotes[MAX_VOTE_MAPS]; - int m_iLastPick; - int m_iMaxMapTime; - int m_iMaxRounds; - int m_iTotalRoundsPlayed; - int m_iMaxRoundsWon; - int m_iStoredSpectValue; - float m_flForceCameraValue; - float m_flForceChaseCamValue; - float m_flFadeToBlackValue; - CBasePlayer *m_pVIP; - CBasePlayer *m_pVIPQueue[MAX_VIP_QUEUES]; - -protected: - float m_flIntermissionEndTime; - float m_flIntermissionStartTime; - BOOL m_iEndIntermissionButtonHit; - float m_tmNextPeriodicThink; - bool m_bGameStarted; // TRUE = the game commencing when there is at least one CT and T, FALSE = scoring will not start until both teams have players (deprecated name m_bFirstConnected) - bool m_bInCareerGame; - float m_fCareerRoundMenuTime; - int m_iCareerMatchWins; - int m_iRoundWinDifference; - float m_fCareerMatchMenuTime; - bool m_bSkipSpawn; - - // custom - bool m_bSkipShowMenu; - bool m_bNeededPlayers; - float m_flEscapeRatio; - float m_flTimeLimit; - float m_flGameStartTime; - bool m_bTeamBalanced; -}; - -typedef struct mapcycle_item_s -{ - struct mapcycle_item_s *next; - char mapname[MAX_MAPNAME_LENGHT]; - int minplayers; - int maxplayers; - char rulebuffer[MAX_RULE_BUFFER]; - -} mapcycle_item_t; - -typedef struct mapcycle_s -{ - struct mapcycle_item_s *items; - struct mapcycle_item_s *next_item; - -} mapcycle_t; - -class CCStrikeGameMgrHelper: public IVoiceGameMgrHelper -{ -public: - virtual bool CanPlayerHearPlayer(CBasePlayer *pListener, CBasePlayer *pSender); - -#ifdef REGAMEDLL_API - bool CanPlayerHearPlayer_OrigFunc(CBasePlayer *pListener, CBasePlayer *pSender); -#endif -}; - -extern CGameRules DLLEXPORT *g_pGameRules; - -#ifdef REGAMEDLL_API -CGameRules *InstallGameRules_OrigFunc(); -#endif - -CGameRules *InstallGameRules(); - -// Gets us at the CS game rules -inline CHalfLifeMultiplay *CSGameRules() -{ - return static_cast(g_pGameRules); -} - -inline void CHalfLifeMultiplay::TerminateRound(float tmDelay, int iWinStatus) -{ - m_iRoundWinStatus = iWinStatus; - m_flRestartRoundTime = gpGlobals->time + tmDelay; - m_bRoundTerminating = true; -} - -inline float CHalfLifeMultiplay::GetRoundRemainingTimeReal() const -{ -#ifdef REGAMEDLL_FIXES - return m_iRoundTimeSecs - gpGlobals->time + m_fRoundStartTimeReal; -#else - return GetRoundRemainingTime(); -#endif -} - -inline float CHalfLifeMultiplay::GetRoundRespawnTime() const -{ -#ifdef REGAMEDLL_ADD - return roundrespawn_time.value; -#else - return ROUND_RESPAWN_TIME; -#endif -} - -inline bool CHalfLifeMultiplay::IsFreeForAll() const -{ -#ifdef REGAMEDLL_ADD - if (freeforall.value != 0.0f) - return true; -#endif - return false; -} - -inline float CHalfLifeMultiplay::GetRoundRestartDelay() const -{ -#ifdef REGAMEDLL_ADD - return round_restart_delay.value; -#else - return ROUND_BEGIN_DELAY; -#endif -} - -inline bool HasRoundInfinite(int flags = 0) -{ -#ifdef REGAMEDLL_ADD - if (round_infinite.string[0] == '1') - return true; - - if (flags && (UTIL_ReadFlags(round_infinite.string) & flags)) - return true; - -#endif - return false; -} - -inline float CGameRules::GetItemKillDelay() -{ -#ifdef REGAMEDLL_ADD - return item_staytime.value; -#else - return ITEM_KILL_DELAY; -#endif -} - -inline float CGameRules::GetRadioTimeout() -{ -#ifdef REGAMEDLL_ADD - return radio_timeout.value; -#else - return RADIO_TIMEOUT; -#endif -} - -bool IsBotSpeaking(); -void SV_Continue_f(); -void SV_Tutor_Toggle_f(); -void SV_Career_Restart_f(); -void SV_Career_EndRound_f(); -void SV_CareerAddTask_f(); -void SV_CareerMatchLimit_f(); -void Broadcast(const char *sentence); -char *GetTeam(int team); -void DestroyMapCycle(mapcycle_t *cycle); -int ReloadMapCycleFile(char *filename, mapcycle_t *cycle); -int CountPlayers(); -void ExtractCommandString(char *s, char *szCommand); -int GetMapCount(); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "game_shared/voice_gamemgr.h" +#include "cmdhandler.h" + +const int MAX_RULE_BUFFER = 1024; +const int MAX_VOTE_MAPS = 100; +const int MAX_VIP_QUEUES = 5; +const int MAX_MONEY_THRESHOLD = 999999; // allowable money limit in the game that can be drawn on the HUD + +const int MAX_MOTD_CHUNK = 60; +const int MAX_MOTD_LENGTH = 1536; // (MAX_MOTD_CHUNK * 4) + +const float ITEM_RESPAWN_TIME = 30.0f; +const float WEAPON_RESPAWN_TIME = 20.0f; +const float AMMO_RESPAWN_TIME = 20.0f; +const float ROUND_RESPAWN_TIME = 20.0f; +const float ROUND_BEGIN_DELAY = 5.0f; // delay before beginning new round +const float ITEM_KILL_DELAY = 300.0f; +const float RADIO_TIMEOUT = 1.5f; + +const int MAX_INTERMISSION_TIME = 120; // longest the intermission can last, in seconds + +// when we are within this close to running out of entities, items +// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn +const int ENTITY_INTOLERANCE = 100; + +enum +{ + WINSTATUS_NONE = 0, + WINSTATUS_CTS, + WINSTATUS_TERRORISTS, + WINSTATUS_DRAW, +}; + +// Custom enum +// Used for EndRoundMessage() logged messages +enum ScenarioEventEndRound +{ + ROUND_NONE, + ROUND_TARGET_BOMB, + ROUND_VIP_ESCAPED, + ROUND_VIP_ASSASSINATED, + ROUND_TERRORISTS_ESCAPED, + ROUND_CTS_PREVENT_ESCAPE, + ROUND_ESCAPING_TERRORISTS_NEUTRALIZED, + ROUND_BOMB_DEFUSED, + ROUND_CTS_WIN, + ROUND_TERRORISTS_WIN, + ROUND_END_DRAW, + ROUND_ALL_HOSTAGES_RESCUED, + ROUND_TARGET_SAVED, + ROUND_HOSTAGE_NOT_RESCUED, + ROUND_TERRORISTS_NOT_ESCAPED, + ROUND_VIP_NOT_ESCAPED, + ROUND_GAME_COMMENCE, + ROUND_GAME_RESTART, + ROUND_GAME_OVER +}; + +enum RewardRules +{ + RR_CTS_WIN, + RR_TERRORISTS_WIN, + RR_TARGET_BOMB, + RR_VIP_ESCAPED, + RR_VIP_ASSASSINATED, + RR_TERRORISTS_ESCAPED, + RR_CTS_PREVENT_ESCAPE, + RR_ESCAPING_TERRORISTS_NEUTRALIZED, + RR_BOMB_DEFUSED, + RR_BOMB_PLANTED, + RR_BOMB_EXPLODED, + RR_ALL_HOSTAGES_RESCUED, + RR_TARGET_BOMB_SAVED, + RR_HOSTAGE_NOT_RESCUED, + RR_VIP_NOT_ESCAPED, + RR_LOSER_BONUS_DEFAULT, + RR_LOSER_BONUS_MIN, + RR_LOSER_BONUS_MAX, + RR_LOSER_BONUS_ADD, + RR_RESCUED_HOSTAGE, + RR_TOOK_HOSTAGE_ACC, + RR_TOOK_HOSTAGE, + RR_END +}; + +// custom enum +enum RewardAccount +{ + REWARD_TARGET_BOMB = 3500, + REWARD_VIP_ESCAPED = 3500, + REWARD_VIP_ASSASSINATED = 3250, + REWARD_TERRORISTS_ESCAPED = 3150, + REWARD_CTS_PREVENT_ESCAPE = 3500, + REWARD_ESCAPING_TERRORISTS_NEUTRALIZED = 3250, + REWARD_BOMB_DEFUSED = 3250, + REWARD_BOMB_PLANTED = 800, + REWARD_BOMB_EXPLODED = 3250, + REWARD_CTS_WIN = 3000, + REWARD_TERRORISTS_WIN = 3000, + REWARD_ALL_HOSTAGES_RESCUED = 2500, + + // the end round was by the expiration time + REWARD_TARGET_BOMB_SAVED = 3250, + REWARD_HOSTAGE_NOT_RESCUED = 3250, + REWARD_VIP_NOT_ESCAPED = 3250, + + // loser bonus + REWARD_LOSER_BONUS_DEFAULT = 1400, + REWARD_LOSER_BONUS_MIN = 1500, + REWARD_LOSER_BONUS_MAX = 3000, + REWARD_LOSER_BONUS_ADD = 500, + + REWARD_RESCUED_HOSTAGE = 750, + REWARD_KILLED_ENEMY = 300, + REWARD_KILLED_VIP = 2500, + REWARD_VIP_HAVE_SELF_RESCUED = 2500, + + REWARD_TAKEN_HOSTAGE = 1000, + REWARD_TOOK_HOSTAGE_ACC = 100, + REWARD_TOOK_HOSTAGE = 150, +}; + +// custom enum +enum PaybackForBadThing +{ + PAYBACK_FOR_KILLED_TEAMMATES = -3300, +}; + +// custom enum +enum InfoMapBuyParam +{ + BUYING_EVERYONE = 0, + BUYING_ONLY_CTS, + BUYING_ONLY_TERRORISTS, + BUYING_NO_ONE, +}; + +// weapon respawning return codes +enum +{ + GR_NONE = 0, + + GR_WEAPON_RESPAWN_YES, + GR_WEAPON_RESPAWN_NO, + + GR_AMMO_RESPAWN_YES, + GR_AMMO_RESPAWN_NO, + + GR_ITEM_RESPAWN_YES, + GR_ITEM_RESPAWN_NO, + + GR_PLR_DROP_GUN_ALL, + GR_PLR_DROP_GUN_ACTIVE, + GR_PLR_DROP_GUN_NO, + + GR_PLR_DROP_AMMO_ALL, + GR_PLR_DROP_AMMO_ACTIVE, + GR_PLR_DROP_AMMO_NO, +}; + +// custom enum +enum +{ + SCENARIO_BLOCK_TIME_EXPRIRED = BIT(0), // flag "a" + SCENARIO_BLOCK_NEED_PLAYERS = BIT(1), // flag "b" + SCENARIO_BLOCK_VIP_ESCAPE = BIT(2), // flag "c" + SCENARIO_BLOCK_PRISON_ESCAPE = BIT(3), // flag "d" + SCENARIO_BLOCK_BOMB = BIT(4), // flag "e" + SCENARIO_BLOCK_TEAM_EXTERMINATION = BIT(5), // flag "f" + SCENARIO_BLOCK_HOSTAGE_RESCUE = BIT(6), // flag "g" +}; + +// Player relationship return codes +enum +{ + GR_NOTTEAMMATE = 0, + GR_TEAMMATE, + GR_ENEMY, + GR_ALLY, + GR_NEUTRAL, +}; + +class CItem; + +class CGameRules +{ +public: + CGameRules(); + virtual ~CGameRules(); + + virtual void RefreshSkillData(); // fill skill data struct with proper values + virtual void Think() = 0; // runs every server frame, should handle any timer tasks, periodic events, etc. + virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity) = 0; // Can this item spawn (eg monsters don't spawn in deathmatch). + + virtual BOOL FAllowFlashlight() = 0; // Are players allowed to switch on their flashlight? + virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) = 0; // should the player switch to this weapon? + virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon) = 0; // I can't use this weapon anymore, get me the next best one. + + // Functions to verify the single/multiplayer status of a game + virtual BOOL IsMultiplayer() = 0; // is this a multiplayer game? (either coop or deathmatch) + virtual BOOL IsDeathmatch() = 0; // is this a deathmatch game? + virtual BOOL IsTeamplay() { return FALSE; } // is this deathmatch game being played with team rules? + virtual BOOL IsCoOp() = 0; // is this a coop game? + virtual const char *GetGameDescription(); // this is the game name that gets seen in the server browser + + // Client connection/disconnection + virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char *szRejectReason) = 0; // a client just connected to the server (player hasn't spawned yet) + virtual void InitHUD(CBasePlayer *pl) = 0; // the client dll is ready for updating + virtual void ClientDisconnected(edict_t *pClient) = 0; // a client just disconnected from the server + virtual void UpdateGameMode(CBasePlayer *pPlayer) {}; // the client needs to be informed of the current game mode + + // Client damage rules + virtual float FlPlayerFallDamage(CBasePlayer *pPlayer) = 0; + virtual BOOL FPlayerCanTakeDamage(CBasePlayer *pPlayer, CBaseEntity *pAttacker) { return TRUE; } // can this player take damage from this attacker? + virtual BOOL ShouldAutoAim(CBasePlayer *pPlayer, edict_t *target) { return TRUE; } + + // Client spawn/respawn control + virtual void PlayerSpawn(CBasePlayer *pPlayer) = 0; // called by CBasePlayer::Spawn just before releasing player into the game + virtual void PlayerThink(CBasePlayer *pPlayer) = 0; // called by CBasePlayer::PreThink every frame, before physics are run and after keys are accepted + virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer) = 0; // is this player allowed to respawn now? + virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer) = 0; // When in the future will this player be able to spawn? + virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); // Place this player on their spawnspot and face them the proper direction. + + virtual BOOL AllowAutoTargetCrosshair() { return TRUE; } + virtual BOOL ClientCommand_DeadOrAlive(CBasePlayer *pPlayer, const char *pcmd) { return FALSE; } + virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd) { return FALSE; } // handles the user commands; returns TRUE if command handled properly + virtual void ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer) {}; // the player has changed userinfo; can change it now + + // Client kills/scoring + virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled) = 0; // how many points do I award whoever kills this player? + virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor) = 0; // Called each time a player dies + virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor) = 0; // Call this from within a GameRules class to report an obituary. + + // Weapon retrieval + virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem); // The player is touching an CBasePlayerItem, do I give it to him? + virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon) = 0; // Called each time a player picks up a weapon from the ground + + // Weapon spawn/respawn control + virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon) = 0; // should this weapon respawn? + virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon) = 0; // when may this weapon respawn? + virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon) = 0; // can i respawn now, and if not, when should i try again? + virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon) = 0; // where in the world should this weapon respawn? + + // Item retrieval + virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem) = 0; // is this player allowed to take this item? + virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem) = 0; // call each time a player picks up an item (battery, healthkit, longjump) + + // Item spawn/respawn control + virtual int ItemShouldRespawn(CItem *pItem) = 0; // Should this item respawn? + virtual float FlItemRespawnTime(CItem *pItem) = 0; // when may this item respawn? + virtual Vector VecItemRespawnSpot(CItem *pItem) = 0; // where in the world should this item respawn? + + // Ammo retrieval + virtual BOOL CanHaveAmmo(CBasePlayer *pPlayer, const char *pszAmmoName, int iMaxCarry); // can this player take more of this ammo? + virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount) = 0; // called each time a player picks up some ammo in the world + + // Ammo spawn/respawn control + virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo) = 0; // should this ammo item respawn? + virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo) = 0; // when should this ammo item respawn? + virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo) = 0; // where in the world should this ammo item respawn? + + // Healthcharger respawn control + virtual float FlHealthChargerRechargeTime() = 0; // how long until a depleted HealthCharger recharges itself? + virtual float FlHEVChargerRechargeTime() { return 0.0f; } // how long until a depleted HealthCharger recharges itself? + + // What happens to a dead player's weapons + virtual int DeadPlayerWeapons(CBasePlayer *pPlayer) = 0; // what do I do with a player's weapons when he's killed? + + // What happens to a dead player's ammo + virtual int DeadPlayerAmmo(CBasePlayer *pPlayer) = 0; // Do I drop ammo when the player dies? How much? + + // Teamplay stuff + virtual const char *GetTeamID(CBaseEntity *pEntity) = 0; // what team is this entity on? + virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget) = 0; // What is the player's relationship with this entity? + virtual int GetTeamIndex(const char *pTeamName) { return -1; } + virtual const char *GetIndexedTeamName(int teamIndex) { return ""; } + virtual BOOL IsValidTeam(const char *pTeamName) { return TRUE; } + virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib) {}; + virtual const char *SetDefaultPlayerTeam(CBasePlayer *pPlayer) { return ""; } + + // Sounds + virtual BOOL PlayTextureSounds() { return TRUE; } + + // Monsters + virtual BOOL FAllowMonsters() = 0; // are monsters allowed + + // Immediately end a multiplayer game + virtual void EndMultiplayerGame() {}; + + // Stuff that is shared between client and server. + virtual BOOL IsFreezePeriod() { return m_bFreezePeriod; } + virtual void ServerDeactivate() {}; + virtual void CheckMapConditions() {}; + + // inline function's + inline bool IsGameOver() const { return m_bGameOver; } + inline void SetGameOver() { m_bGameOver = true; } + static float GetItemKillDelay(); + static float GetRadioTimeout(); + +public: + BOOL m_bFreezePeriod; // TRUE at beginning of round, set to FALSE when the period expires + BOOL m_bBombDropped; + + // custom + char *m_GameDesc; + bool m_bGameOver; // intermission or finale (deprecated name g_fGameOver) +}; + +#define GAMERULES_API_INTERFACE_VERSION "GAMERULES_API_INTERFACE_VERSION001" + +// CHalfLifeRules - rules for the single player Half-Life game. +class CHalfLifeRules: public CGameRules +{ +public: + CHalfLifeRules(); + virtual ~CHalfLifeRules() {}; + + virtual void Think(); + virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity); + virtual BOOL FAllowFlashlight() { return TRUE; } + + virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); + + // Functions to verify the single/multiplayer status of a game + virtual BOOL IsMultiplayer(); + virtual BOOL IsDeathmatch(); + virtual BOOL IsCoOp(); + + // Client connection/disconnection + virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); + virtual void InitHUD(CBasePlayer *pl); // the client dll is ready for updating + virtual void ClientDisconnected(edict_t *pClient); + + // Client damage rules + virtual float FlPlayerFallDamage(CBasePlayer *pPlayer); + + // Client spawn/respawn control + virtual void PlayerSpawn(CBasePlayer *pPlayer); + virtual void PlayerThink(CBasePlayer *pPlayer); + virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer); + virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer); + virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); + + virtual BOOL AllowAutoTargetCrosshair(); + + // Client kills/scoring + virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); + virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + + // Weapon retrieval + virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + + // Weapon spawn/respawn control + virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon); + virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon); + virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon); + virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon); + + // Item retrieval + virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem); + virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem); + + // Item spawn/respawn control + virtual int ItemShouldRespawn(CItem *pItem); + virtual float FlItemRespawnTime(CItem *pItem); + virtual Vector VecItemRespawnSpot(CItem *pItem); + + // Ammo retrieval + virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount); + + // Ammo spawn/respawn control + virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo); + virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo); + virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo); + + // Healthcharger respawn control + virtual float FlHealthChargerRechargeTime(); + + // What happens to a dead player's weapons + virtual int DeadPlayerWeapons(CBasePlayer *pPlayer); + + // What happens to a dead player's ammo + virtual int DeadPlayerAmmo(CBasePlayer *pPlayer); + + // Teamplay stuff + virtual const char *GetTeamID(CBaseEntity *pEntity) { return ""; }; + virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget); + + // Monsters + virtual BOOL FAllowMonsters(); +}; + +// CHalfLifeMultiplay - rules for the basic half life multiplayer competition +class CHalfLifeMultiplay: public CGameRules +{ +public: + CHalfLifeMultiplay(); + virtual ~CHalfLifeMultiplay() {}; + + virtual void RefreshSkillData(); + virtual void Think(); + virtual BOOL IsAllowedToSpawn(CBaseEntity *pEntity); + virtual BOOL FAllowFlashlight(); + + virtual BOOL FShouldSwitchWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + virtual BOOL GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); + + virtual BOOL IsMultiplayer(); + virtual BOOL IsDeathmatch(); + virtual BOOL IsCoOp(); + + // Client connection/disconnection + // If ClientConnected returns FALSE, the connection is rejected and the user is provided the reason specified in szRejectReason + // Only the client's name and remote address are provided to the dll for verification. + virtual BOOL ClientConnected(edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); + virtual void InitHUD(CBasePlayer *pl); // the client dll is ready for updating + virtual void ClientDisconnected(edict_t *pClient); + virtual void UpdateGameMode(CBasePlayer *pPlayer); // the client needs to be informed of the current game mode + + // Client damage rules + virtual float FlPlayerFallDamage(CBasePlayer *pPlayer); + virtual BOOL FPlayerCanTakeDamage(CBasePlayer *pPlayer, CBaseEntity *pAttacker); + + // Client spawn/respawn control + virtual void PlayerSpawn(CBasePlayer *pPlayer); + virtual void PlayerThink(CBasePlayer *pPlayer); + virtual BOOL FPlayerCanRespawn(CBasePlayer *pPlayer); + virtual float FlPlayerSpawnTime(CBasePlayer *pPlayer); + virtual edict_t *GetPlayerSpawnSpot(CBasePlayer *pPlayer); + + virtual BOOL AllowAutoTargetCrosshair(); + virtual BOOL ClientCommand_DeadOrAlive(CBasePlayer *pPlayer, const char *pcmd); + virtual BOOL ClientCommand(CBasePlayer *pPlayer, const char *pcmd); + virtual void ClientUserInfoChanged(CBasePlayer *pPlayer, char *infobuffer); + + // Client kills/scoring + virtual int IPointsForKill(CBasePlayer *pAttacker, CBasePlayer *pKilled); + virtual void PlayerKilled(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + virtual void DeathNotice(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + + // Weapon retrieval + virtual BOOL CanHavePlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); // The player is touching an CBasePlayerItem, do I give it to him? + virtual void PlayerGotWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + + // Weapon spawn/respawn control + virtual int WeaponShouldRespawn(CBasePlayerItem *pWeapon); + virtual float FlWeaponRespawnTime(CBasePlayerItem *pWeapon); + virtual float FlWeaponTryRespawn(CBasePlayerItem *pWeapon); + virtual Vector VecWeaponRespawnSpot(CBasePlayerItem *pWeapon); + + // Item retrieval + virtual BOOL CanHaveItem(CBasePlayer *pPlayer, CItem *pItem); + virtual void PlayerGotItem(CBasePlayer *pPlayer, CItem *pItem); + + // Item spawn/respawn control + virtual int ItemShouldRespawn(CItem *pItem); + virtual float FlItemRespawnTime(CItem *pItem); + virtual Vector VecItemRespawnSpot(CItem *pItem); + + // Ammo retrieval + virtual void PlayerGotAmmo(CBasePlayer *pPlayer, char *szName, int iCount); + + // Ammo spawn/respawn control + virtual int AmmoShouldRespawn(CBasePlayerAmmo *pAmmo); + virtual float FlAmmoRespawnTime(CBasePlayerAmmo *pAmmo); + virtual Vector VecAmmoRespawnSpot(CBasePlayerAmmo *pAmmo); + + // Healthcharger respawn control + virtual float FlHealthChargerRechargeTime(); + virtual float FlHEVChargerRechargeTime(); + + // What happens to a dead player's weapons + virtual int DeadPlayerWeapons(CBasePlayer *pPlayer); + + // What happens to a dead player's ammo + virtual int DeadPlayerAmmo(CBasePlayer *pPlayer); + + // Teamplay stuff + virtual const char *GetTeamID(CBaseEntity *pEntity) { return ""; } + virtual int PlayerRelationship(CBasePlayer *pPlayer, CBaseEntity *pTarget); + virtual void ChangePlayerTeam(CBasePlayer *pPlayer, const char *pTeamName, BOOL bKill, BOOL bGib); + + virtual BOOL PlayTextureSounds() { return FALSE; } + + // Monsters + virtual BOOL FAllowMonsters(); + + // Immediately end a multiplayer game + virtual void EndMultiplayerGame() { GoToIntermission(); } + virtual void ServerDeactivate(); + virtual void CheckMapConditions(); + + // Recreate all the map entities from the map data (preserving their indices), + // then remove everything else except the players. + // Also get rid of all world decals. + virtual void CleanUpMap(); + virtual void RestartRound(); + + // check if the scenario has been won/lost + virtual void CheckWinConditions(); + virtual void RemoveGuns(); + virtual void GiveC4(); + virtual void ChangeLevel(); + virtual void GoToIntermission(); + +#ifdef REGAMEDLL_API + BOOL FShouldSwitchWeapon_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + BOOL GetNextBestWeapon_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); + float FlPlayerFallDamage_OrigFunc(CBasePlayer *pPlayer); + BOOL FPlayerCanTakeDamage_OrigFunc(CBasePlayer *pPlayer, CBaseEntity *pAttacker); + void PlayerSpawn_OrigFunc(CBasePlayer *pPlayer); + BOOL FPlayerCanRespawn_OrigFunc(CBasePlayer *pPlayer); + edict_t *GetPlayerSpawnSpot_OrigFunc(CBasePlayer *pPlayer); + void ClientUserInfoChanged_OrigFunc(CBasePlayer *pPlayer, char *infobuffer); + void PlayerKilled_OrigFunc(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + void DeathNotice_OrigFunc(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor); + BOOL CanHavePlayerItem_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + int DeadPlayerWeapons_OrigFunc(CBasePlayer *pPlayer); + void ServerDeactivate_OrigFunc(); + void CheckMapConditions_OrigFunc(); + void CleanUpMap_OrigFunc(); + void RestartRound_OrigFunc(); + void CheckWinConditions_OrigFunc(); + void RemoveGuns_OrigFunc(); + void GiveC4_OrigFunc(); + void ChangeLevel_OrigFunc(); + void GoToIntermission_OrigFunc(); + void BalanceTeams_OrigFunc(); +#endif + +public: + void ServerActivate(); + void ReadMultiplayCvars(); + + // Checks if it still needs players to start a round, or if it has enough players to start rounds. + // Starts a round and returns true if there are enough players. + bool NeededPlayersCheck(); + + // Setup counts for m_iNumTerrorist, m_iNumCT, m_iNumSpawnableTerrorist, m_iNumSpawnableCT, etc. + VFUNC void InitializePlayerCounts(int &NumAliveTerrorist, int &NumAliveCT, int &NumDeadTerrorist, int &NumDeadCT); + + // Check to see if the round is over for the various game types. Terminates the round + // and returns true if the round should end. + bool PrisonRoundEndCheck(int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT); + bool BombRoundEndCheck(); + bool HostageRescueRoundEndCheck(); + bool VIPRoundEndCheck(); + + // Check to see if the teams exterminated each other. Ends the round and returns true if so. + bool TeamExterminationCheck(int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT); + + // for internal functions API + void OnRoundFreezeEnd(); + bool OnRoundEnd(int winStatus, ScenarioEventEndRound event, float tmDelay); + bool OnRoundEnd_Intercept(int winStatus, ScenarioEventEndRound event, float tmDelay); + + bool RoundOver(float tmDelay); + bool NeededPlayersCheck(float tmDelay); + bool RestartRoundCheck(float tmDelay); + + bool VIP_Escaped(float tmDelay); + bool VIP_Died(float tmDelay); + bool VIP_NotEscaped(float tmDelay); + + bool Prison_Escaped(float tmDelay); + bool Prison_PreventEscape(float tmDelay); + bool Prison_NotEscaped(float tmDelay); + bool Prison_Neutralized(float tmDelay); + + bool Target_Bombed(float tmDelay); + bool Target_Saved(float tmDelay); + bool Target_Defused(float tmDelay); + + // Team extermination + bool Round_Cts(float tmDelay); + bool Round_Ts(float tmDelay); + bool Round_Draw(float tmDelay); + + bool Hostage_Rescue(float tmDelay); + bool Hostage_NotRescued(float tmDelay); + + // Check various conditions to end the map. + bool CheckGameOver(); + bool CheckTimeLimit(); + bool CheckFragLimit(); + bool CheckMaxRounds(); + bool CheckWinLimit(); + + void CheckFreezePeriodExpired(); + void CheckRoundTimeExpired(); + + void CheckLevelInitialized(); + void CheckRestartRound(); + + BOOL IsCareer(); + void QueueCareerRoundEndMenu(float tmDelay, int iWinStatus); + void SetCareerMatchLimit(int minWins, int winDifference); + bool IsInCareerRound(); + void CareerRestart(); + bool ShouldSkipShowMenu() const { return m_bSkipShowMenu; } + void MarkShowMenuSkipped() { m_bSkipShowMenu = false; } + bool ShouldSkipSpawn() const { return m_bSkipSpawn; } + void MarkSpawnSkipped() { m_bSkipSpawn = false; } + void PlayerJoinedTeam(CBasePlayer *pPlayer) { } + float GetRoundRemainingTime() const { return m_iRoundTimeSecs - gpGlobals->time + m_fRoundStartTime; } + float GetRoundRemainingTimeReal() const; + float GetTimeLeft() const { return m_flTimeLimit - gpGlobals->time; } + float GetRoundElapsedTime() const { return gpGlobals->time - m_fRoundStartTime; } + float GetMapElapsedTime() const { return gpGlobals->time; } + + BOOL TeamFull(int team_id); + BOOL TeamStacked(int newTeam_id, int curTeam_id); + bool IsVIPQueueEmpty(); + bool AddToVIPQueue(CBasePlayer *toAdd); + + // VIP FUNCTIONS + void PickNextVIP(); + void StackVIPQueue(); + void ResetCurrentVIP(); + + VFUNC void BalanceTeams(); + VFUNC void SwapAllPlayers(); + VFUNC void UpdateTeamScores(); + VFUNC void EndRoundMessage(const char *sentence, ScenarioEventEndRound event); + VFUNC void SetAccountRules(RewardRules rules, int amount) { m_rgRewardAccountRules[rules] = static_cast(amount); } + VFUNC RewardAccount GetAccountRules(RewardRules rules) const { return m_rgRewardAccountRules[rules]; } + + void DisplayMaps(CBasePlayer *pPlayer, int iVote); + void ResetAllMapVotes(); + void ProcessMapVote(CBasePlayer *pPlayer, int iVote); + + // BOMB MAP FUNCTIONS + VFUNC BOOL IsThereABomber(); + VFUNC BOOL IsThereABomb(); + VFUNC TeamName SelectDefaultTeam(); + + bool IsMatchStarted() { return (m_flRestartRoundTime != 0.0f || m_fCareerRoundMenuTime != 0.0f || m_fCareerMatchMenuTime != 0.0f); } + void SendMOTDToClient(edict_t *client); + + void TerminateRound(float tmDelay, int iWinStatus); + float GetRoundRespawnTime() const; + float GetRoundRestartDelay() const; + + bool IsGameStarted() const { return m_bGameStarted; } + + // has a style of gameplay when aren't any teams + bool IsFreeForAll() const; + bool CanPlayerBuy(CBasePlayer *pPlayer) const; + + VFUNC bool HasRoundTimeExpired(); + VFUNC bool IsBombPlanted(); + +private: + void MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int iTeam); + +public: + static RewardAccount m_rgRewardAccountRules[RR_END]; + static RewardAccount m_rgRewardAccountRules_default[RR_END]; + + CVoiceGameMgr m_VoiceGameMgr; + float m_flRestartRoundTime; // The global time when the round is supposed to end, if this is not 0 (deprecated name m_fTeamCount) + float m_flCheckWinConditions; + float m_fRoundStartTime; // Time round has started (deprecated name m_fRoundCount) + int m_iRoundTime; // (From mp_roundtime) - How many seconds long this round is. + int m_iRoundTimeSecs; + int m_iIntroRoundTime; // (From mp_freezetime) - How many seconds long the intro round (when players are frozen) is. + float m_fRoundStartTimeReal; // The global time when the intro round ends and the real one starts + // wrote the original "m_flRoundTime" comment for this variable). + int m_iAccountTerrorist; + int m_iAccountCT; + int m_iNumTerrorist; // The number of terrorists on the team (this is generated at the end of a round) + int m_iNumCT; // The number of CTs on the team (this is generated at the end of a round) + int m_iNumSpawnableTerrorist; + int m_iNumSpawnableCT; + int m_iSpawnPointCount_Terrorist; // Number of Terrorist spawn points + int m_iSpawnPointCount_CT; // Number of CT spawn points + int m_iHostagesRescued; + int m_iHostagesTouched; + int m_iRoundWinStatus; // 1 == CT's won last round, 2 == Terrorists did, 3 == Draw, no winner + + short m_iNumCTWins; + short m_iNumTerroristWins; + + bool m_bTargetBombed; // whether or not the bomb has been bombed + bool m_bBombDefused; // whether or not the bomb has been defused + + bool m_bMapHasBombTarget; + bool m_bMapHasBombZone; + bool m_bMapHasBuyZone; + bool m_bMapHasRescueZone; + bool m_bMapHasEscapeZone; + + BOOL m_bMapHasVIPSafetyZone; // TRUE = has VIP safety zone, FALSE = does not have VIP safetyzone + BOOL m_bMapHasCameras; + int m_iC4Timer; + int m_iC4Guy; // The current Terrorist who has the C4. + int m_iLoserBonus; // the amount of money the losing team gets. This scales up as they lose more rounds in a row + int m_iNumConsecutiveCTLoses; // the number of rounds the CTs have lost in a row. + int m_iNumConsecutiveTerroristLoses; // the number of rounds the Terrorists have lost in a row. + + float m_fMaxIdlePeriod; // For the idle kick functionality. This is tha max amount of time that the player has to be idle before being kicked + + int m_iLimitTeams; + bool m_bLevelInitialized; + bool m_bRoundTerminating; + bool m_bCompleteReset; // Set to TRUE to have the scores reset next time round restarts + float m_flRequiredEscapeRatio; + int m_iNumEscapers; + int m_iHaveEscaped; + bool m_bCTCantBuy; + bool m_bTCantBuy; // Who can and can't buy. + float m_flBombRadius; + int m_iConsecutiveVIP; + int m_iTotalGunCount; + int m_iTotalGrenadeCount; + int m_iTotalArmourCount; + int m_iUnBalancedRounds; // keeps track of the # of consecutive rounds that have gone by where one team outnumbers the other team by more than 2 + int m_iNumEscapeRounds; // keeps track of the # of consecutive rounds of escape played.. Teams will be swapped after 8 rounds + int m_iMapVotes[MAX_VOTE_MAPS]; + int m_iLastPick; + int m_iMaxMapTime; + int m_iMaxRounds; + int m_iTotalRoundsPlayed; + int m_iMaxRoundsWon; + int m_iStoredSpectValue; + float m_flForceCameraValue; + float m_flForceChaseCamValue; + float m_flFadeToBlackValue; + CBasePlayer *m_pVIP; + CBasePlayer *m_pVIPQueue[MAX_VIP_QUEUES]; + +protected: + float m_flIntermissionEndTime; + float m_flIntermissionStartTime; + BOOL m_iEndIntermissionButtonHit; + float m_tmNextPeriodicThink; + bool m_bGameStarted; // TRUE = the game commencing when there is at least one CT and T, FALSE = scoring will not start until both teams have players (deprecated name m_bFirstConnected) + bool m_bInCareerGame; + float m_fCareerRoundMenuTime; + int m_iCareerMatchWins; + int m_iRoundWinDifference; + float m_fCareerMatchMenuTime; + bool m_bSkipSpawn; + + // custom + bool m_bSkipShowMenu; + bool m_bNeededPlayers; + float m_flEscapeRatio; + float m_flTimeLimit; + float m_flGameStartTime; + bool m_bTeamBalanced; +}; + +typedef struct mapcycle_item_s +{ + struct mapcycle_item_s *next; + char mapname[MAX_MAPNAME_LENGHT]; + int minplayers; + int maxplayers; + char rulebuffer[MAX_RULE_BUFFER]; + +} mapcycle_item_t; + +typedef struct mapcycle_s +{ + struct mapcycle_item_s *items; + struct mapcycle_item_s *next_item; + +} mapcycle_t; + +class CCStrikeGameMgrHelper: public IVoiceGameMgrHelper +{ +public: + virtual bool CanPlayerHearPlayer(CBasePlayer *pListener, CBasePlayer *pSender); + +#ifdef REGAMEDLL_API + bool CanPlayerHearPlayer_OrigFunc(CBasePlayer *pListener, CBasePlayer *pSender); +#endif +}; + +extern CGameRules DLLEXPORT *g_pGameRules; + +#ifdef REGAMEDLL_API +CGameRules *InstallGameRules_OrigFunc(); +#endif + +CGameRules *InstallGameRules(); + +// Gets us at the CS game rules +inline CHalfLifeMultiplay *CSGameRules() +{ + return static_cast(g_pGameRules); +} + +inline void CHalfLifeMultiplay::TerminateRound(float tmDelay, int iWinStatus) +{ + m_iRoundWinStatus = iWinStatus; + m_flRestartRoundTime = gpGlobals->time + tmDelay; + m_bRoundTerminating = true; +} + +inline float CHalfLifeMultiplay::GetRoundRemainingTimeReal() const +{ +#ifdef REGAMEDLL_FIXES + return m_iRoundTimeSecs - gpGlobals->time + m_fRoundStartTimeReal; +#else + return GetRoundRemainingTime(); +#endif +} + +inline float CHalfLifeMultiplay::GetRoundRespawnTime() const +{ +#ifdef REGAMEDLL_ADD + return roundrespawn_time.value; +#else + return ROUND_RESPAWN_TIME; +#endif +} + +inline bool CHalfLifeMultiplay::IsFreeForAll() const +{ +#ifdef REGAMEDLL_ADD + if (freeforall.value != 0.0f) + return true; +#endif + return false; +} + +inline float CHalfLifeMultiplay::GetRoundRestartDelay() const +{ +#ifdef REGAMEDLL_ADD + return round_restart_delay.value; +#else + return ROUND_BEGIN_DELAY; +#endif +} + +inline bool HasRoundInfinite(int flags = 0) +{ +#ifdef REGAMEDLL_ADD + if (round_infinite.string[0] == '1') + return true; + + if (flags && (UTIL_ReadFlags(round_infinite.string) & flags)) + return true; + +#endif + return false; +} + +inline float CGameRules::GetItemKillDelay() +{ +#ifdef REGAMEDLL_ADD + return item_staytime.value; +#else + return ITEM_KILL_DELAY; +#endif +} + +inline float CGameRules::GetRadioTimeout() +{ +#ifdef REGAMEDLL_ADD + return radio_timeout.value; +#else + return RADIO_TIMEOUT; +#endif +} + +bool IsBotSpeaking(); +void SV_Continue_f(); +void SV_Tutor_Toggle_f(); +void SV_Career_Restart_f(); +void SV_Career_EndRound_f(); +void SV_CareerAddTask_f(); +void SV_CareerMatchLimit_f(); +void Broadcast(const char *sentence); +char *GetTeam(int team); +void DestroyMapCycle(mapcycle_t *cycle); +int ReloadMapCycleFile(char *filename, mapcycle_t *cycle); +int CountPlayers(); +void ExtractCommandString(char *s, char *szCommand); +int GetMapCount(); diff --git a/regamedll/dlls/globals.cpp b/regamedll/dlls/globals.cpp index 70d9a8c5..3f698d59 100644 --- a/regamedll/dlls/globals.cpp +++ b/regamedll/dlls/globals.cpp @@ -1,14 +1,14 @@ -#include "precompiled.h" - -const Vector g_vecZero(0, 0, 0); - -int g_Language; -int g_iSkillLevel; - -Vector g_vecAttackDir; -BOOL gDisplayTitle; - -bool g_bIsBeta = false; -bool g_bIsCzeroGame = false; -bool g_bAllowedCSBot = false; -bool g_bHostageImprov = false; +#include "precompiled.h" + +const Vector g_vecZero(0, 0, 0); + +int g_Language; +int g_iSkillLevel; + +Vector g_vecAttackDir; +BOOL gDisplayTitle; + +bool g_bIsBeta = false; +bool g_bIsCzeroGame = false; +bool g_bAllowedCSBot = false; +bool g_bHostageImprov = false; diff --git a/regamedll/dlls/globals.h b/regamedll/dlls/globals.h index d6a47023..02024291 100644 --- a/regamedll/dlls/globals.h +++ b/regamedll/dlls/globals.h @@ -1,41 +1,41 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -extern const Vector g_vecZero; -extern int g_Language; -extern int g_iSkillLevel; - -extern Vector g_vecAttackDir; - -extern BOOL gDisplayTitle; -extern bool g_bIsBeta; -extern bool g_bIsCzeroGame; -extern bool g_bAllowedCSBot; -extern bool g_bHostageImprov; +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +extern const Vector g_vecZero; +extern int g_Language; +extern int g_iSkillLevel; + +extern Vector g_vecAttackDir; + +extern BOOL gDisplayTitle; +extern bool g_bIsBeta; +extern bool g_bIsCzeroGame; +extern bool g_bAllowedCSBot; +extern bool g_bHostageImprov; diff --git a/regamedll/dlls/h_battery.cpp b/regamedll/dlls/h_battery.cpp index 7d36c1c7..f46b8605 100644 --- a/regamedll/dlls/h_battery.cpp +++ b/regamedll/dlls/h_battery.cpp @@ -1,220 +1,220 @@ -#include "precompiled.h" - -TYPEDESCRIPTION CRecharge::m_SaveData[] = -{ - DEFINE_FIELD(CRecharge, m_flNextCharge, FIELD_TIME), - DEFINE_FIELD(CRecharge, m_iReactivate, FIELD_INTEGER), - DEFINE_FIELD(CRecharge, m_iJuice, FIELD_INTEGER), - DEFINE_FIELD(CRecharge, m_iOn, FIELD_INTEGER), - DEFINE_FIELD(CRecharge, m_flSoundTime, FIELD_TIME), -}; - -IMPLEMENT_SAVERESTORE(CRecharge, CBaseEntity) -LINK_ENTITY_TO_CLASS(func_recharge, CRecharge, CCSRecharge) - -void CRecharge::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "style") - || FStrEq(pkvd->szKeyName, "height") - || FStrEq(pkvd->szKeyName, "value1") - || FStrEq(pkvd->szKeyName, "value2") - || FStrEq(pkvd->szKeyName, "value3")) - { - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "dmdelay")) - { - m_iReactivate = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseToggle::KeyValue(pkvd); - } -} - -void CRecharge::Spawn() -{ - Precache(); - - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - // set size and link into world - UTIL_SetOrigin(pev, pev->origin); - UTIL_SetSize(pev, pev->mins, pev->maxs); - SET_MODEL(ENT(pev), STRING(pev->model)); - - int armorValue = (int)gSkillData.suitchargerCapacity; -#ifdef REGAMEDLL_FIXES - if (pev->armorvalue != 0.0f) { - armorValue = (int)pev->armorvalue; - } -#endif - - m_iJuice = armorValue; - pev->frame = 0; -} - -#ifdef REGAMEDLL_FIXES -void CRecharge::Restart() -{ - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - // set size and link into world - UTIL_SetOrigin(pev, pev->origin); - UTIL_SetSize(pev, pev->mins, pev->maxs); - SET_MODEL(ENT(pev), STRING(pev->model)); - - pev->nextthink = pev->ltime + 0.1f; - SetThink(&CRecharge::Recharge); -} - -#endif -void CRecharge::Precache() -{ - PRECACHE_SOUND("items/suitcharge1.wav"); - PRECACHE_SOUND("items/suitchargeno1.wav"); - PRECACHE_SOUND("items/suitchargeok1.wav"); -} - -void CRecharge::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ -#ifdef REGAMEDLL_FIXES - // Make sure that we have a caller - if (!pActivator) - return; - - // if it's not a player, ignore - if (!pActivator->IsPlayer()) - return; -#else - if (!FClassnameIs(pActivator->pev, "player")) - return; -#endif // #ifdef REGAMEDLL_FIXES - - // if there is no juice left, turn it off - if (m_iJuice <= 0 -#ifdef REGAMEDLL_FIXES - && pev->frame != 1.0f // recharging... don't reset think -#endif - ) - { - pev->frame = 1.0f; - Off(); - } - - // if the player doesn't have the suit, or there is no juice left, make the deny noise - if (m_iJuice <= 0 || !(pActivator->pev->weapons & (1 << WEAPON_SUIT)) -#ifdef REGAMEDLL_FIXES - || pActivator->pev->armorvalue >= MAX_CHARGE_ARMOR // don't charge health if we can't more, prevent thinking entity -#endif - ) - { - if (m_flSoundTime <= gpGlobals->time) - { - m_flSoundTime = gpGlobals->time + 0.62f; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeno1.wav", 0.85, ATTN_NORM); - } - - return; - } - - pev->nextthink = pev->ltime + 0.25f; - SetThink(&CRecharge::Off); - - // Time to recharge yet? - if (m_flNextCharge >= gpGlobals->time) - return; - - // Make sure that we have a caller - if (!pActivator) - return; - - m_hActivator = pActivator;//EHANDLE::CBaseEntity *operator= - - // only recharge the player - if (!m_hActivator->IsPlayer()) - return; - - // Play the on sound or the looping charging sound - if (!m_iOn) - { - m_iOn++; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeok1.wav", 0.85, ATTN_NORM); - m_flSoundTime = gpGlobals->time + 0.56f; - } - - if (m_iOn == 1 && m_flSoundTime <= gpGlobals->time) - { - m_iOn++; - EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav", 0.85, ATTN_NORM); - } - - // charge the player - if (m_hActivator->pev->armorvalue < MAX_CHARGE_ARMOR) - { -#ifdef REGAMEDLL_FIXES - CBasePlayer *pPlayer = m_hActivator.Get(); - if (pPlayer->m_iKevlar == ARMOR_NONE) - pPlayer->m_iKevlar = ARMOR_KEVLAR; -#endif - - m_iJuice--; - m_hActivator->pev->armorvalue += AMOUNT_CHARGE_ARMOR; - - if (m_hActivator->pev->armorvalue > MAX_CHARGE_ARMOR) - m_hActivator->pev->armorvalue = MAX_CHARGE_ARMOR; - } - - // govern the rate of charge - m_flNextCharge = gpGlobals->time + 0.1f; -} - -void CRecharge::Recharge() -{ - int armorValue = (int)gSkillData.suitchargerCapacity; -#ifdef REGAMEDLL_FIXES - if (pev->armorvalue != 0.0f) { - armorValue = (int)pev->armorvalue; - } -#endif - - m_iJuice = armorValue; - - pev->frame = 0; - SetThink(&CRecharge::SUB_DoNothing); -} - -void CRecharge::Off() -{ - // Stop looping sound. - if (m_iOn > 1) - STOP_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav"); - - m_iOn = 0; - - if (!m_iJuice) - { - int iReactivate = m_iReactivate; - -#ifdef REGAMEDLL_FIXES - if (iReactivate <= 0) -#endif // #ifdef REGAMEDLL_FIXES - { - if ((iReactivate = g_pGameRules->FlHEVChargerRechargeTime()) <= 0) - return; - } - - if (m_iReactivate <= 0) - m_iReactivate = iReactivate; - - pev->nextthink = pev->ltime + m_iReactivate; - SetThink(&CRecharge::Recharge); - } - else - { - SetThink(&CRecharge::SUB_DoNothing); - } -} +#include "precompiled.h" + +TYPEDESCRIPTION CRecharge::m_SaveData[] = +{ + DEFINE_FIELD(CRecharge, m_flNextCharge, FIELD_TIME), + DEFINE_FIELD(CRecharge, m_iReactivate, FIELD_INTEGER), + DEFINE_FIELD(CRecharge, m_iJuice, FIELD_INTEGER), + DEFINE_FIELD(CRecharge, m_iOn, FIELD_INTEGER), + DEFINE_FIELD(CRecharge, m_flSoundTime, FIELD_TIME), +}; + +IMPLEMENT_SAVERESTORE(CRecharge, CBaseEntity) +LINK_ENTITY_TO_CLASS(func_recharge, CRecharge, CCSRecharge) + +void CRecharge::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "style") + || FStrEq(pkvd->szKeyName, "height") + || FStrEq(pkvd->szKeyName, "value1") + || FStrEq(pkvd->szKeyName, "value2") + || FStrEq(pkvd->szKeyName, "value3")) + { + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "dmdelay")) + { + m_iReactivate = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseToggle::KeyValue(pkvd); + } +} + +void CRecharge::Spawn() +{ + Precache(); + + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + // set size and link into world + UTIL_SetOrigin(pev, pev->origin); + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), STRING(pev->model)); + + int armorValue = (int)gSkillData.suitchargerCapacity; +#ifdef REGAMEDLL_FIXES + if (pev->armorvalue != 0.0f) { + armorValue = (int)pev->armorvalue; + } +#endif + + m_iJuice = armorValue; + pev->frame = 0; +} + +#ifdef REGAMEDLL_FIXES +void CRecharge::Restart() +{ + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + // set size and link into world + UTIL_SetOrigin(pev, pev->origin); + UTIL_SetSize(pev, pev->mins, pev->maxs); + SET_MODEL(ENT(pev), STRING(pev->model)); + + pev->nextthink = pev->ltime + 0.1f; + SetThink(&CRecharge::Recharge); +} + +#endif +void CRecharge::Precache() +{ + PRECACHE_SOUND("items/suitcharge1.wav"); + PRECACHE_SOUND("items/suitchargeno1.wav"); + PRECACHE_SOUND("items/suitchargeok1.wav"); +} + +void CRecharge::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ +#ifdef REGAMEDLL_FIXES + // Make sure that we have a caller + if (!pActivator) + return; + + // if it's not a player, ignore + if (!pActivator->IsPlayer()) + return; +#else + if (!FClassnameIs(pActivator->pev, "player")) + return; +#endif // #ifdef REGAMEDLL_FIXES + + // if there is no juice left, turn it off + if (m_iJuice <= 0 +#ifdef REGAMEDLL_FIXES + && pev->frame != 1.0f // recharging... don't reset think +#endif + ) + { + pev->frame = 1.0f; + Off(); + } + + // if the player doesn't have the suit, or there is no juice left, make the deny noise + if (m_iJuice <= 0 || !(pActivator->pev->weapons & (1 << WEAPON_SUIT)) +#ifdef REGAMEDLL_FIXES + || pActivator->pev->armorvalue >= MAX_CHARGE_ARMOR // don't charge health if we can't more, prevent thinking entity +#endif + ) + { + if (m_flSoundTime <= gpGlobals->time) + { + m_flSoundTime = gpGlobals->time + 0.62f; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeno1.wav", 0.85, ATTN_NORM); + } + + return; + } + + pev->nextthink = pev->ltime + 0.25f; + SetThink(&CRecharge::Off); + + // Time to recharge yet? + if (m_flNextCharge >= gpGlobals->time) + return; + + // Make sure that we have a caller + if (!pActivator) + return; + + m_hActivator = pActivator;//EHANDLE::CBaseEntity *operator= + + // only recharge the player + if (!m_hActivator->IsPlayer()) + return; + + // Play the on sound or the looping charging sound + if (!m_iOn) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/suitchargeok1.wav", 0.85, ATTN_NORM); + m_flSoundTime = gpGlobals->time + 0.56f; + } + + if (m_iOn == 1 && m_flSoundTime <= gpGlobals->time) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav", 0.85, ATTN_NORM); + } + + // charge the player + if (m_hActivator->pev->armorvalue < MAX_CHARGE_ARMOR) + { +#ifdef REGAMEDLL_FIXES + CBasePlayer *pPlayer = m_hActivator.Get(); + if (pPlayer->m_iKevlar == ARMOR_NONE) + pPlayer->m_iKevlar = ARMOR_KEVLAR; +#endif + + m_iJuice--; + m_hActivator->pev->armorvalue += AMOUNT_CHARGE_ARMOR; + + if (m_hActivator->pev->armorvalue > MAX_CHARGE_ARMOR) + m_hActivator->pev->armorvalue = MAX_CHARGE_ARMOR; + } + + // govern the rate of charge + m_flNextCharge = gpGlobals->time + 0.1f; +} + +void CRecharge::Recharge() +{ + int armorValue = (int)gSkillData.suitchargerCapacity; +#ifdef REGAMEDLL_FIXES + if (pev->armorvalue != 0.0f) { + armorValue = (int)pev->armorvalue; + } +#endif + + m_iJuice = armorValue; + + pev->frame = 0; + SetThink(&CRecharge::SUB_DoNothing); +} + +void CRecharge::Off() +{ + // Stop looping sound. + if (m_iOn > 1) + STOP_SOUND(ENT(pev), CHAN_STATIC, "items/suitcharge1.wav"); + + m_iOn = 0; + + if (!m_iJuice) + { + int iReactivate = m_iReactivate; + +#ifdef REGAMEDLL_FIXES + if (iReactivate <= 0) +#endif // #ifdef REGAMEDLL_FIXES + { + if ((iReactivate = g_pGameRules->FlHEVChargerRechargeTime()) <= 0) + return; + } + + if (m_iReactivate <= 0) + m_iReactivate = iReactivate; + + pev->nextthink = pev->ltime + m_iReactivate; + SetThink(&CRecharge::Recharge); + } + else + { + SetThink(&CRecharge::SUB_DoNothing); + } +} diff --git a/regamedll/dlls/healthkit.cpp b/regamedll/dlls/healthkit.cpp index a1b57d31..5a49e483 100644 --- a/regamedll/dlls/healthkit.cpp +++ b/regamedll/dlls/healthkit.cpp @@ -1,244 +1,244 @@ -#include "precompiled.h" - -LINK_ENTITY_TO_CLASS(item_healthkit, CHealthKit, CCSHealthKit) - -void CHealthKit::Spawn() -{ - Precache(); - SET_MODEL(ENT(pev), "models/w_medkit.mdl"); - - CItem::Spawn(); -} - -void CHealthKit::Precache() -{ - PRECACHE_MODEL("models/w_medkit.mdl"); - PRECACHE_SOUND("items/smallmedkit1.wav"); -} - -BOOL CHealthKit::MyTouch(CBasePlayer *pPlayer) -{ -#ifdef REGAMEDLL_ADD - if (pPlayer->HasRestrictItem(ITEM_HEALTHKIT, ITEM_TYPE_TOUCHED)) - return FALSE; -#endif - - auto healthValue = gSkillData.healthkitCapacity; - -#ifdef REGAMEDLL_FIXES - if (pev->health != 0.0f) { - healthValue = pev->health; - } -#endif - - if (pPlayer->TakeHealth(healthValue, DMG_GENERIC)) - { - MESSAGE_BEGIN(MSG_ONE, gmsgItemPickup, nullptr, pPlayer->pev); - WRITE_STRING(pev->classname); - MESSAGE_END(); - - EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/smallmedkit1.wav", VOL_NORM, ATTN_NORM); - - if (g_pGameRules->ItemShouldRespawn(this)) - Respawn(); - else - UTIL_Remove(this); - - return TRUE; - } - - return FALSE; -} - -TYPEDESCRIPTION CWallHealth::m_SaveData[] = -{ - DEFINE_FIELD(CWallHealth, m_flNextCharge, FIELD_TIME), - DEFINE_FIELD(CWallHealth, m_iReactivate, FIELD_INTEGER), - DEFINE_FIELD(CWallHealth, m_iJuice, FIELD_INTEGER), - DEFINE_FIELD(CWallHealth, m_iOn, FIELD_INTEGER), - DEFINE_FIELD(CWallHealth, m_flSoundTime, FIELD_TIME), -}; - -IMPLEMENT_SAVERESTORE(CWallHealth, CBaseEntity) -LINK_ENTITY_TO_CLASS(func_healthcharger, CWallHealth, CCSWallHealth) - -void CWallHealth::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "style") || FStrEq(pkvd->szKeyName, "height") || FStrEq(pkvd->szKeyName, "value1") || FStrEq(pkvd->szKeyName, "value2") || FStrEq(pkvd->szKeyName, "value3")) - { - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "dmdelay")) - { - m_iReactivate = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseToggle::KeyValue(pkvd); - } -} - -void CWallHealth::Spawn() -{ - Precache(); - - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - // set size and link into world - UTIL_SetOrigin(pev, pev->origin); - UTIL_SetSize(pev, pev->mins, pev->maxs); - - SET_MODEL(ENT(pev), pev->model); - - int healthValue = (int)gSkillData.healthchargerCapacity; -#ifdef REGAMEDLL_FIXES - if (pev->health != 0.0f) { - healthValue = (int)pev->health; - } -#endif - - m_iJuice = healthValue; - pev->frame = 0.0f; -} - -#ifdef REGAMEDLL_FIXES -void CWallHealth::Restart() -{ - pev->solid = SOLID_BSP; - pev->movetype = MOVETYPE_PUSH; - - // set size and link into world - UTIL_SetOrigin(pev, pev->origin); - UTIL_SetSize(pev, pev->mins, pev->maxs); - - SET_MODEL(ENT(pev), pev->model); - - pev->nextthink = pev->ltime + 0.1f; - SetThink(&CWallHealth::Recharge); -} -#endif // #ifdef REGAMEDLL_FIXES - -void CWallHealth::Precache() -{ - PRECACHE_SOUND("items/medshot4.wav"); - PRECACHE_SOUND("items/medshotno1.wav"); - PRECACHE_SOUND("items/medcharge4.wav"); -} - -void CWallHealth::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - // Make sure that we have a caller - if (!pActivator) - return; - - // if it's not a player, ignore - if (!pActivator->IsPlayer()) - return; - - // if there is no juice left, turn it off - if (m_iJuice <= 0 -#ifdef REGAMEDLL_FIXES - && pev->frame != 1.0f // recharging... don't reset think -#endif - ) - { - pev->frame = 1.0f; - Off(); - } - - // if the player doesn't have the suit, or there is no juice left, make the deny noise - if (m_iJuice <= 0 || !(pActivator->pev->weapons & (1 << WEAPON_SUIT)) -#ifdef REGAMEDLL_FIXES - || !pActivator->CanTakeHealth(AMOUNT_CHARGE_HEALTH) // don't charge health if we can't more, prevent thinking entity -#endif // REGAMEDLL_FIXES - ) - { - if (gpGlobals->time >= m_flSoundTime) - { - m_flSoundTime = gpGlobals->time + 0.62f; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshotno1.wav", VOL_NORM, ATTN_NORM); - } - - return; - } - - pev->nextthink = pev->ltime + 0.25f; - SetThink(&CWallHealth::Off); - - // Time to recharge yet? - - if (m_flNextCharge >= gpGlobals->time) - return; - - // Play the on sound or the looping charging sound - if (!m_iOn) - { - m_iOn++; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", VOL_NORM, ATTN_NORM); - m_flSoundTime = gpGlobals->time + 0.56f; - } - - if (m_iOn == 1 && gpGlobals->time >= m_flSoundTime) - { - m_iOn++; - EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav", VOL_NORM, ATTN_NORM); - } - - // charge the player - if (pActivator->TakeHealth(AMOUNT_CHARGE_HEALTH, DMG_GENERIC)) - m_iJuice--; - - // govern the rate of charge - m_flNextCharge = gpGlobals->time + 0.1f; -} - -void CWallHealth::Recharge() -{ - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", VOL_NORM, ATTN_NORM); - - int healthValue = (int)gSkillData.healthchargerCapacity; -#ifdef REGAMEDLL_FIXES - if (pev->health != 0.0f) { - healthValue = (int)pev->health; - } -#endif - - m_iJuice = healthValue; - - pev->frame = 0.0f; - SetThink(&CWallHealth::SUB_DoNothing); -} - -void CWallHealth::Off() -{ - // Stop looping sound. - if (m_iOn > 1) - STOP_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav"); - - m_iOn = 0; - - if (!m_iJuice) - { - int iReactivate = m_iReactivate; - -#ifdef REGAMEDLL_FIXES - if (iReactivate <= 0) -#endif // #ifdef REGAMEDLL_FIXES - { - if ((iReactivate = g_pGameRules->FlHealthChargerRechargeTime()) <= 0) - return; - } - - if (m_iReactivate <= 0) - m_iReactivate = iReactivate; - - pev->nextthink = pev->ltime + m_iReactivate; - SetThink(&CWallHealth::Recharge); - } - else - { - SetThink(&CWallHealth::SUB_DoNothing); - } -} +#include "precompiled.h" + +LINK_ENTITY_TO_CLASS(item_healthkit, CHealthKit, CCSHealthKit) + +void CHealthKit::Spawn() +{ + Precache(); + SET_MODEL(ENT(pev), "models/w_medkit.mdl"); + + CItem::Spawn(); +} + +void CHealthKit::Precache() +{ + PRECACHE_MODEL("models/w_medkit.mdl"); + PRECACHE_SOUND("items/smallmedkit1.wav"); +} + +BOOL CHealthKit::MyTouch(CBasePlayer *pPlayer) +{ +#ifdef REGAMEDLL_ADD + if (pPlayer->HasRestrictItem(ITEM_HEALTHKIT, ITEM_TYPE_TOUCHED)) + return FALSE; +#endif + + auto healthValue = gSkillData.healthkitCapacity; + +#ifdef REGAMEDLL_FIXES + if (pev->health != 0.0f) { + healthValue = pev->health; + } +#endif + + if (pPlayer->TakeHealth(healthValue, DMG_GENERIC)) + { + MESSAGE_BEGIN(MSG_ONE, gmsgItemPickup, nullptr, pPlayer->pev); + WRITE_STRING(pev->classname); + MESSAGE_END(); + + EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/smallmedkit1.wav", VOL_NORM, ATTN_NORM); + + if (g_pGameRules->ItemShouldRespawn(this)) + Respawn(); + else + UTIL_Remove(this); + + return TRUE; + } + + return FALSE; +} + +TYPEDESCRIPTION CWallHealth::m_SaveData[] = +{ + DEFINE_FIELD(CWallHealth, m_flNextCharge, FIELD_TIME), + DEFINE_FIELD(CWallHealth, m_iReactivate, FIELD_INTEGER), + DEFINE_FIELD(CWallHealth, m_iJuice, FIELD_INTEGER), + DEFINE_FIELD(CWallHealth, m_iOn, FIELD_INTEGER), + DEFINE_FIELD(CWallHealth, m_flSoundTime, FIELD_TIME), +}; + +IMPLEMENT_SAVERESTORE(CWallHealth, CBaseEntity) +LINK_ENTITY_TO_CLASS(func_healthcharger, CWallHealth, CCSWallHealth) + +void CWallHealth::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "style") || FStrEq(pkvd->szKeyName, "height") || FStrEq(pkvd->szKeyName, "value1") || FStrEq(pkvd->szKeyName, "value2") || FStrEq(pkvd->szKeyName, "value3")) + { + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "dmdelay")) + { + m_iReactivate = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseToggle::KeyValue(pkvd); + } +} + +void CWallHealth::Spawn() +{ + Precache(); + + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + // set size and link into world + UTIL_SetOrigin(pev, pev->origin); + UTIL_SetSize(pev, pev->mins, pev->maxs); + + SET_MODEL(ENT(pev), pev->model); + + int healthValue = (int)gSkillData.healthchargerCapacity; +#ifdef REGAMEDLL_FIXES + if (pev->health != 0.0f) { + healthValue = (int)pev->health; + } +#endif + + m_iJuice = healthValue; + pev->frame = 0.0f; +} + +#ifdef REGAMEDLL_FIXES +void CWallHealth::Restart() +{ + pev->solid = SOLID_BSP; + pev->movetype = MOVETYPE_PUSH; + + // set size and link into world + UTIL_SetOrigin(pev, pev->origin); + UTIL_SetSize(pev, pev->mins, pev->maxs); + + SET_MODEL(ENT(pev), pev->model); + + pev->nextthink = pev->ltime + 0.1f; + SetThink(&CWallHealth::Recharge); +} +#endif // #ifdef REGAMEDLL_FIXES + +void CWallHealth::Precache() +{ + PRECACHE_SOUND("items/medshot4.wav"); + PRECACHE_SOUND("items/medshotno1.wav"); + PRECACHE_SOUND("items/medcharge4.wav"); +} + +void CWallHealth::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + // Make sure that we have a caller + if (!pActivator) + return; + + // if it's not a player, ignore + if (!pActivator->IsPlayer()) + return; + + // if there is no juice left, turn it off + if (m_iJuice <= 0 +#ifdef REGAMEDLL_FIXES + && pev->frame != 1.0f // recharging... don't reset think +#endif + ) + { + pev->frame = 1.0f; + Off(); + } + + // if the player doesn't have the suit, or there is no juice left, make the deny noise + if (m_iJuice <= 0 || !(pActivator->pev->weapons & (1 << WEAPON_SUIT)) +#ifdef REGAMEDLL_FIXES + || !pActivator->CanTakeHealth(AMOUNT_CHARGE_HEALTH) // don't charge health if we can't more, prevent thinking entity +#endif // REGAMEDLL_FIXES + ) + { + if (gpGlobals->time >= m_flSoundTime) + { + m_flSoundTime = gpGlobals->time + 0.62f; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshotno1.wav", VOL_NORM, ATTN_NORM); + } + + return; + } + + pev->nextthink = pev->ltime + 0.25f; + SetThink(&CWallHealth::Off); + + // Time to recharge yet? + + if (m_flNextCharge >= gpGlobals->time) + return; + + // Play the on sound or the looping charging sound + if (!m_iOn) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", VOL_NORM, ATTN_NORM); + m_flSoundTime = gpGlobals->time + 0.56f; + } + + if (m_iOn == 1 && gpGlobals->time >= m_flSoundTime) + { + m_iOn++; + EMIT_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav", VOL_NORM, ATTN_NORM); + } + + // charge the player + if (pActivator->TakeHealth(AMOUNT_CHARGE_HEALTH, DMG_GENERIC)) + m_iJuice--; + + // govern the rate of charge + m_flNextCharge = gpGlobals->time + 0.1f; +} + +void CWallHealth::Recharge() +{ + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/medshot4.wav", VOL_NORM, ATTN_NORM); + + int healthValue = (int)gSkillData.healthchargerCapacity; +#ifdef REGAMEDLL_FIXES + if (pev->health != 0.0f) { + healthValue = (int)pev->health; + } +#endif + + m_iJuice = healthValue; + + pev->frame = 0.0f; + SetThink(&CWallHealth::SUB_DoNothing); +} + +void CWallHealth::Off() +{ + // Stop looping sound. + if (m_iOn > 1) + STOP_SOUND(ENT(pev), CHAN_STATIC, "items/medcharge4.wav"); + + m_iOn = 0; + + if (!m_iJuice) + { + int iReactivate = m_iReactivate; + +#ifdef REGAMEDLL_FIXES + if (iReactivate <= 0) +#endif // #ifdef REGAMEDLL_FIXES + { + if ((iReactivate = g_pGameRules->FlHealthChargerRechargeTime()) <= 0) + return; + } + + if (m_iReactivate <= 0) + m_iReactivate = iReactivate; + + pev->nextthink = pev->ltime + m_iReactivate; + SetThink(&CWallHealth::Recharge); + } + else + { + SetThink(&CWallHealth::SUB_DoNothing); + } +} diff --git a/regamedll/dlls/player.h b/regamedll/dlls/player.h index 88b47585..8624511d 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -1,983 +1,983 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#include "weapons.h" -#include "pm_materials.h" -#include "hintmessage.h" -#include "unisignals.h" - -#define SOUND_FLASHLIGHT_ON "items/flashlight1.wav" -#define SOUND_FLASHLIGHT_OFF "items/flashlight1.wav" - -#ifdef REGAMEDLL_FIXES -const int MIN_BUY_TIME = 0; -#else -const int MIN_BUY_TIME = 15; // the minimum threshold values for cvar mp_buytime 15 sec's -#endif - -const int MAX_PLAYER_NAME_LENGTH = 32; -const int MAX_AUTOBUY_LENGTH = 256; -const int MAX_REBUY_LENGTH = 256; - -const int MAX_RECENT_PATH = 20; -const int MAX_HOSTAGE_ICON = 4; // the maximum number of icons of the hostages in the HUD - -const int MAX_SUIT_NOREPEAT = 32; -const int MAX_SUIT_PLAYLIST = 4; // max of 4 suit sentences queued up at any time - -const int MAX_BUFFER_MENU = 175; -const int MAX_BUFFER_MENU_BRIEFING = 50; - -const float SUIT_UPDATE_TIME = 3.5f; -const float SUIT_FIRST_UPDATE_TIME = 0.1f; - -const float MAX_PLAYER_FATAL_FALL_SPEED = 1100.0f; -const float MAX_PLAYER_SAFE_FALL_SPEED = 500.0f; -const float MAX_PLAYER_USE_RADIUS = 64.0f; - -const float ARMOR_RATIO = 0.5f; // Armor Takes 50% of the damage -const float ARMOR_BONUS = 0.5f; // Each Point of Armor is work 1/x points of health - -const float FLASH_DRAIN_TIME = 1.2f; // 100 units/3 minutes -const float FLASH_CHARGE_TIME = 0.2f; // 100 units/20 seconds (seconds per unit) - -// damage per unit per second. -const float DAMAGE_FOR_FALL_SPEED = 100.0f / (MAX_PLAYER_FATAL_FALL_SPEED - MAX_PLAYER_SAFE_FALL_SPEED); -const float PLAYER_MIN_BOUNCE_SPEED = 350.0f; - -// won't punch player's screen/make scrape noise unless player falling at least this fast. -const float PLAYER_FALL_PUNCH_THRESHHOLD = 250.0f; - -// Money blinks few of times on the freeze period -// NOTE: It works for CZ -const int MONEY_BLINK_AMOUNT = 30; - -// Player time based damage -#define AIRTIME 12 // lung full of air lasts this many seconds -#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage -#define PARALYZE_DAMAGE 1.0f // damage to take each 2 second interval - -#define NERVEGAS_DURATION 2 -#define NERVEGAS_DAMAGE 5.0f - -#define POISON_DURATION 5 -#define POISON_DAMAGE 2.0f - -#define RADIATION_DURATION 2 -#define RADIATION_DAMAGE 1.0f - -#define ACID_DURATION 2 -#define ACID_DAMAGE 5.0f - -#define SLOWBURN_DURATION 2 -#define SLOWBURN_DAMAGE 1.0f - -#define SLOWFREEZE_DURATION 2 -#define SLOWFREEZE_DAMAGE 1.0f - -// Player physics flags bits -// CBasePlayer::m_afPhysicsFlags -#define PFLAG_ONLADDER BIT(0) -#define PFLAG_ONSWING BIT(0) -#define PFLAG_ONTRAIN BIT(1) -#define PFLAG_ONBARNACLE BIT(2) -#define PFLAG_DUCKING BIT(3) // In the process of ducking, but not totally squatted yet -#define PFLAG_USING BIT(4) // Using a continuous entity -#define PFLAG_OBSERVER BIT(5) // Player is locked in stationary cam mode. Spectators can move, observers can't. - -#define TRAIN_OFF 0x00 -#define TRAIN_NEUTRAL 0x01 -#define TRAIN_SLOW 0x02 -#define TRAIN_MEDIUM 0x03 -#define TRAIN_FAST 0x04 -#define TRAIN_BACK 0x05 - -#define TRAIN_ACTIVE 0x80 -#define TRAIN_NEW 0xc0 - -const bool SUIT_GROUP = true; -const bool SUIT_SENTENCE = false; - -const int SUIT_REPEAT_OK = 0; -const int SUIT_NEXT_IN_30SEC = 30; -const int SUIT_NEXT_IN_1MIN = 60; -const int SUIT_NEXT_IN_5MIN = 300; -const int SUIT_NEXT_IN_10MIN = 600; -const int SUIT_NEXT_IN_30MIN = 1800; -const int SUIT_NEXT_IN_1HOUR = 3600; - -const int MAX_TEAM_NAME_LENGTH = 16; - -const auto AUTOAIM_2DEGREES = 0.0348994967025; -const auto AUTOAIM_5DEGREES = 0.08715574274766; -const auto AUTOAIM_8DEGREES = 0.1391731009601; -const auto AUTOAIM_10DEGREES = 0.1736481776669; - -// custom enum -enum RewardType -{ - RT_NONE, - RT_ROUND_BONUS, - RT_PLAYER_RESET, - RT_PLAYER_JOIN, - RT_PLAYER_SPEC_JOIN, - RT_PLAYER_BOUGHT_SOMETHING, - RT_HOSTAGE_TOOK, - RT_HOSTAGE_RESCUED, - RT_HOSTAGE_DAMAGED, - RT_HOSTAGE_KILLED, - RT_TEAMMATES_KILLED, - RT_ENEMY_KILLED, - RT_INTO_GAME, - RT_VIP_KILLED, - RT_VIP_RESCUED_MYSELF -}; - -enum PLAYER_ANIM -{ - PLAYER_IDLE, - PLAYER_WALK, - PLAYER_JUMP, - PLAYER_SUPERJUMP, - PLAYER_DIE, - PLAYER_ATTACK1, - PLAYER_ATTACK2, - PLAYER_FLINCH, - PLAYER_LARGE_FLINCH, - PLAYER_RELOAD, - PLAYER_HOLDBOMB -}; - -enum _Menu -{ - Menu_OFF, - Menu_ChooseTeam, - Menu_IGChooseTeam, - Menu_ChooseAppearance, - Menu_Buy, - Menu_BuyPistol, - Menu_BuyRifle, - Menu_BuyMachineGun, - Menu_BuyShotgun, - Menu_BuySubMachineGun, - Menu_BuyItem, - Menu_Radio1, - Menu_Radio2, - Menu_Radio3, - Menu_ClientBuy -}; - -enum TeamName -{ - UNASSIGNED, - TERRORIST, - CT, - SPECTATOR, -}; - -enum ModelName -{ - MODEL_UNASSIGNED, - MODEL_URBAN, - MODEL_TERROR, - MODEL_LEET, - MODEL_ARCTIC, - MODEL_GSG9, - MODEL_GIGN, - MODEL_SAS, - MODEL_GUERILLA, - MODEL_VIP, - MODEL_MILITIA, - MODEL_SPETSNAZ, - MODEL_AUTO -}; - -enum JoinState -{ - JOINED, - SHOWLTEXT, - READINGLTEXT, - SHOWTEAMSELECT, - PICKINGTEAM, - GETINTOGAME -}; - -enum TrackCommands -{ - CMD_SAY = 0, - CMD_SAYTEAM, - CMD_FULLUPDATE, - CMD_VOTE, - CMD_VOTEMAP, - CMD_LISTMAPS, - CMD_LISTPLAYERS, - CMD_NIGHTVISION, - COMMANDS_TO_TRACK, -}; - -enum IgnoreChatMsg : int -{ - IGNOREMSG_NONE, // Nothing to do - IGNOREMSG_ENEMY, // To ignore any chat messages from the enemy - IGNOREMSG_TEAM // Same as IGNOREMSG_ENEMY but ignore teammates -}; - -struct RebuyStruct -{ - int m_primaryWeapon; - int m_primaryAmmo; - int m_secondaryWeapon; - int m_secondaryAmmo; - int m_heGrenade; - int m_flashbang; - int m_smokeGrenade; - int m_defuser; - int m_nightVision; - ArmorType m_armor; -}; - -enum ThrowDirection -{ - THROW_NONE, - THROW_FORWARD, - THROW_BACKWARD, - THROW_HITVEL, - THROW_BOMB, - THROW_GRENADE, - THROW_HITVEL_MINUS_AIRVEL -}; - -const float MAX_ID_RANGE = 2048.0f; -const float MAX_SPEC_ID_RANGE = 8192.0f; -const int MAX_SBAR_STRING = 128; - -const int SBAR_TARGETTYPE_TEAMMATE = 1; -const int SBAR_TARGETTYPE_ENEMY = 2; -const int SBAR_TARGETTYPE_HOSTAGE = 3; - -enum sbar_data -{ - SBAR_ID_TARGETTYPE = 1, - SBAR_ID_TARGETNAME, - SBAR_ID_TARGETHEALTH, - SBAR_END -}; - -enum MusicState { SILENT, CALM, INTENSE }; - -class CCSPlayer; - -class CStripWeapons: public CPointEntity { -public: - virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); -}; - -// Dead HEV suit prop -class CDeadHEV: public CBaseMonster { -public: - virtual void Spawn(); - virtual void KeyValue(KeyValueData *pkvd); - virtual int Classify() { return CLASS_HUMAN_MILITARY; } - -public: - int m_iPose; // which sequence to display -- temporary, don't need to save - static const char *m_szPoses[]; -}; - -class CSprayCan: public CBaseEntity { -public: - virtual void Think(); - virtual int ObjectCaps() { return FCAP_DONT_SAVE; } - -public: - void Spawn(entvars_t *pevOwner); -}; - -class CBloodSplat: public CBaseEntity { -public: - void Spawn(entvars_t *pevOwner); - void Spray(); -}; - -class CBasePlayer: public CBaseMonster { -public: - virtual void Spawn(); - virtual void Precache(); - virtual int Save(CSave &save); - virtual int Restore(CRestore &restore); - virtual int ObjectCaps(); - virtual int Classify(); - virtual void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - virtual BOOL TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - virtual BOOL TakeHealth(float flHealth, int bitsDamageType); - virtual void Killed(entvars_t *pevAttacker, int iGib); - virtual void AddPoints(int score, BOOL bAllowNegativeScore); - virtual void AddPointsToTeam(int score, BOOL bAllowNegativeScore); - virtual BOOL AddPlayerItem(CBasePlayerItem *pItem); - virtual BOOL RemovePlayerItem(CBasePlayerItem *pItem); - virtual int GiveAmmo(int iAmount, const char *szName, int iMax = -1); -#ifndef REGAMEDLL_API - virtual void StartSneaking() { m_tSneaking = gpGlobals->time - 1; } - virtual void StopSneaking() { m_tSneaking = gpGlobals->time + 30; } -#else - virtual void OnCreate(); - virtual void OnDestroy(); - void StartSneaking() { m_tSneaking = gpGlobals->time - 1; }; -#endif - - virtual BOOL IsSneaking() { return m_tSneaking <= gpGlobals->time; } - virtual BOOL IsAlive() { return (pev->deadflag == DEAD_NO && pev->health > 0.0f); } - virtual BOOL IsPlayer() { return (pev->flags & FL_SPECTATOR) != FL_SPECTATOR; } - virtual BOOL IsNetClient() { return TRUE; } - virtual const char *TeamID(); - virtual BOOL FBecomeProne(); - - // TODO: Need to investigate for what purposes used random to get relative eyes position - virtual Vector BodyTarget(const Vector &posSrc) { return Center() + pev->view_ofs * RANDOM_FLOAT(0.5, 1.1); } - virtual int Illumination(); - virtual BOOL ShouldFadeOnDeath() { return FALSE; } - virtual void ResetMaxSpeed(); - virtual void Jump(); - virtual void Duck(); - virtual void PreThink(); - virtual void PostThink(); - virtual Vector GetGunPosition(); - virtual BOOL IsBot() { return FALSE; } - virtual void UpdateClientData(); - virtual void ImpulseCommands(); - virtual void RoundRespawn(); - virtual Vector GetAutoaimVector(float flDelta); - virtual void Blind(float flUntilTime, float flHoldTime, float flFadeTime, int iAlpha); - virtual void OnTouchingWeapon(CWeaponBox *pWeapon) { } - -#ifdef REGAMEDLL_API - void Spawn_OrigFunc(); - void Precache_OrigFunc(); - int ObjectCaps_OrigFunc(); - int Classify_OrigFunc(); - void TraceAttack_OrigFunc(entvars_t *pevAttacker, float flDamage, VectorRef vecDir, TraceResult *ptr, int bitsDamageType); - BOOL TakeDamage_OrigFunc(entvars_t *pevInflictor, entvars_t *pevAttacker, FloatRef flDamage, int bitsDamageType); - BOOL TakeHealth_OrigFunc(float flHealth, int bitsDamageType); - void Killed_OrigFunc(entvars_t *pevAttacker, int iGib); - void AddPoints_OrigFunc(int score, BOOL bAllowNegativeScore); - void AddPointsToTeam_OrigFunc(int score, BOOL bAllowNegativeScore); - BOOL AddPlayerItem_OrigFunc(CBasePlayerItem *pItem); - BOOL RemovePlayerItem_OrigFunc(CBasePlayerItem *pItem); - int GiveAmmo_OrigFunc(int iAmount, const char *szName, int iMax); - void ResetMaxSpeed_OrigFunc(); - void Jump_OrigFunc(); - void Duck_OrigFunc(); - void PreThink_OrigFunc(); - void PostThink_OrigFunc(); - void UpdateClientData_OrigFunc(); - void ImpulseCommands_OrigFunc(); - void RoundRespawn_OrigFunc(); - void Blind_OrigFunc(float flUntilTime, float flHoldTime, float flFadeTime, int iAlpha); - EXT_FUNC CBasePlayer *Observer_IsValidTarget_OrigFunc(int iPlayerIndex, bool bSameTeam); - void Radio_OrigFunc(const char *msg_id, const char *msg_verbose = nullptr, short pitch = 100, bool showIcon = true); - void AddAccount_OrigFunc(int amount, RewardType type = RT_NONE, bool bTrackChange = true); - void Disappear_OrigFunc(); - void MakeVIP_OrigFunc(); - void GiveDefaultItems_OrigFunc(); - bool SetClientUserInfoName_OrigFunc(char *infobuffer, char *szNewName); - void SetAnimation_OrigFunc(PLAYER_ANIM playerAnim); - void StartObserver_OrigFunc(Vector &vecPosition, Vector &vecViewAngle); - CBaseEntity *DropPlayerItem_OrigFunc(const char *pszItemName); - CBaseEntity *GiveNamedItem_OrigFunc(const char *pszName); - CBaseEntity *DropShield_OrigFunc(bool bDeploy = true); - void GiveShield_OrigFunc(bool bDeploy = true); - bool HasRestrictItem_OrigFunc(ItemID item, ItemRestType type); - void OnSpawnEquip_OrigFunc(bool addDefault = true, bool equipGame = true); - bool MakeBomber_OrigFunc(); - bool GetIntoGame_OrigFunc(); - void StartDeathCam_OrigFunc(); - CGrenade *ThrowGrenade_OrigFunc(CBasePlayerWeapon *pWeapon, VectorRef vecSrc, VectorRef vecThrow, float time, unsigned short usEvent = 0); - void SwitchTeam_OrigFunc(); - bool CanSwitchTeam_OrigFunc(TeamName teamToSwap); - void SetSpawnProtection_OrigFunc(float flProtectionTime); - void RemoveSpawnProtection_OrigFunc(); - bool HintMessageEx_OrigFunc(const char *pMessage, float duration = 6.0f, bool bDisplayIfPlayerDead = false, bool bOverride = false); - void UseEmpty_OrigFunc(); - - CCSPlayer *CSPlayer() const; -#endif // REGAMEDLL_API - -public: - static CBasePlayer *Instance(edict_t *pEdict) { return GET_PRIVATE(pEdict ? pEdict : ENT(0)); } - static CBasePlayer *Instance(entvars_t *pev) { return Instance(ENT(pev)); } - static CBasePlayer *Instance(int offset) { return Instance(ENT(offset)); } - - void SpawnClientSideCorpse(); - void Observer_FindNextPlayer(bool bReverse, const char *name = nullptr); - CBasePlayer *Observer_IsValidTarget(int iPlayerIndex, bool bSameTeam); - void Disconnect(); - void Observer_Think(); - void Observer_HandleButtons(); - void Observer_SetMode(int iMode); - void Observer_CheckTarget(); - void Observer_CheckProperties(); - int GetObserverMode() { return pev->iuser1; } - void PlantC4(); - void Radio(const char *msg_id, const char *msg_verbose = nullptr, short pitch = 100, bool showIcon = true); - CBasePlayer *GetNextRadioRecipient(CBasePlayer *pStartPlayer); - void SmartRadio(); - void ThrowWeapon(char *pszItemName); - void ThrowPrimary(); - CGrenade *ThrowGrenade(CBasePlayerWeapon *pWeapon, Vector vecSrc, Vector vecThrow, float time, unsigned short usEvent = 0); - void AddAccount(int amount, RewardType type = RT_NONE, bool bTrackChange = true); - void Disappear(); - void MakeVIP(); - bool CanPlayerBuy(bool display = false); - bool CanSwitchTeam(TeamName teamToSwap); - void SwitchTeam(); - void TabulateAmmo(); - void Pain(int iLastHitGroup, bool bHasArmour); - BOOL IsBombGuy(); - bool IsLookingAtPosition(Vector *pos, float angleTolerance = 20.0f); - void Reset(); - void SetScoreboardAttributes(CBasePlayer *destination = nullptr); - void RenewItems(); - void PackDeadPlayerItems(); - void GiveDefaultItems(); - void RemoveAllItems(BOOL removeSuit); - void SetBombIcon(BOOL bFlash = FALSE); - void SetProgressBarTime(int time); - void SetProgressBarTime2(int time, float timeElapsed); - void SetPlayerModel(BOOL HasC4); - bool SetClientUserInfoName(char *infobuffer, char *szNewName); - void SetClientUserInfoModel(char *infobuffer, char *szNewModel); - void SetClientUserInfoModel_api(char *infobuffer, char *szNewModel); - void SetNewPlayerModel(const char *modelName); - BOOL SwitchWeapon(CBasePlayerItem *pWeapon); - void CheckPowerups(); - bool CanAffordPrimary(); - bool CanAffordPrimaryAmmo(); - bool CanAffordSecondaryAmmo(); - bool CanAffordArmor(); - bool CanAffordDefuseKit(); - bool CanAffordGrenade(); - bool NeedsPrimaryAmmo(); - bool NeedsSecondaryAmmo(); - bool NeedsArmor(); - bool NeedsDefuseKit(); - bool NeedsGrenade(); - BOOL IsOnLadder(); - BOOL FlashlightIsOn(); - void FlashlightTurnOn(); - void FlashlightTurnOff(); - void UpdatePlayerSound(); - void DeathSound(); - void SetAnimation(PLAYER_ANIM playerAnim); - void SetWeaponAnimType(const char *szExtention) { Q_strcpy(m_szAnimExtention, szExtention); } - void CheatImpulseCommands(int iImpulse); - void StartDeathCam(); - void StartObserver(Vector &vecPosition, Vector &vecViewAngle); - void HandleSignals(); - CBaseEntity *DropPlayerItem(const char *pszItemName); - bool HasPlayerItem(CBasePlayerItem *pCheckItem); - bool HasNamedPlayerItem(const char *pszItemName); - bool HasWeapons(); - void SelectPrevItem(int iItem); - void SelectNextItem(int iItem); - void SelectLastItem(); - void SelectItem(const char *pstr); - void ItemPreFrame(); - void ItemPostFrame(); - CBaseEntity *GiveNamedItem(const char *pszName); - CBaseEntity *GiveNamedItemEx(const char *pszName); - void EnableControl(BOOL fControl); - bool HintMessage(const char *pMessage, BOOL bDisplayIfPlayerDead = FALSE, BOOL bOverride = FALSE); - bool HintMessageEx(const char *pMessage, float duration = 6.0f, bool bDisplayIfPlayerDead = false, bool bOverride = false); - void SendAmmoUpdate(); - void SendFOV(int fov); - void WaterMove(); - void EXPORT PlayerDeathThink(); - void PlayerRespawnThink(); - void PlayerUse(); - void HostageUsed(); - void JoiningThink(); - void RemoveLevelText(); - void MenuPrint(const char *msg); - void ResetMenu(); - void SyncRoundTimer(); - void CheckSuitUpdate(); - void SetSuitUpdate(char *name = nullptr, bool group = SUIT_SENTENCE, int iNoRepeatTime = SUIT_REPEAT_OK); - void UpdateGeigerCounter(); - void CheckTimeBasedDamage(); - void BarnacleVictimBitten(entvars_t *pevBarnacle); - void BarnacleVictimReleased(); - static int GetAmmoIndex(const char *psz); - int AmmoInventory(int iAmmoIndex); - void ResetAutoaim(); - Vector AutoaimDeflection(Vector &vecSrc, float flDist, float flDelta); - void ForceClientDllUpdate(); - void DeathMessage(entvars_t *pevAttacker) {}; - void SetCustomDecalFrames(int nFrames); - int GetCustomDecalFrames(); - void InitStatusBar(); - void UpdateStatusBar(); - void StudioEstimateGait(); - void StudioPlayerBlend(int *pBlend, float *pPitch); - void CalculatePitchBlend(); - void CalculateYawBlend(); - void StudioProcessGait(); - void SendHostagePos(); - void SendHostageIcons(); - void ResetStamina(); - BOOL IsArmored(int nHitGroup); - BOOL ShouldDoLargeFlinch(int nHitGroup, int nGunType); - void SetPrefsFromUserinfo(char *infobuffer); - void SendWeatherInfo(); - void UpdateShieldCrosshair(bool draw); - bool HasShield(); - bool IsProtectedByShield() { return HasShield() && m_bShieldDrawn; } - void RemoveShield(); - CBaseEntity *DropShield(bool bDeploy = true); - void GiveShield(bool bDeploy = true); - bool IsHittingShield(Vector &vecDirection, TraceResult *ptr); - bool SelectSpawnSpot(const char *pEntClassName, CBaseEntity* &pSpot); - bool IsReloading() const; - bool IsBlind() const { return (m_blindUntilTime > gpGlobals->time); } - bool IsAutoFollowAllowed() const { return (gpGlobals->time > m_allowAutoFollowTime); } - void InhibitAutoFollow(float duration) { m_allowAutoFollowTime = gpGlobals->time + duration; } - void AllowAutoFollow() { m_allowAutoFollowTime = 0; } - void ClearAutoBuyData(); - void AddAutoBuyData(const char *str); - void AutoBuy(); - void ClientCommand(const char *cmd, const char *arg1 = nullptr, const char *arg2 = nullptr, const char *arg3 = nullptr); - void PrioritizeAutoBuyString(char *autobuyString, const char *priorityString); - const char *PickPrimaryCareerTaskWeapon(); - const char *PickSecondaryCareerTaskWeapon(); - const char *PickFlashKillWeaponString(); - const char *PickGrenadeKillWeaponString(); - bool ShouldExecuteAutoBuyCommand(AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary); - void PostAutoBuyCommandProcessing(AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary); - void ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary); - AutoBuyInfoStruct *GetAutoBuyCommandInfo(const char *command); - void InitRebuyData(const char *str); - void BuildRebuyStruct(); - void Rebuy(); - void RebuyPrimaryWeapon(); - void RebuyPrimaryAmmo(); - void RebuySecondaryWeapon(); - void RebuySecondaryAmmo(); - void RebuyHEGrenade(); - void RebuyFlashbang(); - void RebuySmokeGrenade(); - void RebuyDefuser(); - void RebuyNightVision(); - void RebuyArmor(); - void UpdateLocation(bool forceUpdate = false); - void SetObserverAutoDirector(bool val) { m_bObserverAutoDirector = val; } - bool IsObservingPlayer(CBasePlayer *pPlayer); - bool CanSwitchObserverModes() const { return m_canSwitchObserverModes; } - void SendItemStatus(); - edict_t *EntSelectSpawnPoint(); - void SetScoreAttrib(CBasePlayer *dest); - void ReloadWeapons(CBasePlayerItem *pWeapon = nullptr, bool bForceReload = false, bool bForceRefill = false); - void TeamChangeUpdate(); - bool HasRestrictItem(ItemID item, ItemRestType type); - void DropSecondary(); - void DropPrimary(); - void OnSpawnEquip(bool addDefault = true, bool equipGame = true); - void RemoveBomb(); - void RemoveDefuser(); - void HideTimer(); - bool MakeBomber(); - bool GetIntoGame(); - bool ShouldToShowAccount(CBasePlayer *pReceiver) const; - bool ShouldToShowHealthInfo(CBasePlayer *pReceiver) const; - - CBasePlayerItem *GetItemByName(const char *itemName); - CBasePlayerItem *GetItemById(WeaponIdType weaponID); - - void SetSpawnProtection(float flProtectionTime); - void RemoveSpawnProtection(); - void UseEmpty(); - - // templates - template - T *ForEachItem(int slot, const Functor &func) const - { - auto item = m_rgpPlayerItems[slot]; - while (item) - { - if (func(static_cast(item))) - return static_cast(item); - - item = item->m_pNext; - } - - return nullptr; - } - - template - T *ForEachItem(const Functor &func) const - { - for (auto item : m_rgpPlayerItems) - { - while (item) - { - if (func(static_cast(item))) - return static_cast(item); - - item = item->m_pNext; - } - } - - return nullptr; - } - - template - T *ForEachItem(const char *pszItemName, const Functor &func) const - { - if (!pszItemName) { - return nullptr; - } - - for (auto item : m_rgpPlayerItems) - { - while (item) - { - if (FClassnameIs(item->pev, pszItemName) && func(static_cast(item))) { - return static_cast(item); - } - - item = item->m_pNext; - } - } - - return nullptr; - } - -public: - enum { MaxLocationLen = 32 }; - - int random_seed; - unsigned short m_usPlayerBleed; - EntityHandle m_hObserverTarget; - float m_flNextObserverInput; - int m_iObserverWeapon; - int m_iObserverC4State; - bool m_bObserverHasDefuser; - int m_iObserverLastMode; - float m_flFlinchTime; - float m_flAnimTime; - bool m_bHighDamage; - float m_flVelocityModifier; - int m_iLastZoom; - bool m_bResumeZoom; - float m_flEjectBrass; - ArmorType m_iKevlar; - bool m_bNotKilled; - TeamName m_iTeam; - int m_iAccount; - bool m_bHasPrimary; - float m_flDeathThrowTime; - int m_iThrowDirection; - float m_flLastTalk; - bool m_bJustConnected; - bool m_bContextHelp; - JoinState m_iJoiningState; - CBaseEntity *m_pIntroCamera; - float m_fIntroCamTime; - float m_fLastMovement; - bool m_bMissionBriefing; - bool m_bTeamChanged; - ModelName m_iModelName; - int m_iTeamKills; - IgnoreChatMsg m_iIgnoreGlobalChat; - bool m_bHasNightVision; - bool m_bNightVisionOn; - Vector m_vRecentPath[MAX_RECENT_PATH]; - float m_flIdleCheckTime; - float m_flRadioTime; - int m_iRadioMessages; - bool m_bIgnoreRadio; - bool m_bHasC4; - bool m_bHasDefuser; - bool m_bKilledByBomb; - Vector m_vBlastVector; - bool m_bKilledByGrenade; - CHintMessageQueue m_hintMessageQueue; - int m_flDisplayHistory; - _Menu m_iMenu; - int m_iChaseTarget; - CBaseEntity *m_pChaseTarget; - float m_fCamSwitch; - bool m_bEscaped; - bool m_bIsVIP; - float m_tmNextRadarUpdate; - Vector m_vLastOrigin; - int m_iCurrentKickVote; - float m_flNextVoteTime; - bool m_bJustKilledTeammate; - int m_iHostagesKilled; - int m_iMapVote; - bool m_bCanShoot; - float m_flLastFired; - float m_flLastAttackedTeammate; - bool m_bHeadshotKilled; - bool m_bPunishedForTK; - bool m_bReceivesNoMoneyNextRound; - int m_iTimeCheckAllowed; - bool m_bHasChangedName; - char m_szNewName[MAX_PLAYER_NAME_LENGTH]; - bool m_bIsDefusing; - float m_tmHandleSignals; - CUnifiedSignals m_signals; - edict_t *m_pentCurBombTarget; - int m_iPlayerSound; - int m_iTargetVolume; - int m_iWeaponVolume; - int m_iExtraSoundTypes; - int m_iWeaponFlash; - float m_flStopExtraSoundTime; - float m_flFlashLightTime; - int m_iFlashBattery; - int m_afButtonLast; - int m_afButtonPressed; - int m_afButtonReleased; - edict_t *m_pentSndLast; - float m_flSndRoomtype; - float m_flSndRange; - float m_flFallVelocity; - int m_rgItems[MAX_ITEMS]; - int m_fNewAmmo; - unsigned int m_afPhysicsFlags; - float m_fNextSuicideTime; - float m_flTimeStepSound; - float m_flTimeWeaponIdle; - float m_flSwimTime; - float m_flDuckTime; - float m_flWallJumpTime; - float m_flSuitUpdate; - int m_rgSuitPlayList[MAX_SUIT_PLAYLIST]; - int m_iSuitPlayNext; - int m_rgiSuitNoRepeat[MAX_SUIT_NOREPEAT]; - float m_rgflSuitNoRepeatTime[MAX_SUIT_NOREPEAT]; - int m_lastDamageAmount; - float m_tbdPrev; - float m_flgeigerRange; - float m_flgeigerDelay; - int m_igeigerRangePrev; - int m_iStepLeft; - char m_szTextureName[MAX_TEXTURENAME_LENGHT]; - char m_chTextureType; - int m_idrowndmg; - int m_idrownrestored; - int m_bitsHUDDamage; - BOOL m_fInitHUD; - BOOL m_fGameHUDInitialized; - int m_iTrain; - BOOL m_fWeapon; - EHANDLE m_pTank; - float m_fDeadTime; - BOOL m_fNoPlayerSound; - BOOL m_fLongJump; - float m_tSneaking; - int m_iUpdateTime; - int m_iClientHealth; - int m_iClientBattery; - int m_iHideHUD; - int m_iClientHideHUD; - int m_iFOV; - int m_iClientFOV; - int m_iNumSpawns; - CBaseEntity *m_pObserver; - CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES]; - CBasePlayerItem *m_pActiveItem; - CBasePlayerItem *m_pClientActiveItem; - CBasePlayerItem *m_pLastItem; - int m_rgAmmo[MAX_AMMO_SLOTS]; - int m_rgAmmoLast[MAX_AMMO_SLOTS]; - Vector m_vecAutoAim; - BOOL m_fOnTarget; - int m_iDeaths; - int m_izSBarState[SBAR_END]; - float m_flNextSBarUpdateTime; - float m_flStatusBarDisappearDelay; - char m_SbarString0[MAX_SBAR_STRING]; - int m_lastx; - int m_lasty; - int m_nCustomSprayFrames; - float m_flNextDecalTime; - char m_szTeamName[MAX_TEAM_NAME_LENGTH]; - - static TYPEDESCRIPTION m_playerSaveData[]; - -/*protected:*/ - int m_modelIndexPlayer; - char m_szAnimExtention[32]; - int m_iGaitsequence; - - float m_flGaitframe; - float m_flGaityaw; - Vector m_prevgaitorigin; - float m_flPitch; - float m_flYaw; - float m_flGaitMovement; - int m_iAutoWepSwitch; - bool m_bVGUIMenus; - bool m_bShowHints; - bool m_bShieldDrawn; - bool m_bOwnsShield; - bool m_bWasFollowing; - float m_flNextFollowTime; - float m_flYawModifier; - float m_blindUntilTime; - float m_blindStartTime; - float m_blindHoldTime; - float m_blindFadeTime; - int m_blindAlpha; - float m_allowAutoFollowTime; - char m_autoBuyString[MAX_AUTOBUY_LENGTH]; - char *m_rebuyString; - RebuyStruct m_rebuyStruct; - bool m_bIsInRebuy; - float m_flLastUpdateTime; - char m_lastLocation[MaxLocationLen]; - float m_progressStart; - float m_progressEnd; - bool m_bObserverAutoDirector; - bool m_canSwitchObserverModes; - float m_heartBeatTime; - float m_intenseTimestamp; - float m_silentTimestamp; - MusicState m_musicState; - float m_flLastCommandTime[COMMANDS_TO_TRACK]; - -#ifdef BUILD_LATEST - int m_iLastAccount; - int m_iLastClientHealth; - float m_tmNextAccountHealthUpdate; -#endif -}; - -class CWShield: public CBaseEntity -{ -public: - virtual void Spawn(); - virtual void EXPORT Touch(CBaseEntity *pOther); - -public: - void SetCantBePickedUpByUser(CBasePlayer *pPlayer, float time) - { - m_hEntToIgnoreTouchesFrom = pPlayer; - m_flTimeToIgnoreTouches = gpGlobals->time + time; - } - -public: - EntityHandle m_hEntToIgnoreTouchesFrom; - float m_flTimeToIgnoreTouches; -}; - -inline bool CBasePlayer::IsReloading() const -{ - CBasePlayerWeapon *pCurrentWeapon = static_cast(m_pActiveItem); - if (pCurrentWeapon && pCurrentWeapon->m_fInReload) - { - return true; - } - - return false; -} - -#ifdef REGAMEDLL_API -inline CCSPlayer *CBasePlayer::CSPlayer() const { - return reinterpret_cast(this->m_pEntity); -} -#endif - -#ifdef REGAMEDLL_FIXES - -// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected otherwise returns NULL -// Index is 1 based -inline CBasePlayer *UTIL_PlayerByIndex(int playerIndex) -{ - return GET_PRIVATE(INDEXENT(playerIndex)); -} - -#endif - -inline CBasePlayer *UTIL_PlayerByIndexSafe(int playerIndex) -{ - CBasePlayer *pPlayer = nullptr; - if (likely(playerIndex > 0 && playerIndex <= gpGlobals->maxClients)) - pPlayer = UTIL_PlayerByIndex(playerIndex); - - return pPlayer; -} - -extern int gEvilImpulse101; -extern entvars_t *g_pevLastInflictor; -extern CBaseEntity *g_pLastSpawn; -extern CBaseEntity *g_pLastCTSpawn; -extern CBaseEntity *g_pLastTerroristSpawn; -extern BOOL gInitHUD; -extern cvar_t *sv_aim; - -void OLD_CheckBuyZone(CBasePlayer *pPlayer); -void OLD_CheckBombTarget(CBasePlayer *pPlayer); -void OLD_CheckRescueZone(CBasePlayer *pPlayer); - -void BuyZoneIcon_Set(CBasePlayer *pPlayer); -void BuyZoneIcon_Clear(CBasePlayer *pPlayer); -void BombTargetFlash_Set(CBasePlayer *pPlayer); -void BombTargetFlash_Clear(CBasePlayer *pPlayer); -void RescueZoneIcon_Set(CBasePlayer *pPlayer); -void RescueZoneIcon_Clear(CBasePlayer *pPlayer); -void EscapeZoneIcon_Set(CBasePlayer *pPlayer); -void EscapeZoneIcon_Clear(CBasePlayer *pPlayer); -void EscapeZoneIcon_Set(CBasePlayer *pPlayer); -void EscapeZoneIcon_Clear(CBasePlayer *pPlayer); -void VIP_SafetyZoneIcon_Set(CBasePlayer *pPlayer); -void VIP_SafetyZoneIcon_Clear(CBasePlayer *pPlayer); - -void SendItemStatus(CBasePlayer *pPlayer); -const char *GetCSModelName(int item_id); -Vector VecVelocityForDamage(float flDamage); -int TrainSpeed(int iSpeed, int iMax); -const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller); -void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name); -bool CanSeeUseable(CBasePlayer *me, CBaseEntity *pEntity); -void FixPlayerCrouchStuck(edict_t *pPlayer); -BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot); -CBaseEntity *FindEntityForward(CBaseEntity *pMe); -real_t GetPlayerPitch(const edict_t *pEdict); -real_t GetPlayerYaw(const edict_t *pEdict); -int GetPlayerGaitsequence(const edict_t *pEdict); -const char *GetBuyStringForWeaponClass(int weaponClass); -bool IsPrimaryWeaponClass(int classId); -bool IsPrimaryWeaponId(int id); -bool IsSecondaryWeaponClass(int classId); -bool IsSecondaryWeaponId(int id); -const char *GetWeaponAliasFromName(const char *weaponName); -bool CurrentWeaponSatisfies(CBasePlayerWeapon *pWeapon, int id, int classId); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "weapons.h" +#include "pm_materials.h" +#include "hintmessage.h" +#include "unisignals.h" + +#define SOUND_FLASHLIGHT_ON "items/flashlight1.wav" +#define SOUND_FLASHLIGHT_OFF "items/flashlight1.wav" + +#ifdef REGAMEDLL_FIXES +const int MIN_BUY_TIME = 0; +#else +const int MIN_BUY_TIME = 15; // the minimum threshold values for cvar mp_buytime 15 sec's +#endif + +const int MAX_PLAYER_NAME_LENGTH = 32; +const int MAX_AUTOBUY_LENGTH = 256; +const int MAX_REBUY_LENGTH = 256; + +const int MAX_RECENT_PATH = 20; +const int MAX_HOSTAGE_ICON = 4; // the maximum number of icons of the hostages in the HUD + +const int MAX_SUIT_NOREPEAT = 32; +const int MAX_SUIT_PLAYLIST = 4; // max of 4 suit sentences queued up at any time + +const int MAX_BUFFER_MENU = 175; +const int MAX_BUFFER_MENU_BRIEFING = 50; + +const float SUIT_UPDATE_TIME = 3.5f; +const float SUIT_FIRST_UPDATE_TIME = 0.1f; + +const float MAX_PLAYER_FATAL_FALL_SPEED = 1100.0f; +const float MAX_PLAYER_SAFE_FALL_SPEED = 500.0f; +const float MAX_PLAYER_USE_RADIUS = 64.0f; + +const float ARMOR_RATIO = 0.5f; // Armor Takes 50% of the damage +const float ARMOR_BONUS = 0.5f; // Each Point of Armor is work 1/x points of health + +const float FLASH_DRAIN_TIME = 1.2f; // 100 units/3 minutes +const float FLASH_CHARGE_TIME = 0.2f; // 100 units/20 seconds (seconds per unit) + +// damage per unit per second. +const float DAMAGE_FOR_FALL_SPEED = 100.0f / (MAX_PLAYER_FATAL_FALL_SPEED - MAX_PLAYER_SAFE_FALL_SPEED); +const float PLAYER_MIN_BOUNCE_SPEED = 350.0f; + +// won't punch player's screen/make scrape noise unless player falling at least this fast. +const float PLAYER_FALL_PUNCH_THRESHHOLD = 250.0f; + +// Money blinks few of times on the freeze period +// NOTE: It works for CZ +const int MONEY_BLINK_AMOUNT = 30; + +// Player time based damage +#define AIRTIME 12 // lung full of air lasts this many seconds +#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage +#define PARALYZE_DAMAGE 1.0f // damage to take each 2 second interval + +#define NERVEGAS_DURATION 2 +#define NERVEGAS_DAMAGE 5.0f + +#define POISON_DURATION 5 +#define POISON_DAMAGE 2.0f + +#define RADIATION_DURATION 2 +#define RADIATION_DAMAGE 1.0f + +#define ACID_DURATION 2 +#define ACID_DAMAGE 5.0f + +#define SLOWBURN_DURATION 2 +#define SLOWBURN_DAMAGE 1.0f + +#define SLOWFREEZE_DURATION 2 +#define SLOWFREEZE_DAMAGE 1.0f + +// Player physics flags bits +// CBasePlayer::m_afPhysicsFlags +#define PFLAG_ONLADDER BIT(0) +#define PFLAG_ONSWING BIT(0) +#define PFLAG_ONTRAIN BIT(1) +#define PFLAG_ONBARNACLE BIT(2) +#define PFLAG_DUCKING BIT(3) // In the process of ducking, but not totally squatted yet +#define PFLAG_USING BIT(4) // Using a continuous entity +#define PFLAG_OBSERVER BIT(5) // Player is locked in stationary cam mode. Spectators can move, observers can't. + +#define TRAIN_OFF 0x00 +#define TRAIN_NEUTRAL 0x01 +#define TRAIN_SLOW 0x02 +#define TRAIN_MEDIUM 0x03 +#define TRAIN_FAST 0x04 +#define TRAIN_BACK 0x05 + +#define TRAIN_ACTIVE 0x80 +#define TRAIN_NEW 0xc0 + +const bool SUIT_GROUP = true; +const bool SUIT_SENTENCE = false; + +const int SUIT_REPEAT_OK = 0; +const int SUIT_NEXT_IN_30SEC = 30; +const int SUIT_NEXT_IN_1MIN = 60; +const int SUIT_NEXT_IN_5MIN = 300; +const int SUIT_NEXT_IN_10MIN = 600; +const int SUIT_NEXT_IN_30MIN = 1800; +const int SUIT_NEXT_IN_1HOUR = 3600; + +const int MAX_TEAM_NAME_LENGTH = 16; + +const auto AUTOAIM_2DEGREES = 0.0348994967025; +const auto AUTOAIM_5DEGREES = 0.08715574274766; +const auto AUTOAIM_8DEGREES = 0.1391731009601; +const auto AUTOAIM_10DEGREES = 0.1736481776669; + +// custom enum +enum RewardType +{ + RT_NONE, + RT_ROUND_BONUS, + RT_PLAYER_RESET, + RT_PLAYER_JOIN, + RT_PLAYER_SPEC_JOIN, + RT_PLAYER_BOUGHT_SOMETHING, + RT_HOSTAGE_TOOK, + RT_HOSTAGE_RESCUED, + RT_HOSTAGE_DAMAGED, + RT_HOSTAGE_KILLED, + RT_TEAMMATES_KILLED, + RT_ENEMY_KILLED, + RT_INTO_GAME, + RT_VIP_KILLED, + RT_VIP_RESCUED_MYSELF +}; + +enum PLAYER_ANIM +{ + PLAYER_IDLE, + PLAYER_WALK, + PLAYER_JUMP, + PLAYER_SUPERJUMP, + PLAYER_DIE, + PLAYER_ATTACK1, + PLAYER_ATTACK2, + PLAYER_FLINCH, + PLAYER_LARGE_FLINCH, + PLAYER_RELOAD, + PLAYER_HOLDBOMB +}; + +enum _Menu +{ + Menu_OFF, + Menu_ChooseTeam, + Menu_IGChooseTeam, + Menu_ChooseAppearance, + Menu_Buy, + Menu_BuyPistol, + Menu_BuyRifle, + Menu_BuyMachineGun, + Menu_BuyShotgun, + Menu_BuySubMachineGun, + Menu_BuyItem, + Menu_Radio1, + Menu_Radio2, + Menu_Radio3, + Menu_ClientBuy +}; + +enum TeamName +{ + UNASSIGNED, + TERRORIST, + CT, + SPECTATOR, +}; + +enum ModelName +{ + MODEL_UNASSIGNED, + MODEL_URBAN, + MODEL_TERROR, + MODEL_LEET, + MODEL_ARCTIC, + MODEL_GSG9, + MODEL_GIGN, + MODEL_SAS, + MODEL_GUERILLA, + MODEL_VIP, + MODEL_MILITIA, + MODEL_SPETSNAZ, + MODEL_AUTO +}; + +enum JoinState +{ + JOINED, + SHOWLTEXT, + READINGLTEXT, + SHOWTEAMSELECT, + PICKINGTEAM, + GETINTOGAME +}; + +enum TrackCommands +{ + CMD_SAY = 0, + CMD_SAYTEAM, + CMD_FULLUPDATE, + CMD_VOTE, + CMD_VOTEMAP, + CMD_LISTMAPS, + CMD_LISTPLAYERS, + CMD_NIGHTVISION, + COMMANDS_TO_TRACK, +}; + +enum IgnoreChatMsg : int +{ + IGNOREMSG_NONE, // Nothing to do + IGNOREMSG_ENEMY, // To ignore any chat messages from the enemy + IGNOREMSG_TEAM // Same as IGNOREMSG_ENEMY but ignore teammates +}; + +struct RebuyStruct +{ + int m_primaryWeapon; + int m_primaryAmmo; + int m_secondaryWeapon; + int m_secondaryAmmo; + int m_heGrenade; + int m_flashbang; + int m_smokeGrenade; + int m_defuser; + int m_nightVision; + ArmorType m_armor; +}; + +enum ThrowDirection +{ + THROW_NONE, + THROW_FORWARD, + THROW_BACKWARD, + THROW_HITVEL, + THROW_BOMB, + THROW_GRENADE, + THROW_HITVEL_MINUS_AIRVEL +}; + +const float MAX_ID_RANGE = 2048.0f; +const float MAX_SPEC_ID_RANGE = 8192.0f; +const int MAX_SBAR_STRING = 128; + +const int SBAR_TARGETTYPE_TEAMMATE = 1; +const int SBAR_TARGETTYPE_ENEMY = 2; +const int SBAR_TARGETTYPE_HOSTAGE = 3; + +enum sbar_data +{ + SBAR_ID_TARGETTYPE = 1, + SBAR_ID_TARGETNAME, + SBAR_ID_TARGETHEALTH, + SBAR_END +}; + +enum MusicState { SILENT, CALM, INTENSE }; + +class CCSPlayer; + +class CStripWeapons: public CPointEntity { +public: + virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); +}; + +// Dead HEV suit prop +class CDeadHEV: public CBaseMonster { +public: + virtual void Spawn(); + virtual void KeyValue(KeyValueData *pkvd); + virtual int Classify() { return CLASS_HUMAN_MILITARY; } + +public: + int m_iPose; // which sequence to display -- temporary, don't need to save + static const char *m_szPoses[]; +}; + +class CSprayCan: public CBaseEntity { +public: + virtual void Think(); + virtual int ObjectCaps() { return FCAP_DONT_SAVE; } + +public: + void Spawn(entvars_t *pevOwner); +}; + +class CBloodSplat: public CBaseEntity { +public: + void Spawn(entvars_t *pevOwner); + void Spray(); +}; + +class CBasePlayer: public CBaseMonster { +public: + virtual void Spawn(); + virtual void Precache(); + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + virtual int ObjectCaps(); + virtual int Classify(); + virtual void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + virtual BOOL TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + virtual BOOL TakeHealth(float flHealth, int bitsDamageType); + virtual void Killed(entvars_t *pevAttacker, int iGib); + virtual void AddPoints(int score, BOOL bAllowNegativeScore); + virtual void AddPointsToTeam(int score, BOOL bAllowNegativeScore); + virtual BOOL AddPlayerItem(CBasePlayerItem *pItem); + virtual BOOL RemovePlayerItem(CBasePlayerItem *pItem); + virtual int GiveAmmo(int iAmount, const char *szName, int iMax = -1); +#ifndef REGAMEDLL_API + virtual void StartSneaking() { m_tSneaking = gpGlobals->time - 1; } + virtual void StopSneaking() { m_tSneaking = gpGlobals->time + 30; } +#else + virtual void OnCreate(); + virtual void OnDestroy(); + void StartSneaking() { m_tSneaking = gpGlobals->time - 1; }; +#endif + + virtual BOOL IsSneaking() { return m_tSneaking <= gpGlobals->time; } + virtual BOOL IsAlive() { return (pev->deadflag == DEAD_NO && pev->health > 0.0f); } + virtual BOOL IsPlayer() { return (pev->flags & FL_SPECTATOR) != FL_SPECTATOR; } + virtual BOOL IsNetClient() { return TRUE; } + virtual const char *TeamID(); + virtual BOOL FBecomeProne(); + + // TODO: Need to investigate for what purposes used random to get relative eyes position + virtual Vector BodyTarget(const Vector &posSrc) { return Center() + pev->view_ofs * RANDOM_FLOAT(0.5, 1.1); } + virtual int Illumination(); + virtual BOOL ShouldFadeOnDeath() { return FALSE; } + virtual void ResetMaxSpeed(); + virtual void Jump(); + virtual void Duck(); + virtual void PreThink(); + virtual void PostThink(); + virtual Vector GetGunPosition(); + virtual BOOL IsBot() { return FALSE; } + virtual void UpdateClientData(); + virtual void ImpulseCommands(); + virtual void RoundRespawn(); + virtual Vector GetAutoaimVector(float flDelta); + virtual void Blind(float flUntilTime, float flHoldTime, float flFadeTime, int iAlpha); + virtual void OnTouchingWeapon(CWeaponBox *pWeapon) { } + +#ifdef REGAMEDLL_API + void Spawn_OrigFunc(); + void Precache_OrigFunc(); + int ObjectCaps_OrigFunc(); + int Classify_OrigFunc(); + void TraceAttack_OrigFunc(entvars_t *pevAttacker, float flDamage, VectorRef vecDir, TraceResult *ptr, int bitsDamageType); + BOOL TakeDamage_OrigFunc(entvars_t *pevInflictor, entvars_t *pevAttacker, FloatRef flDamage, int bitsDamageType); + BOOL TakeHealth_OrigFunc(float flHealth, int bitsDamageType); + void Killed_OrigFunc(entvars_t *pevAttacker, int iGib); + void AddPoints_OrigFunc(int score, BOOL bAllowNegativeScore); + void AddPointsToTeam_OrigFunc(int score, BOOL bAllowNegativeScore); + BOOL AddPlayerItem_OrigFunc(CBasePlayerItem *pItem); + BOOL RemovePlayerItem_OrigFunc(CBasePlayerItem *pItem); + int GiveAmmo_OrigFunc(int iAmount, const char *szName, int iMax); + void ResetMaxSpeed_OrigFunc(); + void Jump_OrigFunc(); + void Duck_OrigFunc(); + void PreThink_OrigFunc(); + void PostThink_OrigFunc(); + void UpdateClientData_OrigFunc(); + void ImpulseCommands_OrigFunc(); + void RoundRespawn_OrigFunc(); + void Blind_OrigFunc(float flUntilTime, float flHoldTime, float flFadeTime, int iAlpha); + EXT_FUNC CBasePlayer *Observer_IsValidTarget_OrigFunc(int iPlayerIndex, bool bSameTeam); + void Radio_OrigFunc(const char *msg_id, const char *msg_verbose = nullptr, short pitch = 100, bool showIcon = true); + void AddAccount_OrigFunc(int amount, RewardType type = RT_NONE, bool bTrackChange = true); + void Disappear_OrigFunc(); + void MakeVIP_OrigFunc(); + void GiveDefaultItems_OrigFunc(); + bool SetClientUserInfoName_OrigFunc(char *infobuffer, char *szNewName); + void SetAnimation_OrigFunc(PLAYER_ANIM playerAnim); + void StartObserver_OrigFunc(Vector &vecPosition, Vector &vecViewAngle); + CBaseEntity *DropPlayerItem_OrigFunc(const char *pszItemName); + CBaseEntity *GiveNamedItem_OrigFunc(const char *pszName); + CBaseEntity *DropShield_OrigFunc(bool bDeploy = true); + void GiveShield_OrigFunc(bool bDeploy = true); + bool HasRestrictItem_OrigFunc(ItemID item, ItemRestType type); + void OnSpawnEquip_OrigFunc(bool addDefault = true, bool equipGame = true); + bool MakeBomber_OrigFunc(); + bool GetIntoGame_OrigFunc(); + void StartDeathCam_OrigFunc(); + CGrenade *ThrowGrenade_OrigFunc(CBasePlayerWeapon *pWeapon, VectorRef vecSrc, VectorRef vecThrow, float time, unsigned short usEvent = 0); + void SwitchTeam_OrigFunc(); + bool CanSwitchTeam_OrigFunc(TeamName teamToSwap); + void SetSpawnProtection_OrigFunc(float flProtectionTime); + void RemoveSpawnProtection_OrigFunc(); + bool HintMessageEx_OrigFunc(const char *pMessage, float duration = 6.0f, bool bDisplayIfPlayerDead = false, bool bOverride = false); + void UseEmpty_OrigFunc(); + + CCSPlayer *CSPlayer() const; +#endif // REGAMEDLL_API + +public: + static CBasePlayer *Instance(edict_t *pEdict) { return GET_PRIVATE(pEdict ? pEdict : ENT(0)); } + static CBasePlayer *Instance(entvars_t *pev) { return Instance(ENT(pev)); } + static CBasePlayer *Instance(int offset) { return Instance(ENT(offset)); } + + void SpawnClientSideCorpse(); + void Observer_FindNextPlayer(bool bReverse, const char *name = nullptr); + CBasePlayer *Observer_IsValidTarget(int iPlayerIndex, bool bSameTeam); + void Disconnect(); + void Observer_Think(); + void Observer_HandleButtons(); + void Observer_SetMode(int iMode); + void Observer_CheckTarget(); + void Observer_CheckProperties(); + int GetObserverMode() { return pev->iuser1; } + void PlantC4(); + void Radio(const char *msg_id, const char *msg_verbose = nullptr, short pitch = 100, bool showIcon = true); + CBasePlayer *GetNextRadioRecipient(CBasePlayer *pStartPlayer); + void SmartRadio(); + void ThrowWeapon(char *pszItemName); + void ThrowPrimary(); + CGrenade *ThrowGrenade(CBasePlayerWeapon *pWeapon, Vector vecSrc, Vector vecThrow, float time, unsigned short usEvent = 0); + void AddAccount(int amount, RewardType type = RT_NONE, bool bTrackChange = true); + void Disappear(); + void MakeVIP(); + bool CanPlayerBuy(bool display = false); + bool CanSwitchTeam(TeamName teamToSwap); + void SwitchTeam(); + void TabulateAmmo(); + void Pain(int iLastHitGroup, bool bHasArmour); + BOOL IsBombGuy(); + bool IsLookingAtPosition(Vector *pos, float angleTolerance = 20.0f); + void Reset(); + void SetScoreboardAttributes(CBasePlayer *destination = nullptr); + void RenewItems(); + void PackDeadPlayerItems(); + void GiveDefaultItems(); + void RemoveAllItems(BOOL removeSuit); + void SetBombIcon(BOOL bFlash = FALSE); + void SetProgressBarTime(int time); + void SetProgressBarTime2(int time, float timeElapsed); + void SetPlayerModel(BOOL HasC4); + bool SetClientUserInfoName(char *infobuffer, char *szNewName); + void SetClientUserInfoModel(char *infobuffer, char *szNewModel); + void SetClientUserInfoModel_api(char *infobuffer, char *szNewModel); + void SetNewPlayerModel(const char *modelName); + BOOL SwitchWeapon(CBasePlayerItem *pWeapon); + void CheckPowerups(); + bool CanAffordPrimary(); + bool CanAffordPrimaryAmmo(); + bool CanAffordSecondaryAmmo(); + bool CanAffordArmor(); + bool CanAffordDefuseKit(); + bool CanAffordGrenade(); + bool NeedsPrimaryAmmo(); + bool NeedsSecondaryAmmo(); + bool NeedsArmor(); + bool NeedsDefuseKit(); + bool NeedsGrenade(); + BOOL IsOnLadder(); + BOOL FlashlightIsOn(); + void FlashlightTurnOn(); + void FlashlightTurnOff(); + void UpdatePlayerSound(); + void DeathSound(); + void SetAnimation(PLAYER_ANIM playerAnim); + void SetWeaponAnimType(const char *szExtention) { Q_strcpy(m_szAnimExtention, szExtention); } + void CheatImpulseCommands(int iImpulse); + void StartDeathCam(); + void StartObserver(Vector &vecPosition, Vector &vecViewAngle); + void HandleSignals(); + CBaseEntity *DropPlayerItem(const char *pszItemName); + bool HasPlayerItem(CBasePlayerItem *pCheckItem); + bool HasNamedPlayerItem(const char *pszItemName); + bool HasWeapons(); + void SelectPrevItem(int iItem); + void SelectNextItem(int iItem); + void SelectLastItem(); + void SelectItem(const char *pstr); + void ItemPreFrame(); + void ItemPostFrame(); + CBaseEntity *GiveNamedItem(const char *pszName); + CBaseEntity *GiveNamedItemEx(const char *pszName); + void EnableControl(BOOL fControl); + bool HintMessage(const char *pMessage, BOOL bDisplayIfPlayerDead = FALSE, BOOL bOverride = FALSE); + bool HintMessageEx(const char *pMessage, float duration = 6.0f, bool bDisplayIfPlayerDead = false, bool bOverride = false); + void SendAmmoUpdate(); + void SendFOV(int fov); + void WaterMove(); + void EXPORT PlayerDeathThink(); + void PlayerRespawnThink(); + void PlayerUse(); + void HostageUsed(); + void JoiningThink(); + void RemoveLevelText(); + void MenuPrint(const char *msg); + void ResetMenu(); + void SyncRoundTimer(); + void CheckSuitUpdate(); + void SetSuitUpdate(char *name = nullptr, bool group = SUIT_SENTENCE, int iNoRepeatTime = SUIT_REPEAT_OK); + void UpdateGeigerCounter(); + void CheckTimeBasedDamage(); + void BarnacleVictimBitten(entvars_t *pevBarnacle); + void BarnacleVictimReleased(); + static int GetAmmoIndex(const char *psz); + int AmmoInventory(int iAmmoIndex); + void ResetAutoaim(); + Vector AutoaimDeflection(Vector &vecSrc, float flDist, float flDelta); + void ForceClientDllUpdate(); + void DeathMessage(entvars_t *pevAttacker) {}; + void SetCustomDecalFrames(int nFrames); + int GetCustomDecalFrames(); + void InitStatusBar(); + void UpdateStatusBar(); + void StudioEstimateGait(); + void StudioPlayerBlend(int *pBlend, float *pPitch); + void CalculatePitchBlend(); + void CalculateYawBlend(); + void StudioProcessGait(); + void SendHostagePos(); + void SendHostageIcons(); + void ResetStamina(); + BOOL IsArmored(int nHitGroup); + BOOL ShouldDoLargeFlinch(int nHitGroup, int nGunType); + void SetPrefsFromUserinfo(char *infobuffer); + void SendWeatherInfo(); + void UpdateShieldCrosshair(bool draw); + bool HasShield(); + bool IsProtectedByShield() { return HasShield() && m_bShieldDrawn; } + void RemoveShield(); + CBaseEntity *DropShield(bool bDeploy = true); + void GiveShield(bool bDeploy = true); + bool IsHittingShield(Vector &vecDirection, TraceResult *ptr); + bool SelectSpawnSpot(const char *pEntClassName, CBaseEntity* &pSpot); + bool IsReloading() const; + bool IsBlind() const { return (m_blindUntilTime > gpGlobals->time); } + bool IsAutoFollowAllowed() const { return (gpGlobals->time > m_allowAutoFollowTime); } + void InhibitAutoFollow(float duration) { m_allowAutoFollowTime = gpGlobals->time + duration; } + void AllowAutoFollow() { m_allowAutoFollowTime = 0; } + void ClearAutoBuyData(); + void AddAutoBuyData(const char *str); + void AutoBuy(); + void ClientCommand(const char *cmd, const char *arg1 = nullptr, const char *arg2 = nullptr, const char *arg3 = nullptr); + void PrioritizeAutoBuyString(char *autobuyString, const char *priorityString); + const char *PickPrimaryCareerTaskWeapon(); + const char *PickSecondaryCareerTaskWeapon(); + const char *PickFlashKillWeaponString(); + const char *PickGrenadeKillWeaponString(); + bool ShouldExecuteAutoBuyCommand(AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary); + void PostAutoBuyCommandProcessing(AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary); + void ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary); + AutoBuyInfoStruct *GetAutoBuyCommandInfo(const char *command); + void InitRebuyData(const char *str); + void BuildRebuyStruct(); + void Rebuy(); + void RebuyPrimaryWeapon(); + void RebuyPrimaryAmmo(); + void RebuySecondaryWeapon(); + void RebuySecondaryAmmo(); + void RebuyHEGrenade(); + void RebuyFlashbang(); + void RebuySmokeGrenade(); + void RebuyDefuser(); + void RebuyNightVision(); + void RebuyArmor(); + void UpdateLocation(bool forceUpdate = false); + void SetObserverAutoDirector(bool val) { m_bObserverAutoDirector = val; } + bool IsObservingPlayer(CBasePlayer *pPlayer); + bool CanSwitchObserverModes() const { return m_canSwitchObserverModes; } + void SendItemStatus(); + edict_t *EntSelectSpawnPoint(); + void SetScoreAttrib(CBasePlayer *dest); + void ReloadWeapons(CBasePlayerItem *pWeapon = nullptr, bool bForceReload = false, bool bForceRefill = false); + void TeamChangeUpdate(); + bool HasRestrictItem(ItemID item, ItemRestType type); + void DropSecondary(); + void DropPrimary(); + void OnSpawnEquip(bool addDefault = true, bool equipGame = true); + void RemoveBomb(); + void RemoveDefuser(); + void HideTimer(); + bool MakeBomber(); + bool GetIntoGame(); + bool ShouldToShowAccount(CBasePlayer *pReceiver) const; + bool ShouldToShowHealthInfo(CBasePlayer *pReceiver) const; + + CBasePlayerItem *GetItemByName(const char *itemName); + CBasePlayerItem *GetItemById(WeaponIdType weaponID); + + void SetSpawnProtection(float flProtectionTime); + void RemoveSpawnProtection(); + void UseEmpty(); + + // templates + template + T *ForEachItem(int slot, const Functor &func) const + { + auto item = m_rgpPlayerItems[slot]; + while (item) + { + if (func(static_cast(item))) + return static_cast(item); + + item = item->m_pNext; + } + + return nullptr; + } + + template + T *ForEachItem(const Functor &func) const + { + for (auto item : m_rgpPlayerItems) + { + while (item) + { + if (func(static_cast(item))) + return static_cast(item); + + item = item->m_pNext; + } + } + + return nullptr; + } + + template + T *ForEachItem(const char *pszItemName, const Functor &func) const + { + if (!pszItemName) { + return nullptr; + } + + for (auto item : m_rgpPlayerItems) + { + while (item) + { + if (FClassnameIs(item->pev, pszItemName) && func(static_cast(item))) { + return static_cast(item); + } + + item = item->m_pNext; + } + } + + return nullptr; + } + +public: + enum { MaxLocationLen = 32 }; + + int random_seed; + unsigned short m_usPlayerBleed; + EntityHandle m_hObserverTarget; + float m_flNextObserverInput; + int m_iObserverWeapon; + int m_iObserverC4State; + bool m_bObserverHasDefuser; + int m_iObserverLastMode; + float m_flFlinchTime; + float m_flAnimTime; + bool m_bHighDamage; + float m_flVelocityModifier; + int m_iLastZoom; + bool m_bResumeZoom; + float m_flEjectBrass; + ArmorType m_iKevlar; + bool m_bNotKilled; + TeamName m_iTeam; + int m_iAccount; + bool m_bHasPrimary; + float m_flDeathThrowTime; + int m_iThrowDirection; + float m_flLastTalk; + bool m_bJustConnected; + bool m_bContextHelp; + JoinState m_iJoiningState; + CBaseEntity *m_pIntroCamera; + float m_fIntroCamTime; + float m_fLastMovement; + bool m_bMissionBriefing; + bool m_bTeamChanged; + ModelName m_iModelName; + int m_iTeamKills; + IgnoreChatMsg m_iIgnoreGlobalChat; + bool m_bHasNightVision; + bool m_bNightVisionOn; + Vector m_vRecentPath[MAX_RECENT_PATH]; + float m_flIdleCheckTime; + float m_flRadioTime; + int m_iRadioMessages; + bool m_bIgnoreRadio; + bool m_bHasC4; + bool m_bHasDefuser; + bool m_bKilledByBomb; + Vector m_vBlastVector; + bool m_bKilledByGrenade; + CHintMessageQueue m_hintMessageQueue; + int m_flDisplayHistory; + _Menu m_iMenu; + int m_iChaseTarget; + CBaseEntity *m_pChaseTarget; + float m_fCamSwitch; + bool m_bEscaped; + bool m_bIsVIP; + float m_tmNextRadarUpdate; + Vector m_vLastOrigin; + int m_iCurrentKickVote; + float m_flNextVoteTime; + bool m_bJustKilledTeammate; + int m_iHostagesKilled; + int m_iMapVote; + bool m_bCanShoot; + float m_flLastFired; + float m_flLastAttackedTeammate; + bool m_bHeadshotKilled; + bool m_bPunishedForTK; + bool m_bReceivesNoMoneyNextRound; + int m_iTimeCheckAllowed; + bool m_bHasChangedName; + char m_szNewName[MAX_PLAYER_NAME_LENGTH]; + bool m_bIsDefusing; + float m_tmHandleSignals; + CUnifiedSignals m_signals; + edict_t *m_pentCurBombTarget; + int m_iPlayerSound; + int m_iTargetVolume; + int m_iWeaponVolume; + int m_iExtraSoundTypes; + int m_iWeaponFlash; + float m_flStopExtraSoundTime; + float m_flFlashLightTime; + int m_iFlashBattery; + int m_afButtonLast; + int m_afButtonPressed; + int m_afButtonReleased; + edict_t *m_pentSndLast; + float m_flSndRoomtype; + float m_flSndRange; + float m_flFallVelocity; + int m_rgItems[MAX_ITEMS]; + int m_fNewAmmo; + unsigned int m_afPhysicsFlags; + float m_fNextSuicideTime; + float m_flTimeStepSound; + float m_flTimeWeaponIdle; + float m_flSwimTime; + float m_flDuckTime; + float m_flWallJumpTime; + float m_flSuitUpdate; + int m_rgSuitPlayList[MAX_SUIT_PLAYLIST]; + int m_iSuitPlayNext; + int m_rgiSuitNoRepeat[MAX_SUIT_NOREPEAT]; + float m_rgflSuitNoRepeatTime[MAX_SUIT_NOREPEAT]; + int m_lastDamageAmount; + float m_tbdPrev; + float m_flgeigerRange; + float m_flgeigerDelay; + int m_igeigerRangePrev; + int m_iStepLeft; + char m_szTextureName[MAX_TEXTURENAME_LENGHT]; + char m_chTextureType; + int m_idrowndmg; + int m_idrownrestored; + int m_bitsHUDDamage; + BOOL m_fInitHUD; + BOOL m_fGameHUDInitialized; + int m_iTrain; + BOOL m_fWeapon; + EHANDLE m_pTank; + float m_fDeadTime; + BOOL m_fNoPlayerSound; + BOOL m_fLongJump; + float m_tSneaking; + int m_iUpdateTime; + int m_iClientHealth; + int m_iClientBattery; + int m_iHideHUD; + int m_iClientHideHUD; + int m_iFOV; + int m_iClientFOV; + int m_iNumSpawns; + CBaseEntity *m_pObserver; + CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES]; + CBasePlayerItem *m_pActiveItem; + CBasePlayerItem *m_pClientActiveItem; + CBasePlayerItem *m_pLastItem; + int m_rgAmmo[MAX_AMMO_SLOTS]; + int m_rgAmmoLast[MAX_AMMO_SLOTS]; + Vector m_vecAutoAim; + BOOL m_fOnTarget; + int m_iDeaths; + int m_izSBarState[SBAR_END]; + float m_flNextSBarUpdateTime; + float m_flStatusBarDisappearDelay; + char m_SbarString0[MAX_SBAR_STRING]; + int m_lastx; + int m_lasty; + int m_nCustomSprayFrames; + float m_flNextDecalTime; + char m_szTeamName[MAX_TEAM_NAME_LENGTH]; + + static TYPEDESCRIPTION m_playerSaveData[]; + +/*protected:*/ + int m_modelIndexPlayer; + char m_szAnimExtention[32]; + int m_iGaitsequence; + + float m_flGaitframe; + float m_flGaityaw; + Vector m_prevgaitorigin; + float m_flPitch; + float m_flYaw; + float m_flGaitMovement; + int m_iAutoWepSwitch; + bool m_bVGUIMenus; + bool m_bShowHints; + bool m_bShieldDrawn; + bool m_bOwnsShield; + bool m_bWasFollowing; + float m_flNextFollowTime; + float m_flYawModifier; + float m_blindUntilTime; + float m_blindStartTime; + float m_blindHoldTime; + float m_blindFadeTime; + int m_blindAlpha; + float m_allowAutoFollowTime; + char m_autoBuyString[MAX_AUTOBUY_LENGTH]; + char *m_rebuyString; + RebuyStruct m_rebuyStruct; + bool m_bIsInRebuy; + float m_flLastUpdateTime; + char m_lastLocation[MaxLocationLen]; + float m_progressStart; + float m_progressEnd; + bool m_bObserverAutoDirector; + bool m_canSwitchObserverModes; + float m_heartBeatTime; + float m_intenseTimestamp; + float m_silentTimestamp; + MusicState m_musicState; + float m_flLastCommandTime[COMMANDS_TO_TRACK]; + +#ifdef BUILD_LATEST + int m_iLastAccount; + int m_iLastClientHealth; + float m_tmNextAccountHealthUpdate; +#endif +}; + +class CWShield: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual void EXPORT Touch(CBaseEntity *pOther); + +public: + void SetCantBePickedUpByUser(CBasePlayer *pPlayer, float time) + { + m_hEntToIgnoreTouchesFrom = pPlayer; + m_flTimeToIgnoreTouches = gpGlobals->time + time; + } + +public: + EntityHandle m_hEntToIgnoreTouchesFrom; + float m_flTimeToIgnoreTouches; +}; + +inline bool CBasePlayer::IsReloading() const +{ + CBasePlayerWeapon *pCurrentWeapon = static_cast(m_pActiveItem); + if (pCurrentWeapon && pCurrentWeapon->m_fInReload) + { + return true; + } + + return false; +} + +#ifdef REGAMEDLL_API +inline CCSPlayer *CBasePlayer::CSPlayer() const { + return reinterpret_cast(this->m_pEntity); +} +#endif + +#ifdef REGAMEDLL_FIXES + +// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected otherwise returns NULL +// Index is 1 based +inline CBasePlayer *UTIL_PlayerByIndex(int playerIndex) +{ + return GET_PRIVATE(INDEXENT(playerIndex)); +} + +#endif + +inline CBasePlayer *UTIL_PlayerByIndexSafe(int playerIndex) +{ + CBasePlayer *pPlayer = nullptr; + if (likely(playerIndex > 0 && playerIndex <= gpGlobals->maxClients)) + pPlayer = UTIL_PlayerByIndex(playerIndex); + + return pPlayer; +} + +extern int gEvilImpulse101; +extern entvars_t *g_pevLastInflictor; +extern CBaseEntity *g_pLastSpawn; +extern CBaseEntity *g_pLastCTSpawn; +extern CBaseEntity *g_pLastTerroristSpawn; +extern BOOL gInitHUD; +extern cvar_t *sv_aim; + +void OLD_CheckBuyZone(CBasePlayer *pPlayer); +void OLD_CheckBombTarget(CBasePlayer *pPlayer); +void OLD_CheckRescueZone(CBasePlayer *pPlayer); + +void BuyZoneIcon_Set(CBasePlayer *pPlayer); +void BuyZoneIcon_Clear(CBasePlayer *pPlayer); +void BombTargetFlash_Set(CBasePlayer *pPlayer); +void BombTargetFlash_Clear(CBasePlayer *pPlayer); +void RescueZoneIcon_Set(CBasePlayer *pPlayer); +void RescueZoneIcon_Clear(CBasePlayer *pPlayer); +void EscapeZoneIcon_Set(CBasePlayer *pPlayer); +void EscapeZoneIcon_Clear(CBasePlayer *pPlayer); +void EscapeZoneIcon_Set(CBasePlayer *pPlayer); +void EscapeZoneIcon_Clear(CBasePlayer *pPlayer); +void VIP_SafetyZoneIcon_Set(CBasePlayer *pPlayer); +void VIP_SafetyZoneIcon_Clear(CBasePlayer *pPlayer); + +void SendItemStatus(CBasePlayer *pPlayer); +const char *GetCSModelName(int item_id); +Vector VecVelocityForDamage(float flDamage); +int TrainSpeed(int iSpeed, int iMax); +const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller); +void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name); +bool CanSeeUseable(CBasePlayer *me, CBaseEntity *pEntity); +void FixPlayerCrouchStuck(edict_t *pPlayer); +BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot); +CBaseEntity *FindEntityForward(CBaseEntity *pMe); +real_t GetPlayerPitch(const edict_t *pEdict); +real_t GetPlayerYaw(const edict_t *pEdict); +int GetPlayerGaitsequence(const edict_t *pEdict); +const char *GetBuyStringForWeaponClass(int weaponClass); +bool IsPrimaryWeaponClass(int classId); +bool IsPrimaryWeaponId(int id); +bool IsSecondaryWeaponClass(int classId); +bool IsSecondaryWeaponId(int id); +const char *GetWeaponAliasFromName(const char *weaponName); +bool CurrentWeaponSatisfies(CBasePlayerWeapon *pWeapon, int id, int classId); diff --git a/regamedll/dlls/triggers.cpp b/regamedll/dlls/triggers.cpp index 133818e1..d640a9c6 100644 --- a/regamedll/dlls/triggers.cpp +++ b/regamedll/dlls/triggers.cpp @@ -1,2459 +1,2459 @@ -#include "precompiled.h" - -// Global Savedata for changelevel friction modifier -TYPEDESCRIPTION CFrictionModifier::m_SaveData[] = -{ - DEFINE_FIELD(CFrictionModifier, m_frictionFraction, FIELD_FLOAT), -}; - -LINK_ENTITY_TO_CLASS(func_friction, CFrictionModifier, CCSFrictionModifier) -IMPLEMENT_SAVERESTORE(CFrictionModifier, CBaseEntity) - -void CFrictionModifier::Spawn() -{ - pev->solid = SOLID_TRIGGER; - - // set size and link into world - SET_MODEL(ENT(pev), STRING(pev->model)); - - pev->movetype = MOVETYPE_NONE; - SetTouch(&CFrictionModifier::ChangeFriction); -} - -// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) -void CFrictionModifier::ChangeFriction(CBaseEntity *pOther) -{ - if (pOther->pev->movetype != MOVETYPE_BOUNCEMISSILE && pOther->pev->movetype != MOVETYPE_BOUNCE) - { - pOther->pev->friction = m_frictionFraction; - } -} - -// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) -void CFrictionModifier::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "modifier")) - { - m_frictionFraction = Q_atof(pkvd->szValue) / 100.0f; - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue(pkvd); - } -} - -TYPEDESCRIPTION CAutoTrigger::m_SaveData[] = -{ - DEFINE_FIELD(CAutoTrigger, m_globalstate, FIELD_STRING), - DEFINE_FIELD(CAutoTrigger, m_triggerType, FIELD_INTEGER), -}; - -LINK_ENTITY_TO_CLASS(trigger_auto, CAutoTrigger, CCSAutoTrigger) -IMPLEMENT_SAVERESTORE(CAutoTrigger, CBaseDelay) - -void CAutoTrigger::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "globalstate")) - { - m_globalstate = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "triggerstate")) - { - int type = Q_atoi(pkvd->szValue); - switch (type) - { - case 0: - m_triggerType = USE_OFF; - break; - case 2: - m_triggerType = USE_TOGGLE; - break; - default: - m_triggerType = USE_ON; - break; - } - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue(pkvd); - } -} - -void CAutoTrigger::Spawn() -{ - Precache(); -} - -void CAutoTrigger::Precache() -{ - pev->nextthink = gpGlobals->time + 0.1f; -} - -void CAutoTrigger::Think() -{ - if (!m_globalstate || gGlobalState.EntityGetState(m_globalstate) == GLOBAL_ON) - { - SUB_UseTargets(this, m_triggerType, 0); - -#ifdef REGAMEDLL_FIXES - if (pev->spawnflags & SF_AUTO_NORESET) -#else - if (pev->spawnflags & SF_AUTO_FIREONCE) -#endif - { - UTIL_Remove(this); - } - } -} - -#ifdef REGAMEDLL_FIXES -void CAutoTrigger::Restart() -{ - if (pev->spawnflags & SF_AUTO_NORESET) - return; - - pev->nextthink = gpGlobals->time + 0.1f; -} -#endif - -TYPEDESCRIPTION CTriggerRelay::m_SaveData[] = -{ - DEFINE_FIELD(CTriggerRelay, m_triggerType, FIELD_INTEGER), -}; - -LINK_ENTITY_TO_CLASS(trigger_relay, CTriggerRelay, CCSTriggerRelay) -IMPLEMENT_SAVERESTORE(CTriggerRelay, CBaseDelay) - -void CTriggerRelay::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "triggerstate")) - { - int type = Q_atoi(pkvd->szValue); - switch (type) - { - case 0: - m_triggerType = USE_OFF; - break; - case 2: - m_triggerType = USE_TOGGLE; - break; - default: - m_triggerType = USE_ON; - break; - } - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue(pkvd); - } -} - -void CTriggerRelay::Spawn() -{ - ; -} - -void CTriggerRelay::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - SUB_UseTargets(this, m_triggerType, 0); - if (pev->spawnflags & SF_RELAY_FIREONCE) - { - UTIL_Remove(this); - } -} - -// Global Savedata for multi_manager -TYPEDESCRIPTION CMultiManager::m_SaveData[] = -{ - DEFINE_FIELD(CMultiManager, m_cTargets, FIELD_INTEGER), - DEFINE_FIELD(CMultiManager, m_index, FIELD_INTEGER), - DEFINE_FIELD(CMultiManager, m_startTime, FIELD_TIME), - DEFINE_ARRAY(CMultiManager, m_iTargetName, FIELD_STRING, MAX_MM_TARGETS), - DEFINE_ARRAY(CMultiManager, m_flTargetDelay, FIELD_FLOAT, MAX_MM_TARGETS), -}; - -LINK_ENTITY_TO_CLASS(multi_manager, CMultiManager, CCSMultiManager) -IMPLEMENT_SAVERESTORE(CMultiManager, CBaseToggle) - -void CMultiManager::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else // add this field to the target list - { - // this assumes that additional fields are targetnames and their values are delay values. - if (m_cTargets < MAX_MM_TARGETS) - { - char tmp[128]; - - UTIL_StripToken(pkvd->szKeyName, tmp); - m_iTargetName[m_cTargets] = ALLOC_STRING(tmp); - m_flTargetDelay[m_cTargets] = Q_atof(pkvd->szValue); - m_cTargets++; - - pkvd->fHandled = TRUE; - } - } -} - -void CMultiManager::Spawn() -{ - pev->solid = SOLID_NOT; - SetUse(&CMultiManager::ManagerUse); - SetThink(&CMultiManager::ManagerThink); - - // Sort targets - // Quick and dirty bubble sort - bool bSwapped = true; - - while (bSwapped) - { - bSwapped = false; - for (int i = 1; i < m_cTargets; i++) - { - if (m_flTargetDelay[i] < m_flTargetDelay[i - 1]) - { - // Swap out of order elements - int name = m_iTargetName[i]; - float delay = m_flTargetDelay[i]; - - m_iTargetName[i] = m_iTargetName[i - 1]; - m_flTargetDelay[i] = m_flTargetDelay[i - 1]; - m_iTargetName[i - 1] = name; - m_flTargetDelay[i - 1] = delay; - bSwapped = true; - } - } - } -} - -void CMultiManager::Restart() -{ -#ifndef REGAMEDLL_FIXES - edict_t *pentTarget = nullptr; - - for (int i = 0; i < m_cTargets; i++) - { - const char *name = STRING(m_iTargetName[i]); - - if (!name) - continue; - - pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); - - if (FNullEnt(pentTarget)) - break; - - CBaseEntity *pTarget = static_cast(CBaseEntity::Instance(pentTarget)); - if (pTarget && !(pTarget->pev->flags & FL_KILLME)) - { - pTarget->Restart(); - } - } -#endif - - SetThink(nullptr); - - if (IsClone()) - { - UTIL_Remove(this); - return; - } - - SetUse(&CMultiManager::ManagerUse); - m_index = 0; -} - -BOOL CMultiManager::HasTarget(string_t targetname) -{ - for (int i = 0; i < m_cTargets; i++) - { - if (FStrEq(STRING(targetname), STRING(m_iTargetName[i]))) - { - return TRUE; - } - } - - return FALSE; -} - -// Designers were using this to fire targets that may or may not exist -- -// so I changed it to use the standard target fire code, made it a little simpler. -void CMultiManager::ManagerThink() -{ - float time; - - time = gpGlobals->time - m_startTime; - while (m_index < m_cTargets && m_flTargetDelay[m_index] <= time) - { - FireTargets(STRING(m_iTargetName[m_index]), m_hActivator, this, USE_TOGGLE, 0); - m_index++; - } - - // have we fired all targets? - if (m_index >= m_cTargets) - { - SetThink(nullptr); - if (IsClone()) - { - UTIL_Remove(this); - return; - } - - // allow manager re-use - SetUse(&CMultiManager::ManagerUse); - } - else - { - pev->nextthink = m_startTime + m_flTargetDelay[m_index]; - } -} - -CMultiManager *CMultiManager::Clone() -{ - CMultiManager *pMulti = GetClassPtr((CMultiManager *)nullptr); - - edict_t *pEdict = pMulti->pev->pContainingEntity; - Q_memcpy(pMulti->pev, pev, sizeof(*pev)); - - pMulti->pev->pContainingEntity = pEdict; - pMulti->pev->spawnflags |= SF_MULTIMAN_CLONE; - pMulti->m_cTargets = m_cTargets; - - Q_memcpy(pMulti->m_iTargetName, m_iTargetName, sizeof(m_iTargetName)); - Q_memcpy(pMulti->m_flTargetDelay, m_flTargetDelay, sizeof(m_flTargetDelay)); - -#ifdef REGAMEDLL_FIXES - // Add entity in hash table, otherwise, - // it will not be reset for the entity via UTIL_RestartRound - MAKE_STRING_CLASS("multi_manager", pMulti->pev); -#endif - - return pMulti; -} - -// The USE function builds the time table and starts the entity thinking. -void CMultiManager::ManagerUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - // In multiplayer games, clone the MM and execute in the clone (like a thread) - // to allow multiple players to trigger the same multimanager - if (ShouldClone()) - { - CMultiManager *pClone = Clone(); - pClone->ManagerUse(pActivator, pCaller, useType, value); - return; - } - - m_hActivator = pActivator; - m_index = 0; - m_startTime = gpGlobals->time; - - // disable use until all targets have fired - SetUse(nullptr); - SetThink(&CMultiManager::ManagerThink); - - pev->nextthink = gpGlobals->time; -} - -LINK_ENTITY_TO_CLASS(env_render, CRenderFxManager, CCSRenderFxManager) - -void CRenderFxManager::Spawn() -{ - pev->solid = SOLID_NOT; -} - -#ifdef REGAMEDLL_FIXES - -void CRenderFxManager::OnDestroy() -{ - m_RenderGroups.Purge(); -} - -void CRenderFxManager::Restart() -{ - if (FStringNull(pev->target)) - return; - - edict_t *pentTarget = nullptr; - while ((pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)))) - { - if (FNullEnt(pentTarget)) - break; - - entvars_t *pevTarget = VARS(pentTarget); - - // find render groups in our list of backup - int index = m_RenderGroups.Find(ENTINDEX(pevTarget)); - if (index == m_RenderGroups.InvalidIndex()) { - // not found - continue; - } - - RenderGroup_t *pGroup = &m_RenderGroups[index]; - if (!(pev->spawnflags & SF_RENDER_MASKFX)) - pevTarget->renderfx = pGroup->renderfx; - - if (!(pev->spawnflags & SF_RENDER_MASKAMT)) - pevTarget->renderamt = pGroup->renderamt; - - if (!(pev->spawnflags & SF_RENDER_MASKMODE)) - pevTarget->rendermode = pGroup->rendermode; - - if (!(pev->spawnflags & SF_RENDER_MASKCOLOR)) - pevTarget->rendercolor = pGroup->rendercolor; - } -} - -#endif // REGAMEDLL_FIXES - -void CRenderFxManager::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - if (FStringNull(pev->target)) - return; - - edict_t *pentTarget = nullptr; - while ((pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)))) - { - if (FNullEnt(pentTarget)) - break; - - entvars_t *pevTarget = VARS(pentTarget); - -#ifdef REGAMEDLL_FIXES - RenderGroup_t group; - group.renderfx = pevTarget->renderfx; - group.renderamt = pevTarget->renderamt; - group.rendermode = pevTarget->rendermode; - group.rendercolor = pevTarget->rendercolor; - - int entityIndex = ENTINDEX(pevTarget); - if (m_RenderGroups.Find(entityIndex) == m_RenderGroups.InvalidIndex()) { - m_RenderGroups.Insert(entityIndex, group); - } -#endif - - if (!(pev->spawnflags & SF_RENDER_MASKFX)) - pevTarget->renderfx = pev->renderfx; - - if (!(pev->spawnflags & SF_RENDER_MASKAMT)) - pevTarget->renderamt = pev->renderamt; - - if (!(pev->spawnflags & SF_RENDER_MASKMODE)) - pevTarget->rendermode = pev->rendermode; - - if (!(pev->spawnflags & SF_RENDER_MASKCOLOR)) - pevTarget->rendercolor = pev->rendercolor; - } -} - -LINK_ENTITY_TO_CLASS(trigger, CBaseTrigger, CCSTrigger) - -void CBaseTrigger::InitTrigger() -{ - // trigger angles are used for one-way touches. An angle of 0 is assumed - // to mean no restrictions, so use a yaw of 360 instead. - if (pev->angles != g_vecZero) - { - SetMovedir(pev); - } - - pev->solid = SOLID_TRIGGER; - pev->movetype = MOVETYPE_NONE; - - // set size and link into world - SET_MODEL(ENT(pev), STRING(pev->model)); - - if (CVAR_GET_FLOAT("showtriggers") == 0) - { - pev->effects |= EF_NODRAW; - } -} - -// Cache user-entity-field values until spawn is called. -void CBaseTrigger::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "damage")) - { - pev->dmg = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "count")) - { - m_cTriggersLeft = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "damagetype")) - { - m_bitsDamageInflict = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseToggle::KeyValue(pkvd); - } -} - -LINK_ENTITY_TO_CLASS(trigger_monsterjump, CTriggerMonsterJump, CCSTriggerMonsterJump) - -void CTriggerMonsterJump::Spawn() -{ - SetMovedir(pev); - - InitTrigger(); - - pev->nextthink = 0; - pev->speed = 200; - m_flHeight = 150; - - if (!FStringNull(pev->targetname)) - { - // if targetted, spawn turned off - pev->solid = SOLID_NOT; - - // Unlink from trigger list - UTIL_SetOrigin(pev, pev->origin); - SetUse(&CTriggerMonsterJump::ToggleUse); - } -} - -void CTriggerMonsterJump::Think() -{ - // kill the trigger for now UNDONE - pev->solid = SOLID_NOT; - - // Unlink from trigger list - UTIL_SetOrigin(pev, pev->origin); - SetThink(nullptr); -} - -void CTriggerMonsterJump::Touch(CBaseEntity *pOther) -{ - entvars_t *pevOther = pOther->pev; - - if (!(pevOther->flags & FL_MONSTER)) - { - // touched by a non-monster. - return; - } - - pevOther->origin.z += 1; - - if ((pevOther->flags & FL_ONGROUND)) - { - // clear the onground so physics don't bitch - pevOther->flags &= ~FL_ONGROUND; - } - - // toss the monster! - pevOther->velocity = pev->movedir * pev->speed; - pevOther->velocity.z += m_flHeight; - pev->nextthink = gpGlobals->time; -} - -LINK_ENTITY_TO_CLASS(trigger_cdaudio, CTriggerCDAudio, CCSTriggerCDAudio) - -// Changes tracks or stops CD when player touches -// HACK: overloaded HEALTH to avoid adding new field -void CTriggerCDAudio::Touch(CBaseEntity *pOther) -{ - // only clients may trigger these events - if (!pOther->IsPlayer()) - { - return; - } - - PlayTrack(pOther->edict()); -} - -void CTriggerCDAudio::Spawn() -{ - InitTrigger(); -} - -void CTriggerCDAudio::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - PlayTrack(pCaller->edict()); -} - -#ifdef REGAMEDLL_FIXES -const char *g_szMP3trackFileMap[] = -{ - "", "", - "media/Half-Life01.mp3", - "media/Prospero01.mp3", - "media/Half-Life12.mp3", - "media/Half-Life07.mp3", - "media/Half-Life10.mp3", - "media/Suspense01.mp3", - "media/Suspense03.mp3", - "media/Half-Life09.mp3", - "media/Half-Life02.mp3", - "media/Half-Life13.mp3", - "media/Half-Life04.mp3", - "media/Half-Life15.mp3", - "media/Half-Life14.mp3", - "media/Half-Life16.mp3", - "media/Suspense02.mp3", - "media/Half-Life03.mp3", - "media/Half-Life08.mp3", - "media/Prospero02.mp3", - "media/Half-Life05.mp3", - "media/Prospero04.mp3", - "media/Half-Life11.mp3", - "media/Half-Life06.mp3", - "media/Prospero03.mp3", - "media/Half-Life17.mp3", - "media/Prospero05.mp3", - "media/Suspense05.mp3", - "media/Suspense07.mp3" -}; -#endif - -void PlayCDTrack(edict_t *pClient, int iTrack) -{ - // Can't play if the client is not connected! - if (!pClient) - return; - - if (iTrack < -1 || iTrack > 30) - { - ALERT(at_console, "TriggerCDAudio - Track %d out of range\n", iTrack); - return; - } - - if (iTrack == -1) - { -#ifdef REGAMEDLL_FIXES - CLIENT_COMMAND(pClient, "mp3 stop\n"); -#else - CLIENT_COMMAND(pClient, "cd stop\n"); -#endif - } - else - { -#ifdef REGAMEDLL_FIXES - CLIENT_COMMAND(pClient, UTIL_VarArgs("mp3 play %s\n", g_szMP3trackFileMap[iTrack])); -#else - char string[64]; - Q_sprintf(string, "cd play %3d\n", iTrack); - CLIENT_COMMAND(pClient, string); -#endif - } -} - -// only plays for ONE client, so only use in single play! -void CTriggerCDAudio::PlayTrack(edict_t *pEdict) -{ - PlayCDTrack(pEdict, int(pev->health)); - - SetTouch(nullptr); - UTIL_Remove(this); -} - -LINK_ENTITY_TO_CLASS(target_cdaudio, CTargetCDAudio, CCSTargetCDAudio) - -void CTargetCDAudio::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "radius")) - { - pev->scale = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CPointEntity::KeyValue(pkvd); - } -} - -void CTargetCDAudio::Spawn() -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - if (pev->scale > 0) - { - pev->nextthink = gpGlobals->time + 1.0f; - } -} - -void CTargetCDAudio::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - Play(pCaller->edict()); -} - -// only plays for ONE client, so only use in single play! -void CTargetCDAudio::Think() -{ - // manually find the single player. - edict_t *pClient = INDEXENT(1); - - // Can't play if the client is not connected! - if (!pClient) - return; - - pev->nextthink = gpGlobals->time + 0.5f; - - if ((pClient->v.origin - pev->origin).Length() <= pev->scale) - { - Play(pClient); - } -} - -void CTargetCDAudio::Play(edict_t *pEdict) -{ - PlayCDTrack(pEdict, int(pev->health)); - UTIL_Remove(this); -} - -LINK_ENTITY_TO_CLASS(trigger_hurt, CTriggerHurt, CCSTriggerHurt) - -void CTriggerHurt::Spawn() -{ - InitTrigger(); - SetTouch(&CTriggerHurt::HurtTouch); - - if (!FStringNull(pev->targetname)) - { - SetUse(&CTriggerHurt::ToggleUse); - } - else - { - SetUse(nullptr); - } - - if (m_bitsDamageInflict & DMG_RADIATION) - { - SetThink(&CTriggerHurt::RadiationThink); - pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); - } - - if (pev->spawnflags & SF_TRIGGER_HURT_START_OFF) - { - // if flagged to Start Turned Off, make trigger nonsolid. - pev->solid = SOLID_NOT; - } - - // Link into the list - UTIL_SetOrigin(pev, pev->origin); -} - -#ifdef REGAMEDLL_FIXES -void CTriggerHurt::Restart() -{ - Vector mins, maxs; - - // Set model is about to destroy these - mins = pev->mins; - maxs = pev->maxs; - - Spawn(); - - UTIL_SetSize(pev, mins, maxs); -} -#endif - -// trigger hurt that causes radiation will do a radius -// check and set the player's geiger counter level -// according to distance from center of trigger -void CTriggerHurt::RadiationThink() -{ - edict_t *pentPlayer; - CBasePlayer *pPlayer = nullptr; - real_t flRange; - entvars_t *pevTarget; - Vector vecSpot1; - Vector vecSpot2; - Vector vecRange; - Vector origin; - Vector view_ofs; - - // check to see if a player is in pvs - // if not, continue - - // set origin to center of trigger so that this check works - origin = pev->origin; - view_ofs = pev->view_ofs; - - pev->origin = (pev->absmin + pev->absmax) * 0.5f; - pev->view_ofs = pev->view_ofs * 0.0f; - - pentPlayer = FIND_CLIENT_IN_PVS(edict()); - - pev->origin = origin; - pev->view_ofs = view_ofs; - - // reset origin - if (!FNullEnt(pentPlayer)) - { - pPlayer = GetClassPtr((CBasePlayer *)VARS(pentPlayer)); - - pevTarget = VARS(pentPlayer); - - // get range to player; - vecSpot1 = (pev->absmin + pev->absmax) * 0.5f; - vecSpot2 = (pevTarget->absmin + pevTarget->absmax) * 0.5f; - - vecRange = vecSpot1 - vecSpot2; - flRange = vecRange.Length(); - - // if player's current geiger counter range is larger - // than range to this trigger hurt, reset player's - // geiger counter range - - if (pPlayer->m_flgeigerRange >= flRange) - { - pPlayer->m_flgeigerRange = flRange; - } - } - - pev->nextthink = gpGlobals->time + 0.25f; -} - -// ToggleUse - If this is the USE function for a trigger, its state will toggle every time it's fired -void CBaseTrigger::ToggleUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - if (pev->solid == SOLID_NOT) - { - // if the trigger is off, turn it on - pev->solid = SOLID_TRIGGER; - - // Force retouch - gpGlobals->force_retouch++; - } - else - { - // turn the trigger off - pev->solid = SOLID_NOT; - } - - UTIL_SetOrigin(pev, pev->origin); -} - -// When touched, a hurt trigger does DMG points of damage each half-second -void CBaseTrigger::HurtTouch(CBaseEntity *pOther) -{ - float fldmg; - - if (!pOther->pev->takedamage) - { - return; - } - - if ((pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH) && !pOther->IsPlayer()) - { - // this trigger is only allowed to touch clients, and this ain't a client. - return; - } - - if ((pev->spawnflags & SF_TRIGGER_HURT_NO_CLIENTS) && pOther->IsPlayer()) - { - return; - } - - // HACKHACK: In multiplayer, players touch this based on packet receipt. - // So the players who send packets later aren't always hurt. Keep track of - // how much time has passed and whether or not you've touched that player - if (g_pGameRules->IsMultiplayer()) - { - if (pev->dmgtime > gpGlobals->time) - { -#ifdef REGAMEDLL_FIXES - if (gpGlobals->time >= pev->pain_finished) -#else - if (gpGlobals->time != pev->pain_finished) -#endif - { - // too early to hurt again, and not same frame with a different entity - if (!pOther->IsPlayer()) - { - return; - } - - int playerMask = 1 << (pOther->entindex() - 1); - - // If I've already touched this player (this time), then bail out - if (pev->impulse & playerMask) - return; - - // Mark this player as touched - // BUGBUG - There can be only 32 players! - pev->impulse |= playerMask; - } - } - else - { - // New clock, "un-touch" all players - pev->impulse = 0; - if (pOther->IsPlayer()) - { - int playerMask = 1 << (pOther->entindex() - 1); - - // Mark this player as touched - // BUGBUG - There can be only 32 players! - pev->impulse |= playerMask; - } - } - } - else - { - // Original code: single player -#ifdef REGAMEDLL_FIXES - if (pev->dmgtime > gpGlobals->time && gpGlobals->time >= pev->pain_finished) -#else - if (pev->dmgtime > gpGlobals->time && gpGlobals->time != pev->pain_finished) -#endif - { - // too early to hurt again, and not same frame with a different entity - return; - } - } - - // If this is time_based damage (poison, radiation), override the pev->dmg with a - // default for the given damage type. Monsters only take time-based damage - // while touching the trigger. Player continues taking damage for a while after - // leaving the trigger - - // 0.5 seconds worth of damage, pev->dmg is damage/second - fldmg = pev->dmg * 0.5f; - - if (fldmg < 0) - { - pOther->TakeHealth(-fldmg, m_bitsDamageInflict); - } - else - { - pOther->TakeDamage(pev, pev, fldmg, m_bitsDamageInflict); - } - - // Store pain time so we can get all of the other entities on this frame - pev->pain_finished = gpGlobals->time; - - // Apply damage every half second - // half second delay until this trigger can hurt toucher again - pev->dmgtime = gpGlobals->time + 0.5f; - - if (pev->target) - { - // trigger has a target it wants to fire. - if (pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE) - { - // if the toucher isn't a client, don't fire the target! - if (!pOther->IsPlayer()) - { - return; - } - } - - SUB_UseTargets(pOther, USE_TOGGLE, 0); - if (pev->spawnflags & SF_TRIGGER_HURT_TARGETONCE) - { - pev->target = 0; - } - } -} - -LINK_ENTITY_TO_CLASS(trigger_multiple, CTriggerMultiple, CCSTriggerMultiple) - -void CTriggerMultiple::Spawn() -{ - if (m_flWait == 0.0f) - m_flWait = 0.2f; - - InitTrigger(); - - assert(("trigger_multiple with health", pev->health == 0)); - - //UTIL_SetOrigin(pev, pev->origin); - //SET_MODEL(ENT(pev), STRING(pev->model)); - - //if (pev->health > 0) - //{ - // if (pev->spawnflags & SF_TRIGGER_MULTIPLE_NOTOUCH) - // { - // ALERT(at_error, "trigger_multiple spawn: health and notouch don't make sense"); - // } - // pev->max_health = pev->health; - // //UNDONE: where to get pfnDie from? - // pev->pfnDie = multi_killed; - // pev->takedamage = DAMAGE_YES; - // pev->solid = SOLID_BBOX; - // // make sure it links into the world - // UTIL_SetOrigin(pev, pev->origin); - //} - //else - { - SetTouch(&CTriggerMultiple::MultiTouch); - } -} - -LINK_ENTITY_TO_CLASS(trigger_once, CTriggerOnce, CCSTriggerOnce) - -void CTriggerOnce::Spawn() -{ -#ifdef REGAMEDLL_FIXES - m_flWait = -2; -#else - m_flWait = -1; -#endif - - CTriggerMultiple::Spawn(); -} - -#ifdef REGAMEDLL_FIXES -void CTriggerOnce::Restart() -{ - m_flWait = -2; - CTriggerMultiple::Spawn(); -} -#endif - -void CBaseTrigger::MultiTouch(CBaseEntity *pOther) -{ - entvars_t *pevToucher = pOther->pev; - - // Only touch clients, monsters, or pushables (depending on flags) - if (((pevToucher->flags & FL_CLIENT) && !(pev->spawnflags & SF_TRIGGER_NOCLIENTS)) - || ((pevToucher->flags & FL_MONSTER) && (pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) - || ((pev->spawnflags & SF_TRIGGER_PUSHABLES) && FClassnameIs(pevToucher, "func_pushable"))) - { - ActivateMultiTrigger(pOther); - } -} - -// the trigger was just touched/killed/used -// self.enemy should be set to the activator so it can be held through a delay -// so wait for the delay time before firing -void CBaseTrigger::ActivateMultiTrigger(CBaseEntity *pActivator) -{ - if (pev->nextthink > gpGlobals->time) - { - // still waiting for reset time - return; - } - - if (!UTIL_IsMasterTriggered(m_sMaster, pActivator)) - { - return; - } - - if (FClassnameIs(pev, "trigger_secret")) - { - if (pev->enemy == nullptr || !FClassnameIs(pev->enemy, "player")) - return; - - gpGlobals->found_secrets++; - } - - if (!FStringNull(pev->noise)) - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *)STRING(pev->noise), VOL_NORM, ATTN_NORM); - } - - // don't trigger again until reset - // pev->takedamage = DAMAGE_NO; - m_hActivator = pActivator; - SUB_UseTargets(m_hActivator, USE_TOGGLE, 0); - - if (pev->message && pActivator->IsPlayer()) - { - UTIL_ShowMessage(STRING(pev->message), pActivator); - } - - if (m_flWait > 0) - { - SetThink(&CBaseTrigger::MultiWaitOver); - pev->nextthink = gpGlobals->time + m_flWait; - } - else - { - // we can't just remove (self) here, because this is a touch function - // called while C code is looping through area links... - SetTouch(nullptr); - pev->nextthink = gpGlobals->time + 0.1f; - -#ifdef REGAMEDLL_FIXES - if (!(pev->spawnflags & SF_TRIGGER_NORESET) && m_flWait == -2) - SetThink(nullptr); - else -#endif - SetThink(&CBaseTrigger::SUB_Remove); - } -} - -// the wait time has passed, so set back up for another activation -void CBaseTrigger::MultiWaitOver() -{ - SetThink(nullptr); -} - -void CBaseTrigger::CounterUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - m_cTriggersLeft--; - m_hActivator = pActivator; - - if (m_cTriggersLeft < 0) - { - return; - } - - BOOL fTellActivator = (m_hActivator && FClassnameIs(m_hActivator->pev, "player") && !(pev->spawnflags & SF_TRIGGER_COUNTER_NOMESSAGE)); - - if (m_cTriggersLeft != 0) - { - if (fTellActivator) - { - // UNDONE: I don't think we want these Quakesque messages - switch (m_cTriggersLeft) - { - case 1: ALERT(at_console, "Only 1 more to go..."); break; - case 2: ALERT(at_console, "Only 2 more to go..."); break; - case 3: ALERT(at_console, "Only 3 more to go..."); break; - default: ALERT(at_console, "There are more to go..."); break; - } - } - - return; - } - - // UNDONE: I don't think we want these Quakesque messages - if (fTellActivator) - { - ALERT(at_console, "Sequence completed!"); - } - - ActivateMultiTrigger(m_hActivator); -} - -LINK_ENTITY_TO_CLASS(trigger_counter, CTriggerCounter, CCSTriggerCounter) - -void CTriggerCounter::Spawn() -{ - // By making the flWait be -1, this counter-trigger will disappear after it's activated - // (but of course it needs cTriggersLeft "uses" before that happens). - m_flWait = -1; - - if (m_cTriggersLeft == 0) - { - m_cTriggersLeft = 2; - } - - SetUse(&CTriggerCounter::CounterUse); -} - -LINK_ENTITY_TO_CLASS(trigger_transition, CTriggerVolume, CCSTriggerVolume) - -// Define space that travels across a level transition -void CTriggerVolume::Spawn() -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - - // set size and link into world - SET_MODEL(ENT(pev), STRING(pev->model)); - - pev->model = 0; - pev->modelindex = 0; -} - -LINK_ENTITY_TO_CLASS(fireanddie, CFireAndDie, CCSFireAndDie) - -void CFireAndDie::Spawn() -{ - MAKE_STRING_CLASS("fireanddie", pev); - // Don't call Precache() - it should be called on restore -} - -void CFireAndDie::Precache() -{ - pev->nextthink = gpGlobals->time + m_flDelay; -} - -void CFireAndDie::Think() -{ - SUB_UseTargets(this, USE_TOGGLE, 0); - UTIL_Remove(this); -} - -// Global Savedata for changelevel trigger -TYPEDESCRIPTION CChangeLevel::m_SaveData[] = -{ - DEFINE_ARRAY(CChangeLevel, m_szMapName, FIELD_CHARACTER, MAX_MAPNAME_LENGHT), - DEFINE_ARRAY(CChangeLevel, m_szLandmarkName, FIELD_CHARACTER, MAX_MAPNAME_LENGHT), - DEFINE_FIELD(CChangeLevel, m_changeTarget, FIELD_STRING), - DEFINE_FIELD(CChangeLevel, m_changeTargetDelay, FIELD_FLOAT), -}; - -LINK_ENTITY_TO_CLASS(trigger_changelevel, CChangeLevel, CCSChangeLevel) -IMPLEMENT_SAVERESTORE(CChangeLevel, CBaseTrigger) - -// Cache user-entity-field values until spawn is called. -void CChangeLevel::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "map")) - { - if (Q_strlen(pkvd->szValue) >= MAX_MAPNAME_LENGHT) - { - ALERT(at_error, "Map name '%s' too long (32 chars)\n", pkvd->szValue); - } - - Q_strcpy(m_szMapName, pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "landmark")) - { - if (Q_strlen(pkvd->szValue) >= MAX_MAPNAME_LENGHT) - { - ALERT(at_error, "Landmark name '%s' too long (32 chars)\n", pkvd->szValue); - } - - Q_strcpy(m_szLandmarkName, pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "changetarget")) - { - m_changeTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "changedelay")) - { - m_changeTargetDelay = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } -#ifdef REGAMEDLL_FIXES - else if (FStrEq(pkvd->szKeyName, "percent_of_players")) - { - m_flPercentOfPlayers = Q_atof(pkvd->szValue); - m_flPercentOfPlayers = clamp(m_flPercentOfPlayers, 0.0f, 1.0f); - pkvd->fHandled = TRUE; - } -#endif - else - { - CBaseTrigger::KeyValue(pkvd); - } -} - -void CChangeLevel::Spawn() -{ - if (FStrEq(m_szMapName, "")) - { - ALERT(at_console, "a trigger_changelevel doesn't have a map"); - } - - if (FStrEq(m_szLandmarkName, "")) - { - ALERT(at_console, "trigger_changelevel to %s doesn't have a landmark", m_szMapName); - } - - if (!FStringNull(pev->targetname)) - { - SetUse(&CChangeLevel::UseChangeLevel); - } - - InitTrigger(); - if (!(pev->spawnflags & SF_CHANGELEVEL_USEONLY)) - { - SetTouch(&CChangeLevel::TouchChangeLevel); - } -} - -void CChangeLevel::ExecuteChangeLevel() -{ - MESSAGE_BEGIN(MSG_ALL, SVC_CDTRACK); - WRITE_BYTE(3); - WRITE_BYTE(3); - MESSAGE_END(); - - MESSAGE_BEGIN(MSG_ALL, SVC_INTERMISSION); - MESSAGE_END(); -} - -edict_t *CChangeLevel::FindLandmark(const char *pLandmarkName) -{ - edict_t *pentLandmark = FIND_ENTITY_BY_STRING(nullptr, "targetname", pLandmarkName); - while (!FNullEnt(pentLandmark)) - { - // Found the landmark - if (FClassnameIs(pentLandmark, "info_landmark")) - return pentLandmark; - else - pentLandmark = FIND_ENTITY_BY_STRING(pentLandmark, "targetname", pLandmarkName); - } - - ALERT(at_error, "Can't find landmark %s\n", pLandmarkName); - return nullptr; -} - -// CChangeLevel::Use - allows level transitions to be -// triggered by buttons, etc. -void CChangeLevel::UseChangeLevel(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - ChangeLevelNow(pActivator); -} - -char st_szNextMap[MAX_MAPNAME_LENGHT]; -char st_szNextSpot[MAX_MAPNAME_LENGHT]; - -void CChangeLevel::ChangeLevelNow(CBaseEntity *pActivator) -{ - edict_t *pentLandmark; - LEVELLIST levels[16]; - - // Don't work in deathmatch - if (g_pGameRules->IsDeathmatch()) - { - return; - } - - // Some people are firing these multiple times in a frame, disable - if (gpGlobals->time == pev->dmgtime) - { - return; - } - - pev->dmgtime = gpGlobals->time; - - CBaseEntity *pPlayer = CBaseEntity::Instance(INDEXENT(1)); - if (!InTransitionVolume(pPlayer, m_szLandmarkName)) - { - ALERT(at_aiconsole, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName); - return; - } - - // Create an entity to fire the changetarget - if (m_changeTarget) - { - CFireAndDie *pFireAndDie = GetClassPtr((CFireAndDie *)nullptr); - - if (pFireAndDie) - { - // Set target and delay - pFireAndDie->pev->target = m_changeTarget; - pFireAndDie->m_flDelay = m_changeTargetDelay; - pFireAndDie->pev->origin = pPlayer->pev->origin; - - // Call spawn - DispatchSpawn(pFireAndDie->edict()); - } - } - - // This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory - Q_strcpy(st_szNextMap, m_szMapName); - - m_hActivator = pActivator; - SUB_UseTargets(pActivator, USE_TOGGLE, 0); - - // Init landmark to nullptr - st_szNextSpot[0] = '\0'; - - // look for a landmark entity - pentLandmark = FindLandmark(m_szLandmarkName); - - if (!FNullEnt(pentLandmark)) - { - Q_strcpy(st_szNextSpot, m_szLandmarkName); - gpGlobals->vecLandmarkOffset = VARS(pentLandmark)->origin; - } - - ALERT(at_console, "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot); - CHANGE_LEVEL(st_szNextMap, st_szNextSpot); -} - -void CChangeLevel::TouchChangeLevel(CBaseEntity *pOther) -{ - if (!pOther->IsPlayer()) - return; - -#ifdef REGAMEDLL_FIXES - if (m_flPercentOfPlayers > 0.0f) - { - int playersInCount = 0; - int playersOutCount = 0; - int playersCount = UTIL_CountPlayersInBrushVolume(true, this, playersInCount, playersOutCount); - - if (m_flPercentOfPlayers > float(playersInCount / playersCount)) - return; - } -#endif - - ChangeLevelNow(pOther); -} - -// Add a transition to the list, but ignore duplicates -// (a designer may have placed multiple trigger_changelevels with the same landmark) -int CChangeLevel::AddTransitionToList(LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark) -{ - int i; - if (!pLevelList || !pMapName || !pLandmarkName || !pentLandmark) - { - return 0; - } - - for (i = 0; i < listCount; i++) - { - if (pLevelList[i].pentLandmark == pentLandmark && Q_strcmp(pLevelList[i].mapName, pMapName) == 0) - { - return 0; - } - } - - Q_strcpy(pLevelList[listCount].mapName, pMapName); - Q_strcpy(pLevelList[listCount].landmarkName, pLandmarkName); - - pLevelList[listCount].pentLandmark = pentLandmark; - pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin; - - return 1; -} - -int BuildChangeList(LEVELLIST *pLevelList, int maxList) -{ - return CChangeLevel::ChangeList(pLevelList, maxList); -} - -int CChangeLevel::InTransitionVolume(CBaseEntity *pEntity, char *pVolumeName) -{ - if (pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION) - return 1; - - // If you're following another entity, follow it through the transition (weapons follow the player) - if (pEntity->pev->movetype == MOVETYPE_FOLLOW) - { - if (pEntity->pev->aiment) - { - pEntity = CBaseEntity::Instance(pEntity->pev->aiment); - } - } - - // Unless we find a trigger_transition, everything is in the volume - int inVolume = 1; - - edict_t *pentVolume = FIND_ENTITY_BY_TARGETNAME(nullptr, pVolumeName); - while (!FNullEnt(pentVolume)) - { - CBaseEntity *pVolume = CBaseEntity::Instance(pentVolume); - - if (pVolume && FClassnameIs(pVolume->pev, "trigger_transition")) - { - // It touches one, it's in the volume - if (pVolume->Intersects(pEntity)) - return 1; - else - { - // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go! - inVolume = 0; - } - } - - pentVolume = FIND_ENTITY_BY_TARGETNAME(pentVolume, pVolumeName); - } - - return inVolume; -} - -// This has grown into a complicated beast -// Can we make this more elegant? -// This builds the list of all transitions on this level and which entities are in their PVS's and can / should -// be moved across. -int CChangeLevel::ChangeList(LEVELLIST *pLevelList, int maxList) -{ - edict_t *pentChangelevel, *pentLandmark; - int i, count = 0; - - // Find all of the possible level changes on this BSP - pentChangelevel = FIND_ENTITY_BY_STRING(nullptr, "classname", "trigger_changelevel"); - - if (FNullEnt(pentChangelevel)) - return 0; - - while (!FNullEnt(pentChangelevel)) - { - CChangeLevel *pTrigger = GetClassPtr((CChangeLevel *)VARS(pentChangelevel)); - if (pTrigger) - { - // Find the corresponding landmark - pentLandmark = FindLandmark(pTrigger->m_szLandmarkName); - if (pentLandmark) - { - // Build a list of unique transitions - if (AddTransitionToList(pLevelList, count, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark)) - { - count++; - - // FULL! - if (count >= maxList) - break; - } - } - } - - pentChangelevel = FIND_ENTITY_BY_STRING(pentChangelevel, "classname", "trigger_changelevel"); - } - - if (gpGlobals->pSaveData && ((SAVERESTOREDATA *)gpGlobals->pSaveData)->pTable) - { - CSave saveHelper((SAVERESTOREDATA *)gpGlobals->pSaveData); - - for (i = 0; i < count; i++) - { - // We can only ever move 512 entities across a transition - const int MAX_ENTITY = 512; - - int j, entityCount = 0; - CBaseEntity *pEntList[MAX_ENTITY]; - int entityFlags[MAX_ENTITY]; - - // Follow the linked list of entities in the PVS of the transition landmark - edict_t *pent = FIND_ENTITY_IN_PVS(pLevelList[i].pentLandmark); - - // Build a list of valid entities in this linked list (we're going to use pent->v.chain again) - while (!FNullEnt(pent)) - { - CBaseEntity *pEntity = CBaseEntity::Instance(pent); - if (pEntity) - { - int caps = pEntity->ObjectCaps(); - - if (!(caps & FCAP_DONT_SAVE)) - { - int flags = 0; - - // If this entity can be moved or is global, mark it - if (caps & FCAP_ACROSS_TRANSITION) - flags |= FENTTABLE_MOVEABLE; - - if (pEntity->pev->globalname && !pEntity->IsDormant()) - flags |= FENTTABLE_GLOBAL; - - if (flags) - { - pEntList[entityCount] = pEntity; - entityFlags[entityCount] = flags; - entityCount++; - - if (entityCount > MAX_ENTITY) - { - ALERT(at_error, "Too many entities across a transition!"); - } - } - } - } - pent = pent->v.chain; - } - - for (j = 0; j < entityCount; j++) - { - // Check to make sure the entity isn't screened out by a trigger_transition - if (entityFlags[j] && InTransitionVolume(pEntList[j], pLevelList[i].landmarkName)) - { - // Mark entity table with 1<mapname = ALLOC_STRING("start"); - pChange = GetClassPtr((CChangeLevel *)nullptr); - Q_strcpy(pChange->m_szMapName, "start"); - } - else - pChange = GetClassPtr((CChangeLevel *)VARS(pent)); - - Q_strcpy(st_szNextMap, pChange->m_szMapName); - g_pGameRules->SetGameOver(); - - if (pChange->pev->nextthink < gpGlobals->time) - { - pChange->SetThink(&CChangeLevel::ExecuteChangeLevel); - pChange->pev->nextthink = gpGlobals->time + 0.1f; - } -} - -LINK_ENTITY_TO_CLASS(func_ladder, CLadder, CCSLadder) - -void CLadder::KeyValue(KeyValueData *pkvd) -{ - CBaseTrigger::KeyValue(pkvd); -} - -// func_ladder - makes an area vertically negotiable -void CLadder::Precache() -{ - // Do all of this in here because we need to 'convert' old saved games - pev->solid = SOLID_NOT; - pev->skin = CONTENTS_LADDER; - - if (CVAR_GET_FLOAT("showtriggers") == 0) - { - pev->rendermode = kRenderTransTexture; - pev->renderamt = 0; - } - - pev->effects &= ~EF_NODRAW; -} - -void CLadder::Spawn() -{ - Precache(); - - // set size and link into world - SET_MODEL(ENT(pev), STRING(pev->model)); - pev->movetype = MOVETYPE_PUSH; -} - -LINK_ENTITY_TO_CLASS(trigger_push, CTriggerPush, CCSTriggerPush) - -void CTriggerPush::KeyValue(KeyValueData *pkvd) -{ - CBaseTrigger::KeyValue(pkvd); -} - -void CTriggerPush::Spawn() -{ - if (pev->angles == g_vecZero) - { - pev->angles.y = 360; - } - - InitTrigger(); - - if (pev->speed == 0) - { - pev->speed = 100; - } - - // if flagged to Start Turned Off, make trigger nonsolid. - if (pev->spawnflags & SF_TRIGGER_PUSH_START_OFF) - { - pev->solid = SOLID_NOT; - } - - SetUse(&CTriggerPush::ToggleUse); - - // Link into the list - UTIL_SetOrigin(pev, pev->origin); -} - -#ifdef REGAMEDLL_FIXES -void CTriggerPush::Restart() -{ - auto tempDir = pev->movedir; - Spawn(); - pev->movedir = tempDir; -} -#endif - -void CTriggerPush::Touch(CBaseEntity *pOther) -{ - entvars_t *pevToucher = pOther->pev; - - // UNDONE: Is there a better way than health to detect things that have physics? (clients/monsters) - switch (pevToucher->movetype) - { - case MOVETYPE_NONE: - case MOVETYPE_PUSH: - case MOVETYPE_NOCLIP: - case MOVETYPE_FOLLOW: - return; - } - - if (pevToucher->solid != SOLID_NOT && pevToucher->solid != SOLID_BSP) - { - // Instant trigger, just transfer velocity and remove - if (pev->spawnflags & SF_TRIGGER_PUSH_ONCE) - { - pevToucher->velocity = pevToucher->velocity + (pev->speed * pev->movedir); - - if (pevToucher->velocity.z > 0) - { - pevToucher->flags &= ~FL_ONGROUND; - } - - UTIL_Remove(this); - } - else - { - // Push field, transfer to base velocity - Vector vecPush = (pev->speed * pev->movedir); - if (pevToucher->flags & FL_BASEVELOCITY) - { - vecPush = vecPush + pevToucher->basevelocity; - } - - pevToucher->basevelocity = vecPush; - pevToucher->flags |= FL_BASEVELOCITY; - } - } -} - -void CBaseTrigger::TeleportTouch(CBaseEntity *pOther) -{ - entvars_t *pevToucher = pOther->pev; - edict_t *pentTarget = nullptr; - - // Only teleport monsters or clients - if (!(pevToucher->flags & (FL_CLIENT | FL_MONSTER))) - { - return; - } - - if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) - { - return; - } - - if (!(pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) - { - // no monsters allowed! - if (pevToucher->flags & FL_MONSTER) - { - return; - } - } - - if ((pev->spawnflags & SF_TRIGGER_NOCLIENTS)) - { - // no clients allowed - if (pOther->IsPlayer()) - { - return; - } - } - - pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); - if (FNullEnt(pentTarget)) - { - return; - } - - Vector tmp = VARS(pentTarget)->origin; - - if (pOther->IsPlayer()) - { - // make origin adjustments in case the teleportee is a player. (origin in center, not at feet) - tmp.z -= pOther->pev->mins.z; - } - - tmp.z++; - - pevToucher->flags &= ~FL_ONGROUND; - - UTIL_SetOrigin(pevToucher, tmp); - - pevToucher->angles = pentTarget->v.angles; - - if (pOther->IsPlayer()) - { - pevToucher->v_angle = pentTarget->v.angles; - } - - pevToucher->fixangle = 1; - pevToucher->velocity = pevToucher->basevelocity = g_vecZero; -} - -LINK_ENTITY_TO_CLASS(trigger_teleport, CTriggerTeleport, CCSTriggerTeleport) - -void CTriggerTeleport::Spawn() -{ - InitTrigger(); - SetTouch(&CTriggerTeleport::TeleportTouch); -} - -LINK_ENTITY_TO_CLASS(info_teleport_destination, CPointEntity, CCSPointEntity) -LINK_ENTITY_TO_CLASS(func_buyzone, CBuyZone, CCSBuyZone) - -void CBuyZone::Spawn() -{ - InitTrigger(); - SetTouch(&CBuyZone::BuyTouch); - - if (pev->team > CT || pev->team < UNASSIGNED) - { - ALERT(at_console, "Bad team number (%i) in func_buyzone\n", pev->team); - pev->team = UNASSIGNED; - } -} - -void CBuyZone::BuyTouch(CBaseEntity *pOther) -{ -#ifdef REGAMEDLL_ADD - if (buytime.value == 0.0f) - return; -#endif - - if (!pOther->IsPlayer()) - return; - - CBasePlayer *pPlayer = static_cast(pOther); - - if (pev->team == UNASSIGNED || pev->team == pPlayer->m_iTeam) - { -#ifdef REGAMEDLL_FIXES - if (!CSGameRules()->CanPlayerBuy(pPlayer)) - return; -#endif - - pPlayer->m_signals.Signal(SIGNAL_BUY); - } -} - -LINK_ENTITY_TO_CLASS(func_bomb_target, CBombTarget, CCSBombTarget) - -void CBombTarget::Spawn() -{ - InitTrigger(); - - SetTouch(&CBombTarget::BombTargetTouch); - SetUse(&CBombTarget::BombTargetUse); -} - -bool CBombTarget::IsPlayerInBombSite(CBasePlayer *pPlayer) -{ - const Vector &absmin = pPlayer->pev->absmin; - const Vector &absmax = pPlayer->pev->absmax; - - // Ensure that player's body is inside func_bomb_target's X,Y axes. - if (pev->absmin.x > absmin.x || pev->absmin.y > absmin.y) - { - return false; - } - if (pev->absmax.x < absmax.x || pev->absmax.y < absmax.y) - { - return false; - } - - return true; -} - -void CBombTarget::BombTargetTouch(CBaseEntity *pOther) -{ - if (!pOther->IsPlayer()) - return; - - CBasePlayer *pPlayer = static_cast(pOther); - - if (pPlayer->m_bHasC4 -#ifdef REGAMEDLL_FIXES - && (legacy_bombtarget_touch.value || IsPlayerInBombSite(pPlayer)) -#endif - ) - { - pPlayer->m_signals.Signal(SIGNAL_BOMB); - pPlayer->m_pentCurBombTarget = ENT(pev); - } -} - -void CBombTarget::BombTargetUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - SUB_UseTargets(nullptr, USE_TOGGLE, 0); -} - -LINK_ENTITY_TO_CLASS(func_hostage_rescue, CHostageRescue, CCSHostageRescue) - -void CHostageRescue::Spawn() -{ - InitTrigger(); - SetTouch(&CHostageRescue::HostageRescueTouch); -} - -void CHostageRescue::HostageRescueTouch(CBaseEntity *pOther) -{ - if (pOther->IsPlayer()) - { - ((CBasePlayer *)pOther)->m_signals.Signal(SIGNAL_RESCUE); - } - - if (FClassnameIs(pOther->pev, "hostage_entity")) - { - ((CHostage *)pOther)->m_bRescueMe = TRUE; - } -} - -LINK_ENTITY_TO_CLASS(func_escapezone, CEscapeZone, CCSEscapeZone) - -void CEscapeZone::Spawn() -{ - InitTrigger(); - SetTouch(&CEscapeZone::EscapeTouch); -} - -void CEscapeZone::EscapeTouch(CBaseEntity *pOther) -{ - if (!pOther->IsPlayer()) - return; - - CBasePlayer *pEscapee = static_cast(pOther); - - switch (pEscapee->m_iTeam) - { - case TERRORIST: - if (!pEscapee->m_bEscaped) - { - pEscapee->m_bEscaped = true; - CSGameRules()->CheckWinConditions(); - - UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Terrorist_Escaped\"\n", - STRING(pEscapee->pev->netname), GETPLAYERUSERID(pEscapee->edict()), GETPLAYERAUTHID(pEscapee->edict())); - - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - - if (!pPlayer || FNullEnt(pPlayer->pev)) - continue; - - if (pPlayer->m_iTeam == pEscapee->m_iTeam) - { - ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "#Terrorist_Escaped"); - } - } - } - break; - case CT: - pEscapee->m_signals.Signal(SIGNAL_ESCAPE); - break; - } -} - -LINK_ENTITY_TO_CLASS(func_vip_safetyzone, CVIP_SafetyZone, CCSVIP_SafetyZone) - -void CVIP_SafetyZone::Spawn() -{ - InitTrigger(); - SetTouch(&CVIP_SafetyZone::VIP_SafetyTouch); -} - -void CVIP_SafetyZone::VIP_SafetyTouch(CBaseEntity *pOther) -{ - if (!pOther->IsPlayer()) - return; - - CBasePlayer *pEscapee = static_cast(pOther); - pEscapee->m_signals.Signal(SIGNAL_VIPSAFETY); - - if (pEscapee->m_bIsVIP) - { - UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Escaped_As_VIP\"\n", - STRING(pEscapee->pev->netname), GETPLAYERUSERID(pEscapee->edict()), GETPLAYERAUTHID(pEscapee->edict())); - - pEscapee->m_bEscaped = true; - - pEscapee->Disappear(); - pEscapee->AddAccount(REWARD_VIP_HAVE_SELF_RESCUED, RT_VIP_RESCUED_MYSELF); - } -} - -LINK_ENTITY_TO_CLASS(trigger_autosave, CTriggerSave, CCSTriggerSave) - -void CTriggerSave::Spawn() -{ - if (g_pGameRules->IsDeathmatch()) - { - REMOVE_ENTITY(ENT(pev)); - return; - } - - InitTrigger(); - SetTouch(&CTriggerSave::SaveTouch); -} - -void CTriggerSave::SaveTouch(CBaseEntity *pOther) -{ - if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) - return; - - // Only save on clients - if (!pOther->IsPlayer()) - return; - - SetTouch(nullptr); - UTIL_Remove(this); - SERVER_COMMAND("autosave\n"); -} - -LINK_ENTITY_TO_CLASS(trigger_endsection, CTriggerEndSection, CCSTriggerEndSection) - -void CTriggerEndSection::EndSectionUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - // Only save on clients - if (pActivator && !pActivator->IsNetClient()) - return; - - SetUse(nullptr); - if (!FStringNull(pev->message)) - { - END_SECTION(STRING(pev->message)); - } - - UTIL_Remove(this); -} - -void CTriggerEndSection::Spawn() -{ - if (g_pGameRules->IsDeathmatch()) - { - REMOVE_ENTITY(ENT(pev)); - return; - } - - InitTrigger(); - SetUse(&CTriggerEndSection::EndSectionUse); - - // If it is a "use only" trigger, then don't set the touch function. - if (!(pev->spawnflags & SF_ENDSECTION_USEONLY)) - { - SetTouch(&CTriggerEndSection::EndSectionTouch); - } -} - -void CTriggerEndSection::EndSectionTouch(CBaseEntity *pOther) -{ - // Only save on clients - if (!pOther->IsNetClient()) - return; - - SetTouch(nullptr); - if (!FStringNull(pev->message)) - { - END_SECTION(STRING(pev->message)); - } - - UTIL_Remove(this); -} - -void CTriggerEndSection::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "section")) - { - // Store this in message so we don't have to write save/restore for this ent - pev->message = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseTrigger::KeyValue(pkvd); - } -} - -LINK_ENTITY_TO_CLASS(trigger_gravity, CTriggerGravity, CCSTriggerGravity) - -void CTriggerGravity::Spawn() -{ - InitTrigger(); - SetTouch(&CTriggerGravity::GravityTouch); -} - -void CTriggerGravity::GravityTouch(CBaseEntity *pOther) -{ - // Only save on clients - if (!pOther->IsPlayer()) - return; - - pOther->pev->gravity = pev->gravity; -} - -TYPEDESCRIPTION CTriggerChangeTarget::m_SaveData[] = -{ - DEFINE_FIELD(CTriggerChangeTarget, m_iszNewTarget, FIELD_STRING), -}; - -LINK_ENTITY_TO_CLASS(trigger_changetarget, CTriggerChangeTarget, CCSTriggerChangeTarget) -IMPLEMENT_SAVERESTORE(CTriggerChangeTarget, CBaseDelay) - -void CTriggerChangeTarget::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "m_iszNewTarget")) - { - m_iszNewTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue(pkvd); - } -} - -void CTriggerChangeTarget::Spawn() -{ - ; -} - -void CTriggerChangeTarget::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - CBaseEntity *pTarget = UTIL_FindEntityByTargetname(nullptr, pev->target); - if (pTarget) - { - pTarget->pev->target = m_iszNewTarget; - - CBaseMonster *pMonster = pTarget->MyMonsterPointer(); - if (pMonster) - { - pMonster->m_pGoalEnt = nullptr; - } - } -} - -// Global Savedata for changelevel friction modifier -TYPEDESCRIPTION CTriggerCamera::m_SaveData[] = -{ - DEFINE_FIELD(CTriggerCamera, m_hPlayer, FIELD_EHANDLE), - DEFINE_FIELD(CTriggerCamera, m_hTarget, FIELD_EHANDLE), - DEFINE_FIELD(CTriggerCamera, m_pentPath, FIELD_CLASSPTR), - DEFINE_FIELD(CTriggerCamera, m_sPath, FIELD_STRING), - DEFINE_FIELD(CTriggerCamera, m_flWait, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_flReturnTime, FIELD_TIME), - DEFINE_FIELD(CTriggerCamera, m_flStopTime, FIELD_TIME), - DEFINE_FIELD(CTriggerCamera, m_moveDistance, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_targetSpeed, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_initialSpeed, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_acceleration, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_deceleration, FIELD_FLOAT), - DEFINE_FIELD(CTriggerCamera, m_state, FIELD_INTEGER), -}; - -LINK_ENTITY_TO_CLASS(trigger_camera, CTriggerCamera, CCSTriggerCamera) -IMPLEMENT_SAVERESTORE(CTriggerCamera, CBaseDelay) - -void CTriggerCamera::Spawn() -{ - pev->movetype = MOVETYPE_NOCLIP; - - // Remove model & collisions - pev->solid = SOLID_NOT; - - // The engine won't draw this model if this is set to 0 and blending is on - pev->renderamt = 0; - pev->rendermode = kRenderTransTexture; - - m_initialSpeed = pev->speed; - - if (m_acceleration == 0) - { - m_acceleration = 500; - } - if (m_deceleration == 0) - { - m_deceleration = 500; - } -} - -void CTriggerCamera::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "moveto")) - { - m_sPath = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "acceleration")) - { - m_acceleration = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "deceleration")) - { - m_deceleration = Q_atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseDelay::KeyValue(pkvd); - } -} - -void CTriggerCamera::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) -{ - if (!ShouldToggle(useType, m_state)) - return; - - // Toggle state - m_state = !m_state; - if (m_state == 0) - { - m_flReturnTime = gpGlobals->time; - - if (pActivator && pActivator->IsPlayer()) - { - ((CBasePlayer *)pActivator)->ResetMaxSpeed(); - } - - return; - } - - if (!pActivator || !pActivator->IsPlayer()) - { - pActivator = CBaseEntity::Instance(INDEXENT(1)); - } - - m_hPlayer = static_cast(pActivator); - m_flReturnTime = gpGlobals->time + m_flWait; - - pev->speed = m_initialSpeed; - m_targetSpeed = m_initialSpeed; - - if (pev->spawnflags & SF_CAMERA_PLAYER_TARGET) - { - m_hTarget = m_hPlayer; - } - else - { - m_hTarget = GetNextTarget(); - } - - // Nothing to look at! - if (!m_hTarget) - { - return; - } - - if (pActivator->IsPlayer()) - { - SET_CLIENT_MAXSPEED(pActivator->edict(), 0.001); - } - - if (pev->spawnflags & SF_CAMERA_PLAYER_TAKECONTROL) - { - ((CBasePlayer *)pActivator)->EnableControl(FALSE); - } - - if (m_sPath) - { - m_pentPath = Instance(FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(m_sPath))); - } - else - { - m_pentPath = nullptr; - } - - m_flStopTime = gpGlobals->time; - if (m_pentPath) - { - if (m_pentPath->pev->speed != 0) - m_targetSpeed = m_pentPath->pev->speed; - - m_flStopTime += m_pentPath->GetDelay(); - } - - // copy over player information - if (pev->spawnflags & SF_CAMERA_PLAYER_POSITION) - { - UTIL_SetOrigin(pev, pActivator->pev->origin + pActivator->pev->view_ofs); - - pev->angles.x = -pActivator->pev->angles.x; - pev->angles.y = pActivator->pev->angles.y; - pev->angles.z = 0; - pev->velocity = pActivator->pev->velocity; - } - else - { - pev->velocity = Vector(0, 0, 0); - } - - SET_VIEW(pActivator->edict(), edict()); - SET_MODEL(ENT(pev), STRING(pActivator->pev->model)); - - // follow the player down - SetThink(&CTriggerCamera::FollowTarget); - - pev->nextthink = gpGlobals->time; - m_moveDistance = 0; - Move(); -} - -void CTriggerCamera::FollowTarget() -{ - if (!m_hPlayer) - return; - - if (!m_hTarget || m_flReturnTime < gpGlobals->time) - { - if (m_hPlayer->IsAlive()) - { - SET_VIEW(m_hPlayer->edict(), m_hPlayer->edict()); - - m_hPlayer->EnableControl(TRUE); - m_hPlayer->ResetMaxSpeed(); - } - - SUB_UseTargets(this, USE_TOGGLE, 0); - pev->avelocity = Vector(0, 0, 0); - m_state = 0; - return; - } - - Vector vecGoal = UTIL_VecToAngles(m_hTarget->pev->origin - pev->origin); - vecGoal.x = -vecGoal.x; - - if (pev->angles.y > 360) - pev->angles.y -= 360; - - if (pev->angles.y < 0) - pev->angles.y += 360; - - real_t dx = vecGoal.x - pev->angles.x; - real_t dy = vecGoal.y - pev->angles.y; - - if (dx < -180) - dx += 360; - if (dx > 180) - dx = dx - 360; - - if (dy < -180) - dy += 360; - if (dy > 180) - dy = dy - 360; - - pev->avelocity.x = dx * 40 * gpGlobals->frametime; - pev->avelocity.y = dy * 40 * gpGlobals->frametime; - - if (!(pev->spawnflags & SF_CAMERA_PLAYER_TAKECONTROL)) - { - pev->velocity = pev->velocity * 0.8f; - - if (pev->velocity.Length() < 10.0) - { - pev->velocity = g_vecZero; - } - } - - pev->nextthink = gpGlobals->time; - Move(); -} - -void CTriggerCamera::Move() -{ - // Not moving on a path, return - if (!m_pentPath) - return; - - // Subtract movement from the previous frame - m_moveDistance -= pev->speed * gpGlobals->frametime; - - // Have we moved enough to reach the target? - if (m_moveDistance <= 0) - { - // Fire the passtarget if there is one - if (!FStringNull(m_pentPath->pev->message)) - { - FireTargets(STRING(m_pentPath->pev->message), this, this, USE_TOGGLE, 0); - - if (m_pentPath->pev->spawnflags & SF_CORNER_FIREONCE) - { - m_pentPath->pev->message = 0; - } - } - - // Time to go to the next target - m_pentPath = m_pentPath->GetNextTarget(); - - // Set up next corner - if (!m_pentPath) - { - pev->velocity = g_vecZero; - } - else - { - if (m_pentPath->pev->speed != 0) - { - m_targetSpeed = m_pentPath->pev->speed; - } - - Vector delta = m_pentPath->pev->origin - pev->origin; - m_moveDistance = delta.Length(); - pev->movedir = delta.Normalize(); - m_flStopTime = gpGlobals->time + m_pentPath->GetDelay(); - } - } - - if (m_flStopTime > gpGlobals->time) - { - pev->speed = UTIL_Approach(0, pev->speed, m_deceleration * gpGlobals->frametime); - } - else - { - pev->speed = UTIL_Approach(m_targetSpeed, pev->speed, m_acceleration * gpGlobals->frametime); - } - - real_t fraction = 2 * gpGlobals->frametime; - pev->velocity = ((pev->movedir * pev->speed) * fraction) + (pev->velocity * (1 - fraction)); -} - -LINK_ENTITY_TO_CLASS(env_snow, CWeather, CCSWeather) -LINK_ENTITY_TO_CLASS(func_snow, CWeather, CCSWeather) -LINK_ENTITY_TO_CLASS(env_rain, CWeather, CCSWeather) -LINK_ENTITY_TO_CLASS(func_rain, CWeather, CCSWeather) - -void CWeather::Spawn() -{ - InitTrigger(); -} - -void CClientFog::KeyValue(KeyValueData *pkvd) -{ -#if 0 - if (FStrEq(pkvd->szKeyName, "startdist")) - { - m_iStartDist = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "enddist")) - { - m_iEndDist = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else -#endif - if (FStrEq(pkvd->szKeyName, "density")) - { - m_fDensity = Q_atof(pkvd->szValue); - - if (m_fDensity < 0 || m_fDensity > 0.01) - m_fDensity = 0; - - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue(pkvd); - } -} - -void CClientFog::Spawn() -{ - pev->movetype = MOVETYPE_NOCLIP; - pev->solid = SOLID_NOT; // Remove model & collisions - pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on - pev->rendermode = kRenderTransTexture; -} - -LINK_ENTITY_TO_CLASS(env_fog, CClientFog, CCSClientFog) +#include "precompiled.h" + +// Global Savedata for changelevel friction modifier +TYPEDESCRIPTION CFrictionModifier::m_SaveData[] = +{ + DEFINE_FIELD(CFrictionModifier, m_frictionFraction, FIELD_FLOAT), +}; + +LINK_ENTITY_TO_CLASS(func_friction, CFrictionModifier, CCSFrictionModifier) +IMPLEMENT_SAVERESTORE(CFrictionModifier, CBaseEntity) + +void CFrictionModifier::Spawn() +{ + pev->solid = SOLID_TRIGGER; + + // set size and link into world + SET_MODEL(ENT(pev), STRING(pev->model)); + + pev->movetype = MOVETYPE_NONE; + SetTouch(&CFrictionModifier::ChangeFriction); +} + +// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) +void CFrictionModifier::ChangeFriction(CBaseEntity *pOther) +{ + if (pOther->pev->movetype != MOVETYPE_BOUNCEMISSILE && pOther->pev->movetype != MOVETYPE_BOUNCE) + { + pOther->pev->friction = m_frictionFraction; + } +} + +// Sets toucher's friction to m_frictionFraction (1.0 = normal friction) +void CFrictionModifier::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "modifier")) + { + m_frictionFraction = Q_atof(pkvd->szValue) / 100.0f; + pkvd->fHandled = TRUE; + } + else + { + CBaseEntity::KeyValue(pkvd); + } +} + +TYPEDESCRIPTION CAutoTrigger::m_SaveData[] = +{ + DEFINE_FIELD(CAutoTrigger, m_globalstate, FIELD_STRING), + DEFINE_FIELD(CAutoTrigger, m_triggerType, FIELD_INTEGER), +}; + +LINK_ENTITY_TO_CLASS(trigger_auto, CAutoTrigger, CCSAutoTrigger) +IMPLEMENT_SAVERESTORE(CAutoTrigger, CBaseDelay) + +void CAutoTrigger::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "globalstate")) + { + m_globalstate = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "triggerstate")) + { + int type = Q_atoi(pkvd->szValue); + switch (type) + { + case 0: + m_triggerType = USE_OFF; + break; + case 2: + m_triggerType = USE_TOGGLE; + break; + default: + m_triggerType = USE_ON; + break; + } + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue(pkvd); + } +} + +void CAutoTrigger::Spawn() +{ + Precache(); +} + +void CAutoTrigger::Precache() +{ + pev->nextthink = gpGlobals->time + 0.1f; +} + +void CAutoTrigger::Think() +{ + if (!m_globalstate || gGlobalState.EntityGetState(m_globalstate) == GLOBAL_ON) + { + SUB_UseTargets(this, m_triggerType, 0); + +#ifdef REGAMEDLL_FIXES + if (pev->spawnflags & SF_AUTO_NORESET) +#else + if (pev->spawnflags & SF_AUTO_FIREONCE) +#endif + { + UTIL_Remove(this); + } + } +} + +#ifdef REGAMEDLL_FIXES +void CAutoTrigger::Restart() +{ + if (pev->spawnflags & SF_AUTO_NORESET) + return; + + pev->nextthink = gpGlobals->time + 0.1f; +} +#endif + +TYPEDESCRIPTION CTriggerRelay::m_SaveData[] = +{ + DEFINE_FIELD(CTriggerRelay, m_triggerType, FIELD_INTEGER), +}; + +LINK_ENTITY_TO_CLASS(trigger_relay, CTriggerRelay, CCSTriggerRelay) +IMPLEMENT_SAVERESTORE(CTriggerRelay, CBaseDelay) + +void CTriggerRelay::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "triggerstate")) + { + int type = Q_atoi(pkvd->szValue); + switch (type) + { + case 0: + m_triggerType = USE_OFF; + break; + case 2: + m_triggerType = USE_TOGGLE; + break; + default: + m_triggerType = USE_ON; + break; + } + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue(pkvd); + } +} + +void CTriggerRelay::Spawn() +{ + ; +} + +void CTriggerRelay::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + SUB_UseTargets(this, m_triggerType, 0); + if (pev->spawnflags & SF_RELAY_FIREONCE) + { + UTIL_Remove(this); + } +} + +// Global Savedata for multi_manager +TYPEDESCRIPTION CMultiManager::m_SaveData[] = +{ + DEFINE_FIELD(CMultiManager, m_cTargets, FIELD_INTEGER), + DEFINE_FIELD(CMultiManager, m_index, FIELD_INTEGER), + DEFINE_FIELD(CMultiManager, m_startTime, FIELD_TIME), + DEFINE_ARRAY(CMultiManager, m_iTargetName, FIELD_STRING, MAX_MM_TARGETS), + DEFINE_ARRAY(CMultiManager, m_flTargetDelay, FIELD_FLOAT, MAX_MM_TARGETS), +}; + +LINK_ENTITY_TO_CLASS(multi_manager, CMultiManager, CCSMultiManager) +IMPLEMENT_SAVERESTORE(CMultiManager, CBaseToggle) + +void CMultiManager::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else // add this field to the target list + { + // this assumes that additional fields are targetnames and their values are delay values. + if (m_cTargets < MAX_MM_TARGETS) + { + char tmp[128]; + + UTIL_StripToken(pkvd->szKeyName, tmp); + m_iTargetName[m_cTargets] = ALLOC_STRING(tmp); + m_flTargetDelay[m_cTargets] = Q_atof(pkvd->szValue); + m_cTargets++; + + pkvd->fHandled = TRUE; + } + } +} + +void CMultiManager::Spawn() +{ + pev->solid = SOLID_NOT; + SetUse(&CMultiManager::ManagerUse); + SetThink(&CMultiManager::ManagerThink); + + // Sort targets + // Quick and dirty bubble sort + bool bSwapped = true; + + while (bSwapped) + { + bSwapped = false; + for (int i = 1; i < m_cTargets; i++) + { + if (m_flTargetDelay[i] < m_flTargetDelay[i - 1]) + { + // Swap out of order elements + int name = m_iTargetName[i]; + float delay = m_flTargetDelay[i]; + + m_iTargetName[i] = m_iTargetName[i - 1]; + m_flTargetDelay[i] = m_flTargetDelay[i - 1]; + m_iTargetName[i - 1] = name; + m_flTargetDelay[i - 1] = delay; + bSwapped = true; + } + } + } +} + +void CMultiManager::Restart() +{ +#ifndef REGAMEDLL_FIXES + edict_t *pentTarget = nullptr; + + for (int i = 0; i < m_cTargets; i++) + { + const char *name = STRING(m_iTargetName[i]); + + if (!name) + continue; + + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); + + if (FNullEnt(pentTarget)) + break; + + CBaseEntity *pTarget = static_cast(CBaseEntity::Instance(pentTarget)); + if (pTarget && !(pTarget->pev->flags & FL_KILLME)) + { + pTarget->Restart(); + } + } +#endif + + SetThink(nullptr); + + if (IsClone()) + { + UTIL_Remove(this); + return; + } + + SetUse(&CMultiManager::ManagerUse); + m_index = 0; +} + +BOOL CMultiManager::HasTarget(string_t targetname) +{ + for (int i = 0; i < m_cTargets; i++) + { + if (FStrEq(STRING(targetname), STRING(m_iTargetName[i]))) + { + return TRUE; + } + } + + return FALSE; +} + +// Designers were using this to fire targets that may or may not exist -- +// so I changed it to use the standard target fire code, made it a little simpler. +void CMultiManager::ManagerThink() +{ + float time; + + time = gpGlobals->time - m_startTime; + while (m_index < m_cTargets && m_flTargetDelay[m_index] <= time) + { + FireTargets(STRING(m_iTargetName[m_index]), m_hActivator, this, USE_TOGGLE, 0); + m_index++; + } + + // have we fired all targets? + if (m_index >= m_cTargets) + { + SetThink(nullptr); + if (IsClone()) + { + UTIL_Remove(this); + return; + } + + // allow manager re-use + SetUse(&CMultiManager::ManagerUse); + } + else + { + pev->nextthink = m_startTime + m_flTargetDelay[m_index]; + } +} + +CMultiManager *CMultiManager::Clone() +{ + CMultiManager *pMulti = GetClassPtr((CMultiManager *)nullptr); + + edict_t *pEdict = pMulti->pev->pContainingEntity; + Q_memcpy(pMulti->pev, pev, sizeof(*pev)); + + pMulti->pev->pContainingEntity = pEdict; + pMulti->pev->spawnflags |= SF_MULTIMAN_CLONE; + pMulti->m_cTargets = m_cTargets; + + Q_memcpy(pMulti->m_iTargetName, m_iTargetName, sizeof(m_iTargetName)); + Q_memcpy(pMulti->m_flTargetDelay, m_flTargetDelay, sizeof(m_flTargetDelay)); + +#ifdef REGAMEDLL_FIXES + // Add entity in hash table, otherwise, + // it will not be reset for the entity via UTIL_RestartRound + MAKE_STRING_CLASS("multi_manager", pMulti->pev); +#endif + + return pMulti; +} + +// The USE function builds the time table and starts the entity thinking. +void CMultiManager::ManagerUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + // In multiplayer games, clone the MM and execute in the clone (like a thread) + // to allow multiple players to trigger the same multimanager + if (ShouldClone()) + { + CMultiManager *pClone = Clone(); + pClone->ManagerUse(pActivator, pCaller, useType, value); + return; + } + + m_hActivator = pActivator; + m_index = 0; + m_startTime = gpGlobals->time; + + // disable use until all targets have fired + SetUse(nullptr); + SetThink(&CMultiManager::ManagerThink); + + pev->nextthink = gpGlobals->time; +} + +LINK_ENTITY_TO_CLASS(env_render, CRenderFxManager, CCSRenderFxManager) + +void CRenderFxManager::Spawn() +{ + pev->solid = SOLID_NOT; +} + +#ifdef REGAMEDLL_FIXES + +void CRenderFxManager::OnDestroy() +{ + m_RenderGroups.Purge(); +} + +void CRenderFxManager::Restart() +{ + if (FStringNull(pev->target)) + return; + + edict_t *pentTarget = nullptr; + while ((pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)))) + { + if (FNullEnt(pentTarget)) + break; + + entvars_t *pevTarget = VARS(pentTarget); + + // find render groups in our list of backup + int index = m_RenderGroups.Find(ENTINDEX(pevTarget)); + if (index == m_RenderGroups.InvalidIndex()) { + // not found + continue; + } + + RenderGroup_t *pGroup = &m_RenderGroups[index]; + if (!(pev->spawnflags & SF_RENDER_MASKFX)) + pevTarget->renderfx = pGroup->renderfx; + + if (!(pev->spawnflags & SF_RENDER_MASKAMT)) + pevTarget->renderamt = pGroup->renderamt; + + if (!(pev->spawnflags & SF_RENDER_MASKMODE)) + pevTarget->rendermode = pGroup->rendermode; + + if (!(pev->spawnflags & SF_RENDER_MASKCOLOR)) + pevTarget->rendercolor = pGroup->rendercolor; + } +} + +#endif // REGAMEDLL_FIXES + +void CRenderFxManager::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + if (FStringNull(pev->target)) + return; + + edict_t *pentTarget = nullptr; + while ((pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)))) + { + if (FNullEnt(pentTarget)) + break; + + entvars_t *pevTarget = VARS(pentTarget); + +#ifdef REGAMEDLL_FIXES + RenderGroup_t group; + group.renderfx = pevTarget->renderfx; + group.renderamt = pevTarget->renderamt; + group.rendermode = pevTarget->rendermode; + group.rendercolor = pevTarget->rendercolor; + + int entityIndex = ENTINDEX(pevTarget); + if (m_RenderGroups.Find(entityIndex) == m_RenderGroups.InvalidIndex()) { + m_RenderGroups.Insert(entityIndex, group); + } +#endif + + if (!(pev->spawnflags & SF_RENDER_MASKFX)) + pevTarget->renderfx = pev->renderfx; + + if (!(pev->spawnflags & SF_RENDER_MASKAMT)) + pevTarget->renderamt = pev->renderamt; + + if (!(pev->spawnflags & SF_RENDER_MASKMODE)) + pevTarget->rendermode = pev->rendermode; + + if (!(pev->spawnflags & SF_RENDER_MASKCOLOR)) + pevTarget->rendercolor = pev->rendercolor; + } +} + +LINK_ENTITY_TO_CLASS(trigger, CBaseTrigger, CCSTrigger) + +void CBaseTrigger::InitTrigger() +{ + // trigger angles are used for one-way touches. An angle of 0 is assumed + // to mean no restrictions, so use a yaw of 360 instead. + if (pev->angles != g_vecZero) + { + SetMovedir(pev); + } + + pev->solid = SOLID_TRIGGER; + pev->movetype = MOVETYPE_NONE; + + // set size and link into world + SET_MODEL(ENT(pev), STRING(pev->model)); + + if (CVAR_GET_FLOAT("showtriggers") == 0) + { + pev->effects |= EF_NODRAW; + } +} + +// Cache user-entity-field values until spawn is called. +void CBaseTrigger::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "damage")) + { + pev->dmg = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "count")) + { + m_cTriggersLeft = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "damagetype")) + { + m_bitsDamageInflict = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseToggle::KeyValue(pkvd); + } +} + +LINK_ENTITY_TO_CLASS(trigger_monsterjump, CTriggerMonsterJump, CCSTriggerMonsterJump) + +void CTriggerMonsterJump::Spawn() +{ + SetMovedir(pev); + + InitTrigger(); + + pev->nextthink = 0; + pev->speed = 200; + m_flHeight = 150; + + if (!FStringNull(pev->targetname)) + { + // if targetted, spawn turned off + pev->solid = SOLID_NOT; + + // Unlink from trigger list + UTIL_SetOrigin(pev, pev->origin); + SetUse(&CTriggerMonsterJump::ToggleUse); + } +} + +void CTriggerMonsterJump::Think() +{ + // kill the trigger for now UNDONE + pev->solid = SOLID_NOT; + + // Unlink from trigger list + UTIL_SetOrigin(pev, pev->origin); + SetThink(nullptr); +} + +void CTriggerMonsterJump::Touch(CBaseEntity *pOther) +{ + entvars_t *pevOther = pOther->pev; + + if (!(pevOther->flags & FL_MONSTER)) + { + // touched by a non-monster. + return; + } + + pevOther->origin.z += 1; + + if ((pevOther->flags & FL_ONGROUND)) + { + // clear the onground so physics don't bitch + pevOther->flags &= ~FL_ONGROUND; + } + + // toss the monster! + pevOther->velocity = pev->movedir * pev->speed; + pevOther->velocity.z += m_flHeight; + pev->nextthink = gpGlobals->time; +} + +LINK_ENTITY_TO_CLASS(trigger_cdaudio, CTriggerCDAudio, CCSTriggerCDAudio) + +// Changes tracks or stops CD when player touches +// HACK: overloaded HEALTH to avoid adding new field +void CTriggerCDAudio::Touch(CBaseEntity *pOther) +{ + // only clients may trigger these events + if (!pOther->IsPlayer()) + { + return; + } + + PlayTrack(pOther->edict()); +} + +void CTriggerCDAudio::Spawn() +{ + InitTrigger(); +} + +void CTriggerCDAudio::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + PlayTrack(pCaller->edict()); +} + +#ifdef REGAMEDLL_FIXES +const char *g_szMP3trackFileMap[] = +{ + "", "", + "media/Half-Life01.mp3", + "media/Prospero01.mp3", + "media/Half-Life12.mp3", + "media/Half-Life07.mp3", + "media/Half-Life10.mp3", + "media/Suspense01.mp3", + "media/Suspense03.mp3", + "media/Half-Life09.mp3", + "media/Half-Life02.mp3", + "media/Half-Life13.mp3", + "media/Half-Life04.mp3", + "media/Half-Life15.mp3", + "media/Half-Life14.mp3", + "media/Half-Life16.mp3", + "media/Suspense02.mp3", + "media/Half-Life03.mp3", + "media/Half-Life08.mp3", + "media/Prospero02.mp3", + "media/Half-Life05.mp3", + "media/Prospero04.mp3", + "media/Half-Life11.mp3", + "media/Half-Life06.mp3", + "media/Prospero03.mp3", + "media/Half-Life17.mp3", + "media/Prospero05.mp3", + "media/Suspense05.mp3", + "media/Suspense07.mp3" +}; +#endif + +void PlayCDTrack(edict_t *pClient, int iTrack) +{ + // Can't play if the client is not connected! + if (!pClient) + return; + + if (iTrack < -1 || iTrack > 30) + { + ALERT(at_console, "TriggerCDAudio - Track %d out of range\n", iTrack); + return; + } + + if (iTrack == -1) + { +#ifdef REGAMEDLL_FIXES + CLIENT_COMMAND(pClient, "mp3 stop\n"); +#else + CLIENT_COMMAND(pClient, "cd stop\n"); +#endif + } + else + { +#ifdef REGAMEDLL_FIXES + CLIENT_COMMAND(pClient, UTIL_VarArgs("mp3 play %s\n", g_szMP3trackFileMap[iTrack])); +#else + char string[64]; + Q_sprintf(string, "cd play %3d\n", iTrack); + CLIENT_COMMAND(pClient, string); +#endif + } +} + +// only plays for ONE client, so only use in single play! +void CTriggerCDAudio::PlayTrack(edict_t *pEdict) +{ + PlayCDTrack(pEdict, int(pev->health)); + + SetTouch(nullptr); + UTIL_Remove(this); +} + +LINK_ENTITY_TO_CLASS(target_cdaudio, CTargetCDAudio, CCSTargetCDAudio) + +void CTargetCDAudio::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "radius")) + { + pev->scale = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CPointEntity::KeyValue(pkvd); + } +} + +void CTargetCDAudio::Spawn() +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + if (pev->scale > 0) + { + pev->nextthink = gpGlobals->time + 1.0f; + } +} + +void CTargetCDAudio::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + Play(pCaller->edict()); +} + +// only plays for ONE client, so only use in single play! +void CTargetCDAudio::Think() +{ + // manually find the single player. + edict_t *pClient = INDEXENT(1); + + // Can't play if the client is not connected! + if (!pClient) + return; + + pev->nextthink = gpGlobals->time + 0.5f; + + if ((pClient->v.origin - pev->origin).Length() <= pev->scale) + { + Play(pClient); + } +} + +void CTargetCDAudio::Play(edict_t *pEdict) +{ + PlayCDTrack(pEdict, int(pev->health)); + UTIL_Remove(this); +} + +LINK_ENTITY_TO_CLASS(trigger_hurt, CTriggerHurt, CCSTriggerHurt) + +void CTriggerHurt::Spawn() +{ + InitTrigger(); + SetTouch(&CTriggerHurt::HurtTouch); + + if (!FStringNull(pev->targetname)) + { + SetUse(&CTriggerHurt::ToggleUse); + } + else + { + SetUse(nullptr); + } + + if (m_bitsDamageInflict & DMG_RADIATION) + { + SetThink(&CTriggerHurt::RadiationThink); + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.0, 0.5); + } + + if (pev->spawnflags & SF_TRIGGER_HURT_START_OFF) + { + // if flagged to Start Turned Off, make trigger nonsolid. + pev->solid = SOLID_NOT; + } + + // Link into the list + UTIL_SetOrigin(pev, pev->origin); +} + +#ifdef REGAMEDLL_FIXES +void CTriggerHurt::Restart() +{ + Vector mins, maxs; + + // Set model is about to destroy these + mins = pev->mins; + maxs = pev->maxs; + + Spawn(); + + UTIL_SetSize(pev, mins, maxs); +} +#endif + +// trigger hurt that causes radiation will do a radius +// check and set the player's geiger counter level +// according to distance from center of trigger +void CTriggerHurt::RadiationThink() +{ + edict_t *pentPlayer; + CBasePlayer *pPlayer = nullptr; + real_t flRange; + entvars_t *pevTarget; + Vector vecSpot1; + Vector vecSpot2; + Vector vecRange; + Vector origin; + Vector view_ofs; + + // check to see if a player is in pvs + // if not, continue + + // set origin to center of trigger so that this check works + origin = pev->origin; + view_ofs = pev->view_ofs; + + pev->origin = (pev->absmin + pev->absmax) * 0.5f; + pev->view_ofs = pev->view_ofs * 0.0f; + + pentPlayer = FIND_CLIENT_IN_PVS(edict()); + + pev->origin = origin; + pev->view_ofs = view_ofs; + + // reset origin + if (!FNullEnt(pentPlayer)) + { + pPlayer = GetClassPtr((CBasePlayer *)VARS(pentPlayer)); + + pevTarget = VARS(pentPlayer); + + // get range to player; + vecSpot1 = (pev->absmin + pev->absmax) * 0.5f; + vecSpot2 = (pevTarget->absmin + pevTarget->absmax) * 0.5f; + + vecRange = vecSpot1 - vecSpot2; + flRange = vecRange.Length(); + + // if player's current geiger counter range is larger + // than range to this trigger hurt, reset player's + // geiger counter range + + if (pPlayer->m_flgeigerRange >= flRange) + { + pPlayer->m_flgeigerRange = flRange; + } + } + + pev->nextthink = gpGlobals->time + 0.25f; +} + +// ToggleUse - If this is the USE function for a trigger, its state will toggle every time it's fired +void CBaseTrigger::ToggleUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + if (pev->solid == SOLID_NOT) + { + // if the trigger is off, turn it on + pev->solid = SOLID_TRIGGER; + + // Force retouch + gpGlobals->force_retouch++; + } + else + { + // turn the trigger off + pev->solid = SOLID_NOT; + } + + UTIL_SetOrigin(pev, pev->origin); +} + +// When touched, a hurt trigger does DMG points of damage each half-second +void CBaseTrigger::HurtTouch(CBaseEntity *pOther) +{ + float fldmg; + + if (!pOther->pev->takedamage) + { + return; + } + + if ((pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH) && !pOther->IsPlayer()) + { + // this trigger is only allowed to touch clients, and this ain't a client. + return; + } + + if ((pev->spawnflags & SF_TRIGGER_HURT_NO_CLIENTS) && pOther->IsPlayer()) + { + return; + } + + // HACKHACK: In multiplayer, players touch this based on packet receipt. + // So the players who send packets later aren't always hurt. Keep track of + // how much time has passed and whether or not you've touched that player + if (g_pGameRules->IsMultiplayer()) + { + if (pev->dmgtime > gpGlobals->time) + { +#ifdef REGAMEDLL_FIXES + if (gpGlobals->time >= pev->pain_finished) +#else + if (gpGlobals->time != pev->pain_finished) +#endif + { + // too early to hurt again, and not same frame with a different entity + if (!pOther->IsPlayer()) + { + return; + } + + int playerMask = 1 << (pOther->entindex() - 1); + + // If I've already touched this player (this time), then bail out + if (pev->impulse & playerMask) + return; + + // Mark this player as touched + // BUGBUG - There can be only 32 players! + pev->impulse |= playerMask; + } + } + else + { + // New clock, "un-touch" all players + pev->impulse = 0; + if (pOther->IsPlayer()) + { + int playerMask = 1 << (pOther->entindex() - 1); + + // Mark this player as touched + // BUGBUG - There can be only 32 players! + pev->impulse |= playerMask; + } + } + } + else + { + // Original code: single player +#ifdef REGAMEDLL_FIXES + if (pev->dmgtime > gpGlobals->time && gpGlobals->time >= pev->pain_finished) +#else + if (pev->dmgtime > gpGlobals->time && gpGlobals->time != pev->pain_finished) +#endif + { + // too early to hurt again, and not same frame with a different entity + return; + } + } + + // If this is time_based damage (poison, radiation), override the pev->dmg with a + // default for the given damage type. Monsters only take time-based damage + // while touching the trigger. Player continues taking damage for a while after + // leaving the trigger + + // 0.5 seconds worth of damage, pev->dmg is damage/second + fldmg = pev->dmg * 0.5f; + + if (fldmg < 0) + { + pOther->TakeHealth(-fldmg, m_bitsDamageInflict); + } + else + { + pOther->TakeDamage(pev, pev, fldmg, m_bitsDamageInflict); + } + + // Store pain time so we can get all of the other entities on this frame + pev->pain_finished = gpGlobals->time; + + // Apply damage every half second + // half second delay until this trigger can hurt toucher again + pev->dmgtime = gpGlobals->time + 0.5f; + + if (pev->target) + { + // trigger has a target it wants to fire. + if (pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE) + { + // if the toucher isn't a client, don't fire the target! + if (!pOther->IsPlayer()) + { + return; + } + } + + SUB_UseTargets(pOther, USE_TOGGLE, 0); + if (pev->spawnflags & SF_TRIGGER_HURT_TARGETONCE) + { + pev->target = 0; + } + } +} + +LINK_ENTITY_TO_CLASS(trigger_multiple, CTriggerMultiple, CCSTriggerMultiple) + +void CTriggerMultiple::Spawn() +{ + if (m_flWait == 0.0f) + m_flWait = 0.2f; + + InitTrigger(); + + assert(("trigger_multiple with health", pev->health == 0)); + + //UTIL_SetOrigin(pev, pev->origin); + //SET_MODEL(ENT(pev), STRING(pev->model)); + + //if (pev->health > 0) + //{ + // if (pev->spawnflags & SF_TRIGGER_MULTIPLE_NOTOUCH) + // { + // ALERT(at_error, "trigger_multiple spawn: health and notouch don't make sense"); + // } + // pev->max_health = pev->health; + // //UNDONE: where to get pfnDie from? + // pev->pfnDie = multi_killed; + // pev->takedamage = DAMAGE_YES; + // pev->solid = SOLID_BBOX; + // // make sure it links into the world + // UTIL_SetOrigin(pev, pev->origin); + //} + //else + { + SetTouch(&CTriggerMultiple::MultiTouch); + } +} + +LINK_ENTITY_TO_CLASS(trigger_once, CTriggerOnce, CCSTriggerOnce) + +void CTriggerOnce::Spawn() +{ +#ifdef REGAMEDLL_FIXES + m_flWait = -2; +#else + m_flWait = -1; +#endif + + CTriggerMultiple::Spawn(); +} + +#ifdef REGAMEDLL_FIXES +void CTriggerOnce::Restart() +{ + m_flWait = -2; + CTriggerMultiple::Spawn(); +} +#endif + +void CBaseTrigger::MultiTouch(CBaseEntity *pOther) +{ + entvars_t *pevToucher = pOther->pev; + + // Only touch clients, monsters, or pushables (depending on flags) + if (((pevToucher->flags & FL_CLIENT) && !(pev->spawnflags & SF_TRIGGER_NOCLIENTS)) + || ((pevToucher->flags & FL_MONSTER) && (pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) + || ((pev->spawnflags & SF_TRIGGER_PUSHABLES) && FClassnameIs(pevToucher, "func_pushable"))) + { + ActivateMultiTrigger(pOther); + } +} + +// the trigger was just touched/killed/used +// self.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +void CBaseTrigger::ActivateMultiTrigger(CBaseEntity *pActivator) +{ + if (pev->nextthink > gpGlobals->time) + { + // still waiting for reset time + return; + } + + if (!UTIL_IsMasterTriggered(m_sMaster, pActivator)) + { + return; + } + + if (FClassnameIs(pev, "trigger_secret")) + { + if (pev->enemy == nullptr || !FClassnameIs(pev->enemy, "player")) + return; + + gpGlobals->found_secrets++; + } + + if (!FStringNull(pev->noise)) + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, (char *)STRING(pev->noise), VOL_NORM, ATTN_NORM); + } + + // don't trigger again until reset + // pev->takedamage = DAMAGE_NO; + m_hActivator = pActivator; + SUB_UseTargets(m_hActivator, USE_TOGGLE, 0); + + if (pev->message && pActivator->IsPlayer()) + { + UTIL_ShowMessage(STRING(pev->message), pActivator); + } + + if (m_flWait > 0) + { + SetThink(&CBaseTrigger::MultiWaitOver); + pev->nextthink = gpGlobals->time + m_flWait; + } + else + { + // we can't just remove (self) here, because this is a touch function + // called while C code is looping through area links... + SetTouch(nullptr); + pev->nextthink = gpGlobals->time + 0.1f; + +#ifdef REGAMEDLL_FIXES + if (!(pev->spawnflags & SF_TRIGGER_NORESET) && m_flWait == -2) + SetThink(nullptr); + else +#endif + SetThink(&CBaseTrigger::SUB_Remove); + } +} + +// the wait time has passed, so set back up for another activation +void CBaseTrigger::MultiWaitOver() +{ + SetThink(nullptr); +} + +void CBaseTrigger::CounterUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + m_cTriggersLeft--; + m_hActivator = pActivator; + + if (m_cTriggersLeft < 0) + { + return; + } + + BOOL fTellActivator = (m_hActivator && FClassnameIs(m_hActivator->pev, "player") && !(pev->spawnflags & SF_TRIGGER_COUNTER_NOMESSAGE)); + + if (m_cTriggersLeft != 0) + { + if (fTellActivator) + { + // UNDONE: I don't think we want these Quakesque messages + switch (m_cTriggersLeft) + { + case 1: ALERT(at_console, "Only 1 more to go..."); break; + case 2: ALERT(at_console, "Only 2 more to go..."); break; + case 3: ALERT(at_console, "Only 3 more to go..."); break; + default: ALERT(at_console, "There are more to go..."); break; + } + } + + return; + } + + // UNDONE: I don't think we want these Quakesque messages + if (fTellActivator) + { + ALERT(at_console, "Sequence completed!"); + } + + ActivateMultiTrigger(m_hActivator); +} + +LINK_ENTITY_TO_CLASS(trigger_counter, CTriggerCounter, CCSTriggerCounter) + +void CTriggerCounter::Spawn() +{ + // By making the flWait be -1, this counter-trigger will disappear after it's activated + // (but of course it needs cTriggersLeft "uses" before that happens). + m_flWait = -1; + + if (m_cTriggersLeft == 0) + { + m_cTriggersLeft = 2; + } + + SetUse(&CTriggerCounter::CounterUse); +} + +LINK_ENTITY_TO_CLASS(trigger_transition, CTriggerVolume, CCSTriggerVolume) + +// Define space that travels across a level transition +void CTriggerVolume::Spawn() +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + + // set size and link into world + SET_MODEL(ENT(pev), STRING(pev->model)); + + pev->model = 0; + pev->modelindex = 0; +} + +LINK_ENTITY_TO_CLASS(fireanddie, CFireAndDie, CCSFireAndDie) + +void CFireAndDie::Spawn() +{ + MAKE_STRING_CLASS("fireanddie", pev); + // Don't call Precache() - it should be called on restore +} + +void CFireAndDie::Precache() +{ + pev->nextthink = gpGlobals->time + m_flDelay; +} + +void CFireAndDie::Think() +{ + SUB_UseTargets(this, USE_TOGGLE, 0); + UTIL_Remove(this); +} + +// Global Savedata for changelevel trigger +TYPEDESCRIPTION CChangeLevel::m_SaveData[] = +{ + DEFINE_ARRAY(CChangeLevel, m_szMapName, FIELD_CHARACTER, MAX_MAPNAME_LENGHT), + DEFINE_ARRAY(CChangeLevel, m_szLandmarkName, FIELD_CHARACTER, MAX_MAPNAME_LENGHT), + DEFINE_FIELD(CChangeLevel, m_changeTarget, FIELD_STRING), + DEFINE_FIELD(CChangeLevel, m_changeTargetDelay, FIELD_FLOAT), +}; + +LINK_ENTITY_TO_CLASS(trigger_changelevel, CChangeLevel, CCSChangeLevel) +IMPLEMENT_SAVERESTORE(CChangeLevel, CBaseTrigger) + +// Cache user-entity-field values until spawn is called. +void CChangeLevel::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "map")) + { + if (Q_strlen(pkvd->szValue) >= MAX_MAPNAME_LENGHT) + { + ALERT(at_error, "Map name '%s' too long (32 chars)\n", pkvd->szValue); + } + + Q_strcpy(m_szMapName, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "landmark")) + { + if (Q_strlen(pkvd->szValue) >= MAX_MAPNAME_LENGHT) + { + ALERT(at_error, "Landmark name '%s' too long (32 chars)\n", pkvd->szValue); + } + + Q_strcpy(m_szLandmarkName, pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "changetarget")) + { + m_changeTarget = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "changedelay")) + { + m_changeTargetDelay = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } +#ifdef REGAMEDLL_FIXES + else if (FStrEq(pkvd->szKeyName, "percent_of_players")) + { + m_flPercentOfPlayers = Q_atof(pkvd->szValue); + m_flPercentOfPlayers = clamp(m_flPercentOfPlayers, 0.0f, 1.0f); + pkvd->fHandled = TRUE; + } +#endif + else + { + CBaseTrigger::KeyValue(pkvd); + } +} + +void CChangeLevel::Spawn() +{ + if (FStrEq(m_szMapName, "")) + { + ALERT(at_console, "a trigger_changelevel doesn't have a map"); + } + + if (FStrEq(m_szLandmarkName, "")) + { + ALERT(at_console, "trigger_changelevel to %s doesn't have a landmark", m_szMapName); + } + + if (!FStringNull(pev->targetname)) + { + SetUse(&CChangeLevel::UseChangeLevel); + } + + InitTrigger(); + if (!(pev->spawnflags & SF_CHANGELEVEL_USEONLY)) + { + SetTouch(&CChangeLevel::TouchChangeLevel); + } +} + +void CChangeLevel::ExecuteChangeLevel() +{ + MESSAGE_BEGIN(MSG_ALL, SVC_CDTRACK); + WRITE_BYTE(3); + WRITE_BYTE(3); + MESSAGE_END(); + + MESSAGE_BEGIN(MSG_ALL, SVC_INTERMISSION); + MESSAGE_END(); +} + +edict_t *CChangeLevel::FindLandmark(const char *pLandmarkName) +{ + edict_t *pentLandmark = FIND_ENTITY_BY_STRING(nullptr, "targetname", pLandmarkName); + while (!FNullEnt(pentLandmark)) + { + // Found the landmark + if (FClassnameIs(pentLandmark, "info_landmark")) + return pentLandmark; + else + pentLandmark = FIND_ENTITY_BY_STRING(pentLandmark, "targetname", pLandmarkName); + } + + ALERT(at_error, "Can't find landmark %s\n", pLandmarkName); + return nullptr; +} + +// CChangeLevel::Use - allows level transitions to be +// triggered by buttons, etc. +void CChangeLevel::UseChangeLevel(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + ChangeLevelNow(pActivator); +} + +char st_szNextMap[MAX_MAPNAME_LENGHT]; +char st_szNextSpot[MAX_MAPNAME_LENGHT]; + +void CChangeLevel::ChangeLevelNow(CBaseEntity *pActivator) +{ + edict_t *pentLandmark; + LEVELLIST levels[16]; + + // Don't work in deathmatch + if (g_pGameRules->IsDeathmatch()) + { + return; + } + + // Some people are firing these multiple times in a frame, disable + if (gpGlobals->time == pev->dmgtime) + { + return; + } + + pev->dmgtime = gpGlobals->time; + + CBaseEntity *pPlayer = CBaseEntity::Instance(INDEXENT(1)); + if (!InTransitionVolume(pPlayer, m_szLandmarkName)) + { + ALERT(at_aiconsole, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName); + return; + } + + // Create an entity to fire the changetarget + if (m_changeTarget) + { + CFireAndDie *pFireAndDie = GetClassPtr((CFireAndDie *)nullptr); + + if (pFireAndDie) + { + // Set target and delay + pFireAndDie->pev->target = m_changeTarget; + pFireAndDie->m_flDelay = m_changeTargetDelay; + pFireAndDie->pev->origin = pPlayer->pev->origin; + + // Call spawn + DispatchSpawn(pFireAndDie->edict()); + } + } + + // This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory + Q_strcpy(st_szNextMap, m_szMapName); + + m_hActivator = pActivator; + SUB_UseTargets(pActivator, USE_TOGGLE, 0); + + // Init landmark to nullptr + st_szNextSpot[0] = '\0'; + + // look for a landmark entity + pentLandmark = FindLandmark(m_szLandmarkName); + + if (!FNullEnt(pentLandmark)) + { + Q_strcpy(st_szNextSpot, m_szLandmarkName); + gpGlobals->vecLandmarkOffset = VARS(pentLandmark)->origin; + } + + ALERT(at_console, "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot); + CHANGE_LEVEL(st_szNextMap, st_szNextSpot); +} + +void CChangeLevel::TouchChangeLevel(CBaseEntity *pOther) +{ + if (!pOther->IsPlayer()) + return; + +#ifdef REGAMEDLL_FIXES + if (m_flPercentOfPlayers > 0.0f) + { + int playersInCount = 0; + int playersOutCount = 0; + int playersCount = UTIL_CountPlayersInBrushVolume(true, this, playersInCount, playersOutCount); + + if (m_flPercentOfPlayers > float(playersInCount / playersCount)) + return; + } +#endif + + ChangeLevelNow(pOther); +} + +// Add a transition to the list, but ignore duplicates +// (a designer may have placed multiple trigger_changelevels with the same landmark) +int CChangeLevel::AddTransitionToList(LEVELLIST *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark) +{ + int i; + if (!pLevelList || !pMapName || !pLandmarkName || !pentLandmark) + { + return 0; + } + + for (i = 0; i < listCount; i++) + { + if (pLevelList[i].pentLandmark == pentLandmark && Q_strcmp(pLevelList[i].mapName, pMapName) == 0) + { + return 0; + } + } + + Q_strcpy(pLevelList[listCount].mapName, pMapName); + Q_strcpy(pLevelList[listCount].landmarkName, pLandmarkName); + + pLevelList[listCount].pentLandmark = pentLandmark; + pLevelList[listCount].vecLandmarkOrigin = VARS(pentLandmark)->origin; + + return 1; +} + +int BuildChangeList(LEVELLIST *pLevelList, int maxList) +{ + return CChangeLevel::ChangeList(pLevelList, maxList); +} + +int CChangeLevel::InTransitionVolume(CBaseEntity *pEntity, char *pVolumeName) +{ + if (pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION) + return 1; + + // If you're following another entity, follow it through the transition (weapons follow the player) + if (pEntity->pev->movetype == MOVETYPE_FOLLOW) + { + if (pEntity->pev->aiment) + { + pEntity = CBaseEntity::Instance(pEntity->pev->aiment); + } + } + + // Unless we find a trigger_transition, everything is in the volume + int inVolume = 1; + + edict_t *pentVolume = FIND_ENTITY_BY_TARGETNAME(nullptr, pVolumeName); + while (!FNullEnt(pentVolume)) + { + CBaseEntity *pVolume = CBaseEntity::Instance(pentVolume); + + if (pVolume && FClassnameIs(pVolume->pev, "trigger_transition")) + { + // It touches one, it's in the volume + if (pVolume->Intersects(pEntity)) + return 1; + else + { + // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go! + inVolume = 0; + } + } + + pentVolume = FIND_ENTITY_BY_TARGETNAME(pentVolume, pVolumeName); + } + + return inVolume; +} + +// This has grown into a complicated beast +// Can we make this more elegant? +// This builds the list of all transitions on this level and which entities are in their PVS's and can / should +// be moved across. +int CChangeLevel::ChangeList(LEVELLIST *pLevelList, int maxList) +{ + edict_t *pentChangelevel, *pentLandmark; + int i, count = 0; + + // Find all of the possible level changes on this BSP + pentChangelevel = FIND_ENTITY_BY_STRING(nullptr, "classname", "trigger_changelevel"); + + if (FNullEnt(pentChangelevel)) + return 0; + + while (!FNullEnt(pentChangelevel)) + { + CChangeLevel *pTrigger = GetClassPtr((CChangeLevel *)VARS(pentChangelevel)); + if (pTrigger) + { + // Find the corresponding landmark + pentLandmark = FindLandmark(pTrigger->m_szLandmarkName); + if (pentLandmark) + { + // Build a list of unique transitions + if (AddTransitionToList(pLevelList, count, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark)) + { + count++; + + // FULL! + if (count >= maxList) + break; + } + } + } + + pentChangelevel = FIND_ENTITY_BY_STRING(pentChangelevel, "classname", "trigger_changelevel"); + } + + if (gpGlobals->pSaveData && ((SAVERESTOREDATA *)gpGlobals->pSaveData)->pTable) + { + CSave saveHelper((SAVERESTOREDATA *)gpGlobals->pSaveData); + + for (i = 0; i < count; i++) + { + // We can only ever move 512 entities across a transition + const int MAX_ENTITY = 512; + + int j, entityCount = 0; + CBaseEntity *pEntList[MAX_ENTITY]; + int entityFlags[MAX_ENTITY]; + + // Follow the linked list of entities in the PVS of the transition landmark + edict_t *pent = FIND_ENTITY_IN_PVS(pLevelList[i].pentLandmark); + + // Build a list of valid entities in this linked list (we're going to use pent->v.chain again) + while (!FNullEnt(pent)) + { + CBaseEntity *pEntity = CBaseEntity::Instance(pent); + if (pEntity) + { + int caps = pEntity->ObjectCaps(); + + if (!(caps & FCAP_DONT_SAVE)) + { + int flags = 0; + + // If this entity can be moved or is global, mark it + if (caps & FCAP_ACROSS_TRANSITION) + flags |= FENTTABLE_MOVEABLE; + + if (pEntity->pev->globalname && !pEntity->IsDormant()) + flags |= FENTTABLE_GLOBAL; + + if (flags) + { + pEntList[entityCount] = pEntity; + entityFlags[entityCount] = flags; + entityCount++; + + if (entityCount > MAX_ENTITY) + { + ALERT(at_error, "Too many entities across a transition!"); + } + } + } + } + pent = pent->v.chain; + } + + for (j = 0; j < entityCount; j++) + { + // Check to make sure the entity isn't screened out by a trigger_transition + if (entityFlags[j] && InTransitionVolume(pEntList[j], pLevelList[i].landmarkName)) + { + // Mark entity table with 1<mapname = ALLOC_STRING("start"); + pChange = GetClassPtr((CChangeLevel *)nullptr); + Q_strcpy(pChange->m_szMapName, "start"); + } + else + pChange = GetClassPtr((CChangeLevel *)VARS(pent)); + + Q_strcpy(st_szNextMap, pChange->m_szMapName); + g_pGameRules->SetGameOver(); + + if (pChange->pev->nextthink < gpGlobals->time) + { + pChange->SetThink(&CChangeLevel::ExecuteChangeLevel); + pChange->pev->nextthink = gpGlobals->time + 0.1f; + } +} + +LINK_ENTITY_TO_CLASS(func_ladder, CLadder, CCSLadder) + +void CLadder::KeyValue(KeyValueData *pkvd) +{ + CBaseTrigger::KeyValue(pkvd); +} + +// func_ladder - makes an area vertically negotiable +void CLadder::Precache() +{ + // Do all of this in here because we need to 'convert' old saved games + pev->solid = SOLID_NOT; + pev->skin = CONTENTS_LADDER; + + if (CVAR_GET_FLOAT("showtriggers") == 0) + { + pev->rendermode = kRenderTransTexture; + pev->renderamt = 0; + } + + pev->effects &= ~EF_NODRAW; +} + +void CLadder::Spawn() +{ + Precache(); + + // set size and link into world + SET_MODEL(ENT(pev), STRING(pev->model)); + pev->movetype = MOVETYPE_PUSH; +} + +LINK_ENTITY_TO_CLASS(trigger_push, CTriggerPush, CCSTriggerPush) + +void CTriggerPush::KeyValue(KeyValueData *pkvd) +{ + CBaseTrigger::KeyValue(pkvd); +} + +void CTriggerPush::Spawn() +{ + if (pev->angles == g_vecZero) + { + pev->angles.y = 360; + } + + InitTrigger(); + + if (pev->speed == 0) + { + pev->speed = 100; + } + + // if flagged to Start Turned Off, make trigger nonsolid. + if (pev->spawnflags & SF_TRIGGER_PUSH_START_OFF) + { + pev->solid = SOLID_NOT; + } + + SetUse(&CTriggerPush::ToggleUse); + + // Link into the list + UTIL_SetOrigin(pev, pev->origin); +} + +#ifdef REGAMEDLL_FIXES +void CTriggerPush::Restart() +{ + auto tempDir = pev->movedir; + Spawn(); + pev->movedir = tempDir; +} +#endif + +void CTriggerPush::Touch(CBaseEntity *pOther) +{ + entvars_t *pevToucher = pOther->pev; + + // UNDONE: Is there a better way than health to detect things that have physics? (clients/monsters) + switch (pevToucher->movetype) + { + case MOVETYPE_NONE: + case MOVETYPE_PUSH: + case MOVETYPE_NOCLIP: + case MOVETYPE_FOLLOW: + return; + } + + if (pevToucher->solid != SOLID_NOT && pevToucher->solid != SOLID_BSP) + { + // Instant trigger, just transfer velocity and remove + if (pev->spawnflags & SF_TRIGGER_PUSH_ONCE) + { + pevToucher->velocity = pevToucher->velocity + (pev->speed * pev->movedir); + + if (pevToucher->velocity.z > 0) + { + pevToucher->flags &= ~FL_ONGROUND; + } + + UTIL_Remove(this); + } + else + { + // Push field, transfer to base velocity + Vector vecPush = (pev->speed * pev->movedir); + if (pevToucher->flags & FL_BASEVELOCITY) + { + vecPush = vecPush + pevToucher->basevelocity; + } + + pevToucher->basevelocity = vecPush; + pevToucher->flags |= FL_BASEVELOCITY; + } + } +} + +void CBaseTrigger::TeleportTouch(CBaseEntity *pOther) +{ + entvars_t *pevToucher = pOther->pev; + edict_t *pentTarget = nullptr; + + // Only teleport monsters or clients + if (!(pevToucher->flags & (FL_CLIENT | FL_MONSTER))) + { + return; + } + + if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) + { + return; + } + + if (!(pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) + { + // no monsters allowed! + if (pevToucher->flags & FL_MONSTER) + { + return; + } + } + + if ((pev->spawnflags & SF_TRIGGER_NOCLIENTS)) + { + // no clients allowed + if (pOther->IsPlayer()) + { + return; + } + } + + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, STRING(pev->target)); + if (FNullEnt(pentTarget)) + { + return; + } + + Vector tmp = VARS(pentTarget)->origin; + + if (pOther->IsPlayer()) + { + // make origin adjustments in case the teleportee is a player. (origin in center, not at feet) + tmp.z -= pOther->pev->mins.z; + } + + tmp.z++; + + pevToucher->flags &= ~FL_ONGROUND; + + UTIL_SetOrigin(pevToucher, tmp); + + pevToucher->angles = pentTarget->v.angles; + + if (pOther->IsPlayer()) + { + pevToucher->v_angle = pentTarget->v.angles; + } + + pevToucher->fixangle = 1; + pevToucher->velocity = pevToucher->basevelocity = g_vecZero; +} + +LINK_ENTITY_TO_CLASS(trigger_teleport, CTriggerTeleport, CCSTriggerTeleport) + +void CTriggerTeleport::Spawn() +{ + InitTrigger(); + SetTouch(&CTriggerTeleport::TeleportTouch); +} + +LINK_ENTITY_TO_CLASS(info_teleport_destination, CPointEntity, CCSPointEntity) +LINK_ENTITY_TO_CLASS(func_buyzone, CBuyZone, CCSBuyZone) + +void CBuyZone::Spawn() +{ + InitTrigger(); + SetTouch(&CBuyZone::BuyTouch); + + if (pev->team > CT || pev->team < UNASSIGNED) + { + ALERT(at_console, "Bad team number (%i) in func_buyzone\n", pev->team); + pev->team = UNASSIGNED; + } +} + +void CBuyZone::BuyTouch(CBaseEntity *pOther) +{ +#ifdef REGAMEDLL_ADD + if (buytime.value == 0.0f) + return; +#endif + + if (!pOther->IsPlayer()) + return; + + CBasePlayer *pPlayer = static_cast(pOther); + + if (pev->team == UNASSIGNED || pev->team == pPlayer->m_iTeam) + { +#ifdef REGAMEDLL_FIXES + if (!CSGameRules()->CanPlayerBuy(pPlayer)) + return; +#endif + + pPlayer->m_signals.Signal(SIGNAL_BUY); + } +} + +LINK_ENTITY_TO_CLASS(func_bomb_target, CBombTarget, CCSBombTarget) + +void CBombTarget::Spawn() +{ + InitTrigger(); + + SetTouch(&CBombTarget::BombTargetTouch); + SetUse(&CBombTarget::BombTargetUse); +} + +bool CBombTarget::IsPlayerInBombSite(CBasePlayer *pPlayer) +{ + const Vector &absmin = pPlayer->pev->absmin; + const Vector &absmax = pPlayer->pev->absmax; + + // Ensure that player's body is inside func_bomb_target's X,Y axes. + if (pev->absmin.x > absmin.x || pev->absmin.y > absmin.y) + { + return false; + } + if (pev->absmax.x < absmax.x || pev->absmax.y < absmax.y) + { + return false; + } + + return true; +} + +void CBombTarget::BombTargetTouch(CBaseEntity *pOther) +{ + if (!pOther->IsPlayer()) + return; + + CBasePlayer *pPlayer = static_cast(pOther); + + if (pPlayer->m_bHasC4 +#ifdef REGAMEDLL_FIXES + && (legacy_bombtarget_touch.value || IsPlayerInBombSite(pPlayer)) +#endif + ) + { + pPlayer->m_signals.Signal(SIGNAL_BOMB); + pPlayer->m_pentCurBombTarget = ENT(pev); + } +} + +void CBombTarget::BombTargetUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + SUB_UseTargets(nullptr, USE_TOGGLE, 0); +} + +LINK_ENTITY_TO_CLASS(func_hostage_rescue, CHostageRescue, CCSHostageRescue) + +void CHostageRescue::Spawn() +{ + InitTrigger(); + SetTouch(&CHostageRescue::HostageRescueTouch); +} + +void CHostageRescue::HostageRescueTouch(CBaseEntity *pOther) +{ + if (pOther->IsPlayer()) + { + ((CBasePlayer *)pOther)->m_signals.Signal(SIGNAL_RESCUE); + } + + if (FClassnameIs(pOther->pev, "hostage_entity")) + { + ((CHostage *)pOther)->m_bRescueMe = TRUE; + } +} + +LINK_ENTITY_TO_CLASS(func_escapezone, CEscapeZone, CCSEscapeZone) + +void CEscapeZone::Spawn() +{ + InitTrigger(); + SetTouch(&CEscapeZone::EscapeTouch); +} + +void CEscapeZone::EscapeTouch(CBaseEntity *pOther) +{ + if (!pOther->IsPlayer()) + return; + + CBasePlayer *pEscapee = static_cast(pOther); + + switch (pEscapee->m_iTeam) + { + case TERRORIST: + if (!pEscapee->m_bEscaped) + { + pEscapee->m_bEscaped = true; + CSGameRules()->CheckWinConditions(); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Terrorist_Escaped\"\n", + STRING(pEscapee->pev->netname), GETPLAYERUSERID(pEscapee->edict()), GETPLAYERAUTHID(pEscapee->edict())); + + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + + if (!pPlayer || FNullEnt(pPlayer->pev)) + continue; + + if (pPlayer->m_iTeam == pEscapee->m_iTeam) + { + ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "#Terrorist_Escaped"); + } + } + } + break; + case CT: + pEscapee->m_signals.Signal(SIGNAL_ESCAPE); + break; + } +} + +LINK_ENTITY_TO_CLASS(func_vip_safetyzone, CVIP_SafetyZone, CCSVIP_SafetyZone) + +void CVIP_SafetyZone::Spawn() +{ + InitTrigger(); + SetTouch(&CVIP_SafetyZone::VIP_SafetyTouch); +} + +void CVIP_SafetyZone::VIP_SafetyTouch(CBaseEntity *pOther) +{ + if (!pOther->IsPlayer()) + return; + + CBasePlayer *pEscapee = static_cast(pOther); + pEscapee->m_signals.Signal(SIGNAL_VIPSAFETY); + + if (pEscapee->m_bIsVIP) + { + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Escaped_As_VIP\"\n", + STRING(pEscapee->pev->netname), GETPLAYERUSERID(pEscapee->edict()), GETPLAYERAUTHID(pEscapee->edict())); + + pEscapee->m_bEscaped = true; + + pEscapee->Disappear(); + pEscapee->AddAccount(REWARD_VIP_HAVE_SELF_RESCUED, RT_VIP_RESCUED_MYSELF); + } +} + +LINK_ENTITY_TO_CLASS(trigger_autosave, CTriggerSave, CCSTriggerSave) + +void CTriggerSave::Spawn() +{ + if (g_pGameRules->IsDeathmatch()) + { + REMOVE_ENTITY(ENT(pev)); + return; + } + + InitTrigger(); + SetTouch(&CTriggerSave::SaveTouch); +} + +void CTriggerSave::SaveTouch(CBaseEntity *pOther) +{ + if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) + return; + + // Only save on clients + if (!pOther->IsPlayer()) + return; + + SetTouch(nullptr); + UTIL_Remove(this); + SERVER_COMMAND("autosave\n"); +} + +LINK_ENTITY_TO_CLASS(trigger_endsection, CTriggerEndSection, CCSTriggerEndSection) + +void CTriggerEndSection::EndSectionUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + // Only save on clients + if (pActivator && !pActivator->IsNetClient()) + return; + + SetUse(nullptr); + if (!FStringNull(pev->message)) + { + END_SECTION(STRING(pev->message)); + } + + UTIL_Remove(this); +} + +void CTriggerEndSection::Spawn() +{ + if (g_pGameRules->IsDeathmatch()) + { + REMOVE_ENTITY(ENT(pev)); + return; + } + + InitTrigger(); + SetUse(&CTriggerEndSection::EndSectionUse); + + // If it is a "use only" trigger, then don't set the touch function. + if (!(pev->spawnflags & SF_ENDSECTION_USEONLY)) + { + SetTouch(&CTriggerEndSection::EndSectionTouch); + } +} + +void CTriggerEndSection::EndSectionTouch(CBaseEntity *pOther) +{ + // Only save on clients + if (!pOther->IsNetClient()) + return; + + SetTouch(nullptr); + if (!FStringNull(pev->message)) + { + END_SECTION(STRING(pev->message)); + } + + UTIL_Remove(this); +} + +void CTriggerEndSection::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "section")) + { + // Store this in message so we don't have to write save/restore for this ent + pev->message = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseTrigger::KeyValue(pkvd); + } +} + +LINK_ENTITY_TO_CLASS(trigger_gravity, CTriggerGravity, CCSTriggerGravity) + +void CTriggerGravity::Spawn() +{ + InitTrigger(); + SetTouch(&CTriggerGravity::GravityTouch); +} + +void CTriggerGravity::GravityTouch(CBaseEntity *pOther) +{ + // Only save on clients + if (!pOther->IsPlayer()) + return; + + pOther->pev->gravity = pev->gravity; +} + +TYPEDESCRIPTION CTriggerChangeTarget::m_SaveData[] = +{ + DEFINE_FIELD(CTriggerChangeTarget, m_iszNewTarget, FIELD_STRING), +}; + +LINK_ENTITY_TO_CLASS(trigger_changetarget, CTriggerChangeTarget, CCSTriggerChangeTarget) +IMPLEMENT_SAVERESTORE(CTriggerChangeTarget, CBaseDelay) + +void CTriggerChangeTarget::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "m_iszNewTarget")) + { + m_iszNewTarget = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue(pkvd); + } +} + +void CTriggerChangeTarget::Spawn() +{ + ; +} + +void CTriggerChangeTarget::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + CBaseEntity *pTarget = UTIL_FindEntityByTargetname(nullptr, pev->target); + if (pTarget) + { + pTarget->pev->target = m_iszNewTarget; + + CBaseMonster *pMonster = pTarget->MyMonsterPointer(); + if (pMonster) + { + pMonster->m_pGoalEnt = nullptr; + } + } +} + +// Global Savedata for changelevel friction modifier +TYPEDESCRIPTION CTriggerCamera::m_SaveData[] = +{ + DEFINE_FIELD(CTriggerCamera, m_hPlayer, FIELD_EHANDLE), + DEFINE_FIELD(CTriggerCamera, m_hTarget, FIELD_EHANDLE), + DEFINE_FIELD(CTriggerCamera, m_pentPath, FIELD_CLASSPTR), + DEFINE_FIELD(CTriggerCamera, m_sPath, FIELD_STRING), + DEFINE_FIELD(CTriggerCamera, m_flWait, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_flReturnTime, FIELD_TIME), + DEFINE_FIELD(CTriggerCamera, m_flStopTime, FIELD_TIME), + DEFINE_FIELD(CTriggerCamera, m_moveDistance, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_targetSpeed, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_initialSpeed, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_acceleration, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_deceleration, FIELD_FLOAT), + DEFINE_FIELD(CTriggerCamera, m_state, FIELD_INTEGER), +}; + +LINK_ENTITY_TO_CLASS(trigger_camera, CTriggerCamera, CCSTriggerCamera) +IMPLEMENT_SAVERESTORE(CTriggerCamera, CBaseDelay) + +void CTriggerCamera::Spawn() +{ + pev->movetype = MOVETYPE_NOCLIP; + + // Remove model & collisions + pev->solid = SOLID_NOT; + + // The engine won't draw this model if this is set to 0 and blending is on + pev->renderamt = 0; + pev->rendermode = kRenderTransTexture; + + m_initialSpeed = pev->speed; + + if (m_acceleration == 0) + { + m_acceleration = 500; + } + if (m_deceleration == 0) + { + m_deceleration = 500; + } +} + +void CTriggerCamera::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "moveto")) + { + m_sPath = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "acceleration")) + { + m_acceleration = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "deceleration")) + { + m_deceleration = Q_atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseDelay::KeyValue(pkvd); + } +} + +void CTriggerCamera::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) +{ + if (!ShouldToggle(useType, m_state)) + return; + + // Toggle state + m_state = !m_state; + if (m_state == 0) + { + m_flReturnTime = gpGlobals->time; + + if (pActivator && pActivator->IsPlayer()) + { + ((CBasePlayer *)pActivator)->ResetMaxSpeed(); + } + + return; + } + + if (!pActivator || !pActivator->IsPlayer()) + { + pActivator = CBaseEntity::Instance(INDEXENT(1)); + } + + m_hPlayer = static_cast(pActivator); + m_flReturnTime = gpGlobals->time + m_flWait; + + pev->speed = m_initialSpeed; + m_targetSpeed = m_initialSpeed; + + if (pev->spawnflags & SF_CAMERA_PLAYER_TARGET) + { + m_hTarget = m_hPlayer; + } + else + { + m_hTarget = GetNextTarget(); + } + + // Nothing to look at! + if (!m_hTarget) + { + return; + } + + if (pActivator->IsPlayer()) + { + SET_CLIENT_MAXSPEED(pActivator->edict(), 0.001); + } + + if (pev->spawnflags & SF_CAMERA_PLAYER_TAKECONTROL) + { + ((CBasePlayer *)pActivator)->EnableControl(FALSE); + } + + if (m_sPath) + { + m_pentPath = Instance(FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(m_sPath))); + } + else + { + m_pentPath = nullptr; + } + + m_flStopTime = gpGlobals->time; + if (m_pentPath) + { + if (m_pentPath->pev->speed != 0) + m_targetSpeed = m_pentPath->pev->speed; + + m_flStopTime += m_pentPath->GetDelay(); + } + + // copy over player information + if (pev->spawnflags & SF_CAMERA_PLAYER_POSITION) + { + UTIL_SetOrigin(pev, pActivator->pev->origin + pActivator->pev->view_ofs); + + pev->angles.x = -pActivator->pev->angles.x; + pev->angles.y = pActivator->pev->angles.y; + pev->angles.z = 0; + pev->velocity = pActivator->pev->velocity; + } + else + { + pev->velocity = Vector(0, 0, 0); + } + + SET_VIEW(pActivator->edict(), edict()); + SET_MODEL(ENT(pev), STRING(pActivator->pev->model)); + + // follow the player down + SetThink(&CTriggerCamera::FollowTarget); + + pev->nextthink = gpGlobals->time; + m_moveDistance = 0; + Move(); +} + +void CTriggerCamera::FollowTarget() +{ + if (!m_hPlayer) + return; + + if (!m_hTarget || m_flReturnTime < gpGlobals->time) + { + if (m_hPlayer->IsAlive()) + { + SET_VIEW(m_hPlayer->edict(), m_hPlayer->edict()); + + m_hPlayer->EnableControl(TRUE); + m_hPlayer->ResetMaxSpeed(); + } + + SUB_UseTargets(this, USE_TOGGLE, 0); + pev->avelocity = Vector(0, 0, 0); + m_state = 0; + return; + } + + Vector vecGoal = UTIL_VecToAngles(m_hTarget->pev->origin - pev->origin); + vecGoal.x = -vecGoal.x; + + if (pev->angles.y > 360) + pev->angles.y -= 360; + + if (pev->angles.y < 0) + pev->angles.y += 360; + + real_t dx = vecGoal.x - pev->angles.x; + real_t dy = vecGoal.y - pev->angles.y; + + if (dx < -180) + dx += 360; + if (dx > 180) + dx = dx - 360; + + if (dy < -180) + dy += 360; + if (dy > 180) + dy = dy - 360; + + pev->avelocity.x = dx * 40 * gpGlobals->frametime; + pev->avelocity.y = dy * 40 * gpGlobals->frametime; + + if (!(pev->spawnflags & SF_CAMERA_PLAYER_TAKECONTROL)) + { + pev->velocity = pev->velocity * 0.8f; + + if (pev->velocity.Length() < 10.0) + { + pev->velocity = g_vecZero; + } + } + + pev->nextthink = gpGlobals->time; + Move(); +} + +void CTriggerCamera::Move() +{ + // Not moving on a path, return + if (!m_pentPath) + return; + + // Subtract movement from the previous frame + m_moveDistance -= pev->speed * gpGlobals->frametime; + + // Have we moved enough to reach the target? + if (m_moveDistance <= 0) + { + // Fire the passtarget if there is one + if (!FStringNull(m_pentPath->pev->message)) + { + FireTargets(STRING(m_pentPath->pev->message), this, this, USE_TOGGLE, 0); + + if (m_pentPath->pev->spawnflags & SF_CORNER_FIREONCE) + { + m_pentPath->pev->message = 0; + } + } + + // Time to go to the next target + m_pentPath = m_pentPath->GetNextTarget(); + + // Set up next corner + if (!m_pentPath) + { + pev->velocity = g_vecZero; + } + else + { + if (m_pentPath->pev->speed != 0) + { + m_targetSpeed = m_pentPath->pev->speed; + } + + Vector delta = m_pentPath->pev->origin - pev->origin; + m_moveDistance = delta.Length(); + pev->movedir = delta.Normalize(); + m_flStopTime = gpGlobals->time + m_pentPath->GetDelay(); + } + } + + if (m_flStopTime > gpGlobals->time) + { + pev->speed = UTIL_Approach(0, pev->speed, m_deceleration * gpGlobals->frametime); + } + else + { + pev->speed = UTIL_Approach(m_targetSpeed, pev->speed, m_acceleration * gpGlobals->frametime); + } + + real_t fraction = 2 * gpGlobals->frametime; + pev->velocity = ((pev->movedir * pev->speed) * fraction) + (pev->velocity * (1 - fraction)); +} + +LINK_ENTITY_TO_CLASS(env_snow, CWeather, CCSWeather) +LINK_ENTITY_TO_CLASS(func_snow, CWeather, CCSWeather) +LINK_ENTITY_TO_CLASS(env_rain, CWeather, CCSWeather) +LINK_ENTITY_TO_CLASS(func_rain, CWeather, CCSWeather) + +void CWeather::Spawn() +{ + InitTrigger(); +} + +void CClientFog::KeyValue(KeyValueData *pkvd) +{ +#if 0 + if (FStrEq(pkvd->szKeyName, "startdist")) + { + m_iStartDist = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "enddist")) + { + m_iEndDist = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else +#endif + if (FStrEq(pkvd->szKeyName, "density")) + { + m_fDensity = Q_atof(pkvd->szValue); + + if (m_fDensity < 0 || m_fDensity > 0.01) + m_fDensity = 0; + + pkvd->fHandled = TRUE; + } + else + { + CBaseEntity::KeyValue(pkvd); + } +} + +void CClientFog::Spawn() +{ + pev->movetype = MOVETYPE_NOCLIP; + pev->solid = SOLID_NOT; // Remove model & collisions + pev->renderamt = 0; // The engine won't draw this model if this is set to 0 and blending is on + pev->rendermode = kRenderTransTexture; +} + +LINK_ENTITY_TO_CLASS(env_fog, CClientFog, CCSClientFog) diff --git a/regamedll/dlls/util.cpp b/regamedll/dlls/util.cpp index 3038b223..60c877ea 100644 --- a/regamedll/dlls/util.cpp +++ b/regamedll/dlls/util.cpp @@ -1,1848 +1,1848 @@ -#include "precompiled.h" - -unsigned int glSeed; -CUtlVector stringsHashTable; - -unsigned int seed_table[256] = -{ - 28985U, 27138U, 26457U, 9451U, 17764U, 10909U, 28790U, 8716U, 6361U, 4853U, 17798U, 21977U, 19643U, 20662U, 10834U, 20103, - 27067U, 28634U, 18623U, 25849U, 8576U, 26234U, 23887U, 18228U, 32587U, 4836U, 3306U, 1811U, 3035U, 24559U, 18399U, 315, - 26766U, 907U, 24102U, 12370U, 9674U, 2972U, 10472U, 16492U, 22683U, 11529U, 27968U, 30406U, 13213U, 2319U, 23620U, 16823, - 10013U, 23772U, 21567U, 1251U, 19579U, 20313U, 18241U, 30130U, 8402U, 20807U, 27354U, 7169U, 21211U, 17293U, 5410U, 19223, - 10255U, 22480U, 27388U, 9946U, 15628U, 24389U, 17308U, 2370U, 9530U, 31683U, 25927U, 23567U, 11694U, 26397U, 32602U, 15031, - 18255U, 17582U, 1422U, 28835U, 23607U, 12597U, 20602U, 10138U, 5212U, 1252U, 10074U, 23166U, 19823U, 31667U, 5902U, 24630, - 18948U, 14330U, 14950U, 8939U, 23540U, 21311U, 22428U, 22391U, 3583U, 29004U, 30498U, 18714U, 4278U, 2437U, 22430U, 3439, - 28313U, 23161U, 25396U, 13471U, 19324U, 15287U, 2563U, 18901U, 13103U, 16867U, 9714U, 14322U, 15197U, 26889U, 19372U, 26241, - 31925U, 14640U, 11497U, 8941U, 10056U, 6451U, 28656U, 10737U, 13874U, 17356U, 8281U, 25937U, 1661U, 4850U, 7448U, 12744, - 21826U, 5477U, 10167U, 16705U, 26897U, 8839U, 30947U, 27978U, 27283U, 24685U, 32298U, 3525U, 12398U, 28726U, 9475U, 10208, - 617U, 13467U, 22287U, 2376U, 6097U, 26312U, 2974U, 9114U, 21787U, 28010U, 4725U, 15387U, 3274U, 10762U, 31695U, 17320, - 18324U, 12441U, 16801U, 27376U, 22464U, 7500U, 5666U, 18144U, 15314U, 31914U, 31627U, 6495U, 5226U, 31203U, 2331U, 4668, - 12650U, 18275U, 351U, 7268U, 31319U, 30119U, 7600U, 2905U, 13826U, 11343U, 13053U, 15583U, 30055U, 31093U, 5067U, 761, - 9685U, 11070U, 21369U, 27155U, 3663U, 26542U, 20169U, 12161U, 15411U, 30401U, 7580U, 31784U, 8985U, 29367U, 20989U, 14203, - 29694U, 21167U, 10337U, 1706U, 28578U, 887U, 3373U, 19477U, 14382U, 675U, 7033U, 15111U, 26138U, 12252U, 30996U, 21409, - 25678U, 18555U, 13256U, 23316U, 22407U, 16727U, 991U, 9236U, 5373U, 29402U, 6117U, 15241U, 27715U, 19291U, 19888U, 19847U -}; - -int g_groupmask = 0; -int g_groupop = 0; - -float UTIL_WeaponTimeBase() -{ -#ifdef CLIENT_WEAPONS - return 0.0; -#else - return gpGlobals->time; -#endif -} - -unsigned int U_Random() -{ - glSeed *= 69069; - glSeed += seed_table[glSeed & 0xFF] + 1; - return (glSeed & 0xFFFFFFF); -} - -void U_Srand(unsigned int seed) -{ - glSeed = seed_table[seed & 0xFF]; -} - -int UTIL_SharedRandomLong(unsigned int seed, int low, int high) -{ - unsigned int range = high - low + 1; - U_Srand((unsigned int)(high + low + seed)); - if (range != 1) - { - int rnum = U_Random(); - int offset = rnum % range; - return (low + offset); - } - - return low; -} - -float UTIL_SharedRandomFloat(unsigned int seed, float low, float high) -{ - unsigned int range = high - low; - U_Srand((unsigned int)seed + *(unsigned int *)&low + *(unsigned int *)&high); - - U_Random(); - U_Random(); - - if (range) - { - int tensixrand = U_Random() & 0xFFFFu; - float offset = float(tensixrand) / 0x10000u; - return (low + offset * range); - } - - return low; -} - -NOXREF void UTIL_ParametricRocket(entvars_t *pev, Vector p_vecOrigin, Vector vecAngles, edict_t *owner) -{ - TraceResult tr; - Vector vecTravel; - float travelTime; - - pev->startpos = p_vecOrigin; - UTIL_MakeVectors(vecAngles); - UTIL_TraceLine(pev->startpos, gpGlobals->v_forward * 8192.0f + pev->startpos, ignore_monsters, owner, &tr); - pev->endpos = tr.vecEndPos; - - vecTravel = pev->endpos - pev->startpos; - if (pev->velocity.Length() > 0.0f) - travelTime = vecTravel.Length() / pev->velocity.Length(); - else - travelTime = 0.0f; - - pev->starttime = gpGlobals->time; - pev->impacttime = travelTime + gpGlobals->time; -} - -void UTIL_SetGroupTrace(int groupmask, int op) -{ - g_groupmask = groupmask; - g_groupop = op; - - ENGINE_SETGROUPMASK(groupmask, op); -} - -void UTIL_UnsetGroupTrace() -{ - g_groupmask = 0; - g_groupop = 0; - - ENGINE_SETGROUPMASK(0, 0); -} - -NOXREF UTIL_GroupTrace::UTIL_GroupTrace(int groupmask, int op) -{ - m_oldgroupmask = g_groupmask; - m_oldgroupop = g_groupop; - - g_groupmask = groupmask; - g_groupop = op; - - ENGINE_SETGROUPMASK(groupmask, op); -} - -NOXREF UTIL_GroupTrace::~UTIL_GroupTrace() -{ - g_groupmask = m_oldgroupmask; - g_groupop = m_oldgroupop; - - ENGINE_SETGROUPMASK(g_groupmask, g_groupop); -} - -NOXREF BOOL UTIL_GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon) -{ - return g_pGameRules->GetNextBestWeapon(pPlayer,pCurrentWeapon); -} - -float UTIL_AngleMod(float a) -{ - if (a < 0.0f) - a = a + 360.0f * (int(a / 360.0f) + 1); - else if (a >= 360.0f) - a = a - 360.0f * int(a / 360.0f); - return a; -} - -NOXREF float UTIL_AngleDiff(float destAngle, float srcAngle) -{ - float delta = destAngle - srcAngle; - if (destAngle > srcAngle) - { - if (delta >= 180.0f) - delta -= 360.0f; - } - else - { - if (delta <= -180.0f) - delta += 360.0f; - } - - return delta; -} - -Vector UTIL_VecToAngles(const Vector &vec) -{ - float rgflVecOut[3]; - VEC_TO_ANGLES(vec, rgflVecOut); - return Vector(rgflVecOut); -} - -NOXREF void UTIL_MoveToOrigin(edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType) -{ - float rgfl[3]; - vecGoal.CopyToArray(rgfl); - MOVE_TO_ORIGIN(pent, rgfl, flDist, iMoveType); -} - -int UTIL_EntitiesInBox(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask) -{ - edict_t *pEdict = INDEXENT(1); - int count = 0; - - if (!pEdict) - return 0; - - for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) - { - if (pEdict->free) - continue; - - if (flagMask && !(pEdict->v.flags & flagMask)) - continue; - - CBaseEntity *pEntity = CBaseEntity::Instance(pEdict); - if (!pEntity) - continue; - - if (!pEntity->Intersects(mins, maxs)) - continue; - - pList[count++] = pEntity; - - if (count >= listMax) - break; - } - - return count; -} - -NOXREF int UTIL_MonstersInSphere(CBaseEntity ** pList, int listMax, const Vector ¢er, float radius) -{ - edict_t *pEdict; - CBaseEntity *pEntity; - int count = 0; - float distance; - float delta; - - float radiusSquared = radius * radius; - pEdict = INDEXENT(1); - - if (!pEdict) - return count; - - for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) - { - if (pEdict->free) - continue; - - if (!(pEdict->v.flags & (FL_CLIENT | FL_MONSTER))) - continue; - - delta = center.x - pEdict->v.origin.x; - delta *= delta; - - if (delta > radiusSquared) - continue; - - distance = delta; - - delta = center.y - pEdict->v.origin.y; - delta *= delta; - - distance += delta; - if (distance > radiusSquared) - continue; - - delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z) * 0.5; - delta *= delta; - - distance += delta; - if (distance > radiusSquared) - continue; - - pEntity = CBaseEntity::Instance(pEdict); - if (!pEntity) - continue; - - pList[count++] = pEntity; - - if (count >= listMax) - return count; - } - - return count; -} - -CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius) -{ - edict_t *pentEntity; - if (pStartEntity) - pentEntity = pStartEntity->edict(); - else - pentEntity = nullptr; - - pentEntity = FIND_ENTITY_IN_SPHERE(pentEntity, vecCenter, flRadius); - if (!FNullEnt(pentEntity)) - { - return CBaseEntity::Instance(pentEntity); - } - - return nullptr; -} - -CBaseEntity *UTIL_FindEntityByString_Old(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue) -{ - edict_t *pentEntity; - if (pStartEntity) - pentEntity = pStartEntity->edict(); - else - pentEntity = nullptr; - - pentEntity = FIND_ENTITY_BY_STRING(pentEntity, szKeyword, szValue); - if (!FNullEnt(pentEntity)) - { - return CBaseEntity::Instance(pentEntity); - } - - return nullptr; -} - -CBaseEntity *EXT_FUNC UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue) -{ - edict_t *pentEntity; - int startEntityIndex; - - if (pStartEntity) - pentEntity = pStartEntity->edict(); - else - pentEntity = nullptr; - - startEntityIndex = ENTINDEX(pentEntity); - - // it best each entity list - if (*szKeyword == 'c') - { - int hash; - hash_item_t *item; - int count; - - hash = CaseInsensitiveHash(szValue, stringsHashTable.Count()); - count = stringsHashTable.Count(); - item = &stringsHashTable[hash]; - - if (!item->pev) - { - item->lastHash = nullptr; - return nullptr; - } - - while (item->pev) - { - if (!Q_strcmp(STRING(item->pev->classname), szValue)) - break; - - hash = (hash + 1) % count; - item = &stringsHashTable[hash]; - } - - if (!item->pev) - { - item->lastHash = nullptr; - return nullptr; - } - - if (pStartEntity) - { - if (item->lastHash && item->lastHash->pevIndex <= startEntityIndex) - item = item->lastHash; - - if (item->pevIndex <= startEntityIndex) - { - while (item->pevIndex <= startEntityIndex) - { - if (!item->next) - break; - - item = item->next; - } - - if (item->pevIndex == startEntityIndex) - { - stringsHashTable[hash].lastHash = nullptr; - return nullptr; - } - } - } - - stringsHashTable[hash].lastHash = item; - pentEntity = ENT(item->pev); - } - else - pentEntity = FIND_ENTITY_BY_STRING(pentEntity, szKeyword, szValue); - - if (!FNullEnt(pentEntity)) - { - return CBaseEntity::Instance(pentEntity); - } - - return nullptr; -} - -CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName) -{ - return UTIL_FindEntityByString(pStartEntity, "classname", szName); -} - -CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName) -{ - return UTIL_FindEntityByString(pStartEntity, "targetname", szName); -} - -CBaseEntity *UTIL_FindEntityGeneric(const char *szWhatever, const Vector &vecSrc, float flRadius) -{ - CBaseEntity *pSearch = nullptr; - CBaseEntity *pEntity = UTIL_FindEntityByTargetname(nullptr, szWhatever); - if (pEntity) - return pEntity; - - float flMaxDist2 = flRadius * flRadius; - while ((pSearch = UTIL_FindEntityByClassname(pSearch, szWhatever))) - { - float flDist2 = (pSearch->pev->origin - vecSrc).Length(); - flDist2 = flDist2 * flDist2; - if (flMaxDist2 > flDist2) - { - pEntity = pSearch; - flMaxDist2 = flDist2; - } - } - - return pEntity; -} - -#ifndef REGAMEDLL_FIXES -CBasePlayer *EXT_FUNC UTIL_PlayerByIndex(int playerIndex) -{ - CBasePlayer *pPlayer = nullptr; - if (playerIndex > 0 && playerIndex <= gpGlobals->maxClients) - { - edict_t *pPlayerEdict = INDEXENT(playerIndex); - if (pPlayerEdict && !pPlayerEdict->free) - pPlayer = CBasePlayer::Instance(pPlayerEdict); - } - - return pPlayer; -} -#endif - -void UTIL_MakeVectors(const Vector &vecAngles) -{ - MAKE_VECTORS(vecAngles); -} - -void UTIL_MakeAimVectors(const Vector &vecAngles) -{ - float rgflVec[3]; - vecAngles.CopyToArray(rgflVec); - rgflVec[0] = -rgflVec[0]; - MAKE_VECTORS(rgflVec); -} - -void UTIL_MakeInvVectors(const Vector &vec, globalvars_t *pgv) -{ - MAKE_VECTORS(vec); - - pgv->v_right = pgv->v_right * -1; - - SWAP(pgv->v_forward.y, pgv->v_right.x); - SWAP(pgv->v_forward.z, pgv->v_up.x); - SWAP(pgv->v_right.z, pgv->v_up.y); -} - -void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch) -{ - float rgfl[3]; - vecOrigin.CopyToArray(rgfl); - - if (samp && *samp == '!') - { - char name[32]; - if (SENTENCEG_Lookup(samp, name) >= 0) - EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch); - } - else - EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch); -} - -unsigned short FixedUnsigned16(float value, float scale) -{ - int output = value * scale; - if (output < 0) - output = 0; - - if (output > USHRT_MAX) - output = USHRT_MAX; - - return (unsigned short)output; -} - -short FixedSigned16(float value, float scale) -{ - int output = value * scale; - if (output > SHRT_MAX) - output = SHRT_MAX; - - if (output < SHRT_MIN) - output = SHRT_MIN; - - return (short)output; -} - -void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius) -{ - int i; - float localAmplitude; - ScreenShake shake; - - shake.duration = FixedUnsigned16(duration, (1<<12)); - shake.frequency = FixedUnsigned16(frequency, (1<<8)); - - for (i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer || !(pPlayer->pev->flags & FL_ONGROUND)) - continue; - - localAmplitude = 0; - if (radius > 0) - { - Vector delta = center - pPlayer->pev->origin; - float distance = delta.Length(); - - if (distance < radius) - localAmplitude = amplitude; - } - else - localAmplitude = amplitude; - - if (localAmplitude) - { - shake.amplitude = FixedUnsigned16(localAmplitude, 1<<12); - - MESSAGE_BEGIN(MSG_ONE, gmsgShake, nullptr, pPlayer->edict()); - WRITE_SHORT(shake.amplitude); - WRITE_SHORT(shake.duration); - WRITE_SHORT(shake.frequency); - MESSAGE_END(); - } - } -} - -NOXREF void UTIL_ScreenShakeAll(const Vector ¢er, float amplitude, float frequency, float duration) -{ - UTIL_ScreenShake(center, amplitude, frequency, duration, 0); -} - -void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) -{ - fade.duration = FixedUnsigned16(fadeTime, 1<<12); - fade.holdTime = FixedUnsigned16(fadeHold, 1<<12); - fade.r = int(color.x); - fade.g = int(color.y); - fade.b = int(color.z); - fade.a = alpha; - fade.fadeFlags = flags; -} - -void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity) -{ - if (!pEntity || !pEntity->IsNetClient()) - return; - - MESSAGE_BEGIN(MSG_ONE, gmsgFade, nullptr, pEntity->edict()); - WRITE_SHORT(fade.duration); - WRITE_SHORT(fade.holdTime); - WRITE_SHORT(fade.fadeFlags); - WRITE_BYTE(fade.r); - WRITE_BYTE(fade.g); - WRITE_BYTE(fade.b); - WRITE_BYTE(fade.a); - MESSAGE_END(); -} - -void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) -{ - int i; - ScreenFade fade; - UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags); - for (i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - UTIL_ScreenFadeWrite(fade, pPlayer); - } -} - -void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) -{ - ScreenFade fade; - UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags); - UTIL_ScreenFadeWrite(fade, pEntity); -} - -void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage) -{ - if (!pEntity || !pEntity->IsNetClient()) - return; - - MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, pEntity->edict()); - WRITE_BYTE(TE_TEXTMESSAGE); - WRITE_BYTE(textparms.channel & 0xFF); - WRITE_SHORT(FixedSigned16(textparms.x, (1<<13))); - WRITE_SHORT(FixedSigned16(textparms.y, (1<<13))); - WRITE_BYTE(textparms.effect); - WRITE_BYTE(textparms.r1); - WRITE_BYTE(textparms.g1); - WRITE_BYTE(textparms.b1); - WRITE_BYTE(textparms.a1); - WRITE_BYTE(textparms.r2); - WRITE_BYTE(textparms.g2); - WRITE_BYTE(textparms.b2); - WRITE_BYTE(textparms.a2); - WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, (1<<8))); - WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, (1<<8))); - WRITE_SHORT(FixedUnsigned16(textparms.holdTime, (1<<8))); - - if (textparms.effect == 2) - WRITE_SHORT(FixedUnsigned16(textparms.fxTime, (1<<8))); - - if (!pMessage) - WRITE_STRING(" "); - else - { - if (Q_strlen(pMessage) >= 512) - { - char tmp[512]; - Q_strlcpy(tmp, pMessage); - WRITE_STRING(tmp); - } - else - { - WRITE_STRING(pMessage); - } - } - MESSAGE_END(); -} - -void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage) -{ - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - { - UTIL_HudMessage(pPlayer, textparms, pMessage); - } - } -} - -void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4) -{ - MESSAGE_BEGIN(MSG_ALL, gmsgTextMsg); - WRITE_BYTE(msg_dest); - WRITE_STRING(msg_name); - if (param1) - WRITE_STRING(param1); - if (param2) - WRITE_STRING(param2); - if (param3) - WRITE_STRING(param3); - if (param4) - WRITE_STRING(param4); - MESSAGE_END(); -} - -void ClientPrint(entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4) -{ - MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, nullptr, client); - WRITE_BYTE(msg_dest); - WRITE_STRING(msg_name); - if (param1) - WRITE_STRING(param1); - if (param2) - WRITE_STRING(param2); - if (param3) - WRITE_STRING(param3); - if (param4) - WRITE_STRING(param4); - MESSAGE_END(); -} - -void UTIL_Log(const char *fmt, ...) -{ - va_list ap; - static char string[1024]; - - va_start(ap, fmt); - Q_vsnprintf(string, sizeof(string), fmt, ap); - va_end(ap); - - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; - - FILE *fp = fopen("regamedll.log", "at"); - if (fp) - { - fprintf(fp, "%s", string); - fclose(fp); - } -} - -void UTIL_ServerPrint(const char *fmt, ...) -{ -#ifdef PLAY_GAMEDLL - // Check is null, test the demo started before than searches pointer to refs - if (&g_engfuncs == nullptr || g_engfuncs.pfnServerPrint == nullptr) - return; -#endif - - static char string[1024]; - va_list ap; - va_start(ap, fmt); - Q_vsnprintf(string, sizeof(string), fmt, ap); - va_end(ap); - - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; - - SERVER_PRINT(string); -} - -void UTIL_PrintConsole(edict_t *pEdict, const char *fmt, ...) -{ - CBaseEntity *pEntity = GET_PRIVATE(pEdict); - if (!pEntity || !pEntity->IsNetClient()) - return; - - static char string[1024]; - - va_list ap; - va_start(ap, fmt); - Q_vsnprintf(string, sizeof(string), fmt, ap); - va_end(ap); - - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; - - ClientPrint(pEntity->pev, HUD_PRINTCONSOLE, string); -} - -void UTIL_SayText(edict_t *pEdict, const char *fmt, ...) -{ - CBaseEntity *pEntity = GET_PRIVATE(pEdict); - if (!pEntity || !pEntity->IsNetClient()) - return; - - static char string[1024]; - - va_list ap; - va_start(ap, fmt); - Q_vsnprintf(string, sizeof(string), fmt, ap); - va_end(ap); - - if (Q_strlen(string) < sizeof(string) - 2) - Q_strcat(string, "\n"); - else - string[Q_strlen(string) - 1] = '\n'; - - MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pEntity->edict()); - WRITE_BYTE(pEntity->entindex()); - WRITE_STRING(string); - MESSAGE_END(); -} - -void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity) -{ - MESSAGE_BEGIN(MSG_ALL, gmsgSayText); - WRITE_BYTE(pEntity->entindex()); - WRITE_STRING(pText); - MESSAGE_END(); -} - -char *UTIL_dtos1(int d) -{ - static char buf[8]; - Q_sprintf(buf, "%d", d); - return buf; -} - -char *UTIL_dtos2(int d) -{ - static char buf[8]; - Q_sprintf(buf, "%d", d); - return buf; -} - -NOXREF char *UTIL_dtos3(int d) -{ - static char buf[8]; - Q_sprintf(buf, "%d", d); - return buf; -} - -NOXREF char *UTIL_dtos4(int d) -{ - static char buf[8]; - Q_sprintf(buf, "%d", d); - return buf; -} - -void UTIL_ShowMessageArgs(const char *pString, CBaseEntity *pPlayer, CUtlVector *args, bool isHint) -{ - if (!pPlayer) - return; - - if (!pPlayer->IsNetClient()) - return; - - if (args) - { - MESSAGE_BEGIN(MSG_ONE, gmsgHudTextArgs, nullptr, pPlayer->pev); - WRITE_STRING(pString); - WRITE_BYTE(isHint); - WRITE_BYTE(args->Count()); - - for (int i = 0; i < args->Count(); i++) - WRITE_STRING((*args)[i]); - - MESSAGE_END(); - } - else - { - MESSAGE_BEGIN(MSG_ONE, gmsgHudTextPro, nullptr, pPlayer->pev); - WRITE_STRING(pString); - WRITE_BYTE(isHint); - MESSAGE_END(); - } -} - -void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity, bool isHint) -{ - if (!pEntity || !pEntity->IsNetClient()) - return; - - MESSAGE_BEGIN(MSG_ONE, gmsgHudTextPro, nullptr, pEntity->edict()); - WRITE_STRING(pString); - WRITE_BYTE(int(isHint)); - MESSAGE_END(); -} - -void UTIL_ShowMessageAll(const char *pString, bool isHint) -{ - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - UTIL_ShowMessage(pString, pPlayer, isHint); - } -} - -void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr) -{ - TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters), pentIgnore, ptr); -} - -// OVERLOAD -void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr) -{ - TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters) | (ignoreGlass ? 0x100 : 0), pentIgnore, ptr); -} - -void UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr) -{ - TRACE_HULL(vecStart, vecEnd, (igmon == ignore_monsters), hullNumber, pentIgnore, ptr); -} - -void UTIL_TraceModel(const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr) -{ - TRACE_MODEL(vecStart, vecEnd, hullNumber, pentModel, ptr); -} - -NOXREF TraceResult UTIL_GetGlobalTrace() -{ - TraceResult tr; - - tr.flFraction = gpGlobals->trace_fraction; - tr.fInWater = int(gpGlobals->trace_inwater); - tr.fAllSolid = int(gpGlobals->trace_allsolid); - tr.fStartSolid = int(gpGlobals->trace_startsolid); - tr.fInOpen = int(gpGlobals->trace_inopen); - tr.vecEndPos = gpGlobals->trace_endpos; - tr.flPlaneDist = gpGlobals->trace_plane_dist; - tr.vecPlaneNormal = gpGlobals->trace_plane_normal; - tr.iHitgroup = gpGlobals->trace_hitgroup; - tr.pHit = gpGlobals->trace_ent; - - return tr; -} - -void UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax) -{ - SET_SIZE(ENT(pev), vecMin, vecMax); -} - -float UTIL_VecToYaw(const Vector &vec) -{ - return VEC_TO_YAW(vec); -} - -void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin) -{ - edict_t *pEdict = ENT(pev); - if (pEdict) - { - SET_ORIGIN(pEdict, vecOrigin); - } -} - -NOXREF void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount) -{ - PARTICLE_EFFECT(vecOrigin, vecDirection, float(ulColor), float(ulCount)); -} - -float UTIL_Approach(float target, float value, float speed) -{ - float delta = target - value; - if (delta > speed) - value += speed; - else if (delta < -speed) - value -= speed; - else - value = target; - - return value; -} - -real_t UTIL_ApproachAngle(float target, float value, float speed) -{ - target = UTIL_AngleMod(target); - -#ifdef REGAMEDLL_FIXES - value = UTIL_AngleMod(value); -#else - value = UTIL_AngleMod(target); -#endif - - float delta = target - value; - if (speed < 0.0f) - speed = -speed; - - if (delta < -180.0f) - delta += 360.0f; - else if (delta > 180.0f) - delta -= 360.0f; - - if (delta > speed) - value += speed; - else if (delta < -speed) - value -= speed; - else - value = target; - - return value; -} - -real_t UTIL_AngleDistance(float next, float cur) -{ - real_t delta; - - delta = next - cur; - - if (delta < -180.0f) - delta += 360.0f; - - else if (delta > 180.0f) - delta -= 360.0f; - - return delta; -} - -float UTIL_SplineFraction(float value, float scale) -{ - float valueSquared = value * scale; - return 3.0f * (valueSquared * valueSquared) - (valueSquared * valueSquared * valueSquared + valueSquared * valueSquared * valueSquared); -} - -char *UTIL_VarArgs(char *format, ...) -{ - va_list argptr; - static char string[1024]; - - va_start(argptr, format); - vsprintf(string, format, argptr); - va_end(argptr); - - return string; -} - -NOXREF Vector UTIL_GetAimVector(edict_t *pent, float flSpeed) -{ - Vector tmp; - GET_AIM_VECTOR(pent, flSpeed, tmp); - return tmp; -} - -bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator) -{ - if (sMaster) - { - edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(sMaster)); - if (!FNullEnt(pentTarget)) - { - CBaseEntity *pMaster = CBaseEntity::Instance(pentTarget); - if (pMaster && (pMaster->ObjectCaps() & FCAP_MASTER)) - return pMaster->IsTriggered(pActivator) != FALSE; - } - - ALERT(at_console, "Master was null or not a master!\n"); - } - - return true; -} - -BOOL UTIL_ShouldShowBlood(int color) -{ - if (color != DONT_BLEED) - { - if (color == BLOOD_COLOR_RED) - { - if (CVAR_GET_FLOAT("violence_hblood") != 0.0f) - return TRUE; - } - else - { - if (CVAR_GET_FLOAT("violence_ablood") != 0.0f) - return TRUE; - } - } - - return FALSE; -} - -int UTIL_PointContents(const Vector &vec) -{ - return POINT_CONTENTS(vec); -} - -void UTIL_BloodStream(const Vector &origin, const Vector &direction, int color, int amount) -{ - if (!UTIL_ShouldShowBlood(color)) - return; - - if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED) - color = 0; - - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); - WRITE_BYTE(TE_BLOODSTREAM); - WRITE_COORD(origin.x); - WRITE_COORD(origin.y); - WRITE_COORD(origin.z); - WRITE_COORD(direction.x); - WRITE_COORD(direction.y); - WRITE_COORD(direction.z); - WRITE_BYTE(color); - WRITE_BYTE(Q_min(amount, 255)); - MESSAGE_END(); -} - -void UTIL_BloodDrips(const Vector &origin, const Vector &direction, int color, int amount) -{ - if (!UTIL_ShouldShowBlood(color)) - return; - - if (color == DONT_BLEED || amount == 0) - return; - - if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED) - color = 0; - - if (g_pGameRules->IsMultiplayer()) - amount *= 2; - - if (amount > 255) - amount = 255; - - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); - WRITE_BYTE(TE_BLOODSPRITE); - WRITE_COORD(origin.x); - WRITE_COORD(origin.y); - WRITE_COORD(origin.z); - WRITE_SHORT(g_sModelIndexBloodSpray); - WRITE_SHORT(g_sModelIndexBloodDrop); - WRITE_BYTE(color); - WRITE_BYTE(clamp(amount / 10, 3, 16)); - MESSAGE_END(); -} - -Vector UTIL_RandomBloodVector() -{ - Vector direction; - direction.x = RANDOM_FLOAT(-1, 1); - direction.y = RANDOM_FLOAT(-1, 1); - direction.z = RANDOM_FLOAT(0, 1); - return direction; -} - -void UTIL_BloodDecalTrace(TraceResult *pTrace, int bloodColor) -{ - if (UTIL_ShouldShowBlood(bloodColor)) - { - if (bloodColor == BLOOD_COLOR_RED) - UTIL_DecalTrace(pTrace, DECAL_BLOOD1 + RANDOM_LONG(0, 5)); - else - UTIL_DecalTrace(pTrace, DECAL_YBLOOD1 + RANDOM_LONG(0, 5)); - } -} - -void UTIL_DecalTrace(TraceResult *pTrace, int decalNumber) -{ - short entityIndex; - int index; - int message; - - if (decalNumber < 0) - return; - - index = gDecals[decalNumber].index; - if (index < 0 || pTrace->flFraction == 1.0f) - return; - - if (pTrace->pHit) - { - CBaseEntity *pEntity = CBaseEntity::Instance(pTrace->pHit); - if (pEntity && !pEntity->IsBSPModel()) - return; - - entityIndex = ENTINDEX(pTrace->pHit); - } - else - entityIndex = 0; - - message = TE_DECAL; - if (entityIndex) - { - if (index > 255) - { - message = TE_DECALHIGH; - index -= 256; - } - } - else - { - message = TE_WORLDDECAL; - if (index > 255) - { - message = TE_WORLDDECALHIGH; - index -= 256; - } - } - - MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(message); - WRITE_COORD(pTrace->vecEndPos.x); - WRITE_COORD(pTrace->vecEndPos.y); - WRITE_COORD(pTrace->vecEndPos.z); - WRITE_BYTE(index); - if (entityIndex) - WRITE_SHORT(entityIndex); - MESSAGE_END(); -} - -void UTIL_PlayerDecalTrace(TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom) -{ - int index; - if (!bIsCustom) - { - if (decalNumber < 0) - return; - - index = gDecals[decalNumber].index; - if (index < 0) - return; - } - else - index = decalNumber; - - if (pTrace->flFraction != 1.0f) - { - MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(TE_PLAYERDECAL); - WRITE_BYTE(playernum); - WRITE_COORD(pTrace->vecEndPos.x); - WRITE_COORD(pTrace->vecEndPos.y); - WRITE_COORD(pTrace->vecEndPos.z); - WRITE_SHORT(int(ENTINDEX(pTrace->pHit))); - WRITE_BYTE(index); - MESSAGE_END(); - } -} - -void UTIL_GunshotDecalTrace(TraceResult *pTrace, int decalNumber, bool ClientOnly, entvars_t *pShooter) -{ - if (decalNumber < 0) - return; - - int index = gDecals[decalNumber].index; - if (index < 0 || pTrace->flFraction == 1.0f) - return; - - if (ClientOnly) - MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, pTrace->vecEndPos, pShooter); - else - MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, pTrace->vecEndPos); - - WRITE_BYTE(TE_GUNSHOTDECAL); - WRITE_COORD(pTrace->vecEndPos.x); - WRITE_COORD(pTrace->vecEndPos.y); - WRITE_COORD(pTrace->vecEndPos.z); - WRITE_SHORT(int(ENTINDEX(pTrace->pHit))); - WRITE_BYTE(index); - MESSAGE_END(); -} - -void UTIL_Sparks(const Vector &position) -{ - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position); - WRITE_BYTE(TE_SPARKS); - WRITE_COORD(position.x); - WRITE_COORD(position.y); - WRITE_COORD(position.z); - MESSAGE_END(); -} - -void UTIL_Ricochet(const Vector &position, float scale) -{ - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position); - WRITE_BYTE(TE_ARMOR_RICOCHET); - WRITE_COORD(position.x); - WRITE_COORD(position.y); - WRITE_COORD(position.z); - WRITE_BYTE(int(scale * 10.0f)); - MESSAGE_END(); -} - -bool UTIL_TeamsMatch(const char *pTeamName1, const char *pTeamName2) -{ - if (!g_pGameRules->IsTeamplay()) - return true; - - if (*pTeamName1 != '\0' && *pTeamName2 != '\0') - { - if (!Q_stricmp(pTeamName1, pTeamName2)) - return true; - } - - return false; -} - -void UTIL_StringToVector(float *pVector, const char *pString) -{ - char *pstr; - char *pfront; - char tempString[128]; - int j; - - Q_strlcpy(tempString, pString); - - pstr = tempString; - pfront = tempString; - - for (j = 0; j < 3; j++) - { - pVector[j] = Q_atof(pfront); - - while (*pstr && *pstr != ' ') - pstr++; - - if (!*pstr) - break; - - pstr++; - pfront = pstr; - } - - for (j++; j < 3; j++) - pVector[j] = 0; -} - -void UTIL_StringToVector(Vector &vecIn, const char *pString, char cSeparator) -{ - return UTIL_StringToVectorND(vecIn, 3, pString, cSeparator); -} - -void UTIL_StringToVectorND(Vector &vecIn, int nCount, const char *pString, char cSeparator) -{ - int i; - char *pstr; - char *pfront; - char tempString[128]; - - Q_strlcpy(tempString, pString); - - pstr = tempString; - pfront = tempString; - - for (i = 0; i < nCount; i++) - { - vecIn[i] = Q_atof(pfront); - - while (*pstr && *pstr != cSeparator) - pstr++; - - if (!*pstr) - break; - - pstr++; - pfront = pstr; - } - - if (++i < nCount) - { - Q_memset(&vecIn[i], 0, sizeof(float) * (nCount - i)); - } -} - -void UTIL_StringToIntArray(int *pVector, int count, const char *pString) -{ - char *pstr; - char *pfront; - char tempString[128]; - int j; - - Q_strlcpy(tempString, pString); - - pstr = tempString; - pfront = tempString; - - for (j = 0; j < count; j++) - { - pVector[j] = Q_atoi(pfront); - - while (*pstr && *pstr != ' ') - pstr++; - - if (!*pstr) - break; - - pstr++; - pfront = pstr; - } - - for (j++; j < count; j++) - pVector[j] = 0; -} - -Vector UTIL_ClampVectorToBox(const Vector &input, const Vector &clampSize) -{ - Vector sourceVector = input; - if (sourceVector.x > clampSize.x) - sourceVector.x -= clampSize.x; - else if (sourceVector.x < -clampSize.x) - sourceVector.x += clampSize.x; - else - sourceVector.x = 0; - if (sourceVector.y > clampSize.y) - sourceVector.y -= clampSize.y; - else if (sourceVector.y < -clampSize.y) - sourceVector.y += clampSize.y; - else - sourceVector.y = 0; - if (sourceVector.z > clampSize.z) - sourceVector.z -= clampSize.z; - else if (sourceVector.z < -clampSize.z) - sourceVector.z += clampSize.z; - else - sourceVector.z = 0; - return sourceVector.Normalize(); -} - -float UTIL_WaterLevel(const Vector &position, float minz, float maxz) -{ - Vector midUp; - float diff; - - midUp = position; - midUp.z = minz; - - if (UTIL_PointContents(midUp) != CONTENTS_WATER) - return minz; - - midUp.z = maxz; - if (UTIL_PointContents(midUp) == CONTENTS_WATER) - return maxz; - - diff = maxz - minz; - while (diff > 1) - { - midUp.z = minz + diff / 2; - if (UTIL_PointContents(midUp) == CONTENTS_WATER) - minz = midUp.z; - else - maxz = midUp.z; - - diff = maxz - minz; - } - - return midUp.z; -} - -void UTIL_Bubbles(Vector mins, Vector maxs, int count) -{ - Vector mid = (mins + maxs) * 0.5f; - float flHeight = UTIL_WaterLevel(mid, mid.z, mid.z + 1024.0f) - mins.z; - - MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, mid); - WRITE_BYTE(TE_BUBBLES); - WRITE_COORD(mins.x); - WRITE_COORD(mins.y); - WRITE_COORD(mins.z); - WRITE_COORD(maxs.x); - WRITE_COORD(maxs.y); - WRITE_COORD(maxs.z); - WRITE_COORD(flHeight); - WRITE_SHORT(g_sModelIndexBubbles); - WRITE_BYTE(count); - WRITE_COORD(8); - MESSAGE_END(); -} - -void UTIL_BubbleTrail(Vector from, Vector to, int count) -{ - float flHeight = UTIL_WaterLevel(from, from.z, from.z + 256.0f) - from.z; - if (flHeight < 8.0f) - { - flHeight = UTIL_WaterLevel(to, to.z, to.z + 256.0f) - to.z; - - if (flHeight < 8.0f) - return; - - flHeight = flHeight + to.z - from.z; - } - - if (count > 255) - count = 255; - - MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(TE_BUBBLETRAIL); - WRITE_COORD(from.x); - WRITE_COORD(from.y); - WRITE_COORD(from.z); - WRITE_COORD(to.x); - WRITE_COORD(to.y); - WRITE_COORD(to.z); - WRITE_COORD(flHeight); - WRITE_SHORT(g_sModelIndexBubbles); - WRITE_BYTE(count); - WRITE_COORD(8); - MESSAGE_END(); -} - -void UTIL_Remove(CBaseEntity *pEntity) -{ - if (!pEntity) - return; - -#ifdef REGAMEDLL_FIXES - if (pEntity->pev == VARS(eoNullEntity) || pEntity->IsPlayer() || (pEntity->pev->flags & FL_KILLME) == FL_KILLME) - return; -#endif - - pEntity->UpdateOnRemove(); - - pEntity->pev->solid = SOLID_NOT; - pEntity->pev->flags |= FL_KILLME; - pEntity->pev->targetname = 0; -} - -NOXREF BOOL UTIL_IsValidEntity(edict_t *pent) -{ - if (!pent || pent->free || (pent->v.flags & FL_KILLME)) - return FALSE; - - return TRUE; -} - -void UTIL_PrecacheOther(const char *szClassname) -{ - edict_t *pent = CREATE_NAMED_ENTITY(MAKE_STRING(szClassname)); - if (FNullEnt(pent)) - { - ALERT(at_console, "NULL Ent in UTIL_PrecacheOther classname `%s`\n", szClassname); - return; - } - - CBaseEntity *pEntity = CBaseEntity::Instance(VARS(pent)); - if (pEntity) - { - pEntity->Precache(); - } - - REMOVE_ENTITY(pent); -} - -void UTIL_RestartOther(const char *szClassname) -{ - CBaseEntity *pEntity = nullptr; - while ((pEntity = UTIL_FindEntityByClassname(pEntity, szClassname))) - { - pEntity->Restart(); - } -} - -void UTIL_ResetEntities() -{ - edict_t *pEdict = INDEXENT(1); - for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) - { - if (pEdict->free) - continue; - - CBaseEntity *pEntity = CBaseEntity::Instance(pEdict); - if (!pEntity) - continue; - - // only non-player entities - if (pEntity->IsPlayer()) - continue; - - int caps = pEntity->ObjectCaps(); - if ((caps & FCAP_MUST_RELEASE) == FCAP_MUST_RELEASE) - UTIL_Remove(pEntity); - - else if ((caps & FCAP_MUST_RESET) == FCAP_MUST_RESET) - pEntity->Restart(); - } -} - -void UTIL_RemoveOther(const char *szClassname, int nRemoveCount) -{ - int num = 0; - CBaseEntity *pEntity = nullptr; - while ((pEntity = UTIL_FindEntityByClassname(pEntity, szClassname))) - { -#ifndef REGAMEDLL_FIXES - if (nRemoveCount > 0 && num++ >= nRemoveCount) - break; -#endif - - UTIL_Remove(pEntity); - } -} - -void UTIL_LogPrintf(const char *fmt, ...) -{ - va_list argptr; - static char string[1024]; - - va_start(argptr, fmt); - vsprintf(string, fmt, argptr); - va_end(argptr); - - ALERT(at_logged, "%s", string); -} - -NOXREF float UTIL_DotPoints(const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir) -{ - Vector2D vec2LOS = (vecCheck - vecSrc).Make2D(); - vec2LOS = vec2LOS.Normalize(); - return DotProduct(vec2LOS, (vecDir.Make2D())); -} - -char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd) -{ - char chTextureType; - float rgfl1[3]; - float rgfl2[3]; - const char *pTextureName; - char szbuffer[64]; - CBaseEntity *pEntity = CBaseEntity::Instance(ptr->pHit); - - if (pEntity && pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE) - return CHAR_TEX_FLESH; - - vecSrc.CopyToArray(rgfl1); - vecEnd.CopyToArray(rgfl2); - - if (pEntity) - pTextureName = TRACE_TEXTURE(ENT(pEntity->pev), rgfl1, rgfl2); - else - pTextureName = TRACE_TEXTURE(ENT(0), rgfl1, rgfl2); - - if (pTextureName) - { - if (*pTextureName == '-' || *pTextureName == '+') - pTextureName += 2; - - if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') - pTextureName++; - - Q_strcpy(szbuffer, pTextureName); - szbuffer[16] = '\0'; - chTextureType = TEXTURETYPE_Find(szbuffer); - } - else - chTextureType = '\0'; - - return chTextureType; -} - -NOXREF int GetPlayerTeam(int index) -{ - CBasePlayer *pPlayer = UTIL_PlayerByIndexSafe(index); - if (pPlayer) - { - return pPlayer->m_iTeam; - } - - return 0; -} - -bool UTIL_IsGame(const char *pszGameName) -{ -#ifndef CSTRIKE - if (pszGameName) - { - char szGameDir[256]; - GET_GAME_DIR(szGameDir); - return (Q_stricmp(szGameDir, pszGameName) == 0); - } -#endif - - return false; -} - -real_t UTIL_GetPlayerGaitYaw(int playerIndex) -{ - CBasePlayer *pPlayer = UTIL_PlayerByIndex(playerIndex); - if (pPlayer) - { - return pPlayer->m_flGaityaw; - } - - return 0; -} - -int UTIL_ReadFlags(const char *c) -{ - int flags = 0; - - while (*c) - { - if (*c >= 'a' && *c <= 'z') - { - flags |= (1 << (*c - 'a')); - } - - c++; - } - - return flags; -} - -// Determine whether bots can be used or not -bool UTIL_AreBotsAllowed() -{ -#ifdef REGAMEDLL_ADD - if (g_engfuncs.pfnEngCheckParm == nullptr) - return false; -#endif - - if (AreRunningCZero()) - { -#ifdef REGAMEDLL_ADD - // If they pass in -nobots, don't allow bots. This is for people who host servers, to - // allow them to disallow bots to enforce CPU limits. - int nobots = ENG_CHECK_PARM("-nobots", nullptr); - if (nobots) - { - return false; - } -#endif - - return true; - } - -#ifdef REGAMEDLL_ADD - // let enables zBot by default from listen server? - if (!IS_DEDICATED_SERVER()) - { - return true; - } - - // allow the using of bots for CS 1.6 - int bots = ENG_CHECK_PARM("-bots", nullptr); - if (bots) - { - return true; - } -#endif - - return false; -} - -bool UTIL_IsBeta() -{ -#ifdef BUILD_LATEST - if (g_engfuncs.pfnEngCheckParm == nullptr) - return false; - - // always beta from listen server - if (!IS_DEDICATED_SERVER()) - { - return true; - } - - int beta = ENG_CHECK_PARM("-beta", nullptr); - if (beta) - { - return true; - } -#endif // #ifdef BUILD_LATEST - - return false; -} - -bool UTIL_AreHostagesImprov() -{ - if (AreRunningCZero()) - { - return true; - } - -#ifdef REGAMEDLL_ADD - // someday in CS 1.6 - int improv = ENG_CHECK_PARM("-host-improv", nullptr); - if (improv) - { - return true; - } -#endif - - return false; -} - -int UTIL_GetNumPlayers() -{ - int nNumPlayers = 0; - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - if (pPlayer) - { - nNumPlayers++; - } - } - - return nNumPlayers; -} - -bool UTIL_IsSpawnPointOccupied(CBaseEntity *pSpot) -{ - if (!pSpot) - return false; - - const int maxList = 8; - CBaseEntity *pList[maxList]; - - Vector mins(pSpot->pev->origin + VEC_SPOT_HULL_MIN - 8.0); - Vector maxs(pSpot->pev->origin + VEC_SPOT_HULL_MAX + 8.0); - - int nCount = UTIL_EntitiesInBox(pList, maxList, mins, maxs, FL_MONSTER | FL_CLIENT); - for (int i = 0; i < nCount; i++) - { - if (pList[i]->pev->solid != SOLID_NOT) - return false; - } - - return false; -} - -void MAKE_STRING_CLASS(const char *str, entvars_t *pev) -{ - if (!FStringNull(pev->classname)) - { - RemoveEntityHashValue(pev, STRING(pev->classname), CLASSNAME); - } - - pev->classname = MAKE_STRING(str); - AddEntityHashValue(pev, STRING(pev->classname), CLASSNAME); -} - -void NORETURN Sys_Error(const char *error, ...) -{ - va_list argptr; - static char text[1024]; - - va_start(argptr, error); - vsnprintf(text, sizeof(text), error, argptr); - va_end(argptr); - - FILE *fl = fopen("regamedll_error.txt", "w"); - if (fl) - { - fprintf(fl, "%s\n", text); - fclose(fl); - } - - CONSOLE_ECHO("FATAL ERROR (shutting down): %s\n", text); - - //TerminateProcess(GetCurrentProcess(), 1); - int *null = 0; - *null = 0; - exit(-1); -} - -int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, int &playersInCount, int &playersOutCount, CPlayerInVolumeAdapter *pAdapter) -{ - playersInCount = 0; - playersOutCount = 0; - - if (pBrushEntity) - { - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); - - if (!pPlayer || !pPlayer->IsInWorld()) - continue; - - if (bOnlyAlive && !pPlayer->IsAlive()) - continue; - - TraceResult trace; - int hullNumber = (pPlayer->pev->flags & FL_DUCKING) ? head_hull : human_hull; - UTIL_TraceModel(pPlayer->pev->origin, pPlayer->pev->origin, hullNumber, pBrushEntity->edict(), &trace); - - bool fInVolume = trace.fStartSolid > 0.0f; - if (fInVolume) - { - playersInCount++; - } - else - { - playersOutCount++; - } - - if (pAdapter) { - pAdapter->PlayerDetected(fInVolume, pPlayer); - } - } - } - else - { - playersOutCount = UTIL_GetNumPlayers(); - } - - return playersInCount + playersOutCount; -} +#include "precompiled.h" + +unsigned int glSeed; +CUtlVector stringsHashTable; + +unsigned int seed_table[256] = +{ + 28985U, 27138U, 26457U, 9451U, 17764U, 10909U, 28790U, 8716U, 6361U, 4853U, 17798U, 21977U, 19643U, 20662U, 10834U, 20103, + 27067U, 28634U, 18623U, 25849U, 8576U, 26234U, 23887U, 18228U, 32587U, 4836U, 3306U, 1811U, 3035U, 24559U, 18399U, 315, + 26766U, 907U, 24102U, 12370U, 9674U, 2972U, 10472U, 16492U, 22683U, 11529U, 27968U, 30406U, 13213U, 2319U, 23620U, 16823, + 10013U, 23772U, 21567U, 1251U, 19579U, 20313U, 18241U, 30130U, 8402U, 20807U, 27354U, 7169U, 21211U, 17293U, 5410U, 19223, + 10255U, 22480U, 27388U, 9946U, 15628U, 24389U, 17308U, 2370U, 9530U, 31683U, 25927U, 23567U, 11694U, 26397U, 32602U, 15031, + 18255U, 17582U, 1422U, 28835U, 23607U, 12597U, 20602U, 10138U, 5212U, 1252U, 10074U, 23166U, 19823U, 31667U, 5902U, 24630, + 18948U, 14330U, 14950U, 8939U, 23540U, 21311U, 22428U, 22391U, 3583U, 29004U, 30498U, 18714U, 4278U, 2437U, 22430U, 3439, + 28313U, 23161U, 25396U, 13471U, 19324U, 15287U, 2563U, 18901U, 13103U, 16867U, 9714U, 14322U, 15197U, 26889U, 19372U, 26241, + 31925U, 14640U, 11497U, 8941U, 10056U, 6451U, 28656U, 10737U, 13874U, 17356U, 8281U, 25937U, 1661U, 4850U, 7448U, 12744, + 21826U, 5477U, 10167U, 16705U, 26897U, 8839U, 30947U, 27978U, 27283U, 24685U, 32298U, 3525U, 12398U, 28726U, 9475U, 10208, + 617U, 13467U, 22287U, 2376U, 6097U, 26312U, 2974U, 9114U, 21787U, 28010U, 4725U, 15387U, 3274U, 10762U, 31695U, 17320, + 18324U, 12441U, 16801U, 27376U, 22464U, 7500U, 5666U, 18144U, 15314U, 31914U, 31627U, 6495U, 5226U, 31203U, 2331U, 4668, + 12650U, 18275U, 351U, 7268U, 31319U, 30119U, 7600U, 2905U, 13826U, 11343U, 13053U, 15583U, 30055U, 31093U, 5067U, 761, + 9685U, 11070U, 21369U, 27155U, 3663U, 26542U, 20169U, 12161U, 15411U, 30401U, 7580U, 31784U, 8985U, 29367U, 20989U, 14203, + 29694U, 21167U, 10337U, 1706U, 28578U, 887U, 3373U, 19477U, 14382U, 675U, 7033U, 15111U, 26138U, 12252U, 30996U, 21409, + 25678U, 18555U, 13256U, 23316U, 22407U, 16727U, 991U, 9236U, 5373U, 29402U, 6117U, 15241U, 27715U, 19291U, 19888U, 19847U +}; + +int g_groupmask = 0; +int g_groupop = 0; + +float UTIL_WeaponTimeBase() +{ +#ifdef CLIENT_WEAPONS + return 0.0; +#else + return gpGlobals->time; +#endif +} + +unsigned int U_Random() +{ + glSeed *= 69069; + glSeed += seed_table[glSeed & 0xFF] + 1; + return (glSeed & 0xFFFFFFF); +} + +void U_Srand(unsigned int seed) +{ + glSeed = seed_table[seed & 0xFF]; +} + +int UTIL_SharedRandomLong(unsigned int seed, int low, int high) +{ + unsigned int range = high - low + 1; + U_Srand((unsigned int)(high + low + seed)); + if (range != 1) + { + int rnum = U_Random(); + int offset = rnum % range; + return (low + offset); + } + + return low; +} + +float UTIL_SharedRandomFloat(unsigned int seed, float low, float high) +{ + unsigned int range = high - low; + U_Srand((unsigned int)seed + *(unsigned int *)&low + *(unsigned int *)&high); + + U_Random(); + U_Random(); + + if (range) + { + int tensixrand = U_Random() & 0xFFFFu; + float offset = float(tensixrand) / 0x10000u; + return (low + offset * range); + } + + return low; +} + +NOXREF void UTIL_ParametricRocket(entvars_t *pev, Vector p_vecOrigin, Vector vecAngles, edict_t *owner) +{ + TraceResult tr; + Vector vecTravel; + float travelTime; + + pev->startpos = p_vecOrigin; + UTIL_MakeVectors(vecAngles); + UTIL_TraceLine(pev->startpos, gpGlobals->v_forward * 8192.0f + pev->startpos, ignore_monsters, owner, &tr); + pev->endpos = tr.vecEndPos; + + vecTravel = pev->endpos - pev->startpos; + if (pev->velocity.Length() > 0.0f) + travelTime = vecTravel.Length() / pev->velocity.Length(); + else + travelTime = 0.0f; + + pev->starttime = gpGlobals->time; + pev->impacttime = travelTime + gpGlobals->time; +} + +void UTIL_SetGroupTrace(int groupmask, int op) +{ + g_groupmask = groupmask; + g_groupop = op; + + ENGINE_SETGROUPMASK(groupmask, op); +} + +void UTIL_UnsetGroupTrace() +{ + g_groupmask = 0; + g_groupop = 0; + + ENGINE_SETGROUPMASK(0, 0); +} + +NOXREF UTIL_GroupTrace::UTIL_GroupTrace(int groupmask, int op) +{ + m_oldgroupmask = g_groupmask; + m_oldgroupop = g_groupop; + + g_groupmask = groupmask; + g_groupop = op; + + ENGINE_SETGROUPMASK(groupmask, op); +} + +NOXREF UTIL_GroupTrace::~UTIL_GroupTrace() +{ + g_groupmask = m_oldgroupmask; + g_groupop = m_oldgroupop; + + ENGINE_SETGROUPMASK(g_groupmask, g_groupop); +} + +NOXREF BOOL UTIL_GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon) +{ + return g_pGameRules->GetNextBestWeapon(pPlayer,pCurrentWeapon); +} + +float UTIL_AngleMod(float a) +{ + if (a < 0.0f) + a = a + 360.0f * (int(a / 360.0f) + 1); + else if (a >= 360.0f) + a = a - 360.0f * int(a / 360.0f); + return a; +} + +NOXREF float UTIL_AngleDiff(float destAngle, float srcAngle) +{ + float delta = destAngle - srcAngle; + if (destAngle > srcAngle) + { + if (delta >= 180.0f) + delta -= 360.0f; + } + else + { + if (delta <= -180.0f) + delta += 360.0f; + } + + return delta; +} + +Vector UTIL_VecToAngles(const Vector &vec) +{ + float rgflVecOut[3]; + VEC_TO_ANGLES(vec, rgflVecOut); + return Vector(rgflVecOut); +} + +NOXREF void UTIL_MoveToOrigin(edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType) +{ + float rgfl[3]; + vecGoal.CopyToArray(rgfl); + MOVE_TO_ORIGIN(pent, rgfl, flDist, iMoveType); +} + +int UTIL_EntitiesInBox(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask) +{ + edict_t *pEdict = INDEXENT(1); + int count = 0; + + if (!pEdict) + return 0; + + for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) + { + if (pEdict->free) + continue; + + if (flagMask && !(pEdict->v.flags & flagMask)) + continue; + + CBaseEntity *pEntity = CBaseEntity::Instance(pEdict); + if (!pEntity) + continue; + + if (!pEntity->Intersects(mins, maxs)) + continue; + + pList[count++] = pEntity; + + if (count >= listMax) + break; + } + + return count; +} + +NOXREF int UTIL_MonstersInSphere(CBaseEntity ** pList, int listMax, const Vector ¢er, float radius) +{ + edict_t *pEdict; + CBaseEntity *pEntity; + int count = 0; + float distance; + float delta; + + float radiusSquared = radius * radius; + pEdict = INDEXENT(1); + + if (!pEdict) + return count; + + for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) + { + if (pEdict->free) + continue; + + if (!(pEdict->v.flags & (FL_CLIENT | FL_MONSTER))) + continue; + + delta = center.x - pEdict->v.origin.x; + delta *= delta; + + if (delta > radiusSquared) + continue; + + distance = delta; + + delta = center.y - pEdict->v.origin.y; + delta *= delta; + + distance += delta; + if (distance > radiusSquared) + continue; + + delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z) * 0.5; + delta *= delta; + + distance += delta; + if (distance > radiusSquared) + continue; + + pEntity = CBaseEntity::Instance(pEdict); + if (!pEntity) + continue; + + pList[count++] = pEntity; + + if (count >= listMax) + return count; + } + + return count; +} + +CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius) +{ + edict_t *pentEntity; + if (pStartEntity) + pentEntity = pStartEntity->edict(); + else + pentEntity = nullptr; + + pentEntity = FIND_ENTITY_IN_SPHERE(pentEntity, vecCenter, flRadius); + if (!FNullEnt(pentEntity)) + { + return CBaseEntity::Instance(pentEntity); + } + + return nullptr; +} + +CBaseEntity *UTIL_FindEntityByString_Old(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue) +{ + edict_t *pentEntity; + if (pStartEntity) + pentEntity = pStartEntity->edict(); + else + pentEntity = nullptr; + + pentEntity = FIND_ENTITY_BY_STRING(pentEntity, szKeyword, szValue); + if (!FNullEnt(pentEntity)) + { + return CBaseEntity::Instance(pentEntity); + } + + return nullptr; +} + +CBaseEntity *EXT_FUNC UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue) +{ + edict_t *pentEntity; + int startEntityIndex; + + if (pStartEntity) + pentEntity = pStartEntity->edict(); + else + pentEntity = nullptr; + + startEntityIndex = ENTINDEX(pentEntity); + + // it best each entity list + if (*szKeyword == 'c') + { + int hash; + hash_item_t *item; + int count; + + hash = CaseInsensitiveHash(szValue, stringsHashTable.Count()); + count = stringsHashTable.Count(); + item = &stringsHashTable[hash]; + + if (!item->pev) + { + item->lastHash = nullptr; + return nullptr; + } + + while (item->pev) + { + if (!Q_strcmp(STRING(item->pev->classname), szValue)) + break; + + hash = (hash + 1) % count; + item = &stringsHashTable[hash]; + } + + if (!item->pev) + { + item->lastHash = nullptr; + return nullptr; + } + + if (pStartEntity) + { + if (item->lastHash && item->lastHash->pevIndex <= startEntityIndex) + item = item->lastHash; + + if (item->pevIndex <= startEntityIndex) + { + while (item->pevIndex <= startEntityIndex) + { + if (!item->next) + break; + + item = item->next; + } + + if (item->pevIndex == startEntityIndex) + { + stringsHashTable[hash].lastHash = nullptr; + return nullptr; + } + } + } + + stringsHashTable[hash].lastHash = item; + pentEntity = ENT(item->pev); + } + else + pentEntity = FIND_ENTITY_BY_STRING(pentEntity, szKeyword, szValue); + + if (!FNullEnt(pentEntity)) + { + return CBaseEntity::Instance(pentEntity); + } + + return nullptr; +} + +CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName) +{ + return UTIL_FindEntityByString(pStartEntity, "classname", szName); +} + +CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName) +{ + return UTIL_FindEntityByString(pStartEntity, "targetname", szName); +} + +CBaseEntity *UTIL_FindEntityGeneric(const char *szWhatever, const Vector &vecSrc, float flRadius) +{ + CBaseEntity *pSearch = nullptr; + CBaseEntity *pEntity = UTIL_FindEntityByTargetname(nullptr, szWhatever); + if (pEntity) + return pEntity; + + float flMaxDist2 = flRadius * flRadius; + while ((pSearch = UTIL_FindEntityByClassname(pSearch, szWhatever))) + { + float flDist2 = (pSearch->pev->origin - vecSrc).Length(); + flDist2 = flDist2 * flDist2; + if (flMaxDist2 > flDist2) + { + pEntity = pSearch; + flMaxDist2 = flDist2; + } + } + + return pEntity; +} + +#ifndef REGAMEDLL_FIXES +CBasePlayer *EXT_FUNC UTIL_PlayerByIndex(int playerIndex) +{ + CBasePlayer *pPlayer = nullptr; + if (playerIndex > 0 && playerIndex <= gpGlobals->maxClients) + { + edict_t *pPlayerEdict = INDEXENT(playerIndex); + if (pPlayerEdict && !pPlayerEdict->free) + pPlayer = CBasePlayer::Instance(pPlayerEdict); + } + + return pPlayer; +} +#endif + +void UTIL_MakeVectors(const Vector &vecAngles) +{ + MAKE_VECTORS(vecAngles); +} + +void UTIL_MakeAimVectors(const Vector &vecAngles) +{ + float rgflVec[3]; + vecAngles.CopyToArray(rgflVec); + rgflVec[0] = -rgflVec[0]; + MAKE_VECTORS(rgflVec); +} + +void UTIL_MakeInvVectors(const Vector &vec, globalvars_t *pgv) +{ + MAKE_VECTORS(vec); + + pgv->v_right = pgv->v_right * -1; + + SWAP(pgv->v_forward.y, pgv->v_right.x); + SWAP(pgv->v_forward.z, pgv->v_up.x); + SWAP(pgv->v_right.z, pgv->v_up.y); +} + +void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch) +{ + float rgfl[3]; + vecOrigin.CopyToArray(rgfl); + + if (samp && *samp == '!') + { + char name[32]; + if (SENTENCEG_Lookup(samp, name) >= 0) + EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch); + } + else + EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch); +} + +unsigned short FixedUnsigned16(float value, float scale) +{ + int output = value * scale; + if (output < 0) + output = 0; + + if (output > USHRT_MAX) + output = USHRT_MAX; + + return (unsigned short)output; +} + +short FixedSigned16(float value, float scale) +{ + int output = value * scale; + if (output > SHRT_MAX) + output = SHRT_MAX; + + if (output < SHRT_MIN) + output = SHRT_MIN; + + return (short)output; +} + +void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius) +{ + int i; + float localAmplitude; + ScreenShake shake; + + shake.duration = FixedUnsigned16(duration, (1<<12)); + shake.frequency = FixedUnsigned16(frequency, (1<<8)); + + for (i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer || !(pPlayer->pev->flags & FL_ONGROUND)) + continue; + + localAmplitude = 0; + if (radius > 0) + { + Vector delta = center - pPlayer->pev->origin; + float distance = delta.Length(); + + if (distance < radius) + localAmplitude = amplitude; + } + else + localAmplitude = amplitude; + + if (localAmplitude) + { + shake.amplitude = FixedUnsigned16(localAmplitude, 1<<12); + + MESSAGE_BEGIN(MSG_ONE, gmsgShake, nullptr, pPlayer->edict()); + WRITE_SHORT(shake.amplitude); + WRITE_SHORT(shake.duration); + WRITE_SHORT(shake.frequency); + MESSAGE_END(); + } + } +} + +NOXREF void UTIL_ScreenShakeAll(const Vector ¢er, float amplitude, float frequency, float duration) +{ + UTIL_ScreenShake(center, amplitude, frequency, duration, 0); +} + +void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) +{ + fade.duration = FixedUnsigned16(fadeTime, 1<<12); + fade.holdTime = FixedUnsigned16(fadeHold, 1<<12); + fade.r = int(color.x); + fade.g = int(color.y); + fade.b = int(color.z); + fade.a = alpha; + fade.fadeFlags = flags; +} + +void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity) +{ + if (!pEntity || !pEntity->IsNetClient()) + return; + + MESSAGE_BEGIN(MSG_ONE, gmsgFade, nullptr, pEntity->edict()); + WRITE_SHORT(fade.duration); + WRITE_SHORT(fade.holdTime); + WRITE_SHORT(fade.fadeFlags); + WRITE_BYTE(fade.r); + WRITE_BYTE(fade.g); + WRITE_BYTE(fade.b); + WRITE_BYTE(fade.a); + MESSAGE_END(); +} + +void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) +{ + int i; + ScreenFade fade; + UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags); + for (i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + UTIL_ScreenFadeWrite(fade, pPlayer); + } +} + +void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags) +{ + ScreenFade fade; + UTIL_ScreenFadeBuild(fade, color, fadeTime, fadeHold, alpha, flags); + UTIL_ScreenFadeWrite(fade, pEntity); +} + +void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage) +{ + if (!pEntity || !pEntity->IsNetClient()) + return; + + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, pEntity->edict()); + WRITE_BYTE(TE_TEXTMESSAGE); + WRITE_BYTE(textparms.channel & 0xFF); + WRITE_SHORT(FixedSigned16(textparms.x, (1<<13))); + WRITE_SHORT(FixedSigned16(textparms.y, (1<<13))); + WRITE_BYTE(textparms.effect); + WRITE_BYTE(textparms.r1); + WRITE_BYTE(textparms.g1); + WRITE_BYTE(textparms.b1); + WRITE_BYTE(textparms.a1); + WRITE_BYTE(textparms.r2); + WRITE_BYTE(textparms.g2); + WRITE_BYTE(textparms.b2); + WRITE_BYTE(textparms.a2); + WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, (1<<8))); + WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, (1<<8))); + WRITE_SHORT(FixedUnsigned16(textparms.holdTime, (1<<8))); + + if (textparms.effect == 2) + WRITE_SHORT(FixedUnsigned16(textparms.fxTime, (1<<8))); + + if (!pMessage) + WRITE_STRING(" "); + else + { + if (Q_strlen(pMessage) >= 512) + { + char tmp[512]; + Q_strlcpy(tmp, pMessage); + WRITE_STRING(tmp); + } + else + { + WRITE_STRING(pMessage); + } + } + MESSAGE_END(); +} + +void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (pPlayer) + { + UTIL_HudMessage(pPlayer, textparms, pMessage); + } + } +} + +void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4) +{ + MESSAGE_BEGIN(MSG_ALL, gmsgTextMsg); + WRITE_BYTE(msg_dest); + WRITE_STRING(msg_name); + if (param1) + WRITE_STRING(param1); + if (param2) + WRITE_STRING(param2); + if (param3) + WRITE_STRING(param3); + if (param4) + WRITE_STRING(param4); + MESSAGE_END(); +} + +void ClientPrint(entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4) +{ + MESSAGE_BEGIN(MSG_ONE, gmsgTextMsg, nullptr, client); + WRITE_BYTE(msg_dest); + WRITE_STRING(msg_name); + if (param1) + WRITE_STRING(param1); + if (param2) + WRITE_STRING(param2); + if (param3) + WRITE_STRING(param3); + if (param4) + WRITE_STRING(param4); + MESSAGE_END(); +} + +void UTIL_Log(const char *fmt, ...) +{ + va_list ap; + static char string[1024]; + + va_start(ap, fmt); + Q_vsnprintf(string, sizeof(string), fmt, ap); + va_end(ap); + + if (Q_strlen(string) < sizeof(string) - 2) + Q_strcat(string, "\n"); + else + string[Q_strlen(string) - 1] = '\n'; + + FILE *fp = fopen("regamedll.log", "at"); + if (fp) + { + fprintf(fp, "%s", string); + fclose(fp); + } +} + +void UTIL_ServerPrint(const char *fmt, ...) +{ +#ifdef PLAY_GAMEDLL + // Check is null, test the demo started before than searches pointer to refs + if (&g_engfuncs == nullptr || g_engfuncs.pfnServerPrint == nullptr) + return; +#endif + + static char string[1024]; + va_list ap; + va_start(ap, fmt); + Q_vsnprintf(string, sizeof(string), fmt, ap); + va_end(ap); + + if (Q_strlen(string) < sizeof(string) - 2) + Q_strcat(string, "\n"); + else + string[Q_strlen(string) - 1] = '\n'; + + SERVER_PRINT(string); +} + +void UTIL_PrintConsole(edict_t *pEdict, const char *fmt, ...) +{ + CBaseEntity *pEntity = GET_PRIVATE(pEdict); + if (!pEntity || !pEntity->IsNetClient()) + return; + + static char string[1024]; + + va_list ap; + va_start(ap, fmt); + Q_vsnprintf(string, sizeof(string), fmt, ap); + va_end(ap); + + if (Q_strlen(string) < sizeof(string) - 2) + Q_strcat(string, "\n"); + else + string[Q_strlen(string) - 1] = '\n'; + + ClientPrint(pEntity->pev, HUD_PRINTCONSOLE, string); +} + +void UTIL_SayText(edict_t *pEdict, const char *fmt, ...) +{ + CBaseEntity *pEntity = GET_PRIVATE(pEdict); + if (!pEntity || !pEntity->IsNetClient()) + return; + + static char string[1024]; + + va_list ap; + va_start(ap, fmt); + Q_vsnprintf(string, sizeof(string), fmt, ap); + va_end(ap); + + if (Q_strlen(string) < sizeof(string) - 2) + Q_strcat(string, "\n"); + else + string[Q_strlen(string) - 1] = '\n'; + + MESSAGE_BEGIN(MSG_ONE, gmsgSayText, nullptr, pEntity->edict()); + WRITE_BYTE(pEntity->entindex()); + WRITE_STRING(string); + MESSAGE_END(); +} + +void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity) +{ + MESSAGE_BEGIN(MSG_ALL, gmsgSayText); + WRITE_BYTE(pEntity->entindex()); + WRITE_STRING(pText); + MESSAGE_END(); +} + +char *UTIL_dtos1(int d) +{ + static char buf[8]; + Q_sprintf(buf, "%d", d); + return buf; +} + +char *UTIL_dtos2(int d) +{ + static char buf[8]; + Q_sprintf(buf, "%d", d); + return buf; +} + +NOXREF char *UTIL_dtos3(int d) +{ + static char buf[8]; + Q_sprintf(buf, "%d", d); + return buf; +} + +NOXREF char *UTIL_dtos4(int d) +{ + static char buf[8]; + Q_sprintf(buf, "%d", d); + return buf; +} + +void UTIL_ShowMessageArgs(const char *pString, CBaseEntity *pPlayer, CUtlVector *args, bool isHint) +{ + if (!pPlayer) + return; + + if (!pPlayer->IsNetClient()) + return; + + if (args) + { + MESSAGE_BEGIN(MSG_ONE, gmsgHudTextArgs, nullptr, pPlayer->pev); + WRITE_STRING(pString); + WRITE_BYTE(isHint); + WRITE_BYTE(args->Count()); + + for (int i = 0; i < args->Count(); i++) + WRITE_STRING((*args)[i]); + + MESSAGE_END(); + } + else + { + MESSAGE_BEGIN(MSG_ONE, gmsgHudTextPro, nullptr, pPlayer->pev); + WRITE_STRING(pString); + WRITE_BYTE(isHint); + MESSAGE_END(); + } +} + +void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity, bool isHint) +{ + if (!pEntity || !pEntity->IsNetClient()) + return; + + MESSAGE_BEGIN(MSG_ONE, gmsgHudTextPro, nullptr, pEntity->edict()); + WRITE_STRING(pString); + WRITE_BYTE(int(isHint)); + MESSAGE_END(); +} + +void UTIL_ShowMessageAll(const char *pString, bool isHint) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (pPlayer) + UTIL_ShowMessage(pString, pPlayer, isHint); + } +} + +void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr) +{ + TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters), pentIgnore, ptr); +} + +// OVERLOAD +void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr) +{ + TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters) | (ignoreGlass ? 0x100 : 0), pentIgnore, ptr); +} + +void UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr) +{ + TRACE_HULL(vecStart, vecEnd, (igmon == ignore_monsters), hullNumber, pentIgnore, ptr); +} + +void UTIL_TraceModel(const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr) +{ + TRACE_MODEL(vecStart, vecEnd, hullNumber, pentModel, ptr); +} + +NOXREF TraceResult UTIL_GetGlobalTrace() +{ + TraceResult tr; + + tr.flFraction = gpGlobals->trace_fraction; + tr.fInWater = int(gpGlobals->trace_inwater); + tr.fAllSolid = int(gpGlobals->trace_allsolid); + tr.fStartSolid = int(gpGlobals->trace_startsolid); + tr.fInOpen = int(gpGlobals->trace_inopen); + tr.vecEndPos = gpGlobals->trace_endpos; + tr.flPlaneDist = gpGlobals->trace_plane_dist; + tr.vecPlaneNormal = gpGlobals->trace_plane_normal; + tr.iHitgroup = gpGlobals->trace_hitgroup; + tr.pHit = gpGlobals->trace_ent; + + return tr; +} + +void UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax) +{ + SET_SIZE(ENT(pev), vecMin, vecMax); +} + +float UTIL_VecToYaw(const Vector &vec) +{ + return VEC_TO_YAW(vec); +} + +void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin) +{ + edict_t *pEdict = ENT(pev); + if (pEdict) + { + SET_ORIGIN(pEdict, vecOrigin); + } +} + +NOXREF void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount) +{ + PARTICLE_EFFECT(vecOrigin, vecDirection, float(ulColor), float(ulCount)); +} + +float UTIL_Approach(float target, float value, float speed) +{ + float delta = target - value; + if (delta > speed) + value += speed; + else if (delta < -speed) + value -= speed; + else + value = target; + + return value; +} + +real_t UTIL_ApproachAngle(float target, float value, float speed) +{ + target = UTIL_AngleMod(target); + +#ifdef REGAMEDLL_FIXES + value = UTIL_AngleMod(value); +#else + value = UTIL_AngleMod(target); +#endif + + float delta = target - value; + if (speed < 0.0f) + speed = -speed; + + if (delta < -180.0f) + delta += 360.0f; + else if (delta > 180.0f) + delta -= 360.0f; + + if (delta > speed) + value += speed; + else if (delta < -speed) + value -= speed; + else + value = target; + + return value; +} + +real_t UTIL_AngleDistance(float next, float cur) +{ + real_t delta; + + delta = next - cur; + + if (delta < -180.0f) + delta += 360.0f; + + else if (delta > 180.0f) + delta -= 360.0f; + + return delta; +} + +float UTIL_SplineFraction(float value, float scale) +{ + float valueSquared = value * scale; + return 3.0f * (valueSquared * valueSquared) - (valueSquared * valueSquared * valueSquared + valueSquared * valueSquared * valueSquared); +} + +char *UTIL_VarArgs(char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start(argptr, format); + vsprintf(string, format, argptr); + va_end(argptr); + + return string; +} + +NOXREF Vector UTIL_GetAimVector(edict_t *pent, float flSpeed) +{ + Vector tmp; + GET_AIM_VECTOR(pent, flSpeed, tmp); + return tmp; +} + +bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator) +{ + if (sMaster) + { + edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME(nullptr, STRING(sMaster)); + if (!FNullEnt(pentTarget)) + { + CBaseEntity *pMaster = CBaseEntity::Instance(pentTarget); + if (pMaster && (pMaster->ObjectCaps() & FCAP_MASTER)) + return pMaster->IsTriggered(pActivator) != FALSE; + } + + ALERT(at_console, "Master was null or not a master!\n"); + } + + return true; +} + +BOOL UTIL_ShouldShowBlood(int color) +{ + if (color != DONT_BLEED) + { + if (color == BLOOD_COLOR_RED) + { + if (CVAR_GET_FLOAT("violence_hblood") != 0.0f) + return TRUE; + } + else + { + if (CVAR_GET_FLOAT("violence_ablood") != 0.0f) + return TRUE; + } + } + + return FALSE; +} + +int UTIL_PointContents(const Vector &vec) +{ + return POINT_CONTENTS(vec); +} + +void UTIL_BloodStream(const Vector &origin, const Vector &direction, int color, int amount) +{ + if (!UTIL_ShouldShowBlood(color)) + return; + + if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED) + color = 0; + + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); + WRITE_BYTE(TE_BLOODSTREAM); + WRITE_COORD(origin.x); + WRITE_COORD(origin.y); + WRITE_COORD(origin.z); + WRITE_COORD(direction.x); + WRITE_COORD(direction.y); + WRITE_COORD(direction.z); + WRITE_BYTE(color); + WRITE_BYTE(Q_min(amount, 255)); + MESSAGE_END(); +} + +void UTIL_BloodDrips(const Vector &origin, const Vector &direction, int color, int amount) +{ + if (!UTIL_ShouldShowBlood(color)) + return; + + if (color == DONT_BLEED || amount == 0) + return; + + if (g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED) + color = 0; + + if (g_pGameRules->IsMultiplayer()) + amount *= 2; + + if (amount > 255) + amount = 255; + + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); + WRITE_BYTE(TE_BLOODSPRITE); + WRITE_COORD(origin.x); + WRITE_COORD(origin.y); + WRITE_COORD(origin.z); + WRITE_SHORT(g_sModelIndexBloodSpray); + WRITE_SHORT(g_sModelIndexBloodDrop); + WRITE_BYTE(color); + WRITE_BYTE(clamp(amount / 10, 3, 16)); + MESSAGE_END(); +} + +Vector UTIL_RandomBloodVector() +{ + Vector direction; + direction.x = RANDOM_FLOAT(-1, 1); + direction.y = RANDOM_FLOAT(-1, 1); + direction.z = RANDOM_FLOAT(0, 1); + return direction; +} + +void UTIL_BloodDecalTrace(TraceResult *pTrace, int bloodColor) +{ + if (UTIL_ShouldShowBlood(bloodColor)) + { + if (bloodColor == BLOOD_COLOR_RED) + UTIL_DecalTrace(pTrace, DECAL_BLOOD1 + RANDOM_LONG(0, 5)); + else + UTIL_DecalTrace(pTrace, DECAL_YBLOOD1 + RANDOM_LONG(0, 5)); + } +} + +void UTIL_DecalTrace(TraceResult *pTrace, int decalNumber) +{ + short entityIndex; + int index; + int message; + + if (decalNumber < 0) + return; + + index = gDecals[decalNumber].index; + if (index < 0 || pTrace->flFraction == 1.0f) + return; + + if (pTrace->pHit) + { + CBaseEntity *pEntity = CBaseEntity::Instance(pTrace->pHit); + if (pEntity && !pEntity->IsBSPModel()) + return; + + entityIndex = ENTINDEX(pTrace->pHit); + } + else + entityIndex = 0; + + message = TE_DECAL; + if (entityIndex) + { + if (index > 255) + { + message = TE_DECALHIGH; + index -= 256; + } + } + else + { + message = TE_WORLDDECAL; + if (index > 255) + { + message = TE_WORLDDECALHIGH; + index -= 256; + } + } + + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(message); + WRITE_COORD(pTrace->vecEndPos.x); + WRITE_COORD(pTrace->vecEndPos.y); + WRITE_COORD(pTrace->vecEndPos.z); + WRITE_BYTE(index); + if (entityIndex) + WRITE_SHORT(entityIndex); + MESSAGE_END(); +} + +void UTIL_PlayerDecalTrace(TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom) +{ + int index; + if (!bIsCustom) + { + if (decalNumber < 0) + return; + + index = gDecals[decalNumber].index; + if (index < 0) + return; + } + else + index = decalNumber; + + if (pTrace->flFraction != 1.0f) + { + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(TE_PLAYERDECAL); + WRITE_BYTE(playernum); + WRITE_COORD(pTrace->vecEndPos.x); + WRITE_COORD(pTrace->vecEndPos.y); + WRITE_COORD(pTrace->vecEndPos.z); + WRITE_SHORT(int(ENTINDEX(pTrace->pHit))); + WRITE_BYTE(index); + MESSAGE_END(); + } +} + +void UTIL_GunshotDecalTrace(TraceResult *pTrace, int decalNumber, bool ClientOnly, entvars_t *pShooter) +{ + if (decalNumber < 0) + return; + + int index = gDecals[decalNumber].index; + if (index < 0 || pTrace->flFraction == 1.0f) + return; + + if (ClientOnly) + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, pTrace->vecEndPos, pShooter); + else + MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, pTrace->vecEndPos); + + WRITE_BYTE(TE_GUNSHOTDECAL); + WRITE_COORD(pTrace->vecEndPos.x); + WRITE_COORD(pTrace->vecEndPos.y); + WRITE_COORD(pTrace->vecEndPos.z); + WRITE_SHORT(int(ENTINDEX(pTrace->pHit))); + WRITE_BYTE(index); + MESSAGE_END(); +} + +void UTIL_Sparks(const Vector &position) +{ + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position); + WRITE_BYTE(TE_SPARKS); + WRITE_COORD(position.x); + WRITE_COORD(position.y); + WRITE_COORD(position.z); + MESSAGE_END(); +} + +void UTIL_Ricochet(const Vector &position, float scale) +{ + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, position); + WRITE_BYTE(TE_ARMOR_RICOCHET); + WRITE_COORD(position.x); + WRITE_COORD(position.y); + WRITE_COORD(position.z); + WRITE_BYTE(int(scale * 10.0f)); + MESSAGE_END(); +} + +bool UTIL_TeamsMatch(const char *pTeamName1, const char *pTeamName2) +{ + if (!g_pGameRules->IsTeamplay()) + return true; + + if (*pTeamName1 != '\0' && *pTeamName2 != '\0') + { + if (!Q_stricmp(pTeamName1, pTeamName2)) + return true; + } + + return false; +} + +void UTIL_StringToVector(float *pVector, const char *pString) +{ + char *pstr; + char *pfront; + char tempString[128]; + int j; + + Q_strlcpy(tempString, pString); + + pstr = tempString; + pfront = tempString; + + for (j = 0; j < 3; j++) + { + pVector[j] = Q_atof(pfront); + + while (*pstr && *pstr != ' ') + pstr++; + + if (!*pstr) + break; + + pstr++; + pfront = pstr; + } + + for (j++; j < 3; j++) + pVector[j] = 0; +} + +void UTIL_StringToVector(Vector &vecIn, const char *pString, char cSeparator) +{ + return UTIL_StringToVectorND(vecIn, 3, pString, cSeparator); +} + +void UTIL_StringToVectorND(Vector &vecIn, int nCount, const char *pString, char cSeparator) +{ + int i; + char *pstr; + char *pfront; + char tempString[128]; + + Q_strlcpy(tempString, pString); + + pstr = tempString; + pfront = tempString; + + for (i = 0; i < nCount; i++) + { + vecIn[i] = Q_atof(pfront); + + while (*pstr && *pstr != cSeparator) + pstr++; + + if (!*pstr) + break; + + pstr++; + pfront = pstr; + } + + if (++i < nCount) + { + Q_memset(&vecIn[i], 0, sizeof(float) * (nCount - i)); + } +} + +void UTIL_StringToIntArray(int *pVector, int count, const char *pString) +{ + char *pstr; + char *pfront; + char tempString[128]; + int j; + + Q_strlcpy(tempString, pString); + + pstr = tempString; + pfront = tempString; + + for (j = 0; j < count; j++) + { + pVector[j] = Q_atoi(pfront); + + while (*pstr && *pstr != ' ') + pstr++; + + if (!*pstr) + break; + + pstr++; + pfront = pstr; + } + + for (j++; j < count; j++) + pVector[j] = 0; +} + +Vector UTIL_ClampVectorToBox(const Vector &input, const Vector &clampSize) +{ + Vector sourceVector = input; + if (sourceVector.x > clampSize.x) + sourceVector.x -= clampSize.x; + else if (sourceVector.x < -clampSize.x) + sourceVector.x += clampSize.x; + else + sourceVector.x = 0; + if (sourceVector.y > clampSize.y) + sourceVector.y -= clampSize.y; + else if (sourceVector.y < -clampSize.y) + sourceVector.y += clampSize.y; + else + sourceVector.y = 0; + if (sourceVector.z > clampSize.z) + sourceVector.z -= clampSize.z; + else if (sourceVector.z < -clampSize.z) + sourceVector.z += clampSize.z; + else + sourceVector.z = 0; + return sourceVector.Normalize(); +} + +float UTIL_WaterLevel(const Vector &position, float minz, float maxz) +{ + Vector midUp; + float diff; + + midUp = position; + midUp.z = minz; + + if (UTIL_PointContents(midUp) != CONTENTS_WATER) + return minz; + + midUp.z = maxz; + if (UTIL_PointContents(midUp) == CONTENTS_WATER) + return maxz; + + diff = maxz - minz; + while (diff > 1) + { + midUp.z = minz + diff / 2; + if (UTIL_PointContents(midUp) == CONTENTS_WATER) + minz = midUp.z; + else + maxz = midUp.z; + + diff = maxz - minz; + } + + return midUp.z; +} + +void UTIL_Bubbles(Vector mins, Vector maxs, int count) +{ + Vector mid = (mins + maxs) * 0.5f; + float flHeight = UTIL_WaterLevel(mid, mid.z, mid.z + 1024.0f) - mins.z; + + MESSAGE_BEGIN(MSG_PAS, SVC_TEMPENTITY, mid); + WRITE_BYTE(TE_BUBBLES); + WRITE_COORD(mins.x); + WRITE_COORD(mins.y); + WRITE_COORD(mins.z); + WRITE_COORD(maxs.x); + WRITE_COORD(maxs.y); + WRITE_COORD(maxs.z); + WRITE_COORD(flHeight); + WRITE_SHORT(g_sModelIndexBubbles); + WRITE_BYTE(count); + WRITE_COORD(8); + MESSAGE_END(); +} + +void UTIL_BubbleTrail(Vector from, Vector to, int count) +{ + float flHeight = UTIL_WaterLevel(from, from.z, from.z + 256.0f) - from.z; + if (flHeight < 8.0f) + { + flHeight = UTIL_WaterLevel(to, to.z, to.z + 256.0f) - to.z; + + if (flHeight < 8.0f) + return; + + flHeight = flHeight + to.z - from.z; + } + + if (count > 255) + count = 255; + + MESSAGE_BEGIN(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(TE_BUBBLETRAIL); + WRITE_COORD(from.x); + WRITE_COORD(from.y); + WRITE_COORD(from.z); + WRITE_COORD(to.x); + WRITE_COORD(to.y); + WRITE_COORD(to.z); + WRITE_COORD(flHeight); + WRITE_SHORT(g_sModelIndexBubbles); + WRITE_BYTE(count); + WRITE_COORD(8); + MESSAGE_END(); +} + +void UTIL_Remove(CBaseEntity *pEntity) +{ + if (!pEntity) + return; + +#ifdef REGAMEDLL_FIXES + if (pEntity->pev == VARS(eoNullEntity) || pEntity->IsPlayer() || (pEntity->pev->flags & FL_KILLME) == FL_KILLME) + return; +#endif + + pEntity->UpdateOnRemove(); + + pEntity->pev->solid = SOLID_NOT; + pEntity->pev->flags |= FL_KILLME; + pEntity->pev->targetname = 0; +} + +NOXREF BOOL UTIL_IsValidEntity(edict_t *pent) +{ + if (!pent || pent->free || (pent->v.flags & FL_KILLME)) + return FALSE; + + return TRUE; +} + +void UTIL_PrecacheOther(const char *szClassname) +{ + edict_t *pent = CREATE_NAMED_ENTITY(MAKE_STRING(szClassname)); + if (FNullEnt(pent)) + { + ALERT(at_console, "NULL Ent in UTIL_PrecacheOther classname `%s`\n", szClassname); + return; + } + + CBaseEntity *pEntity = CBaseEntity::Instance(VARS(pent)); + if (pEntity) + { + pEntity->Precache(); + } + + REMOVE_ENTITY(pent); +} + +void UTIL_RestartOther(const char *szClassname) +{ + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityByClassname(pEntity, szClassname))) + { + pEntity->Restart(); + } +} + +void UTIL_ResetEntities() +{ + edict_t *pEdict = INDEXENT(1); + for (int i = 1; i < gpGlobals->maxEntities; i++, pEdict++) + { + if (pEdict->free) + continue; + + CBaseEntity *pEntity = CBaseEntity::Instance(pEdict); + if (!pEntity) + continue; + + // only non-player entities + if (pEntity->IsPlayer()) + continue; + + int caps = pEntity->ObjectCaps(); + if ((caps & FCAP_MUST_RELEASE) == FCAP_MUST_RELEASE) + UTIL_Remove(pEntity); + + else if ((caps & FCAP_MUST_RESET) == FCAP_MUST_RESET) + pEntity->Restart(); + } +} + +void UTIL_RemoveOther(const char *szClassname, int nRemoveCount) +{ + int num = 0; + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityByClassname(pEntity, szClassname))) + { +#ifndef REGAMEDLL_FIXES + if (nRemoveCount > 0 && num++ >= nRemoveCount) + break; +#endif + + UTIL_Remove(pEntity); + } +} + +void UTIL_LogPrintf(const char *fmt, ...) +{ + va_list argptr; + static char string[1024]; + + va_start(argptr, fmt); + vsprintf(string, fmt, argptr); + va_end(argptr); + + ALERT(at_logged, "%s", string); +} + +NOXREF float UTIL_DotPoints(const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir) +{ + Vector2D vec2LOS = (vecCheck - vecSrc).Make2D(); + vec2LOS = vec2LOS.Normalize(); + return DotProduct(vec2LOS, (vecDir.Make2D())); +} + +char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd) +{ + char chTextureType; + float rgfl1[3]; + float rgfl2[3]; + const char *pTextureName; + char szbuffer[64]; + CBaseEntity *pEntity = CBaseEntity::Instance(ptr->pHit); + + if (pEntity && pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE) + return CHAR_TEX_FLESH; + + vecSrc.CopyToArray(rgfl1); + vecEnd.CopyToArray(rgfl2); + + if (pEntity) + pTextureName = TRACE_TEXTURE(ENT(pEntity->pev), rgfl1, rgfl2); + else + pTextureName = TRACE_TEXTURE(ENT(0), rgfl1, rgfl2); + + if (pTextureName) + { + if (*pTextureName == '-' || *pTextureName == '+') + pTextureName += 2; + + if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') + pTextureName++; + + Q_strcpy(szbuffer, pTextureName); + szbuffer[16] = '\0'; + chTextureType = TEXTURETYPE_Find(szbuffer); + } + else + chTextureType = '\0'; + + return chTextureType; +} + +NOXREF int GetPlayerTeam(int index) +{ + CBasePlayer *pPlayer = UTIL_PlayerByIndexSafe(index); + if (pPlayer) + { + return pPlayer->m_iTeam; + } + + return 0; +} + +bool UTIL_IsGame(const char *pszGameName) +{ +#ifndef CSTRIKE + if (pszGameName) + { + char szGameDir[256]; + GET_GAME_DIR(szGameDir); + return (Q_stricmp(szGameDir, pszGameName) == 0); + } +#endif + + return false; +} + +real_t UTIL_GetPlayerGaitYaw(int playerIndex) +{ + CBasePlayer *pPlayer = UTIL_PlayerByIndex(playerIndex); + if (pPlayer) + { + return pPlayer->m_flGaityaw; + } + + return 0; +} + +int UTIL_ReadFlags(const char *c) +{ + int flags = 0; + + while (*c) + { + if (*c >= 'a' && *c <= 'z') + { + flags |= (1 << (*c - 'a')); + } + + c++; + } + + return flags; +} + +// Determine whether bots can be used or not +bool UTIL_AreBotsAllowed() +{ +#ifdef REGAMEDLL_ADD + if (g_engfuncs.pfnEngCheckParm == nullptr) + return false; +#endif + + if (AreRunningCZero()) + { +#ifdef REGAMEDLL_ADD + // If they pass in -nobots, don't allow bots. This is for people who host servers, to + // allow them to disallow bots to enforce CPU limits. + int nobots = ENG_CHECK_PARM("-nobots", nullptr); + if (nobots) + { + return false; + } +#endif + + return true; + } + +#ifdef REGAMEDLL_ADD + // let enables zBot by default from listen server? + if (!IS_DEDICATED_SERVER()) + { + return true; + } + + // allow the using of bots for CS 1.6 + int bots = ENG_CHECK_PARM("-bots", nullptr); + if (bots) + { + return true; + } +#endif + + return false; +} + +bool UTIL_IsBeta() +{ +#ifdef BUILD_LATEST + if (g_engfuncs.pfnEngCheckParm == nullptr) + return false; + + // always beta from listen server + if (!IS_DEDICATED_SERVER()) + { + return true; + } + + int beta = ENG_CHECK_PARM("-beta", nullptr); + if (beta) + { + return true; + } +#endif // #ifdef BUILD_LATEST + + return false; +} + +bool UTIL_AreHostagesImprov() +{ + if (AreRunningCZero()) + { + return true; + } + +#ifdef REGAMEDLL_ADD + // someday in CS 1.6 + int improv = ENG_CHECK_PARM("-host-improv", nullptr); + if (improv) + { + return true; + } +#endif + + return false; +} + +int UTIL_GetNumPlayers() +{ + int nNumPlayers = 0; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (pPlayer) + { + nNumPlayers++; + } + } + + return nNumPlayers; +} + +bool UTIL_IsSpawnPointOccupied(CBaseEntity *pSpot) +{ + if (!pSpot) + return false; + + const int maxList = 8; + CBaseEntity *pList[maxList]; + + Vector mins(pSpot->pev->origin + VEC_SPOT_HULL_MIN - 8.0); + Vector maxs(pSpot->pev->origin + VEC_SPOT_HULL_MAX + 8.0); + + int nCount = UTIL_EntitiesInBox(pList, maxList, mins, maxs, FL_MONSTER | FL_CLIENT); + for (int i = 0; i < nCount; i++) + { + if (pList[i]->pev->solid != SOLID_NOT) + return false; + } + + return false; +} + +void MAKE_STRING_CLASS(const char *str, entvars_t *pev) +{ + if (!FStringNull(pev->classname)) + { + RemoveEntityHashValue(pev, STRING(pev->classname), CLASSNAME); + } + + pev->classname = MAKE_STRING(str); + AddEntityHashValue(pev, STRING(pev->classname), CLASSNAME); +} + +void NORETURN Sys_Error(const char *error, ...) +{ + va_list argptr; + static char text[1024]; + + va_start(argptr, error); + vsnprintf(text, sizeof(text), error, argptr); + va_end(argptr); + + FILE *fl = fopen("regamedll_error.txt", "w"); + if (fl) + { + fprintf(fl, "%s\n", text); + fclose(fl); + } + + CONSOLE_ECHO("FATAL ERROR (shutting down): %s\n", text); + + //TerminateProcess(GetCurrentProcess(), 1); + int *null = 0; + *null = 0; + exit(-1); +} + +int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, int &playersInCount, int &playersOutCount, CPlayerInVolumeAdapter *pAdapter) +{ + playersInCount = 0; + playersOutCount = 0; + + if (pBrushEntity) + { + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + + if (!pPlayer || !pPlayer->IsInWorld()) + continue; + + if (bOnlyAlive && !pPlayer->IsAlive()) + continue; + + TraceResult trace; + int hullNumber = (pPlayer->pev->flags & FL_DUCKING) ? head_hull : human_hull; + UTIL_TraceModel(pPlayer->pev->origin, pPlayer->pev->origin, hullNumber, pBrushEntity->edict(), &trace); + + bool fInVolume = trace.fStartSolid > 0.0f; + if (fInVolume) + { + playersInCount++; + } + else + { + playersOutCount++; + } + + if (pAdapter) { + pAdapter->PlayerDetected(fInVolume, pPlayer); + } + } + } + else + { + playersOutCount = UTIL_GetNumPlayers(); + } + + return playersInCount + playersOutCount; +} diff --git a/regamedll/dlls/util.h b/regamedll/dlls/util.h index af7a0a65..baeea363 100644 --- a/regamedll/dlls/util.h +++ b/regamedll/dlls/util.h @@ -1,379 +1,379 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -#include "shake.h" -#include "activity.h" -#include "enginecallback.h" -#include "utlvector.h" - -#define GROUP_OP_AND 0 -#define GROUP_OP_NAND 1 - -// Dot products for view cone checking -#define VIEW_FIELD_FULL -1.0 // +-180 degrees -#define VIEW_FIELD_WIDE -0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks -#define VIEW_FIELD_NARROW 0.7 // +-45 degrees, more narrow check used to set up ranged attacks -#define VIEW_FIELD_ULTRA_NARROW 0.9 // +-25 degrees, more narrow check used to set up ranged attacks - -#define SND_STOP BIT(5) // duplicated in protocol.h stop sound -#define SND_CHANGE_VOL BIT(6) // duplicated in protocol.h change sound vol -#define SND_CHANGE_PITCH BIT(7) // duplicated in protocol.h change sound pitch -#define SND_SPAWNING BIT(8) // duplicated in protocol.h we're spawing, used in some cases for ambients - -// All monsters need this data -#define DONT_BLEED -1 -#define BLOOD_COLOR_DARKRED (byte)223 -#define BLOOD_COLOR_RED (byte)247 -#define BLOOD_COLOR_YELLOW (byte)195 -#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW - -#define GERMAN_GIB_COUNT 4 -#define HUMAN_GIB_COUNT 6 -#define ALIEN_GIB_COUNT 4 - -#define LANGUAGE_ENGLISH 0 -#define LANGUAGE_GERMAN 1 -#define LANGUAGE_FRENCH 2 -#define LANGUAGE_BRITISH 3 - -#define SVC_NOP 1 -#define SVC_SOUND 6 -#define SVC_TEMPENTITY 23 -#define SVC_INTERMISSION 30 -#define SVC_CDTRACK 32 -#define SVC_WEAPONANIM 35 -#define SVC_ROOMTYPE 37 -#define SVC_DIRECTOR 51 - -#define VEC_HULL_MIN_Z Vector(0, 0, -36) -#define VEC_DUCK_HULL_MIN_Z Vector(0, 0, -18) - -#define VEC_HULL_MIN Vector(-16, -16, -36) -#define VEC_HULL_MAX Vector(16, 16, 36) - -#define VEC_VIEW Vector(0, 0, 17) - -#define VEC_SPOT_HULL_MIN Vector(-16, -16, 0) -#define VEC_SPOT_HULL_MAX Vector(16, 16, 72) - -#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18) -#define VEC_DUCK_HULL_MAX Vector(16, 16, 32) -#define VEC_DUCK_VIEW Vector(0, 0, 12) - -#define PRECACHE_SOUND_ARRAY(a) \ - { for (int i = 0; i < ARRAYSIZE(a); i++) PRECACHE_SOUND((char *)a[i]); } - -#define PLAYBACK_EVENT(flags, who, index)\ - PLAYBACK_EVENT_FULL(flags, who, index, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0) - -#define PLAYBACK_EVENT_DELAY(flags, who, index, delay)\ - PLAYBACK_EVENT_FULL(flags, who, index, delay, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0) - -#define LINK_ENTITY_TO_CLASS(mapClassName, DLLClassName, DLLClassWrapName)\ - C_DLLEXPORT void EXT_FUNC mapClassName(entvars_t *pev);\ - void mapClassName(entvars_t *pev)\ - {\ - GetClassPtr((DLLClassName *)pev);\ - } - -const EOFFSET eoNullEntity = (EOFFSET)0; // Testing the three types of "entity" for nullity - -class UTIL_GroupTrace -{ -public: - UTIL_GroupTrace(int groupmask, int op); - ~UTIL_GroupTrace(); -private: - int m_oldgroupmask; - int m_oldgroupop; -}; - -// Inlines -inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); } -inline edict_t *FIND_ENTITY_BY_TARGETNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "targetname", pszName); } - -inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } -inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } -inline EOFFSET OFFSET(const edict_t *pent) { return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); } -inline EOFFSET OFFSET(const entvars_t *pev) { return OFFSET(ENT(pev)); } - -inline entvars_t *VARS(edict_t *pent) -{ - if (!pent) - return nullptr; - - return &pent->v; -} - -inline entvars_t *VARS(EOFFSET eoffset) -{ - return VARS(ENT(eoffset)); -} - -#ifndef ENTINDEX -inline int ENTINDEX(const edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } -inline int ENTINDEX(const entvars_t *pev) { return (*g_engfuncs.pfnIndexOfEdict)(ENT(pev)); } -#endif // ENTINDEX - -#ifndef INDEXENT -inline edict_t *INDEXENT(int iEdictNum) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } -#endif // INDEXENT - -inline void MESSAGE_BEGIN(int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent) { MESSAGE_BEGIN(msg_dest, msg_type, pOrigin, ENT(ent)); } -inline bool FNullEnt(EOFFSET eoffset) { return (eoffset == 0); } -inline bool FNullEnt(entvars_t *pev) { return (pev == nullptr || FNullEnt(OFFSET(pev))); } -inline bool FNullEnt(const edict_t *pent) { return (pent == nullptr || pent->free || FNullEnt(OFFSET(pent))); } -inline bool FStringNull(string_t iString) { return (iString == iStringNull); } -inline bool FStrEq(const char *sz1, const char *sz2) { return (Q_strcmp(sz1, sz2) == 0); } -inline bool FStrnEq(const char *sz1, const char *sz2, size_t elem) { return (Q_strncmp(sz1, sz2, elem) == 0); } - -inline bool FClassnameIs(entvars_t *pev, const char *szClassname) { return FStrEq(STRING(pev->classname), szClassname); } -inline bool FClassnameIs(edict_t *pent, const char *szClassname) { return FStrEq(STRING(VARS(pent)->classname), szClassname); } -inline void UTIL_MakeVectorsPrivate(Vector vecAngles, float *p_vForward, float *p_vRight, float *p_vUp) { g_engfuncs.pfnAngleVectors(vecAngles, p_vForward, p_vRight, p_vUp); } - -#include "ehandle.h" - -extern void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch); - -// NOTE: use EMIT_SOUND_DYN to set the pitch of a sound. Pitch of 100 -// is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 -// down to 1 is a lower pitch. 150 to 70 is the realistic range. -// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as -// fast as EMIT_SOUND (the pitchshift mixer is not native coded). -inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation) -{ - EMIT_SOUND_DYN(entity, channel, sample, volume, attenuation, 0, PITCH_NORM); -} - -inline void STOP_SOUND(edict_t *entity, int channel, const char *sample) -{ - EMIT_SOUND_DYN(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM); -} - -inline void EMIT_SOUND_MSG(edict_t *entity, int msg_type, int channel, const char *sample, float volume, float attenuation, int flags, int pitch = PITCH_NORM, Vector vecOrigin = g_vecZero, edict_t *player = nullptr) -{ - BUILD_SOUND_MSG(entity, channel, sample, (volume * 255.0f), attenuation, flags, pitch, msg_type, SVC_NOP, vecOrigin, player); -} - -inline void STOP_SOUND_MSG(edict_t *entity, int msg_type, int channel, const char *sample, edict_t *player) -{ - BUILD_SOUND_MSG(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM, msg_type, SVC_NOP, g_vecZero, player); -} - -class CBaseEntity; -class CBasePlayer; -class CBasePlayerItem; - -float UTIL_WeaponTimeBase(); -unsigned int U_Random(); -void U_Srand(unsigned int seed); -int UTIL_SharedRandomLong(unsigned int seed, int low, int high); -float UTIL_SharedRandomFloat(unsigned int seed, float low, float high); -void UTIL_ParametricRocket(entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner); -void UTIL_SetGroupTrace(int groupmask, int op); -void UTIL_UnsetGroupTrace(); -BOOL UTIL_GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); -float UTIL_AngleMod(float a); -float UTIL_AngleDiff(float destAngle, float srcAngle); -Vector UTIL_VecToAngles(const Vector &vec); -void UTIL_MoveToOrigin(edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType); -int UTIL_EntitiesInBox(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask); -int UTIL_MonstersInSphere(CBaseEntity **pList, int listMax, const Vector ¢er, float radius); -CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius); -CBaseEntity *UTIL_FindEntityByString_Old(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue); -CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue); -CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName); -CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName); -CBaseEntity *UTIL_FindEntityGeneric(const char *szWhatever, const Vector &vecSrc, float flRadius); -#ifndef REGAMEDLL_FIXES -CBasePlayer *UTIL_PlayerByIndex(int playerIndex); -#endif -void UTIL_MakeVectors(const Vector &vecAngles); -void UTIL_MakeAimVectors(const Vector &vecAngles); -void UTIL_MakeInvVectors(const Vector &vec, globalvars_t *pgv); -void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch); -unsigned short FixedUnsigned16(float value, float scale); -short FixedSigned16(float value, float scale); -void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius); -void UTIL_ScreenShakeAll(const Vector ¢er, float amplitude, float frequency, float duration); -void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); -void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity); -void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); -void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold = 0.0f, int alpha = 0, int flags = 0); -void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage); -void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage); -void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1 = nullptr, const char *param2 = nullptr, const char *param3 = nullptr, const char *param4 = nullptr); -void ClientPrint(entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = nullptr, const char *param2 = nullptr, const char *param3 = nullptr, const char *param4 = nullptr); -void UTIL_Log(const char *fmt, ...); -void UTIL_ServerPrint(const char *fmt, ...); -void UTIL_PrintConsole(edict_t *pEdict, const char *fmt, ...); -void UTIL_SayText(CBaseEntity *pEntity, const char *fmt, ...); -void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity); -char *UTIL_dtos1(int d); -char *UTIL_dtos2(int d); -char *UTIL_dtos3(int d); -char *UTIL_dtos4(int d); -void UTIL_ShowMessageArgs(const char *pString, CBaseEntity *pPlayer, CUtlVector *args, bool isHint = false); -void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity, bool isHint = false); -void UTIL_ShowMessageAll(const char *pString, bool isHint = false); -void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); -void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); -void UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); -void UTIL_TraceModel(const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); -TraceResult UTIL_GetGlobalTrace(); -void UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax); -float UTIL_VecToYaw(const Vector &vec); -void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin); -void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount); -float UTIL_Approach(float target, float value, float speed); -real_t UTIL_ApproachAngle(float target, float value, float speed); -real_t UTIL_AngleDistance(float next, float cur); -float UTIL_SplineFraction(float value, float scale); -char *UTIL_VarArgs(char *format, ...); -Vector UTIL_GetAimVector(edict_t *pent, float flSpeed); -bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator); -BOOL UTIL_ShouldShowBlood(int color); -int UTIL_PointContents(const Vector &vec); -void UTIL_BloodStream(const Vector &origin, const Vector &direction, int color, int amount); -void UTIL_BloodDrips(const Vector &origin, const Vector &direction, int color, int amount); -Vector UTIL_RandomBloodVector(); -void UTIL_BloodDecalTrace(TraceResult *pTrace, int bloodColor); -void UTIL_DecalTrace(TraceResult *pTrace, int decalNumber); -void UTIL_PlayerDecalTrace(TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom); -void UTIL_GunshotDecalTrace(TraceResult *pTrace, int decalNumber, bool ClientOnly, entvars_t *pShooter); -void UTIL_Sparks(const Vector &position); -void UTIL_Ricochet(const Vector &position, float scale); -bool UTIL_TeamsMatch(const char *pTeamName1, const char *pTeamName2); -void UTIL_StringToVector(float *pVector, const char *pString); -void UTIL_StringToVector(Vector &vecIn, const char *pString, char cSeparator); -void UTIL_StringToVectorND(Vector &vecIn, int nCount, const char *pString, char cSeparator); -void UTIL_StringToIntArray(int *pVector, int count, const char *pString); -Vector UTIL_ClampVectorToBox(const Vector &input, const Vector &clampSize); -float UTIL_WaterLevel(const Vector &position, float minz, float maxz); -void UTIL_Bubbles(Vector mins, Vector maxs, int count); -void UTIL_BubbleTrail(Vector from, Vector to, int count); -void UTIL_Remove(CBaseEntity *pEntity); -BOOL UTIL_IsValidEntity(edict_t *pent); -void UTIL_PrecacheOther(const char *szClassname); -void UTIL_RestartOther(const char *szClassname); -void UTIL_ResetEntities(); -void UTIL_RemoveOther(const char *szClassname, int nCount = 0); -void UTIL_LogPrintf(const char *fmt, ...); -float UTIL_DotPoints(const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir); -void EntvarsKeyvalue(entvars_t *pev, KeyValueData *pkvd); -char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd); -int GetPlayerTeam(int index); -bool UTIL_IsGame(const char *pszGameName); -real_t UTIL_GetPlayerGaitYaw(int playerIndex); -int UTIL_ReadFlags(const char *c); -bool UTIL_AreBotsAllowed(); -bool UTIL_IsBeta(); -bool UTIL_AreHostagesImprov(); -int UTIL_GetNumPlayers(); -bool UTIL_IsSpawnPointOccupied(CBaseEntity *pSpot); -void MAKE_STRING_CLASS(const char *str, entvars_t *pev); -void NORETURN Sys_Error(const char *error, ...); - -// Inlines -template -inline T *UTIL_FindEntityByClassname(T *pStartEntity, const char *szName) -{ - return (T *)UTIL_FindEntityByString(pStartEntity, "classname", szName); -} - -template -inline T *UTIL_FindEntityByTargetname(T *pStartEntity, const char *szName) -{ - return (T *)UTIL_FindEntityByString(pStartEntity, "targetname", szName); -} - -template -inline T *UTIL_FindEntityInSphere(T *pStartEntity, const Vector &vecCenter, float flRadius) -{ - edict_t *pentEntity; - if (pStartEntity) - pentEntity = pStartEntity->edict(); - else - pentEntity = nullptr; - - pentEntity = FIND_ENTITY_IN_SPHERE(pentEntity, vecCenter, flRadius); - if (!FNullEnt(pentEntity)) - { - // a1ba: can't use CBaseEntity::Instance here, because circular dependency in cbase.h - return GET_PRIVATE(pentEntity); - } - - return nullptr; -} - -template -void UTIL_StripToken(const char *pKey, char (&pDest)[nSize]) -{ - int i = 0; - while (i < nSize && pKey[i] && pKey[i] != '#') - { - pDest[i] = pKey[i]; - i++; - } - - pDest[i] = '\0'; -} - -class CPlayerInVolumeAdapter -{ -public: - virtual ~CPlayerInVolumeAdapter() {}; - virtual void PlayerDetected(const bool fInVolume, CBasePlayer *pPlayer) = 0; -}; - -int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, int &playersInCount, int &playersOutCount, CPlayerInVolumeAdapter *pAdapter = nullptr); - -inline real_t UTIL_FixupAngle(real_t v) -{ - real_t angle = v; - - while (angle < 0) - angle += 360; - - while (angle > 360) - angle -= 360; - - return angle; -} - -inline void UTIL_FixupAngles(Vector &v) -{ - v.x = UTIL_FixupAngle(v.x); - v.y = UTIL_FixupAngle(v.y); - v.z = UTIL_FixupAngle(v.z); -} - -extern int g_groupmask; -extern int g_groupop; +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "shake.h" +#include "activity.h" +#include "enginecallback.h" +#include "utlvector.h" + +#define GROUP_OP_AND 0 +#define GROUP_OP_NAND 1 + +// Dot products for view cone checking +#define VIEW_FIELD_FULL -1.0 // +-180 degrees +#define VIEW_FIELD_WIDE -0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW 0.7 // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW 0.9 // +-25 degrees, more narrow check used to set up ranged attacks + +#define SND_STOP BIT(5) // duplicated in protocol.h stop sound +#define SND_CHANGE_VOL BIT(6) // duplicated in protocol.h change sound vol +#define SND_CHANGE_PITCH BIT(7) // duplicated in protocol.h change sound pitch +#define SND_SPAWNING BIT(8) // duplicated in protocol.h we're spawing, used in some cases for ambients + +// All monsters need this data +#define DONT_BLEED -1 +#define BLOOD_COLOR_DARKRED (byte)223 +#define BLOOD_COLOR_RED (byte)247 +#define BLOOD_COLOR_YELLOW (byte)195 +#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW + +#define GERMAN_GIB_COUNT 4 +#define HUMAN_GIB_COUNT 6 +#define ALIEN_GIB_COUNT 4 + +#define LANGUAGE_ENGLISH 0 +#define LANGUAGE_GERMAN 1 +#define LANGUAGE_FRENCH 2 +#define LANGUAGE_BRITISH 3 + +#define SVC_NOP 1 +#define SVC_SOUND 6 +#define SVC_TEMPENTITY 23 +#define SVC_INTERMISSION 30 +#define SVC_CDTRACK 32 +#define SVC_WEAPONANIM 35 +#define SVC_ROOMTYPE 37 +#define SVC_DIRECTOR 51 + +#define VEC_HULL_MIN_Z Vector(0, 0, -36) +#define VEC_DUCK_HULL_MIN_Z Vector(0, 0, -18) + +#define VEC_HULL_MIN Vector(-16, -16, -36) +#define VEC_HULL_MAX Vector(16, 16, 36) + +#define VEC_VIEW Vector(0, 0, 17) + +#define VEC_SPOT_HULL_MIN Vector(-16, -16, 0) +#define VEC_SPOT_HULL_MAX Vector(16, 16, 72) + +#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18) +#define VEC_DUCK_HULL_MAX Vector(16, 16, 32) +#define VEC_DUCK_VIEW Vector(0, 0, 12) + +#define PRECACHE_SOUND_ARRAY(a) \ + { for (int i = 0; i < ARRAYSIZE(a); i++) PRECACHE_SOUND((char *)a[i]); } + +#define PLAYBACK_EVENT(flags, who, index)\ + PLAYBACK_EVENT_FULL(flags, who, index, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0) + +#define PLAYBACK_EVENT_DELAY(flags, who, index, delay)\ + PLAYBACK_EVENT_FULL(flags, who, index, delay, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0) + +#define LINK_ENTITY_TO_CLASS(mapClassName, DLLClassName, DLLClassWrapName)\ + C_DLLEXPORT void EXT_FUNC mapClassName(entvars_t *pev);\ + void mapClassName(entvars_t *pev)\ + {\ + GetClassPtr((DLLClassName *)pev);\ + } + +const EOFFSET eoNullEntity = (EOFFSET)0; // Testing the three types of "entity" for nullity + +class UTIL_GroupTrace +{ +public: + UTIL_GroupTrace(int groupmask, int op); + ~UTIL_GroupTrace(); +private: + int m_oldgroupmask; + int m_oldgroupop; +}; + +// Inlines +inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); } +inline edict_t *FIND_ENTITY_BY_TARGETNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "targetname", pszName); } + +inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } +inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } +inline EOFFSET OFFSET(const edict_t *pent) { return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); } +inline EOFFSET OFFSET(const entvars_t *pev) { return OFFSET(ENT(pev)); } + +inline entvars_t *VARS(edict_t *pent) +{ + if (!pent) + return nullptr; + + return &pent->v; +} + +inline entvars_t *VARS(EOFFSET eoffset) +{ + return VARS(ENT(eoffset)); +} + +#ifndef ENTINDEX +inline int ENTINDEX(const edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } +inline int ENTINDEX(const entvars_t *pev) { return (*g_engfuncs.pfnIndexOfEdict)(ENT(pev)); } +#endif // ENTINDEX + +#ifndef INDEXENT +inline edict_t *INDEXENT(int iEdictNum) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } +#endif // INDEXENT + +inline void MESSAGE_BEGIN(int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent) { MESSAGE_BEGIN(msg_dest, msg_type, pOrigin, ENT(ent)); } +inline bool FNullEnt(EOFFSET eoffset) { return (eoffset == 0); } +inline bool FNullEnt(entvars_t *pev) { return (pev == nullptr || FNullEnt(OFFSET(pev))); } +inline bool FNullEnt(const edict_t *pent) { return (pent == nullptr || pent->free || FNullEnt(OFFSET(pent))); } +inline bool FStringNull(string_t iString) { return (iString == iStringNull); } +inline bool FStrEq(const char *sz1, const char *sz2) { return (Q_strcmp(sz1, sz2) == 0); } +inline bool FStrnEq(const char *sz1, const char *sz2, size_t elem) { return (Q_strncmp(sz1, sz2, elem) == 0); } + +inline bool FClassnameIs(entvars_t *pev, const char *szClassname) { return FStrEq(STRING(pev->classname), szClassname); } +inline bool FClassnameIs(edict_t *pent, const char *szClassname) { return FStrEq(STRING(VARS(pent)->classname), szClassname); } +inline void UTIL_MakeVectorsPrivate(Vector vecAngles, float *p_vForward, float *p_vRight, float *p_vUp) { g_engfuncs.pfnAngleVectors(vecAngles, p_vForward, p_vRight, p_vUp); } + +#include "ehandle.h" + +extern void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch); + +// NOTE: use EMIT_SOUND_DYN to set the pitch of a sound. Pitch of 100 +// is no pitch shift. Pitch > 100 up to 255 is a higher pitch, pitch < 100 +// down to 1 is a lower pitch. 150 to 70 is the realistic range. +// EMIT_SOUND_DYN with pitch != 100 should be used sparingly, as it's not quite as +// fast as EMIT_SOUND (the pitchshift mixer is not native coded). +inline void EMIT_SOUND(edict_t *entity, int channel, const char *sample, float volume, float attenuation) +{ + EMIT_SOUND_DYN(entity, channel, sample, volume, attenuation, 0, PITCH_NORM); +} + +inline void STOP_SOUND(edict_t *entity, int channel, const char *sample) +{ + EMIT_SOUND_DYN(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM); +} + +inline void EMIT_SOUND_MSG(edict_t *entity, int msg_type, int channel, const char *sample, float volume, float attenuation, int flags, int pitch = PITCH_NORM, Vector vecOrigin = g_vecZero, edict_t *player = nullptr) +{ + BUILD_SOUND_MSG(entity, channel, sample, (volume * 255.0f), attenuation, flags, pitch, msg_type, SVC_NOP, vecOrigin, player); +} + +inline void STOP_SOUND_MSG(edict_t *entity, int msg_type, int channel, const char *sample, edict_t *player) +{ + BUILD_SOUND_MSG(entity, channel, sample, 0, 0, SND_STOP, PITCH_NORM, msg_type, SVC_NOP, g_vecZero, player); +} + +class CBaseEntity; +class CBasePlayer; +class CBasePlayerItem; + +float UTIL_WeaponTimeBase(); +unsigned int U_Random(); +void U_Srand(unsigned int seed); +int UTIL_SharedRandomLong(unsigned int seed, int low, int high); +float UTIL_SharedRandomFloat(unsigned int seed, float low, float high); +void UTIL_ParametricRocket(entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner); +void UTIL_SetGroupTrace(int groupmask, int op); +void UTIL_UnsetGroupTrace(); +BOOL UTIL_GetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon); +float UTIL_AngleMod(float a); +float UTIL_AngleDiff(float destAngle, float srcAngle); +Vector UTIL_VecToAngles(const Vector &vec); +void UTIL_MoveToOrigin(edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType); +int UTIL_EntitiesInBox(CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask); +int UTIL_MonstersInSphere(CBaseEntity **pList, int listMax, const Vector ¢er, float radius); +CBaseEntity *UTIL_FindEntityInSphere(CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius); +CBaseEntity *UTIL_FindEntityByString_Old(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue); +CBaseEntity *UTIL_FindEntityByString(CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue); +CBaseEntity *UTIL_FindEntityByClassname(CBaseEntity *pStartEntity, const char *szName); +CBaseEntity *UTIL_FindEntityByTargetname(CBaseEntity *pStartEntity, const char *szName); +CBaseEntity *UTIL_FindEntityGeneric(const char *szWhatever, const Vector &vecSrc, float flRadius); +#ifndef REGAMEDLL_FIXES +CBasePlayer *UTIL_PlayerByIndex(int playerIndex); +#endif +void UTIL_MakeVectors(const Vector &vecAngles); +void UTIL_MakeAimVectors(const Vector &vecAngles); +void UTIL_MakeInvVectors(const Vector &vec, globalvars_t *pgv); +void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch); +unsigned short FixedUnsigned16(float value, float scale); +short FixedSigned16(float value, float scale); +void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius); +void UTIL_ScreenShakeAll(const Vector ¢er, float amplitude, float frequency, float duration); +void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); +void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity); +void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); +void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold = 0.0f, int alpha = 0, int flags = 0); +void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage); +void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage); +void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1 = nullptr, const char *param2 = nullptr, const char *param3 = nullptr, const char *param4 = nullptr); +void ClientPrint(entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = nullptr, const char *param2 = nullptr, const char *param3 = nullptr, const char *param4 = nullptr); +void UTIL_Log(const char *fmt, ...); +void UTIL_ServerPrint(const char *fmt, ...); +void UTIL_PrintConsole(edict_t *pEdict, const char *fmt, ...); +void UTIL_SayText(CBaseEntity *pEntity, const char *fmt, ...); +void UTIL_SayTextAll(const char *pText, CBaseEntity *pEntity); +char *UTIL_dtos1(int d); +char *UTIL_dtos2(int d); +char *UTIL_dtos3(int d); +char *UTIL_dtos4(int d); +void UTIL_ShowMessageArgs(const char *pString, CBaseEntity *pPlayer, CUtlVector *args, bool isHint = false); +void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity, bool isHint = false); +void UTIL_ShowMessageAll(const char *pString, bool isHint = false); +void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); +void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); +void UTIL_TraceHull(const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); +void UTIL_TraceModel(const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); +TraceResult UTIL_GetGlobalTrace(); +void UTIL_SetSize(entvars_t *pev, const Vector &vecMin, const Vector &vecMax); +float UTIL_VecToYaw(const Vector &vec); +void UTIL_SetOrigin(entvars_t *pev, const Vector &vecOrigin); +void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount); +float UTIL_Approach(float target, float value, float speed); +real_t UTIL_ApproachAngle(float target, float value, float speed); +real_t UTIL_AngleDistance(float next, float cur); +float UTIL_SplineFraction(float value, float scale); +char *UTIL_VarArgs(char *format, ...); +Vector UTIL_GetAimVector(edict_t *pent, float flSpeed); +bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator); +BOOL UTIL_ShouldShowBlood(int color); +int UTIL_PointContents(const Vector &vec); +void UTIL_BloodStream(const Vector &origin, const Vector &direction, int color, int amount); +void UTIL_BloodDrips(const Vector &origin, const Vector &direction, int color, int amount); +Vector UTIL_RandomBloodVector(); +void UTIL_BloodDecalTrace(TraceResult *pTrace, int bloodColor); +void UTIL_DecalTrace(TraceResult *pTrace, int decalNumber); +void UTIL_PlayerDecalTrace(TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom); +void UTIL_GunshotDecalTrace(TraceResult *pTrace, int decalNumber, bool ClientOnly, entvars_t *pShooter); +void UTIL_Sparks(const Vector &position); +void UTIL_Ricochet(const Vector &position, float scale); +bool UTIL_TeamsMatch(const char *pTeamName1, const char *pTeamName2); +void UTIL_StringToVector(float *pVector, const char *pString); +void UTIL_StringToVector(Vector &vecIn, const char *pString, char cSeparator); +void UTIL_StringToVectorND(Vector &vecIn, int nCount, const char *pString, char cSeparator); +void UTIL_StringToIntArray(int *pVector, int count, const char *pString); +Vector UTIL_ClampVectorToBox(const Vector &input, const Vector &clampSize); +float UTIL_WaterLevel(const Vector &position, float minz, float maxz); +void UTIL_Bubbles(Vector mins, Vector maxs, int count); +void UTIL_BubbleTrail(Vector from, Vector to, int count); +void UTIL_Remove(CBaseEntity *pEntity); +BOOL UTIL_IsValidEntity(edict_t *pent); +void UTIL_PrecacheOther(const char *szClassname); +void UTIL_RestartOther(const char *szClassname); +void UTIL_ResetEntities(); +void UTIL_RemoveOther(const char *szClassname, int nCount = 0); +void UTIL_LogPrintf(const char *fmt, ...); +float UTIL_DotPoints(const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir); +void EntvarsKeyvalue(entvars_t *pev, KeyValueData *pkvd); +char UTIL_TextureHit(TraceResult *ptr, Vector vecSrc, Vector vecEnd); +int GetPlayerTeam(int index); +bool UTIL_IsGame(const char *pszGameName); +real_t UTIL_GetPlayerGaitYaw(int playerIndex); +int UTIL_ReadFlags(const char *c); +bool UTIL_AreBotsAllowed(); +bool UTIL_IsBeta(); +bool UTIL_AreHostagesImprov(); +int UTIL_GetNumPlayers(); +bool UTIL_IsSpawnPointOccupied(CBaseEntity *pSpot); +void MAKE_STRING_CLASS(const char *str, entvars_t *pev); +void NORETURN Sys_Error(const char *error, ...); + +// Inlines +template +inline T *UTIL_FindEntityByClassname(T *pStartEntity, const char *szName) +{ + return (T *)UTIL_FindEntityByString(pStartEntity, "classname", szName); +} + +template +inline T *UTIL_FindEntityByTargetname(T *pStartEntity, const char *szName) +{ + return (T *)UTIL_FindEntityByString(pStartEntity, "targetname", szName); +} + +template +inline T *UTIL_FindEntityInSphere(T *pStartEntity, const Vector &vecCenter, float flRadius) +{ + edict_t *pentEntity; + if (pStartEntity) + pentEntity = pStartEntity->edict(); + else + pentEntity = nullptr; + + pentEntity = FIND_ENTITY_IN_SPHERE(pentEntity, vecCenter, flRadius); + if (!FNullEnt(pentEntity)) + { + // a1ba: can't use CBaseEntity::Instance here, because circular dependency in cbase.h + return GET_PRIVATE(pentEntity); + } + + return nullptr; +} + +template +void UTIL_StripToken(const char *pKey, char (&pDest)[nSize]) +{ + int i = 0; + while (i < nSize && pKey[i] && pKey[i] != '#') + { + pDest[i] = pKey[i]; + i++; + } + + pDest[i] = '\0'; +} + +class CPlayerInVolumeAdapter +{ +public: + virtual ~CPlayerInVolumeAdapter() {}; + virtual void PlayerDetected(const bool fInVolume, CBasePlayer *pPlayer) = 0; +}; + +int UTIL_CountPlayersInBrushVolume(bool bOnlyAlive, CBaseEntity *pBrushEntity, int &playersInCount, int &playersOutCount, CPlayerInVolumeAdapter *pAdapter = nullptr); + +inline real_t UTIL_FixupAngle(real_t v) +{ + real_t angle = v; + + while (angle < 0) + angle += 360; + + while (angle > 360) + angle -= 360; + + return angle; +} + +inline void UTIL_FixupAngles(Vector &v) +{ + v.x = UTIL_FixupAngle(v.x); + v.y = UTIL_FixupAngle(v.y); + v.z = UTIL_FixupAngle(v.z); +} + +extern int g_groupmask; +extern int g_groupop; diff --git a/regamedll/dlls/vector.h b/regamedll/dlls/vector.h index 5de02486..016622a5 100644 --- a/regamedll/dlls/vector.h +++ b/regamedll/dlls/vector.h @@ -1,426 +1,426 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -// Used for many pathfinding and many other operations that are treated as planar rather than 3D. -class Vector2D -{ -public: - // Construction/destruction - Vector2D() : x(), y() {} - Vector2D(float X, float Y) : x(X), y(Y) {} - Vector2D(const Vector2D &v) { *(int *)&x = *(int *)&v.x; *(int *)&y = *(int *)&v.y; } - - // Operators - decltype(auto) operator-() const { return Vector2D(-x, -y); } - bool operator==(const Vector2D &v) const { return x == v.x && y == v.y; } - bool operator!=(const Vector2D &v) const { return !(*this == v); } - - decltype(auto) operator+(const Vector2D &v) const { return Vector2D(x + v.x, y + v.y); } - decltype(auto) operator-(const Vector2D &v) const { return Vector2D(x - v.x, y - v.y); } - decltype(auto) operator*(const Vector2D &v) const { return Vector2D(x * v.x, y * v.y); } - decltype(auto) operator/(const Vector2D &v) const { return Vector2D(x / v.x, y / v.y); } - - decltype(auto) operator+=(const Vector2D &v) { return (*this = *this + v); } - decltype(auto) operator-=(const Vector2D &v) { return (*this = *this - v); } - decltype(auto) operator*=(const Vector2D &v) { return (*this = *this * v); } - decltype(auto) operator/=(const Vector2D &v) { return (*this = *this / v); } - - decltype(auto) operator+(float fl) const { return Vector2D(x + fl, y + fl); } - decltype(auto) operator-(float fl) const { return Vector2D(x - fl, y - fl); } - - // TODO: FIX ME!! -#ifdef PLAY_GAMEDLL - decltype(auto) operator*(float fl) const { return Vector2D(vec_t(x * fl), vec_t(y * fl)); } - decltype(auto) operator/(float fl) const { return Vector2D(vec_t(x / fl), vec_t(y / fl)); } -#else - decltype(auto) operator*(float fl) const { return Vector2D(x * fl, y * fl); } - decltype(auto) operator/(float fl) const { return Vector2D(x / fl, y / fl); } -#endif - - decltype(auto) operator=(std::nullptr_t) { return Vector2D(0, 0); } - decltype(auto) operator+=(float fl) { return (*this = *this + fl); } - decltype(auto) operator-=(float fl) { return (*this = *this - fl); } - decltype(auto) operator*=(float fl) { return (*this = *this * fl); } - decltype(auto) operator/=(float fl) { return (*this = *this / fl); } - - // Methods - inline void Clear() { x = 0; y = 0; } - inline void CopyToArray(float *rgfl) const { *(int *)&rgfl[0] = *(int *)&x; *(int *)&rgfl[1] = *(int *)&y; } - inline real_t Length() const { return Q_sqrt(real_t(x * x + y * y)); } // Get the vector's magnitude - inline float LengthSquared() const { return (x * x + y * y); } // Get the vector's magnitude squared - - operator float*() { return &x; } // Vectors will now automatically convert to float * when needed - operator const float*() const { return &x; } // Vectors will now automatically convert to float * when needed - - Vector2D Normalize() const - { - real_t flLen = Length(); - if (!flLen) - return Vector2D(0, 0); - - flLen = 1 / flLen; - -#ifdef PLAY_GAMEDLL - return Vector2D(vec_t(x * flLen), vec_t(y * flLen)); -#else - return Vector2D(x * flLen, y * flLen); -#endif // PLAY_GAMEDLL - } - inline bool IsLengthLessThan (float length) const { return (LengthSquared() < length * length); } - inline bool IsLengthGreaterThan(float length) const { return (LengthSquared() > length * length); } - real_t NormalizeInPlace() - { - real_t flLen = Length(); - if (flLen > 0.0) - { - x = vec_t(1 / flLen * x); - y = vec_t(1 / flLen * y); - } - else - { - x = 1.0; - y = 0.0; - } - return flLen; - } - bool IsZero(float tolerance = 0.01f) const - { - return (x > -tolerance && x < tolerance && - y > -tolerance && y < tolerance); - } - - // Members - vec_t x, y; -}; - -inline real_t DotProduct(const Vector2D &a, const Vector2D &b) -{ - return (a.x * b.x + a.y * b.y); -} - -inline Vector2D operator*(float fl, const Vector2D &v) -{ - return v * fl; -} - -// 3D Vector -// Same data-layout as engine's vec3_t, which is a vec_t[3] -class Vector -{ -public: - // Construction/destruction - Vector() : x(), y(), z() {} - Vector(float X, float Y, float Z) : x(X), y(Y), z(Z) {} - Vector(const Vector &v) { *(int *)&x = *(int *)&v.x; *(int *)&y = *(int *)&v.y; *(int *)&z = *(int *)&v.z; } - Vector(const float rgfl[3]) { *(int *)&x = *(int *)&rgfl[0]; *(int *)&y = *(int *)&rgfl[1]; *(int *)&z = *(int *)&rgfl[2]; } - - // Operators - decltype(auto) operator-() const { return Vector(-x, -y, -z); } - bool operator==(const Vector &v) const { return x == v.x && y == v.y && z == v.z; } - bool operator!=(const Vector &v) const { return !(*this == v); } - - decltype(auto) operator+(const Vector &v) const { return Vector(x + v.x, y + v.y, z + v.z); } - decltype(auto) operator-(const Vector &v) const { return Vector(x - v.x, y - v.y, z - v.z); } - decltype(auto) operator*(const Vector &v) const { return Vector(x * v.x, y * v.y, z * v.z); } - decltype(auto) operator/(const Vector &v) const { return Vector(x / v.x, y / v.y, z / v.z); } - - decltype(auto) operator+=(const Vector &v) { return (*this = *this + v); } - decltype(auto) operator-=(const Vector &v) { return (*this = *this - v); } - decltype(auto) operator*=(const Vector &v) { return (*this = *this * v); } - decltype(auto) operator/=(const Vector &v) { return (*this = *this / v); } - - decltype(auto) operator+(float fl) const { return Vector(x + fl, y + fl, z + fl); } - decltype(auto) operator-(float fl) const { return Vector(x - fl, y - fl, z - fl); } - - // TODO: FIX ME!! -#ifdef PLAY_GAMEDLL - decltype(auto) operator*(float fl) const { return Vector(vec_t(x * fl), vec_t(y * fl), vec_t(z * fl)); } - decltype(auto) operator/(float fl) const { return Vector(vec_t(x / fl), vec_t(y / fl), vec_t(z / fl)); } -#else - decltype(auto) operator*(float fl) const { return Vector(x * fl, y * fl, z * fl); } - decltype(auto) operator/(float fl) const { return Vector(x / fl, y / fl, z / fl); } -#endif - - decltype(auto) operator=(std::nullptr_t) { return Vector(0, 0, 0); } - decltype(auto) operator+=(float fl) { return (*this = *this + fl); } - decltype(auto) operator-=(float fl) { return (*this = *this - fl); } - decltype(auto) operator*=(float fl) { return (*this = *this * fl); } - decltype(auto) operator/=(float fl) { return (*this = *this / fl); } - - // Methods - void Clear() - { - x = 0; - y = 0; - z = 0; - } - - void CopyToArray(float *rgfl) const - { - *(int *)&rgfl[0] = *(int *)&x; - *(int *)&rgfl[1] = *(int *)&y; - *(int *)&rgfl[2] = *(int *)&z; - } - - // Get the vector's magnitude - real_t Length() const - { - real_t x1 = real_t(x); - real_t y1 = real_t(y); - real_t z1 = real_t(z); - - return Q_sqrt(x1 * x1 + y1 * y1 + z1 * z1); - } - - // Get the vector's magnitude squared - real_t LengthSquared() const { return (x * x + y * y + z * z); } - - operator float*() { return &x; } // Vectors will now automatically convert to float * when needed - operator const float*() const { return &x; } // Vectors will now automatically convert to float * when needed - -#ifndef PLAY_GAMEDLL - Vector Normalize() const - { - float flLen = Length(); - if (flLen == 0) - return Vector(0, 0, 1); - - flLen = 1 / flLen; - return Vector(x * flLen, y * flLen, z * flLen); - } -#else - Vector Normalize() - { - real_t flLen = Length(); - if (flLen == 0) - return Vector(0, 0, 1); - - vec_t fTemp = vec_t(1 / flLen); - return Vector(x * fTemp, y * fTemp, z * fTemp); - } -#endif // PLAY_GAMEDLL - // for out precision normalize - Vector NormalizePrecision() const - { -#ifndef PLAY_GAMEDLL - return Normalize(); -#else - real_t flLen = Length(); - if (flLen == 0) - return Vector(0, 0, 1); - - flLen = 1 / flLen; - return Vector(vec_t(x * flLen), vec_t(y * flLen), vec_t(z * flLen)); -#endif // PLAY_GAMEDLL - } - Vector2D Make2D() const - { - Vector2D Vec2; - *(int *)&Vec2.x = *(int *)&x; - *(int *)&Vec2.y = *(int *)&y; - return Vec2; - } - - real_t Length2D() const { return Q_sqrt(real_t(x * x + y * y)); } - - inline bool IsLengthLessThan (float length) const { return (LengthSquared() < length * length); } - inline bool IsLengthGreaterThan(float length) const { return (LengthSquared() > length * length); } - -#ifdef PLAY_GAMEDLL - template - real_t NormalizeInPlace() - { - T flLen = Length(); - - if (flLen > 0) - { - x = vec_t(1 / flLen * x); - y = vec_t(1 / flLen * y); - z = vec_t(1 / flLen * z); - } - else - { - x = 0; - y = 0; - z = 1; - } - - return flLen; - } -#else // PLAY_GAMEDLL - float NormalizeInPlace() - { - float flLen = Length(); - if (flLen > 0) - { - x /= flLen; - y /= flLen; - z /= flLen; - } - else - { - x = 0; - y = 0; - z = 1; - } - return flLen; - } -#endif // PLAY_GAMEDLL - bool IsZero(float tolerance = 0.01f) const - { - return (x > -tolerance && x < tolerance && - y > -tolerance && y < tolerance && - z > -tolerance && z < tolerance); - } - - // Members - vec_t x, y, z; -}; - -inline Vector operator*(float fl, const Vector &v) -{ - return v * fl; -} - -inline real_t DotProduct(const Vector &a, const Vector &b) -{ - return (a.x * b.x + a.y * b.y + a.z * b.z); -} - -inline real_t DotProduct2D(const Vector &a, const Vector &b) -{ - return (a.x * b.x + a.y * b.y); -} - -inline Vector CrossProduct(const Vector &a, const Vector &b) -{ - return Vector(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); -} - -template< - typename X, - typename Y, - typename Z, - typename LenType -> -inline LenType LengthSubtract(Vector vecStart, Vector vecDest) -{ - X floatX = (vecDest.x - vecStart.x); - Y floatY = (vecDest.y - vecStart.y); - Z floatZ = (vecDest.z - vecStart.z); - - return Q_sqrt(real_t(floatX * floatX + floatY * floatY + floatZ * floatZ)); -} - -template< - typename X, - typename Y, - typename Z, - typename LenType -> -inline Vector NormalizeSubtract(Vector vecStart, Vector vecDest) -{ - Vector dir; - -#ifdef PLAY_GAMEDLL - - X floatX = (vecDest.x - vecStart.x); - Y floatY = (vecDest.y - vecStart.y); - Z floatZ = (vecDest.z - vecStart.z); - - LenType flLen = Q_sqrt(real_t(floatX * floatX + floatY * floatY + floatZ * floatZ)); - - if (flLen == 0.0) - { - dir = Vector(0, 0, 1); - } - else - { - flLen = 1.0 / flLen; - - dir.x = vec_t(floatX * flLen); - dir.y = vec_t(floatY * flLen); - dir.z = vec_t(floatZ * flLen); - } -#else - dir = (vecDest - vecStart).Normalize(); -#endif // PLAY_GAMEDLL - - return dir; -} - -#ifdef PLAY_GAMEDLL -template -inline Vector NormalizeMulScalar(Vector2D vec, float scalar) -{ - LenType flLen; - X floatX; - Y floatY; - - flLen = (LenType)vec.Length(); - - if (flLen <= 0.0) - { - floatX = 1; - floatY = 0; - } - else - { - flLen = 1 / flLen; - - floatX = vec.x * flLen; - floatY = vec.y * flLen; - } - - return Vector(vec_t(floatX * scalar), vec_t(floatY * scalar), 0); -} -template -inline Vector NormalizeMulScalar(Vector vec, float scalar) -{ - LenType flLen; - X floatX = vec.x; - Y floatY = vec.y; - - flLen = (LenType)vec.Length(); - - if (flLen <= 0.0) - { - floatX = 1; - floatY = 0; - } - else - { - floatX = floatX * LenCast(1 / flLen); - floatY = floatY * LenCast(1 / flLen); - } - - return Vector(vec_t(floatX * scalar), vec_t(floatY * scalar), 0); -} -#endif // PLAY_GAMEDLL +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +// Used for many pathfinding and many other operations that are treated as planar rather than 3D. +class Vector2D +{ +public: + // Construction/destruction + Vector2D() : x(), y() {} + Vector2D(float X, float Y) : x(X), y(Y) {} + Vector2D(const Vector2D &v) { *(int *)&x = *(int *)&v.x; *(int *)&y = *(int *)&v.y; } + + // Operators + decltype(auto) operator-() const { return Vector2D(-x, -y); } + bool operator==(const Vector2D &v) const { return x == v.x && y == v.y; } + bool operator!=(const Vector2D &v) const { return !(*this == v); } + + decltype(auto) operator+(const Vector2D &v) const { return Vector2D(x + v.x, y + v.y); } + decltype(auto) operator-(const Vector2D &v) const { return Vector2D(x - v.x, y - v.y); } + decltype(auto) operator*(const Vector2D &v) const { return Vector2D(x * v.x, y * v.y); } + decltype(auto) operator/(const Vector2D &v) const { return Vector2D(x / v.x, y / v.y); } + + decltype(auto) operator+=(const Vector2D &v) { return (*this = *this + v); } + decltype(auto) operator-=(const Vector2D &v) { return (*this = *this - v); } + decltype(auto) operator*=(const Vector2D &v) { return (*this = *this * v); } + decltype(auto) operator/=(const Vector2D &v) { return (*this = *this / v); } + + decltype(auto) operator+(float fl) const { return Vector2D(x + fl, y + fl); } + decltype(auto) operator-(float fl) const { return Vector2D(x - fl, y - fl); } + + // TODO: FIX ME!! +#ifdef PLAY_GAMEDLL + decltype(auto) operator*(float fl) const { return Vector2D(vec_t(x * fl), vec_t(y * fl)); } + decltype(auto) operator/(float fl) const { return Vector2D(vec_t(x / fl), vec_t(y / fl)); } +#else + decltype(auto) operator*(float fl) const { return Vector2D(x * fl, y * fl); } + decltype(auto) operator/(float fl) const { return Vector2D(x / fl, y / fl); } +#endif + + decltype(auto) operator=(std::nullptr_t) { return Vector2D(0, 0); } + decltype(auto) operator+=(float fl) { return (*this = *this + fl); } + decltype(auto) operator-=(float fl) { return (*this = *this - fl); } + decltype(auto) operator*=(float fl) { return (*this = *this * fl); } + decltype(auto) operator/=(float fl) { return (*this = *this / fl); } + + // Methods + inline void Clear() { x = 0; y = 0; } + inline void CopyToArray(float *rgfl) const { *(int *)&rgfl[0] = *(int *)&x; *(int *)&rgfl[1] = *(int *)&y; } + inline real_t Length() const { return Q_sqrt(real_t(x * x + y * y)); } // Get the vector's magnitude + inline float LengthSquared() const { return (x * x + y * y); } // Get the vector's magnitude squared + + operator float*() { return &x; } // Vectors will now automatically convert to float * when needed + operator const float*() const { return &x; } // Vectors will now automatically convert to float * when needed + + Vector2D Normalize() const + { + real_t flLen = Length(); + if (!flLen) + return Vector2D(0, 0); + + flLen = 1 / flLen; + +#ifdef PLAY_GAMEDLL + return Vector2D(vec_t(x * flLen), vec_t(y * flLen)); +#else + return Vector2D(x * flLen, y * flLen); +#endif // PLAY_GAMEDLL + } + inline bool IsLengthLessThan (float length) const { return (LengthSquared() < length * length); } + inline bool IsLengthGreaterThan(float length) const { return (LengthSquared() > length * length); } + real_t NormalizeInPlace() + { + real_t flLen = Length(); + if (flLen > 0.0) + { + x = vec_t(1 / flLen * x); + y = vec_t(1 / flLen * y); + } + else + { + x = 1.0; + y = 0.0; + } + return flLen; + } + bool IsZero(float tolerance = 0.01f) const + { + return (x > -tolerance && x < tolerance && + y > -tolerance && y < tolerance); + } + + // Members + vec_t x, y; +}; + +inline real_t DotProduct(const Vector2D &a, const Vector2D &b) +{ + return (a.x * b.x + a.y * b.y); +} + +inline Vector2D operator*(float fl, const Vector2D &v) +{ + return v * fl; +} + +// 3D Vector +// Same data-layout as engine's vec3_t, which is a vec_t[3] +class Vector +{ +public: + // Construction/destruction + Vector() : x(), y(), z() {} + Vector(float X, float Y, float Z) : x(X), y(Y), z(Z) {} + Vector(const Vector &v) { *(int *)&x = *(int *)&v.x; *(int *)&y = *(int *)&v.y; *(int *)&z = *(int *)&v.z; } + Vector(const float rgfl[3]) { *(int *)&x = *(int *)&rgfl[0]; *(int *)&y = *(int *)&rgfl[1]; *(int *)&z = *(int *)&rgfl[2]; } + + // Operators + decltype(auto) operator-() const { return Vector(-x, -y, -z); } + bool operator==(const Vector &v) const { return x == v.x && y == v.y && z == v.z; } + bool operator!=(const Vector &v) const { return !(*this == v); } + + decltype(auto) operator+(const Vector &v) const { return Vector(x + v.x, y + v.y, z + v.z); } + decltype(auto) operator-(const Vector &v) const { return Vector(x - v.x, y - v.y, z - v.z); } + decltype(auto) operator*(const Vector &v) const { return Vector(x * v.x, y * v.y, z * v.z); } + decltype(auto) operator/(const Vector &v) const { return Vector(x / v.x, y / v.y, z / v.z); } + + decltype(auto) operator+=(const Vector &v) { return (*this = *this + v); } + decltype(auto) operator-=(const Vector &v) { return (*this = *this - v); } + decltype(auto) operator*=(const Vector &v) { return (*this = *this * v); } + decltype(auto) operator/=(const Vector &v) { return (*this = *this / v); } + + decltype(auto) operator+(float fl) const { return Vector(x + fl, y + fl, z + fl); } + decltype(auto) operator-(float fl) const { return Vector(x - fl, y - fl, z - fl); } + + // TODO: FIX ME!! +#ifdef PLAY_GAMEDLL + decltype(auto) operator*(float fl) const { return Vector(vec_t(x * fl), vec_t(y * fl), vec_t(z * fl)); } + decltype(auto) operator/(float fl) const { return Vector(vec_t(x / fl), vec_t(y / fl), vec_t(z / fl)); } +#else + decltype(auto) operator*(float fl) const { return Vector(x * fl, y * fl, z * fl); } + decltype(auto) operator/(float fl) const { return Vector(x / fl, y / fl, z / fl); } +#endif + + decltype(auto) operator=(std::nullptr_t) { return Vector(0, 0, 0); } + decltype(auto) operator+=(float fl) { return (*this = *this + fl); } + decltype(auto) operator-=(float fl) { return (*this = *this - fl); } + decltype(auto) operator*=(float fl) { return (*this = *this * fl); } + decltype(auto) operator/=(float fl) { return (*this = *this / fl); } + + // Methods + void Clear() + { + x = 0; + y = 0; + z = 0; + } + + void CopyToArray(float *rgfl) const + { + *(int *)&rgfl[0] = *(int *)&x; + *(int *)&rgfl[1] = *(int *)&y; + *(int *)&rgfl[2] = *(int *)&z; + } + + // Get the vector's magnitude + real_t Length() const + { + real_t x1 = real_t(x); + real_t y1 = real_t(y); + real_t z1 = real_t(z); + + return Q_sqrt(x1 * x1 + y1 * y1 + z1 * z1); + } + + // Get the vector's magnitude squared + real_t LengthSquared() const { return (x * x + y * y + z * z); } + + operator float*() { return &x; } // Vectors will now automatically convert to float * when needed + operator const float*() const { return &x; } // Vectors will now automatically convert to float * when needed + +#ifndef PLAY_GAMEDLL + Vector Normalize() const + { + float flLen = Length(); + if (flLen == 0) + return Vector(0, 0, 1); + + flLen = 1 / flLen; + return Vector(x * flLen, y * flLen, z * flLen); + } +#else + Vector Normalize() + { + real_t flLen = Length(); + if (flLen == 0) + return Vector(0, 0, 1); + + vec_t fTemp = vec_t(1 / flLen); + return Vector(x * fTemp, y * fTemp, z * fTemp); + } +#endif // PLAY_GAMEDLL + // for out precision normalize + Vector NormalizePrecision() const + { +#ifndef PLAY_GAMEDLL + return Normalize(); +#else + real_t flLen = Length(); + if (flLen == 0) + return Vector(0, 0, 1); + + flLen = 1 / flLen; + return Vector(vec_t(x * flLen), vec_t(y * flLen), vec_t(z * flLen)); +#endif // PLAY_GAMEDLL + } + Vector2D Make2D() const + { + Vector2D Vec2; + *(int *)&Vec2.x = *(int *)&x; + *(int *)&Vec2.y = *(int *)&y; + return Vec2; + } + + real_t Length2D() const { return Q_sqrt(real_t(x * x + y * y)); } + + inline bool IsLengthLessThan (float length) const { return (LengthSquared() < length * length); } + inline bool IsLengthGreaterThan(float length) const { return (LengthSquared() > length * length); } + +#ifdef PLAY_GAMEDLL + template + real_t NormalizeInPlace() + { + T flLen = Length(); + + if (flLen > 0) + { + x = vec_t(1 / flLen * x); + y = vec_t(1 / flLen * y); + z = vec_t(1 / flLen * z); + } + else + { + x = 0; + y = 0; + z = 1; + } + + return flLen; + } +#else // PLAY_GAMEDLL + float NormalizeInPlace() + { + float flLen = Length(); + if (flLen > 0) + { + x /= flLen; + y /= flLen; + z /= flLen; + } + else + { + x = 0; + y = 0; + z = 1; + } + return flLen; + } +#endif // PLAY_GAMEDLL + bool IsZero(float tolerance = 0.01f) const + { + return (x > -tolerance && x < tolerance && + y > -tolerance && y < tolerance && + z > -tolerance && z < tolerance); + } + + // Members + vec_t x, y, z; +}; + +inline Vector operator*(float fl, const Vector &v) +{ + return v * fl; +} + +inline real_t DotProduct(const Vector &a, const Vector &b) +{ + return (a.x * b.x + a.y * b.y + a.z * b.z); +} + +inline real_t DotProduct2D(const Vector &a, const Vector &b) +{ + return (a.x * b.x + a.y * b.y); +} + +inline Vector CrossProduct(const Vector &a, const Vector &b) +{ + return Vector(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +template< + typename X, + typename Y, + typename Z, + typename LenType +> +inline LenType LengthSubtract(Vector vecStart, Vector vecDest) +{ + X floatX = (vecDest.x - vecStart.x); + Y floatY = (vecDest.y - vecStart.y); + Z floatZ = (vecDest.z - vecStart.z); + + return Q_sqrt(real_t(floatX * floatX + floatY * floatY + floatZ * floatZ)); +} + +template< + typename X, + typename Y, + typename Z, + typename LenType +> +inline Vector NormalizeSubtract(Vector vecStart, Vector vecDest) +{ + Vector dir; + +#ifdef PLAY_GAMEDLL + + X floatX = (vecDest.x - vecStart.x); + Y floatY = (vecDest.y - vecStart.y); + Z floatZ = (vecDest.z - vecStart.z); + + LenType flLen = Q_sqrt(real_t(floatX * floatX + floatY * floatY + floatZ * floatZ)); + + if (flLen == 0.0) + { + dir = Vector(0, 0, 1); + } + else + { + flLen = 1.0 / flLen; + + dir.x = vec_t(floatX * flLen); + dir.y = vec_t(floatY * flLen); + dir.z = vec_t(floatZ * flLen); + } +#else + dir = (vecDest - vecStart).Normalize(); +#endif // PLAY_GAMEDLL + + return dir; +} + +#ifdef PLAY_GAMEDLL +template +inline Vector NormalizeMulScalar(Vector2D vec, float scalar) +{ + LenType flLen; + X floatX; + Y floatY; + + flLen = (LenType)vec.Length(); + + if (flLen <= 0.0) + { + floatX = 1; + floatY = 0; + } + else + { + flLen = 1 / flLen; + + floatX = vec.x * flLen; + floatY = vec.y * flLen; + } + + return Vector(vec_t(floatX * scalar), vec_t(floatY * scalar), 0); +} +template +inline Vector NormalizeMulScalar(Vector vec, float scalar) +{ + LenType flLen; + X floatX = vec.x; + Y floatY = vec.y; + + flLen = (LenType)vec.Length(); + + if (flLen <= 0.0) + { + floatX = 1; + floatY = 0; + } + else + { + floatX = floatX * LenCast(1 / flLen); + floatY = floatY * LenCast(1 / flLen); + } + + return Vector(vec_t(floatX * scalar), vec_t(floatY * scalar), 0); +} +#endif // PLAY_GAMEDLL diff --git a/regamedll/dlls/weapons.cpp b/regamedll/dlls/weapons.cpp index 011d0202..2761eb7d 100644 --- a/regamedll/dlls/weapons.cpp +++ b/regamedll/dlls/weapons.cpp @@ -1,2441 +1,2441 @@ -#include "precompiled.h" - -short g_sModelIndexLaser; // holds the index for the laser beam -short g_sModelIndexLaserDot; // holds the index for the laser beam dot -short g_sModelIndexFireball; // holds the index for the fireball -short g_sModelIndexSmoke; // holds the index for the smoke cloud -short g_sModelIndexWExplosion; // holds the index for the underwater explosion -short g_sModelIndexBubbles; // holds the index for the bubbles model -short g_sModelIndexBloodDrop; // holds the sprite index for the initial blood -short g_sModelIndexBloodSpray; // holds the sprite index for splattered blood -short g_sModelIndexSmokePuff; -short g_sModelIndexFireball2; -short g_sModelIndexFireball3; -short g_sModelIndexFireball4; -short g_sModelIndexRadio; - -short int g_sModelIndexCTGhost; -short int g_sModelIndexTGhost; -short int g_sModelIndexC4Glow; - -int giAmmoIndex; - -MULTIDAMAGE gMultiDamage; - -// Pass in a name and this function will tell -// you the maximum amount of that type of ammunition that a player can carry. -int MaxAmmoCarry(const char *szName) -{ - for (int i = 0; i < MAX_WEAPONS; i++) - { - ItemInfo *info = &CBasePlayerItem::m_ItemInfoArray[i]; - if (info->pszAmmo1 && !Q_stricmp(szName, info->pszAmmo1)) - { - return info->iMaxAmmo1; - } - - if (info->pszAmmo2 && !Q_stricmp(szName, info->pszAmmo2)) - { - return info->iMaxAmmo2; - } - } - - ALERT(at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", szName); - return -1; -} - -int MaxAmmoCarry(WeaponIdType weaponId) -{ - return CBasePlayerItem::m_ItemInfoArray[weaponId].iMaxAmmo1; -} - -// Resets the global multi damage accumulator -void ClearMultiDamage() -{ - gMultiDamage.pEntity = nullptr; - gMultiDamage.amount = 0; - gMultiDamage.type = 0; -} - -// Inflicts contents of global multi damage register on gMultiDamage.pEntity -void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker) -{ - if (!gMultiDamage.pEntity) - return; - - gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type); - -} - -void AddMultiDamage(entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType) -{ - if (!pEntity) - return; - - gMultiDamage.type |= bitsDamageType; - - if (pEntity != gMultiDamage.pEntity) - { - // UNDONE: wrong attacker! - ApplyMultiDamage(pevInflictor, pevInflictor); - gMultiDamage.pEntity = pEntity; - gMultiDamage.amount = 0; - } - - gMultiDamage.amount += flDamage; -} - -void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) -{ - UTIL_BloodDrips(vecSpot, g_vecAttackDir, bloodColor, int(flDamage)); -} - -NOXREF int DamageDecal(CBaseEntity *pEntity, int bitsDamageType) -{ - if (pEntity) - { - return pEntity->DamageDecal(bitsDamageType); - } - - return RANDOM_LONG(DECAL_GUNSHOT4, DECAL_GUNSHOT5); -} - -void DecalGunshot(TraceResult *pTrace, int iBulletType, bool ClientOnly, entvars_t *pShooter, bool bHitMetal) -{ - ; -} - -// EjectBrass - tosses a brass shell from passed origin at passed velocity -void EjectBrass(const Vector &vecOrigin, const Vector &vecLeft, const Vector &vecVelocity, float rotation, int model, int soundtype, int entityIndex) -{ - bool useNewBehavior = AreRunningCZero(); - - MESSAGE_BEGIN(MSG_PVS, gmsgBrass, vecOrigin); - if (!useNewBehavior) - { - // noxref - WRITE_BYTE(TE_MODEL); - } - WRITE_COORD(vecOrigin.x); // origin - WRITE_COORD(vecOrigin.y); - WRITE_COORD(vecOrigin.z); - if (!useNewBehavior) - { - // noxref - // it parses the client side, but does not use it - WRITE_COORD(vecLeft.x); - WRITE_COORD(vecLeft.y); - WRITE_COORD(vecLeft.z); - } - WRITE_COORD(vecVelocity.x); // velocity - WRITE_COORD(vecVelocity.y); - WRITE_COORD(vecVelocity.z); - WRITE_ANGLE(rotation); - WRITE_SHORT(model); - WRITE_BYTE(soundtype); - if (!useNewBehavior) - { - // noxref - WRITE_BYTE(25);// 2.5 seconds - } - WRITE_BYTE(entityIndex); - MESSAGE_END(); -} - -NOXREF void EjectBrass2(const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype, entvars_t *pev) -{ - MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, pev); - WRITE_BYTE(TE_MODEL); - WRITE_COORD(vecOrigin.x); - WRITE_COORD(vecOrigin.y); - WRITE_COORD(vecOrigin.z); - WRITE_COORD(vecVelocity.x); - WRITE_COORD(vecVelocity.y); - WRITE_COORD(vecVelocity.z); - WRITE_ANGLE(rotation); - WRITE_SHORT(model); - WRITE_BYTE(0); - WRITE_BYTE(5);// 0.5 seconds - MESSAGE_END(); -} - -#ifdef REGAMEDLL_ADD -struct { - AmmoType type; - const char *name; -} ammoIndex[] = -{ - { AMMO_338MAGNUM, "338Magnum" }, - { AMMO_762NATO, "762Nato" }, - { AMMO_556NATOBOX, "556NatoBox" }, - { AMMO_556NATO, "556Nato" }, - { AMMO_BUCKSHOT, "buckshot" }, - { AMMO_45ACP, "45acp" }, - { AMMO_57MM, "57mm" }, - { AMMO_50AE, "50AE" }, - { AMMO_357SIG, "357SIG" }, - { AMMO_9MM, "9mm" }, - { AMMO_FLASHBANG, "Flashbang" }, - { AMMO_HEGRENADE, "HEGrenade" }, - { AMMO_SMOKEGRENADE, "SmokeGrenade" }, - { AMMO_C4, "C4" }, -}; -#endif - -// Precaches the ammo and queues the ammo info for sending to clients -void AddAmmoNameToAmmoRegistry(const char *szAmmoname) -{ - // make sure it's not already in the registry - for (int i = 0; i < MAX_AMMO_SLOTS; i++) - { - if (!CBasePlayerItem::m_AmmoInfoArray[i].pszName) - continue; - - if (!Q_stricmp(CBasePlayerItem::m_AmmoInfoArray[i].pszName, szAmmoname)) - { - // ammo already in registry, just quite - return; - } - } - - giAmmoIndex++; - assert(giAmmoIndex < MAX_AMMO_SLOTS); - - if (giAmmoIndex >= MAX_AMMO_SLOTS) - giAmmoIndex = 0; - -#ifdef REGAMEDLL_ADD - for (auto& ammo : ammoIndex) - { - if (Q_stricmp(ammo.name, szAmmoname)) - continue; - - if (ammo.type != giAmmoIndex) { - CONSOLE_ECHO("Warning: ammo '%s' index mismatch; expected %i, real %i\n", szAmmoname, ammo.type, giAmmoIndex); - } - break; - } -#endif - - CBasePlayerItem::m_AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; - - // Yes, this info is redundant - CBasePlayerItem::m_AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; -} - -// Precaches the weapon and queues the weapon info for sending to clients -void UTIL_PrecacheOtherWeapon(const char *szClassname) -{ - edict_t *pEdict = CREATE_NAMED_ENTITY(MAKE_STRING(szClassname)); - if (FNullEnt(pEdict)) - { - ALERT(at_console, "NULL Ent in UTIL_PrecacheOtherWeapon classname `%s`\n", szClassname); - return; - } - - CBasePlayerItem *pItem = GET_PRIVATE(pEdict); - if (pItem) - { - ItemInfo info; - Q_memset(&info, 0, sizeof(info)); - - pItem->Precache(); - if (pItem->GetItemInfo(&info)) - { - CBasePlayerItem::m_ItemInfoArray[info.iId] = info; - - if (info.pszAmmo1 && info.pszAmmo1[0] != '\0') - { - AddAmmoNameToAmmoRegistry(info.pszAmmo1); - } - - if (info.pszAmmo2 && info.pszAmmo2[0] != '\0') - { - AddAmmoNameToAmmoRegistry(info.pszAmmo2); - } - } - } - - REMOVE_ENTITY(pEdict); -} - -// Called by worldspawn -void WeaponsPrecache() -{ - Q_memset(CBasePlayerItem::m_ItemInfoArray, 0, sizeof(CBasePlayerItem::m_ItemInfoArray)); - Q_memset(CBasePlayerItem::m_AmmoInfoArray, 0, sizeof(CBasePlayerItem::m_AmmoInfoArray)); - - giAmmoIndex = 0; - - // custom items... - - // common world objects - UTIL_PrecacheOther("item_suit"); - UTIL_PrecacheOther("item_battery"); - UTIL_PrecacheOther("item_antidote"); - UTIL_PrecacheOther("item_security"); - UTIL_PrecacheOther("item_longjump"); - UTIL_PrecacheOther("item_kevlar"); - UTIL_PrecacheOther("item_assaultsuit"); - UTIL_PrecacheOther("item_thighpack"); - - // awp magnum - UTIL_PrecacheOtherWeapon("weapon_awp"); - UTIL_PrecacheOther("ammo_338magnum"); - - UTIL_PrecacheOtherWeapon("weapon_g3sg1"); - UTIL_PrecacheOtherWeapon("weapon_ak47"); - UTIL_PrecacheOtherWeapon("weapon_scout"); - UTIL_PrecacheOther("ammo_762nato"); - - // m249 - UTIL_PrecacheOtherWeapon("weapon_m249"); - UTIL_PrecacheOther("ammo_556natobox"); - - UTIL_PrecacheOtherWeapon("weapon_m4a1"); - UTIL_PrecacheOtherWeapon("weapon_sg552"); - UTIL_PrecacheOtherWeapon("weapon_aug"); - UTIL_PrecacheOtherWeapon("weapon_sg550"); - UTIL_PrecacheOther("ammo_556nato"); - - // shotgun - UTIL_PrecacheOtherWeapon("weapon_m3"); - UTIL_PrecacheOtherWeapon("weapon_xm1014"); - UTIL_PrecacheOther("ammo_buckshot"); - - UTIL_PrecacheOtherWeapon("weapon_usp"); - UTIL_PrecacheOtherWeapon("weapon_mac10"); - UTIL_PrecacheOtherWeapon("weapon_ump45"); - UTIL_PrecacheOther("ammo_45acp"); - - UTIL_PrecacheOtherWeapon("weapon_fiveseven"); - UTIL_PrecacheOtherWeapon("weapon_p90"); - UTIL_PrecacheOther("ammo_57mm"); - - // deagle - UTIL_PrecacheOtherWeapon("weapon_deagle"); - UTIL_PrecacheOther("ammo_50ae"); - - // p228 - UTIL_PrecacheOtherWeapon("weapon_p228"); - UTIL_PrecacheOther("ammo_357sig"); - - // knife - UTIL_PrecacheOtherWeapon("weapon_knife"); - - UTIL_PrecacheOtherWeapon("weapon_glock18"); - UTIL_PrecacheOtherWeapon("weapon_mp5navy"); - UTIL_PrecacheOtherWeapon("weapon_tmp"); - UTIL_PrecacheOtherWeapon("weapon_elite"); - UTIL_PrecacheOther("ammo_9mm"); - - UTIL_PrecacheOtherWeapon("weapon_flashbang"); - UTIL_PrecacheOtherWeapon("weapon_hegrenade"); - UTIL_PrecacheOtherWeapon("weapon_smokegrenade"); - UTIL_PrecacheOtherWeapon("weapon_c4"); - UTIL_PrecacheOtherWeapon("weapon_galil"); - UTIL_PrecacheOtherWeapon("weapon_famas"); - - if (g_pGameRules->IsDeathmatch()) - { - // container for dropped deathmatch weapons - UTIL_PrecacheOther("weaponbox"); - } - - g_sModelIndexFireball = PRECACHE_MODEL("sprites/zerogxplode.spr"); // fireball - g_sModelIndexWExplosion = PRECACHE_MODEL("sprites/WXplo1.spr"); // underwater fireball - g_sModelIndexSmoke = PRECACHE_MODEL("sprites/steam1.spr"); // smoke - g_sModelIndexBubbles = PRECACHE_MODEL("sprites/bubble.spr"); // bubbles - g_sModelIndexBloodSpray = PRECACHE_MODEL("sprites/bloodspray.spr"); // initial blood - g_sModelIndexBloodDrop = PRECACHE_MODEL("sprites/blood.spr"); // splattered blood - - g_sModelIndexSmokePuff = PRECACHE_MODEL("sprites/smokepuff.spr"); - g_sModelIndexFireball2 = PRECACHE_MODEL("sprites/eexplo.spr"); - g_sModelIndexFireball3 = PRECACHE_MODEL("sprites/fexplo.spr"); - g_sModelIndexFireball4 = PRECACHE_MODEL("sprites/fexplo1.spr"); - g_sModelIndexRadio = PRECACHE_MODEL("sprites/radio.spr"); - - g_sModelIndexCTGhost = PRECACHE_MODEL("sprites/b-tele1.spr"); - g_sModelIndexTGhost = PRECACHE_MODEL("sprites/c-tele1.spr"); - g_sModelIndexC4Glow = PRECACHE_MODEL("sprites/ledglow.spr"); - - g_sModelIndexLaser = PRECACHE_MODEL("sprites/laserbeam.spr"); - g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr"); - - // used by explosions - PRECACHE_MODEL("models/grenade.mdl"); - PRECACHE_MODEL("sprites/explode1.spr"); - - PRECACHE_SOUND("weapons/debris1.wav"); // explosion aftermaths - PRECACHE_SOUND("weapons/debris2.wav"); // explosion aftermaths - PRECACHE_SOUND("weapons/debris3.wav"); // explosion aftermaths - - PRECACHE_SOUND("weapons/grenade_hit1.wav"); // grenade - PRECACHE_SOUND("weapons/grenade_hit2.wav"); // grenade - PRECACHE_SOUND("weapons/grenade_hit3.wav"); // grenade - - PRECACHE_SOUND("weapons/bullet_hit1.wav"); // hit by bullet - PRECACHE_SOUND("weapons/bullet_hit2.wav"); // hit by bullet - - PRECACHE_SOUND("items/weapondrop1.wav"); // weapon falls to the ground - PRECACHE_SOUND("weapons/generic_reload.wav"); -} - -ItemInfo CBasePlayerItem::m_ItemInfoArray[MAX_WEAPONS]; -AmmoInfo CBasePlayerItem::m_AmmoInfoArray[MAX_AMMO_SLOTS]; - -TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = -{ - DEFINE_FIELD(CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR), - DEFINE_FIELD(CBasePlayerItem, m_pNext, FIELD_CLASSPTR), - DEFINE_FIELD(CBasePlayerItem, m_iId, FIELD_INTEGER), -}; - -IMPLEMENT_SAVERESTORE(CBasePlayerItem, CBaseAnimating) - -TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = -{ - DEFINE_FIELD(CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME), - DEFINE_FIELD(CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME), - DEFINE_FIELD(CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME), - DEFINE_FIELD(CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER), - DEFINE_FIELD(CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER), - DEFINE_FIELD(CBasePlayerWeapon, m_iClip, FIELD_INTEGER), - DEFINE_FIELD(CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER), -}; - -IMPLEMENT_SAVERESTORE(CBasePlayerWeapon, CBasePlayerItem) - -void CBasePlayerItem::SetObjectCollisionBox() -{ - pev->absmin = pev->origin + Vector(-24, -24, 0); - pev->absmax = pev->origin + Vector(24, 24, 16); -} - -// Sets up movetype, size, solidtype for a new weapon. -void CBasePlayerItem::FallInit() -{ - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_BBOX; - - UTIL_SetOrigin(pev, pev->origin); - - // pointsize until it lands on the ground. - UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - - SetTouch(&CBasePlayerItem::DefaultTouch); - SetThink(&CBasePlayerItem::FallThink); - - pev->nextthink = gpGlobals->time + 0.1f; -} - -// FallThink - Items that have just spawned run this think -// to catch them when they hit the ground. Once we're sure -// that the object is grounded, we change its solid type -// to trigger and set it in a large box that helps the -// player get it. -void CBasePlayerItem::FallThink() -{ - pev->nextthink = gpGlobals->time + 0.1f; - - if (pev->flags & FL_ONGROUND) - { - // clatter if we have an owner (i.e., dropped by someone) - // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) - if (!FNullEnt(pev->owner)) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", VOL_NORM, ATTN_NORM, 0, RANDOM_LONG(0, 29) + 95); - } - - // lie flat - pev->angles.x = 0.0f; - pev->angles.z = 0.0f; - - Materialize(); - } -} - -// Materialize - make a CBasePlayerItem visible and tangible -void CBasePlayerItem::Materialize() -{ - if (pev->effects & EF_NODRAW) - { - // changing from invisible state to visible. - if (g_pGameRules->IsMultiplayer()) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", VOL_NORM, ATTN_NORM, 0, 150); - } - - pev->effects &= ~EF_NODRAW; - pev->effects |= EF_MUZZLEFLASH; - } - - pev->solid = SOLID_TRIGGER; - - // link into world. - UTIL_SetOrigin(pev, pev->origin); - SetTouch(&CBasePlayerItem::DefaultTouch); - - if (g_pGameRules->IsMultiplayer()) - { - if (!CanDrop()) - { - SetTouch(nullptr); - } - - SetThink(&CBaseEntity::SUB_Remove); - pev->nextthink = gpGlobals->time + 1.0f; - } - else - { - SetThink(nullptr); - } -} - -// AttemptToMaterialize - the item is trying to rematerialize, -// should it do so now or wait longer? -void CBasePlayerItem::AttemptToMaterialize() -{ - float time = g_pGameRules->FlWeaponTryRespawn(this); - - if (time == 0) - { - Materialize(); - return; - } - - pev->nextthink = gpGlobals->time + time; -} - -// CheckRespawn - a player is taking this weapon, should -// it respawn? -void CBasePlayerItem::CheckRespawn() -{ - switch (g_pGameRules->WeaponShouldRespawn(this)) - { - case GR_WEAPON_RESPAWN_YES: - return; - case GR_WEAPON_RESPAWN_NO: - return; - } -} - -// Respawn - this item is already in the world, but it is -// invisible and intangible. Make it visible and tangible. -CBaseEntity *CBasePlayerItem::Respawn() -{ - // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code - // will decide when to make the weapon visible and touchable. - CBaseEntity *pNewWeapon = CBaseEntity::Create(pev->classname, g_pGameRules->VecWeaponRespawnSpot(this), pev->angles, pev->owner); - - if (pNewWeapon) - { - // invisible for now - pNewWeapon->pev->effects |= EF_NODRAW; - - // no touch - pNewWeapon->SetTouch(nullptr); - pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize); - - DROP_TO_FLOOR(ENT(pev)); - - // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, - // but when it should respawn is based on conditions belonging to the weapon that was taken. - pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime(this); - } - else - { - ALERT(at_console, "Respawn failed to create %s!\n", STRING(pev->classname)); - } - - return pNewWeapon; -} - -// whats going on here is that if the player drops this weapon, they shouldn't take it back themselves -// for a little while. But if they throw it at someone else, the other player should get it immediately. -void CBasePlayerItem::DefaultTouch(CBaseEntity *pOther) -{ - // if it's not a player, ignore - if (!pOther->IsPlayer()) - { - return; - } - - CBasePlayer *pPlayer = static_cast(pOther); - - if (pPlayer->m_bIsVIP - && m_iId != WEAPON_USP - && m_iId != WEAPON_GLOCK18 - && m_iId != WEAPON_P228 - && m_iId != WEAPON_DEAGLE - && m_iId != WEAPON_KNIFE) - { - return; - } - - // can I have this? - if (!g_pGameRules->CanHavePlayerItem(pPlayer, this)) - { - if (gEvilImpulse101) - { - UTIL_Remove(this); - } - - return; - } - - if (pOther->AddPlayerItem(this)) - { - AttachToPlayer(pPlayer); -#ifndef REGAMEDLL_FIXES - SetThink(nullptr); -#endif - EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM); - } - - SUB_UseTargets(pOther, USE_TOGGLE, 0); -} - -void CBasePlayerWeapon::SetPlayerShieldAnim() -{ - if (!m_pPlayer->HasShield()) - return; - - if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); - } - else - { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); - } -} - -void CBasePlayerWeapon::ResetPlayerShieldAnim() -{ - if (m_pPlayer->HasShield()) - { - if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - { - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); - } - } -} - -void CBasePlayerWeapon::EjectBrassLate() -{ - int soundType; - Vector vecUp, vecRight, vecShellVelocity; - - UTIL_MakeVectors(m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle); - - vecUp = RANDOM_FLOAT(100, 150) * gpGlobals->v_up; - vecRight = RANDOM_FLOAT(50, 70) * gpGlobals->v_right; - - vecShellVelocity = (m_pPlayer->pev->velocity + vecRight + vecUp) + gpGlobals->v_forward * 25; - soundType = (m_iId == WEAPON_XM1014 || m_iId == WEAPON_M3) ? 2 : 1; - - EjectBrass(pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -9 + gpGlobals->v_forward * 16, gpGlobals->v_right * -9, - vecShellVelocity, pev->angles.y, m_iShellId, soundType, m_pPlayer->entindex()); -} - -bool CBasePlayerWeapon::ShieldSecondaryFire(int iUpAnim, int iDownAnim) -{ - if (!m_pPlayer->HasShield()) - return false; - - if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) - { - m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; - SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); - m_fMaxSpeed = 250.0f; - m_pPlayer->m_bShieldDrawn = false; - } - else - { - m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; - SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); - Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); - m_fMaxSpeed = 180.0f; - m_pPlayer->m_bShieldDrawn = true; - } - - m_pPlayer->UpdateShieldCrosshair((m_iWeaponState & WPNSTATE_SHIELD_DRAWN) != WPNSTATE_SHIELD_DRAWN); - m_pPlayer->ResetMaxSpeed(); - - m_flNextSecondaryAttack = 0.4f; - m_flNextPrimaryAttack = 0.4f; - m_flTimeWeaponIdle = 0.6f; - - return true; -} - -void CBasePlayerWeapon::KickBack(float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change) -{ - real_t flKickUp; - float flKickLateral; - - if (m_iShotsFired == 1) - { - flKickUp = up_base; - flKickLateral = lateral_base; - } - else - { - flKickUp = m_iShotsFired * up_modifier + up_base; - flKickLateral = m_iShotsFired * lateral_modifier + lateral_base; - } - - m_pPlayer->pev->punchangle.x -= flKickUp; - - if (m_pPlayer->pev->punchangle.x < -up_max) - { - m_pPlayer->pev->punchangle.x = -up_max; - } - - if (m_iDirection == 1) - { - m_pPlayer->pev->punchangle.y += flKickLateral; - - if (m_pPlayer->pev->punchangle.y > lateral_max) - m_pPlayer->pev->punchangle.y = lateral_max; - } - else - { - m_pPlayer->pev->punchangle.y -= flKickLateral; - - if (m_pPlayer->pev->punchangle.y < -lateral_max) - m_pPlayer->pev->punchangle.y = -lateral_max; - } - - if (!RANDOM_LONG(0, direction_change)) - { - m_iDirection = !m_iDirection; - } -} - -void CBasePlayerWeapon::FireRemaining(int &shotsFired, float &shootTime, BOOL bIsGlock) -{ - float nexttime = 0.1f; - if (--m_iClip < 0) - { - m_iClip = 0; - shotsFired = 3; - shootTime = 0; - return; - } - - UTIL_MakeVectors(m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle); - - Vector vecSrc = m_pPlayer->GetGunPosition(); - Vector vecDir; - - int flag; -#ifdef CLIENT_WEAPONS - flag = FEV_NOTHOST; -#else - flag = 0; -#endif - - if (bIsGlock) - { - vecDir = m_pPlayer->FireBullets3(vecSrc, gpGlobals->v_forward, 0.05, 8192, 1, BULLET_PLAYER_9MM, 18, 0.9, m_pPlayer->pev, true, m_pPlayer->random_seed); - --m_pPlayer->ammo_9mm; - - PLAYBACK_EVENT_FULL(flag, m_pPlayer->edict(), m_usFireGlock18, 0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, - int(m_pPlayer->pev->punchangle.x * 10000), int(m_pPlayer->pev->punchangle.y * 10000), m_iClip == 0, FALSE); - } - else - { - - vecDir = m_pPlayer->FireBullets3(vecSrc, gpGlobals->v_forward, m_fBurstSpread, 8192, 2, BULLET_PLAYER_556MM, 30, 0.96, m_pPlayer->pev, false, m_pPlayer->random_seed); - --m_pPlayer->ammo_556nato; - - PLAYBACK_EVENT_FULL(flag, m_pPlayer->edict(), m_usFireFamas, 0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, - int(m_pPlayer->pev->punchangle.x * 10000000), int(m_pPlayer->pev->punchangle.y * 10000000), FALSE, FALSE); - } - - m_pPlayer->pev->effects |= EF_MUZZLEFLASH; - m_pPlayer->SetAnimation(PLAYER_ATTACK1); - - if (++shotsFired != 3) - { - shootTime = gpGlobals->time + nexttime; - } - else - shootTime = 0; -} - -BOOL CanAttack(float attack_time, float curtime, BOOL isPredicted) -{ -#ifdef CLIENT_WEAPONS - if (!isPredicted) -#else - if (1) -#endif - { - return (attack_time <= curtime) ? TRUE : FALSE; - } - else - { - return (attack_time <= 0.0f) ? TRUE : FALSE; - } -} - -bool CBasePlayerWeapon::HasSecondaryAttack() -{ - if (m_pPlayer && m_pPlayer->HasShield()) - { - return true; - } - -#ifdef REGAMEDLL_API - if (CSPlayerWeapon()->m_bHasSecondaryAttack) - { - return true; - } -#endif - - switch (m_iId) - { - case WEAPON_AK47: - case WEAPON_XM1014: - case WEAPON_MAC10: - case WEAPON_ELITE: - case WEAPON_FIVESEVEN: - case WEAPON_MP5N: -#ifdef BUILD_LATEST_FIXES - case WEAPON_UMP45: -#endif - case WEAPON_M249: - case WEAPON_M3: - case WEAPON_TMP: - case WEAPON_DEAGLE: - case WEAPON_P228: - case WEAPON_P90: - case WEAPON_C4: - case WEAPON_GALIL: - return false; - default: - break; - } - - return true; -} - -void CBasePlayerWeapon::HandleInfiniteAmmo() -{ - int nInfiniteAmmo = 0; - -#ifdef REGAMEDLL_API - nInfiniteAmmo = m_pPlayer->CSPlayer()->m_iWeaponInfiniteAmmo; -#endif - - if (!nInfiniteAmmo) - nInfiniteAmmo = static_cast(infiniteAmmo.value); - - if (nInfiniteAmmo == WPNMODE_INFINITE_CLIP && !IsGrenadeWeapon(m_iId)) - { - m_iClip = iMaxClip(); - } - else if ((nInfiniteAmmo == WPNMODE_INFINITE_BPAMMO && -#ifdef REGAMEDLL_API - ((m_pPlayer->CSPlayer()->m_iWeaponInfiniteIds & (1 << m_iId)) || (m_pPlayer->CSPlayer()->m_iWeaponInfiniteIds <= 0 && !IsGrenadeWeapon(m_iId))) -#endif - ) - || (IsGrenadeWeapon(m_iId) && infiniteGrenades.value == 1.0f)) - { - if (pszAmmo1()) - { - m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] = iMaxAmmo1(); - } - - if (pszAmmo2()) - { - m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] = iMaxAmmo2(); - } - } -} - -void CBasePlayerWeapon::ItemPostFrame() -{ - int usableButtons = m_pPlayer->pev->button; - - if (!HasSecondaryAttack()) - { - usableButtons &= ~IN_ATTACK2; - } - - if (m_flGlock18Shoot != 0) - { - FireRemaining(m_iGlock18ShotsFired, m_flGlock18Shoot, TRUE); - } - else if (gpGlobals->time > m_flFamasShoot && m_flFamasShoot != 0) - { - FireRemaining(m_iFamasShotsFired, m_flFamasShoot, FALSE); - } - - // Return zoom level back to previous zoom level before we fired a shot. - // This is used only for the AWP and Scout - if (m_flNextPrimaryAttack <= UTIL_WeaponTimeBase()) - { - if (m_pPlayer->m_bResumeZoom) - { - m_pPlayer->m_iFOV = m_pPlayer->m_iLastZoom; - m_pPlayer->pev->fov = m_pPlayer->m_iFOV; - - if (m_pPlayer->m_iFOV == m_pPlayer->m_iLastZoom) - { - // return the fade level in zoom. - m_pPlayer->m_bResumeZoom = false; - } - } - } - - if (m_pPlayer->m_flEjectBrass != 0 && m_pPlayer->m_flEjectBrass <= gpGlobals->time) - { - m_pPlayer->m_flEjectBrass = 0; - EjectBrassLate(); - } - - if (!(m_pPlayer->pev->button & IN_ATTACK)) - { - m_flLastFireTime = 0; - } - - if (m_pPlayer->HasShield()) - { - if (m_fInReload && (m_pPlayer->pev->button & IN_ATTACK2)) - { - SecondaryAttack(); - m_pPlayer->pev->button &= ~IN_ATTACK2; - m_fInReload = FALSE; - m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase(); - } - } - - if (m_fInReload && m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase()) - { - // complete the reload. - int j = Q_min(iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - - // Add them to the clip - m_iClip += j; - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; - - m_pPlayer->TabulateAmmo(); - m_fInReload = FALSE; - } - - if ((usableButtons & IN_ATTACK2) && CanAttack(m_flNextSecondaryAttack, UTIL_WeaponTimeBase(), UseDecrement()) -#ifdef REGAMEDLL_FIXES - && !m_pPlayer->m_bIsDefusing // In-line: I think it's fine to block secondary attack, when defusing. It's better then blocking speed resets in weapons. -#endif - ) - { - if (pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()]) - { - m_fFireOnEmpty = TRUE; - } - - SecondaryAttack(); - m_pPlayer->pev->button &= ~IN_ATTACK2; - } - else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack(m_flNextPrimaryAttack, UTIL_WeaponTimeBase(), UseDecrement())) - { - if ((m_iClip == 0 && pszAmmo1()) || (iMaxClip() == WEAPON_NOCLIP && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()])) - { - m_fFireOnEmpty = TRUE; - } - - m_pPlayer->TabulateAmmo(); - - // Can't shoot during the freeze period - // Always allow firing in single player - if ((m_pPlayer->m_bCanShoot && g_pGameRules->IsMultiplayer() && !g_pGameRules->IsFreezePeriod() && !m_pPlayer->m_bIsDefusing) || !g_pGameRules->IsMultiplayer()) - { - PrimaryAttack(); - } - } - else if ((m_pPlayer->pev->button & IN_RELOAD) && iMaxClip() != WEAPON_NOCLIP && !m_fInReload && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) - { - // reload when reload is pressed, or if no buttons are down and weapon is empty. - if (m_flFamasShoot == 0 && m_flGlock18Shoot == 0) - { - if (!(m_iWeaponState & WPNSTATE_SHIELD_DRAWN)) - { - // reload when reload is pressed, or if no buttons are down and weapon is empty. - Reload(); - } - } - } - else if (!(usableButtons & (IN_ATTACK | IN_ATTACK2))) - { - // no fire buttons down - - // The following code prevents the player from tapping the firebutton repeatedly - // to simulate full auto and retaining the single shot accuracy of single fire - if (m_bDelayFire) - { - m_bDelayFire = false; - - if (m_iShotsFired > 15) - { - m_iShotsFired = 15; - } - - m_flDecreaseShotsFired = gpGlobals->time + 0.4f; - } - - m_fFireOnEmpty = FALSE; - - // if it's a pistol then set the shots fired to 0 after the player releases a button - if (IsSecondaryWeapon(m_iId)) - { - m_iShotsFired = 0; - } - else - { - if (m_iShotsFired > 0 && m_flDecreaseShotsFired < gpGlobals->time) - { - m_flDecreaseShotsFired = gpGlobals->time + 0.0225f; - m_iShotsFired--; - } - } - - if (!IsUseable() && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) - { -#if 0 - // weapon isn't useable, switch. - if (!(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon(m_pPlayer, this)) - { - m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3f; - return; - } -#endif - } - else - { - if (!(m_iWeaponState & WPNSTATE_SHIELD_DRAWN)) - { - // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing - if (!m_iClip && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) - { - if (m_flFamasShoot == 0 && m_flGlock18Shoot == 0) - { - Reload(); - return; - } - } - } - } - -#ifdef BUILD_LATEST - HandleInfiniteAmmo(); -#endif - - WeaponIdle(); - return; - } - -#ifdef BUILD_LATEST - HandleInfiniteAmmo(); -#endif - - // catch all - if (ShouldWeaponIdle()) - { - WeaponIdle(); - } -} - -void CBasePlayerItem::DestroyItem() -{ - if (m_pPlayer) - { - // if attached to a player, remove. - m_pPlayer->RemovePlayerItem(this); - } - - Kill(); -} - -int CBasePlayerItem::AddToPlayer(CBasePlayer *pPlayer) -{ - m_pPlayer = pPlayer; - - MESSAGE_BEGIN(MSG_ONE, gmsgWeapPickup, nullptr, pPlayer->pev); - WRITE_BYTE(m_iId); - MESSAGE_END(); - - return TRUE; -} - -void CBasePlayerItem::Drop() -{ - SetTouch(nullptr); - SetThink(&CBaseEntity::SUB_Remove); - pev->nextthink = gpGlobals->time + 0.1f; -} - -void CBasePlayerItem::Kill() -{ - SetTouch(nullptr); - SetThink(&CBaseEntity::SUB_Remove); - pev->nextthink = gpGlobals->time + 0.1f; -} - -void CBasePlayerItem::Holster(int skiplocal) -{ - m_pPlayer->pev->viewmodel = 0; - m_pPlayer->pev->weaponmodel = 0; -} - -void CBasePlayerItem::AttachToPlayer(CBasePlayer *pPlayer) -{ - pev->movetype = MOVETYPE_FOLLOW; - pev->solid = SOLID_NOT; - pev->aiment = pPlayer->edict(); - pev->effects = EF_NODRAW; - - // server won't send down to clients if modelindex == 0 - pev->modelindex = 0; - pev->model = 0; - pev->owner = pPlayer->edict(); - -#ifndef REGAMEDLL_FIXES - pev->nextthink = gpGlobals->time + 0.1f; -#else - // Remove think - prevents futher attempts to materialize - pev->nextthink = 0; - SetThink(nullptr); -#endif - - SetTouch(nullptr); -} - -void CBasePlayerWeapon::Spawn() -{ - ItemInfo info; - Q_memset(&info, 0, sizeof(info)); - - if (GetItemInfo(&info)) { - CSPlayerItem()->SetItemInfo(&info); - } - - CSPlayerWeapon()->m_bHasSecondaryAttack = HasSecondaryAttack(); -} - -// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal -int CBasePlayerWeapon::AddDuplicate(CBasePlayerItem *pOriginal) -{ - if (m_iDefaultAmmo) - { - return ExtractAmmo((CBasePlayerWeapon *)pOriginal); - } - else - { - // a dead player dropped this. - return ExtractClipAmmo((CBasePlayerWeapon *)pOriginal); - } -} - -int CBasePlayerWeapon::AddToPlayer(CBasePlayer *pPlayer) -{ - m_pPlayer = pPlayer; - pPlayer->pev->weapons |= (1 << m_iId); - - if (!m_iPrimaryAmmoType) - { - m_iPrimaryAmmoType = pPlayer->GetAmmoIndex(pszAmmo1()); - m_iSecondaryAmmoType = pPlayer->GetAmmoIndex(pszAmmo2()); - } - - if (AddWeapon()) - { - return CBasePlayerItem::AddToPlayer(pPlayer); - } - - return FALSE; -} - -int CBasePlayerWeapon::UpdateClientData(CBasePlayer *pPlayer) -{ - bool bSend = false; - int state = 0; - - if (pPlayer->m_pActiveItem == this) - { - if (pPlayer->m_fOnTarget) - state = WEAPON_IS_ONTARGET; - else - state = 1; - } - - if (!pPlayer->m_fWeapon) - bSend = true; - - if (this == pPlayer->m_pActiveItem || this == pPlayer->m_pClientActiveItem) - { - if (pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem) - bSend = true; - } - - if (m_iClip != m_iClientClip || state != m_iClientWeaponState || pPlayer->m_iFOV != pPlayer->m_iClientFOV) - bSend = true; - - if (bSend) - { - MESSAGE_BEGIN(MSG_ONE, gmsgCurWeapon, nullptr, pPlayer->pev); - WRITE_BYTE(state); - WRITE_BYTE(m_iId); - WRITE_BYTE(m_iClip); - MESSAGE_END(); - - m_iClientClip = m_iClip; - m_iClientWeaponState = state; - pPlayer->m_fWeapon = TRUE; - } - - if (m_pNext) - { - m_pNext->UpdateClientData(pPlayer); - } - - return 1; -} - -void CBasePlayerWeapon::SendWeaponAnim(int iAnim, int skiplocal) -{ - m_pPlayer->pev->weaponanim = iAnim; - -#ifdef CLIENT_WEAPONS - if (skiplocal && ENGINE_CANSKIP(m_pPlayer->edict())) - return; -#endif - - MESSAGE_BEGIN(MSG_ONE, SVC_WEAPONANIM, nullptr, m_pPlayer->pev); - WRITE_BYTE(iAnim); // sequence number - WRITE_BYTE(pev->body); // weaponmodel bodygroup. - MESSAGE_END(); -} - -BOOL CBasePlayerWeapon::AddPrimaryAmmo(int iCount, char *szName, int iMaxClip, int iMaxCarry) -{ - int iIdAmmo; - - if (iMaxClip < 1) - { - m_iClip = -1; - iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMaxCarry); - } - else if (m_iClip == 0) - { - int i; - i = Q_min(m_iClip + iCount, iMaxClip); - m_iClip += i; - - iIdAmmo = m_pPlayer->GiveAmmo(iCount - i, szName, iMaxCarry); - } - else - { - iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMaxCarry); - } - - if (iIdAmmo > 0) - { - m_iPrimaryAmmoType = iIdAmmo; - if (m_pPlayer->HasPlayerItem(this)) - { - // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. - // if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us. - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); - } - } - - return iIdAmmo > 0 ? TRUE : FALSE; -} - -BOOL CBasePlayerWeapon::AddSecondaryAmmo(int iCount, char *szName, int iMax) -{ - int iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMax); - - if (iIdAmmo > 0) - { - m_iSecondaryAmmoType = iIdAmmo; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); - } - - return iIdAmmo > 0 ? TRUE : FALSE; -} - -// IsUseable - this function determines whether or not a -// weapon is useable by the player in its current state. -// (does it have ammo loaded? do I have any ammo for the -// weapon?, etc) -BOOL CBasePlayerWeapon::IsUseable() -{ - if (m_iClip <= 0) - { - if (m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] <= 0 && iMaxAmmo1() != -1) - { - // clip is empty (or nonexistant) and the player has no more ammo of this type. - return FALSE; - } - } - - return TRUE; -} - -BOOL CBasePlayerWeapon::CanDeploy() -{ - return TRUE; -} - -BOOL CBasePlayerWeapon::DefaultDeploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal) -{ - if (!CanDeploy()) - return FALSE; - - m_pPlayer->TabulateAmmo(); - m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); - m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); - model_name = m_pPlayer->pev->viewmodel; - Q_strcpy(m_pPlayer->m_szAnimExtention, szAnimExt); - SendWeaponAnim(iAnim, skiplocal); - - m_pPlayer->m_flNextAttack = 0.75f; - m_flTimeWeaponIdle = 1.5f; - m_flLastFireTime = 0.0f; - m_flDecreaseShotsFired = gpGlobals->time; - - m_pPlayer->m_iFOV = DEFAULT_FOV; - m_pPlayer->pev->fov = DEFAULT_FOV; - m_pPlayer->m_iLastZoom = DEFAULT_FOV; - m_pPlayer->m_bResumeZoom = false; - - return TRUE; -} - -void CBasePlayerWeapon::ReloadSound() -{ - CBasePlayer *pPlayer = nullptr; - while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) - { - if (pPlayer->IsDormant()) - break; - - if (pPlayer == m_pPlayer) - continue; - - float distance = (m_pPlayer->pev->origin - pPlayer->pev->origin).Length(); - if (distance <= MAX_DIST_RELOAD_SOUND) - { - MESSAGE_BEGIN(MSG_ONE, gmsgReloadSound, nullptr, pPlayer->pev); - WRITE_BYTE(int((1.0f - (distance / MAX_DIST_RELOAD_SOUND)) * 255.0f)); - if (!Q_strcmp(STRING(pev->classname), "weapon_m3") || !Q_strcmp(STRING(pev->classname), "weapon_xm1014")) - WRITE_BYTE(0); - else - WRITE_BYTE(1); - MESSAGE_END(); - } - } -} - -int CBasePlayerWeapon::DefaultReload(int iClipSize, int iAnim, float fDelay) -{ - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - return FALSE; - - int j = Q_min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - if (!j) - { - return FALSE; - } - - m_pPlayer->m_flNextAttack = fDelay; - - ReloadSound(); - SendWeaponAnim(iAnim, UseDecrement() ? 1 : 0); - - m_fInReload = TRUE; - m_flTimeWeaponIdle = fDelay + 0.5f; - - return TRUE; -} - -BOOL CBasePlayerWeapon::PlayEmptySound() -{ - if (m_iPlayEmptySound) - { - switch (m_iId) - { - case WEAPON_USP: - case WEAPON_GLOCK18: - case WEAPON_P228: - case WEAPON_DEAGLE: - case WEAPON_ELITE: - case WEAPON_FIVESEVEN: - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/dryfire_pistol.wav", 0.8, ATTN_NORM); - break; - default: - EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/dryfire_rifle.wav", 0.8, ATTN_NORM); - break; - } - } - - return FALSE; -} - -void CBasePlayerWeapon::ResetEmptySound() -{ - m_iPlayEmptySound = 1; -} - -int CBasePlayerWeapon::PrimaryAmmoIndex() -{ - return m_iPrimaryAmmoType; -} - -int CBasePlayerWeapon::SecondaryAmmoIndex() -{ - return -1; -} - -void CBasePlayerWeapon::Holster(int skiplocal) -{ - // cancel any reload in progress. - m_fInReload = FALSE; - m_pPlayer->pev->viewmodel = 0; - m_pPlayer->pev->weaponmodel = 0; -} - -// called by the new item with the existing item as parameter -// -// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for -// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it. -// if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in -// the weapon clip comes along. -int CBasePlayerWeapon::ExtractAmmo(CBasePlayerWeapon *pWeapon) -{ - int res = 0; - if (pszAmmo1()) - { - // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, - // we only get the ammo in the weapon's clip, which is what we want. - res = pWeapon->AddPrimaryAmmo(m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1()); - m_iDefaultAmmo = 0; - } - - if (pszAmmo2()) - { - res = AddSecondaryAmmo(0, (char *)pszAmmo2(), iMaxAmmo2()); - } - - return res; -} - -// called by the new item's class with the existing item as parameter -int CBasePlayerWeapon::ExtractClipAmmo(CBasePlayerWeapon *pWeapon) -{ - int iAmmo; - if (m_iClip == WEAPON_NOCLIP) - { - // guns with no clips always come empty if they are second-hand - iAmmo = 0; - } - else - { - iAmmo = m_iClip; - } - - return pWeapon->m_pPlayer->GiveAmmo(iAmmo, (char *)pszAmmo1(), iMaxAmmo1()); -} - -// RetireWeapon - no more ammo for this gun, put it away. -void CBasePlayerWeapon::RetireWeapon() -{ - // first, no viewmodel at all. - m_pPlayer->pev->viewmodel = iStringNull; - m_pPlayer->pev->weaponmodel = iStringNull; - - g_pGameRules->GetNextBestWeapon(m_pPlayer, this); -} - -// GetNextAttackDelay - An accurate way of calcualting the next attack time. -float CBasePlayerWeapon::GetNextAttackDelay(float delay) -{ -#ifndef REGAMEDLL_FIXES - if (m_flLastFireTime == 0.0f || m_flNextPrimaryAttack == -1.0f) - { - // At this point, we are assuming that the client has stopped firing - // and we are going to reset our book keeping variables. - m_flPrevPrimaryAttack = delay; - m_flLastFireTime = gpGlobals->time; - } -#endif - -#ifdef REGAMEDLL_BUILD_6153 - - // TODO: Build 6xxx - // at build 6153 beta this removed - // maybe it was initiated due to the delay of the shot - - // calculate the time between this shot and the previous - float flTimeBetweenFires = gpGlobals->time - m_flLastFireTime; - float flCreep = 0.0f; - - if (flTimeBetweenFires > 0) - { - flCreep = flTimeBetweenFires - m_flPrevPrimaryAttack; - } - - float flNextAttack = UTIL_WeaponTimeBase() + delay - flCreep; -#else - float flNextAttack = UTIL_WeaponTimeBase() + delay; -#endif - - // save the last fire time - m_flLastFireTime = gpGlobals->time; - - // we need to remember what the m_flNextPrimaryAttack time is set to for each shot, - // store it as m_flPrevPrimaryAttack. - m_flPrevPrimaryAttack = flNextAttack - UTIL_WeaponTimeBase(); - - return flNextAttack; -} - -// true - keep the amount of bpammo -// false - let take away bpammo -void CBasePlayerWeapon::InstantReload(bool bCanRefillBPAmmo) -{ - // if you already reload - //if (m_fInReload) - // return; - - if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) - return; - - m_fInReload = FALSE; - m_pPlayer->m_flNextAttack = 0; - - // complete the reload. - int j = Q_min(iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); - if (j == 0) - return; - - // Add them to the clip - m_iClip += j; - - if (!bCanRefillBPAmmo) { - m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; - } - - m_pPlayer->TabulateAmmo(); -} - -TYPEDESCRIPTION CWeaponBox::m_SaveData[] = -{ - DEFINE_ARRAY(CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS), - DEFINE_ARRAY(CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS), - DEFINE_ARRAY(CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES), - DEFINE_FIELD(CWeaponBox, m_cAmmoTypes, FIELD_INTEGER), -}; - -LINK_ENTITY_TO_CLASS(weaponbox, CWeaponBox, CCSWeaponBox) -IMPLEMENT_SAVERESTORE(CWeaponBox, CBaseEntity) - -void CWeaponBox::Precache() -{ - PRECACHE_MODEL("models/w_weaponbox.mdl"); -} - -void CWeaponBox::KeyValue(KeyValueData *pkvd) -{ - if (m_cAmmoTypes >= MAX_AMMO_SLOTS) - { - ALERT(at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS); - return; - } - - PackAmmo(ALLOC_STRING(pkvd->szKeyName), Q_atoi(pkvd->szValue)); - - // count this new ammo type. - m_cAmmoTypes++; - pkvd->fHandled = TRUE; -} - -void CWeaponBox::BombThink() -{ - if (!m_bIsBomb) - return; - - CBasePlayer *pPlayer = nullptr; - while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) - { - if (FNullEnt(pPlayer->edict())) - break; - - if (!pPlayer->IsPlayer() || pPlayer->IsDormant()) - continue; - - CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pPlayer->pev); - - if (pTempPlayer->pev->deadflag == DEAD_NO && pTempPlayer->m_iTeam == TERRORIST) - { - MESSAGE_BEGIN(MSG_ONE, gmsgBombDrop, nullptr, pTempPlayer->edict()); - WRITE_COORD(pev->origin.x); - WRITE_COORD(pev->origin.y); - WRITE_COORD(pev->origin.z); - WRITE_BYTE(BOMB_FLAG_DROPPED); - MESSAGE_END(); - } - } - - pev->nextthink = gpGlobals->time + 1.0f; -} - -LINK_HOOK_CLASS_VOID_CHAIN(CWeaponBox, SetModel, (const char *pszModelName), pszModelName) - -void CWeaponBox::__API_HOOK(SetModel)(const char *pszModelName) -{ - SET_MODEL(ENT(pev), pszModelName); -} - -void CWeaponBox::Spawn() -{ - Precache(); - - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_TRIGGER; - - m_bIsBomb = false; - - UTIL_SetSize(pev, g_vecZero, g_vecZero); - SET_MODEL(ENT(pev), "models/w_weaponbox.mdl"); -} - -// The think function that removes the box from the world. -void CWeaponBox::Kill() -{ - // destroy the weapons - for (int i = 0; i < MAX_ITEM_TYPES; i++) - { - CBasePlayerItem *pWeapon = m_rgpPlayerItems[i]; - while (pWeapon) - { - pWeapon->SetThink(&CBaseEntity::SUB_Remove); - pWeapon->pev->nextthink = gpGlobals->time + 0.1f; - pWeapon = pWeapon->m_pNext; - } - } - - // remove the box - UTIL_Remove(this); -} - -// Try to add my contents to the toucher if the toucher is a player. -void CWeaponBox::Touch(CBaseEntity *pOther) -{ - if (!(pev->flags & FL_ONGROUND)) - { - return; - } - - if (!pOther->IsPlayer()) - { - // only players may touch a weaponbox. - return; - } - - if (!pOther->IsAlive()) - { - // no dead guys. - return; - } - - CBasePlayer *pPlayer = static_cast(pOther); - - if (pPlayer->m_bIsVIP || pPlayer->m_bShieldDrawn) - return; - - pPlayer->OnTouchingWeapon(this); - - bool bRemove = true; - bool bEmitSound = false; - - // go through my weapons and try to give the usable ones to the player. - // it's important the the player be given ammo first, so the weapons code doesn't refuse - // to deploy a better weapon that the player may pick up because he has no ammo for it. - for (int i = 0; i < MAX_ITEM_TYPES; i++) - { - if (!m_rgpPlayerItems[i]) - continue; - - CBasePlayerItem *pItem = m_rgpPlayerItems[i]; - - // have at least one weapon in this slot - while (pItem) - { - if ((pPlayer->HasShield() && pItem->m_iId == WEAPON_ELITE) - || (pPlayer->IsBot() && TheCSBots() && !TheCSBots()->IsWeaponUseable(pItem))) - { - return; - } - -#ifdef REGAMEDLL_ADD - if (pPlayer->HasRestrictItem((pItem->m_iId == WEAPON_SHIELDGUN) ? ITEM_SHIELDGUN : (ItemID)pItem->m_iId, ITEM_TYPE_TOUCHED)) - return; -#endif - - if (FClassnameIs(pItem->pev, "weapon_c4")) - { -#ifdef REGAMEDLL_FIXES - if (pPlayer->m_iTeam != TERRORIST) - return; -#else - if (pPlayer->m_iTeam != TERRORIST || pPlayer->pev->deadflag != DEAD_NO) - return; -#endif - - if (pPlayer->m_bShowHints && !(pPlayer->m_flDisplayHistory & DHF_BOMB_RETRIEVED)) - { - pPlayer->m_flDisplayHistory |= DHF_BOMB_RETRIEVED; - pPlayer->HintMessage("#Hint_you_have_the_bomb"); - } - else - ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "#Got_bomb"); - - UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Got_The_Bomb\"\n", - STRING(pPlayer->pev->netname), - GETPLAYERUSERID(pPlayer->edict()), - GETPLAYERAUTHID(pPlayer->edict())); - - g_pGameRules->m_bBombDropped = FALSE; - - MESSAGE_BEGIN(MSG_SPEC, SVC_DIRECTOR); - WRITE_BYTE(9); - WRITE_BYTE(DRC_CMD_EVENT); - WRITE_SHORT(ENTINDEX(pPlayer->edict())); - WRITE_SHORT(ENTINDEX(edict())); - WRITE_LONG(6); - MESSAGE_END(); - - pPlayer->m_bHasC4 = true; - pPlayer->SetBombIcon(FALSE); - pPlayer->pev->body = 1; - - CBaseEntity *pEntity = nullptr; - while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) - { - if (FNullEnt(pEntity->edict())) - break; - - if (!pEntity->IsPlayer()) - continue; - - if (pEntity->pev->flags == FL_DORMANT) - continue; - - CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); - - if (pTempPlayer->pev->deadflag == DEAD_NO && pTempPlayer->m_iTeam == TERRORIST) - { - if (pTempPlayer != pPlayer) - { - ClientPrint(pTempPlayer->pev, HUD_PRINTCENTER, "#Game_bomb_pickup", STRING(pPlayer->pev->netname)); - } - - MESSAGE_BEGIN(MSG_ONE, gmsgBombPickup, nullptr, pTempPlayer->pev); - MESSAGE_END(); - } - } - - if (TheCSBots()) - { - TheCSBots()->SetLooseBomb(nullptr); - } - - if (TheBots) - { - TheBots->OnEvent(EVENT_BOMB_PICKED_UP, pPlayer); - } - } - - if (i >= PRIMARY_WEAPON_SLOT && i <= PISTOL_SLOT && pPlayer->m_rgpPlayerItems[i]) - { - // ... - } - else if (i == GRENADE_SLOT) - { - CBasePlayerWeapon *pGrenade = static_cast(m_rgpPlayerItems[i]); - if (pGrenade && pGrenade->IsWeapon()) - { - int playerGrenades = pPlayer->m_rgAmmo[pGrenade->m_iPrimaryAmmoType]; - -#ifdef REGAMEDLL_FIXES - auto info = GetWeaponInfo(pGrenade->m_iId); - if (info && playerGrenades < info->maxRounds) - { - auto pNext = m_rgpPlayerItems[i]->m_pNext; - if (pPlayer->AddPlayerItem(pItem)) - { - pItem->AttachToPlayer(pPlayer); - bEmitSound = true; - } - - // unlink this weapon from the box - m_rgpPlayerItems[i] = pItem = pNext; - continue; - } -#else - - int maxGrenades = 0; - const char *grenadeName = nullptr; - - switch (pGrenade->m_iId) - { - case WEAPON_HEGRENADE: - grenadeName = "weapon_hegrenade"; - maxGrenades = 1; - break; - case WEAPON_SMOKEGRENADE: - grenadeName = "weapon_smokegrenade"; - maxGrenades = 1; - break; - case WEAPON_FLASHBANG: - grenadeName = "weapon_flashbang"; - maxGrenades = 2; - break; - } - - if (playerGrenades < maxGrenades && grenadeName) - { - // CRITICAL BUG: since gives a new entity using GiveNamedItem, - // but the entity is packaged in a weaponbox still exists and will never used or removed. It's leak! - // How reproduced: Drop your grenade on the ground, check output of command `entity_dump`, - // there we will see only get one grenade. Next step - pick it up, do check again `entity_dump`, - // but this time we'll see them x2. - - bEmitSound = true; - pPlayer->GiveNamedItem(grenadeName); - - // unlink this weapon from the box - pItem = m_rgpPlayerItems[i]->m_pNext; - m_rgpPlayerItems[i] = pItem; - continue; - } -#endif - - } - } - else if (pPlayer->HasShield() && i == PRIMARY_WEAPON_SLOT) - { - // ... - } - else - { - auto pNext = m_rgpPlayerItems[i]->m_pNext; - if (pPlayer->AddPlayerItem(pItem)) - { - pItem->AttachToPlayer(pPlayer); - bEmitSound = true; - } - - // unlink this weapon from the box - m_rgpPlayerItems[i] = pItem = pNext; - continue; - } - - bRemove = false; - pItem = m_rgpPlayerItems[i]->m_pNext; - } - } - - if (bRemove) - { - // dole out ammo - for (int n = 0; n < MAX_AMMO_SLOTS; n++) - { - if (!FStringNull(m_rgiszAmmo[n])) - { - // there's some ammo of this type. - pPlayer->GiveAmmo(m_rgAmmo[n], (char *)STRING(m_rgiszAmmo[n]), MaxAmmoCarry(m_rgiszAmmo[n])); - - // now empty the ammo from the weaponbox since we just gave it to the player - m_rgiszAmmo[n] = iStringNull; - m_rgAmmo[n] = 0; - } - } - } - - if (bEmitSound) - { - EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM); - } - - if (bRemove) - { - SetTouch(nullptr); - UTIL_Remove(this); - } -} - -// Add this weapon to the box -BOOL CWeaponBox::PackWeapon(CBasePlayerItem *pWeapon) -{ - // is one of these weapons already packed in this box? - if (HasWeapon(pWeapon)) - { - // box can only hold one of each weapon type - return FALSE; - } - - if (pWeapon->m_pPlayer) - { - if (pWeapon->m_pPlayer->m_pActiveItem == pWeapon) - { - pWeapon->Holster(); - } - - if (!pWeapon->m_pPlayer->RemovePlayerItem(pWeapon)) - { - // failed to unhook the weapon from the player! - return FALSE; - } - } - - int iWeaponSlot = pWeapon->iItemSlot(); - if (m_rgpPlayerItems[iWeaponSlot]) - { - // there's already one weapon in this slot, so link this into the slot's column - pWeapon->m_pNext = m_rgpPlayerItems[iWeaponSlot]; - m_rgpPlayerItems[iWeaponSlot] = pWeapon; - } - else - { - // first weapon we have for this slot - m_rgpPlayerItems[iWeaponSlot] = pWeapon; - pWeapon->m_pNext = nullptr; - } - - // never respawn - pWeapon->pev->spawnflags |= SF_NORESPAWN; - pWeapon->pev->movetype = MOVETYPE_NONE; - pWeapon->pev->solid = SOLID_NOT; - pWeapon->pev->effects = EF_NODRAW; - pWeapon->pev->modelindex = 0; - pWeapon->pev->model = 0; - pWeapon->pev->owner = ENT(pev); - pWeapon->SetThink(nullptr); - pWeapon->SetTouch(nullptr); - pWeapon->m_pPlayer = nullptr; - - return TRUE; -} - -BOOL CWeaponBox::PackAmmo(string_t iszName, int iCount) -{ - if (!iszName) - { - // error here - ALERT(at_console, "NULL String in PackAmmo!\n"); - return FALSE; - } - - int iMaxCarry = MaxAmmoCarry(iszName); - if (iMaxCarry != -1 && iCount > 0) - { - GiveAmmo(iCount, (char *)STRING(iszName), iMaxCarry); - return TRUE; - } - - return FALSE; -} - -int CWeaponBox::GiveAmmo(int iCount, char *szName, int iMax, int *pIndex) -{ - int i; - for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull(m_rgiszAmmo[i]); i++) - { - if (!Q_stricmp(szName, STRING(m_rgiszAmmo[i]))) - { - if (pIndex) - *pIndex = i; - - int iAdd = Q_min(iCount, iMax - m_rgAmmo[i]); - if (iCount == 0 || iAdd > 0) - { - m_rgAmmo[i] += iAdd; - return i; - } - - return -1; - } - } - - if (i < MAX_AMMO_SLOTS) - { - if (pIndex) - *pIndex = i; - - m_rgiszAmmo[i] = MAKE_STRING(szName); - m_rgAmmo[i] = iCount; - - return i; - } - - ALERT(at_console, "out of named ammo slots\n"); - return i; -} - -// Is a weapon of this type already packed in this box? -BOOL CWeaponBox::HasWeapon(CBasePlayerItem *pCheckItem) -{ - CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; - while (pItem) - { - if (FClassnameIs(pItem->pev, pCheckItem->pev->classname)) { - return TRUE; - } - - pItem = pItem->m_pNext; - } - - return FALSE; -} - -// Is there anything in this box? -BOOL CWeaponBox::IsEmpty() -{ - int i; - for (i = 0; i < MAX_ITEM_TYPES; i++) - { - if (m_rgpPlayerItems[i]) - { - return FALSE; - } - } - - for (i = 0; i < MAX_AMMO_SLOTS; i++) - { - if (m_rgiszAmmo[i]) - { - // still have a bit of this type of ammo - return FALSE; - } - } - - return TRUE; -} - -void CWeaponBox::SetObjectCollisionBox() -{ - pev->absmin = pev->origin + Vector(-16, -16, 0); - pev->absmax = pev->origin + Vector(16, 16, 16); -} - -char *CArmoury::m_ItemModels[] = { - "models/w_mp5.mdl", - "models/w_tmp.mdl", - "models/w_p90.mdl", - "models/w_mac10.mdl", - "models/w_ak47.mdl", - "models/w_sg552.mdl", - "models/w_m4a1.mdl", - "models/w_aug.mdl", - "models/w_scout.mdl", - "models/w_g3sg1.mdl", - "models/w_awp.mdl", - "models/w_m3.mdl", - "models/w_xm1014.mdl", - "models/w_m249.mdl", - "models/w_flashbang.mdl", - "models/w_hegrenade.mdl", - "models/w_kevlar.mdl", - "models/w_assault.mdl", - "models/w_smokegrenade.mdl", - -#ifdef REGAMEDLL_ADD - "models/w_shield.mdl", - "models/w_famas.mdl", - "models/w_sg550.mdl", - "models/w_galil.mdl", - "models/w_ump45.mdl", - "models/w_glock18.mdl", - "models/w_usp.mdl", - "models/w_elite.mdl", - "models/w_fiveseven.mdl", - "models/w_p228.mdl", - "models/w_deagle.mdl" -#endif - -}; - -void CArmoury::Spawn() -{ - Precache(); - - pev->movetype = MOVETYPE_TOSS; - pev->solid = SOLID_TRIGGER; - - UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); - UTIL_SetOrigin(pev, pev->origin); - - SetTouch(&CArmoury::ArmouryTouch); - - if (m_iItem < ARRAYSIZE(m_ItemModels)) - { - SET_MODEL(ENT(pev), m_ItemModels[m_iItem]); - } - - if (m_iCount <= 0) - { - m_iCount = 1; - } - -#ifdef REGAMEDLL_FIXES - // Cache the placed origin of source point - pev->oldorigin = pev->origin; -#endif - - m_bAlreadyCounted = false; - m_iInitialCount = m_iCount; -} - -void CArmoury::Restart() -{ -#ifdef REGAMEDLL_FIXES - // This code refers to the mode of Escape. (Because there is relationship to the team Terrorists) - if (CSGameRules()->m_bMapHasEscapeZone) -#endif - { - if (m_iItem == ARMOURY_FLASHBANG || m_iItem == ARMOURY_HEGRENADE) - { - if (!m_bAlreadyCounted) - { - m_bAlreadyCounted = true; - CSGameRules()->m_iTotalGrenadeCount += m_iInitialCount; - m_iCount = m_iInitialCount; - Draw(); - return; - } - - float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalGrenadeCount) * real_t(CSGameRules()->m_iNumTerrorist) * 1.75; - m_iCount = int(flRatio); - } - else if (m_iItem == ARMOURY_KEVLAR || m_iItem == ARMOURY_ASSAULT) - { - if (!m_bAlreadyCounted) - { - m_bAlreadyCounted = true; - CSGameRules()->m_iTotalArmourCount += m_iInitialCount; - m_iCount = m_iInitialCount; - Draw(); - return; - } - - float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalArmourCount) * real_t(CSGameRules()->m_iNumTerrorist); - m_iCount = int(flRatio); - } - else - { - if (!m_bAlreadyCounted) - { - m_bAlreadyCounted = true; - CSGameRules()->m_iTotalGunCount += m_iInitialCount; - m_iCount = m_iInitialCount; - Draw(); - return; - } - - float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalGunCount) * real_t(CSGameRules()->m_iNumTerrorist) * 0.85; - m_iCount = int(flRatio); - } - } - -#ifdef REGAMEDLL_FIXES - else - { - m_iCount = m_iInitialCount; - } -#endif - - if (m_iCount < 1) - m_iCount = 1; - - Draw(); - -#ifdef REGAMEDLL_FIXES - // Restored origin from the cache - UTIL_SetOrigin(pev, pev->oldorigin); - DROP_TO_FLOOR(edict()); -#endif -} - -void CArmoury::Precache() -{ - if (m_iItem < ARRAYSIZE(m_ItemModels)) - { - PRECACHE_MODEL(m_ItemModels[m_iItem]); - } -} - -void CArmoury::Draw() -{ - pev->effects &= ~EF_NODRAW; - -#ifdef REGAMEDLL_FIXES - pev->solid = SOLID_TRIGGER; -#endif -} - -void CArmoury::Hide() -{ - pev->effects |= EF_NODRAW; - -#ifdef REGAMEDLL_FIXES - // more not to touch with the world. - pev->solid = SOLID_NOT; -#endif -} - -struct ArmouryItemStruct -{ - const char *entityName; - char *ammoName; - int giveAmount; - int maxRounds; -} armouryItemInfo[] = { - { "weapon_mp5navy", "9mm", 60, MAX_AMMO_9MM }, // ARMOURY_MP5NAVY - { "weapon_tmp", "9mm", 60, MAX_AMMO_9MM }, // ARMOURY_TMP - { "weapon_p90", "57mm", 50, MAX_AMMO_57MM }, // ARMOURY_P90 - { "weapon_mac10", "45acp", 60, MAX_AMMO_45ACP }, // ARMOURY_MAC10 - { "weapon_ak47", "762Nato", 60, MAX_AMMO_762NATO }, // ARMOURY_AK47 - { "weapon_sg552", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_SG552 - { "weapon_m4a1", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_M4A1 - { "weapon_aug", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_AUG - { "weapon_scout", "762Nato", 30, MAX_AMMO_762NATO }, // ARMOURY_SCOUT - { "weapon_g3sg1", "762Nato", 30, MAX_AMMO_762NATO }, // ARMOURY_G3SG1 - { "weapon_awp", "338Magnum", 20, MAX_AMMO_338MAGNUM }, // ARMOURY_AWP - { "weapon_m3", "buckshot", 24, MAX_AMMO_BUCKSHOT }, // ARMOURY_M3 - { "weapon_xm1014", "buckshot", 24, MAX_AMMO_BUCKSHOT }, // ARMOURY_XM1014 - { "weapon_m249", "556NatoBox", 60, MAX_AMMO_556NATOBOX }, // ARMOURY_M249 - { nullptr, nullptr, 0, 0 }, // ARMOURY_FLASHBANG - { nullptr, nullptr, 0, 0 }, // ARMOURY_HEGRENADE - { nullptr, nullptr, 0, 0 }, // ARMOURY_KEVLAR - { nullptr, nullptr, 0, 0 }, // ARMOURY_ASSAULT - { nullptr, nullptr, 0, 0 }, // ARMOURY_SMOKEGRENADE - { nullptr, nullptr, 0, 0 }, // ARMOURY_SHIELD - { "weapon_famas", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_FAMAS - { "weapon_sg550", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_SG550 - { "weapon_galil", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_GALIL - { "weapon_ump45", "45acp", 100, MAX_AMMO_45ACP }, // ARMOURY_UMP45 - { "weapon_glock18", "9mm", 120, MAX_AMMO_9MM }, // ARMOURY_GLOCK18 - { "weapon_usp", "45acp", 100, MAX_AMMO_45ACP }, // ARMOURY_USP - { "weapon_elite", "9mm", 120, MAX_AMMO_9MM }, // ARMOURY_ELITE - { "weapon_fiveseven", "57mm", 100, MAX_AMMO_57MM }, // ARMOURY_FIVESEVEN - { "weapon_p228", "357SIG", 52, MAX_AMMO_357SIG }, // ARMOURY_P228 - { "weapon_deagle", "50AE", 35, MAX_AMMO_50AE }, // ARMOURY_DEAGLE -}; - -void CArmoury::ArmouryTouch(CBaseEntity *pOther) -{ - if (!pOther->IsPlayer()) - return; - - CBasePlayer *pToucher = static_cast(pOther); - - if (pToucher->m_bIsVIP) - return; - -#ifdef REGAMEDLL_ADD - if (pToucher->HasRestrictItem(GetItemIdByArmoury(m_iItem), ITEM_TYPE_TOUCHED)) - return; -#endif - -#ifdef REGAMEDLL_FIXES - if (pToucher->IsBot() && TheCSBots() && !TheCSBots()->IsWeaponUseable(m_iItem)) - return; -#endif - - // primary weapons - if (m_iCount > 0 && (m_iItem <= ARMOURY_M249 -#ifdef REGAMEDLL_ADD - || (m_iItem >= ARMOURY_FAMAS && m_iItem <= ARMOURY_UMP45) -#endif -)) - { - if (pToucher->m_bHasPrimary) - return; - - m_iCount--; - auto item = &armouryItemInfo[m_iItem]; - -#ifdef REGAMEDLL_FIXES - pToucher->GiveNamedItemEx(item->entityName); -#else - pToucher->GiveNamedItem(item->entityName); -#endif - - pToucher->GiveAmmo(item->giveAmount, item->ammoName, item->maxRounds); - } -#ifdef REGAMEDLL_ADD - // secondary weapons (pistols) - else if (m_iCount > 0 && m_iItem >= ARMOURY_GLOCK18) - { - if (pToucher->m_rgpPlayerItems[PISTOL_SLOT]) - return; - - if (pToucher->HasShield() && m_iItem == ARMOURY_ELITE) - return; - - m_iCount--; - auto item = &armouryItemInfo[m_iItem]; - - pToucher->GiveNamedItemEx(item->entityName); - pToucher->GiveAmmo(item->giveAmount, item->ammoName, item->maxRounds); - } -#endif - // items & grenades - else if (m_iCount > 0 && m_iItem >= ARMOURY_FLASHBANG) - { - switch (m_iItem) - { - case ARMOURY_FLASHBANG: - { - if (pToucher->AmmoInventory(AMMO_FLASHBANG) >= MaxAmmoCarry(WEAPON_FLASHBANG)) - return; - - pToucher->GiveNamedItem("weapon_flashbang"); - m_iCount--; - break; - } - case ARMOURY_HEGRENADE: - { - if (pToucher->AmmoInventory(AMMO_HEGRENADE) >= MaxAmmoCarry(WEAPON_HEGRENADE)) - return; - - pToucher->GiveNamedItem("weapon_hegrenade"); - m_iCount--; - break; - } - case ARMOURY_KEVLAR: - { - if (pToucher->m_iKevlar == ARMOR_KEVLAR) - return; - - pToucher->GiveNamedItem("item_kevlar"); - m_iCount--; - break; - } - case ARMOURY_ASSAULT: - { - if (pToucher->m_iKevlar == ARMOR_VESTHELM) - return; - - pToucher->GiveNamedItem("item_assaultsuit"); - m_iCount--; - break; - } - case ARMOURY_SMOKEGRENADE: - { - if (pToucher->AmmoInventory(AMMO_SMOKEGRENADE) >= MaxAmmoCarry(WEAPON_SMOKEGRENADE)) - return; - - pToucher->GiveNamedItem("weapon_smokegrenade"); - m_iCount--; - break; - } -#ifdef REGAMEDLL_ADD - case ARMOURY_SHIELD: - { - if (pToucher->m_bHasPrimary || (pToucher->m_rgpPlayerItems[PISTOL_SLOT] && pToucher->GetItemById(WEAPON_ELITE))) - return; - - pToucher->GiveNamedItemEx("weapon_shield"); - m_iCount--; - break; - } -#endif - } - } - - if (!m_iCount) - Hide(); -} - -void CArmoury::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "item")) - { - m_iItem = (ArmouryItemPack)Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "count")) - { - m_iCount = Q_atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CBaseEntity::KeyValue(pkvd); - } -} - -#ifdef REGAMEDLL_FIXES -void CArmoury::SetObjectCollisionBox() -{ - pev->absmin = pev->origin + Vector(-16, -16, 0); - pev->absmax = pev->origin + Vector(16, 16, 16); -} -#endif - -LINK_ENTITY_TO_CLASS(armoury_entity, CArmoury, CCSArmoury) - -#ifdef REGAMEDLL_API -#define m_ItemInfoEx CSPlayerItem()->m_ItemInfo -#else -#define m_ItemInfoEx m_ItemInfoArray[m_iId] -#endif - -const char *CBasePlayerItem::pszAmmo1() const -{ - return m_ItemInfoEx.pszAmmo1; -} - -int CBasePlayerItem::iMaxAmmo1() const -{ - return m_ItemInfoEx.iMaxAmmo1; -} - -const char *CBasePlayerItem::pszAmmo2() const -{ - return m_ItemInfoEx.pszAmmo2; -} - -int CBasePlayerItem::iMaxAmmo2() const -{ - return m_ItemInfoEx.iMaxAmmo2; -} - -const char *CBasePlayerItem::pszName() const -{ - return m_ItemInfoEx.pszName; -} - -int CBasePlayerItem::iMaxClip() const -{ - return m_ItemInfoEx.iMaxClip; -} - -int CBasePlayerItem::iWeight() const -{ - return m_ItemInfoEx.iWeight; -} - -int CBasePlayerItem::iFlags() const -{ - return m_ItemInfoEx.iFlags; -} +#include "precompiled.h" + +short g_sModelIndexLaser; // holds the index for the laser beam +short g_sModelIndexLaserDot; // holds the index for the laser beam dot +short g_sModelIndexFireball; // holds the index for the fireball +short g_sModelIndexSmoke; // holds the index for the smoke cloud +short g_sModelIndexWExplosion; // holds the index for the underwater explosion +short g_sModelIndexBubbles; // holds the index for the bubbles model +short g_sModelIndexBloodDrop; // holds the sprite index for the initial blood +short g_sModelIndexBloodSpray; // holds the sprite index for splattered blood +short g_sModelIndexSmokePuff; +short g_sModelIndexFireball2; +short g_sModelIndexFireball3; +short g_sModelIndexFireball4; +short g_sModelIndexRadio; + +short int g_sModelIndexCTGhost; +short int g_sModelIndexTGhost; +short int g_sModelIndexC4Glow; + +int giAmmoIndex; + +MULTIDAMAGE gMultiDamage; + +// Pass in a name and this function will tell +// you the maximum amount of that type of ammunition that a player can carry. +int MaxAmmoCarry(const char *szName) +{ + for (int i = 0; i < MAX_WEAPONS; i++) + { + ItemInfo *info = &CBasePlayerItem::m_ItemInfoArray[i]; + if (info->pszAmmo1 && !Q_stricmp(szName, info->pszAmmo1)) + { + return info->iMaxAmmo1; + } + + if (info->pszAmmo2 && !Q_stricmp(szName, info->pszAmmo2)) + { + return info->iMaxAmmo2; + } + } + + ALERT(at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", szName); + return -1; +} + +int MaxAmmoCarry(WeaponIdType weaponId) +{ + return CBasePlayerItem::m_ItemInfoArray[weaponId].iMaxAmmo1; +} + +// Resets the global multi damage accumulator +void ClearMultiDamage() +{ + gMultiDamage.pEntity = nullptr; + gMultiDamage.amount = 0; + gMultiDamage.type = 0; +} + +// Inflicts contents of global multi damage register on gMultiDamage.pEntity +void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker) +{ + if (!gMultiDamage.pEntity) + return; + + gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type); + +} + +void AddMultiDamage(entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType) +{ + if (!pEntity) + return; + + gMultiDamage.type |= bitsDamageType; + + if (pEntity != gMultiDamage.pEntity) + { + // UNDONE: wrong attacker! + ApplyMultiDamage(pevInflictor, pevInflictor); + gMultiDamage.pEntity = pEntity; + gMultiDamage.amount = 0; + } + + gMultiDamage.amount += flDamage; +} + +void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) +{ + UTIL_BloodDrips(vecSpot, g_vecAttackDir, bloodColor, int(flDamage)); +} + +NOXREF int DamageDecal(CBaseEntity *pEntity, int bitsDamageType) +{ + if (pEntity) + { + return pEntity->DamageDecal(bitsDamageType); + } + + return RANDOM_LONG(DECAL_GUNSHOT4, DECAL_GUNSHOT5); +} + +void DecalGunshot(TraceResult *pTrace, int iBulletType, bool ClientOnly, entvars_t *pShooter, bool bHitMetal) +{ + ; +} + +// EjectBrass - tosses a brass shell from passed origin at passed velocity +void EjectBrass(const Vector &vecOrigin, const Vector &vecLeft, const Vector &vecVelocity, float rotation, int model, int soundtype, int entityIndex) +{ + bool useNewBehavior = AreRunningCZero(); + + MESSAGE_BEGIN(MSG_PVS, gmsgBrass, vecOrigin); + if (!useNewBehavior) + { + // noxref + WRITE_BYTE(TE_MODEL); + } + WRITE_COORD(vecOrigin.x); // origin + WRITE_COORD(vecOrigin.y); + WRITE_COORD(vecOrigin.z); + if (!useNewBehavior) + { + // noxref + // it parses the client side, but does not use it + WRITE_COORD(vecLeft.x); + WRITE_COORD(vecLeft.y); + WRITE_COORD(vecLeft.z); + } + WRITE_COORD(vecVelocity.x); // velocity + WRITE_COORD(vecVelocity.y); + WRITE_COORD(vecVelocity.z); + WRITE_ANGLE(rotation); + WRITE_SHORT(model); + WRITE_BYTE(soundtype); + if (!useNewBehavior) + { + // noxref + WRITE_BYTE(25);// 2.5 seconds + } + WRITE_BYTE(entityIndex); + MESSAGE_END(); +} + +NOXREF void EjectBrass2(const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype, entvars_t *pev) +{ + MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, pev); + WRITE_BYTE(TE_MODEL); + WRITE_COORD(vecOrigin.x); + WRITE_COORD(vecOrigin.y); + WRITE_COORD(vecOrigin.z); + WRITE_COORD(vecVelocity.x); + WRITE_COORD(vecVelocity.y); + WRITE_COORD(vecVelocity.z); + WRITE_ANGLE(rotation); + WRITE_SHORT(model); + WRITE_BYTE(0); + WRITE_BYTE(5);// 0.5 seconds + MESSAGE_END(); +} + +#ifdef REGAMEDLL_ADD +struct { + AmmoType type; + const char *name; +} ammoIndex[] = +{ + { AMMO_338MAGNUM, "338Magnum" }, + { AMMO_762NATO, "762Nato" }, + { AMMO_556NATOBOX, "556NatoBox" }, + { AMMO_556NATO, "556Nato" }, + { AMMO_BUCKSHOT, "buckshot" }, + { AMMO_45ACP, "45acp" }, + { AMMO_57MM, "57mm" }, + { AMMO_50AE, "50AE" }, + { AMMO_357SIG, "357SIG" }, + { AMMO_9MM, "9mm" }, + { AMMO_FLASHBANG, "Flashbang" }, + { AMMO_HEGRENADE, "HEGrenade" }, + { AMMO_SMOKEGRENADE, "SmokeGrenade" }, + { AMMO_C4, "C4" }, +}; +#endif + +// Precaches the ammo and queues the ammo info for sending to clients +void AddAmmoNameToAmmoRegistry(const char *szAmmoname) +{ + // make sure it's not already in the registry + for (int i = 0; i < MAX_AMMO_SLOTS; i++) + { + if (!CBasePlayerItem::m_AmmoInfoArray[i].pszName) + continue; + + if (!Q_stricmp(CBasePlayerItem::m_AmmoInfoArray[i].pszName, szAmmoname)) + { + // ammo already in registry, just quite + return; + } + } + + giAmmoIndex++; + assert(giAmmoIndex < MAX_AMMO_SLOTS); + + if (giAmmoIndex >= MAX_AMMO_SLOTS) + giAmmoIndex = 0; + +#ifdef REGAMEDLL_ADD + for (auto& ammo : ammoIndex) + { + if (Q_stricmp(ammo.name, szAmmoname)) + continue; + + if (ammo.type != giAmmoIndex) { + CONSOLE_ECHO("Warning: ammo '%s' index mismatch; expected %i, real %i\n", szAmmoname, ammo.type, giAmmoIndex); + } + break; + } +#endif + + CBasePlayerItem::m_AmmoInfoArray[giAmmoIndex].pszName = szAmmoname; + + // Yes, this info is redundant + CBasePlayerItem::m_AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; +} + +// Precaches the weapon and queues the weapon info for sending to clients +void UTIL_PrecacheOtherWeapon(const char *szClassname) +{ + edict_t *pEdict = CREATE_NAMED_ENTITY(MAKE_STRING(szClassname)); + if (FNullEnt(pEdict)) + { + ALERT(at_console, "NULL Ent in UTIL_PrecacheOtherWeapon classname `%s`\n", szClassname); + return; + } + + CBasePlayerItem *pItem = GET_PRIVATE(pEdict); + if (pItem) + { + ItemInfo info; + Q_memset(&info, 0, sizeof(info)); + + pItem->Precache(); + if (pItem->GetItemInfo(&info)) + { + CBasePlayerItem::m_ItemInfoArray[info.iId] = info; + + if (info.pszAmmo1 && info.pszAmmo1[0] != '\0') + { + AddAmmoNameToAmmoRegistry(info.pszAmmo1); + } + + if (info.pszAmmo2 && info.pszAmmo2[0] != '\0') + { + AddAmmoNameToAmmoRegistry(info.pszAmmo2); + } + } + } + + REMOVE_ENTITY(pEdict); +} + +// Called by worldspawn +void WeaponsPrecache() +{ + Q_memset(CBasePlayerItem::m_ItemInfoArray, 0, sizeof(CBasePlayerItem::m_ItemInfoArray)); + Q_memset(CBasePlayerItem::m_AmmoInfoArray, 0, sizeof(CBasePlayerItem::m_AmmoInfoArray)); + + giAmmoIndex = 0; + + // custom items... + + // common world objects + UTIL_PrecacheOther("item_suit"); + UTIL_PrecacheOther("item_battery"); + UTIL_PrecacheOther("item_antidote"); + UTIL_PrecacheOther("item_security"); + UTIL_PrecacheOther("item_longjump"); + UTIL_PrecacheOther("item_kevlar"); + UTIL_PrecacheOther("item_assaultsuit"); + UTIL_PrecacheOther("item_thighpack"); + + // awp magnum + UTIL_PrecacheOtherWeapon("weapon_awp"); + UTIL_PrecacheOther("ammo_338magnum"); + + UTIL_PrecacheOtherWeapon("weapon_g3sg1"); + UTIL_PrecacheOtherWeapon("weapon_ak47"); + UTIL_PrecacheOtherWeapon("weapon_scout"); + UTIL_PrecacheOther("ammo_762nato"); + + // m249 + UTIL_PrecacheOtherWeapon("weapon_m249"); + UTIL_PrecacheOther("ammo_556natobox"); + + UTIL_PrecacheOtherWeapon("weapon_m4a1"); + UTIL_PrecacheOtherWeapon("weapon_sg552"); + UTIL_PrecacheOtherWeapon("weapon_aug"); + UTIL_PrecacheOtherWeapon("weapon_sg550"); + UTIL_PrecacheOther("ammo_556nato"); + + // shotgun + UTIL_PrecacheOtherWeapon("weapon_m3"); + UTIL_PrecacheOtherWeapon("weapon_xm1014"); + UTIL_PrecacheOther("ammo_buckshot"); + + UTIL_PrecacheOtherWeapon("weapon_usp"); + UTIL_PrecacheOtherWeapon("weapon_mac10"); + UTIL_PrecacheOtherWeapon("weapon_ump45"); + UTIL_PrecacheOther("ammo_45acp"); + + UTIL_PrecacheOtherWeapon("weapon_fiveseven"); + UTIL_PrecacheOtherWeapon("weapon_p90"); + UTIL_PrecacheOther("ammo_57mm"); + + // deagle + UTIL_PrecacheOtherWeapon("weapon_deagle"); + UTIL_PrecacheOther("ammo_50ae"); + + // p228 + UTIL_PrecacheOtherWeapon("weapon_p228"); + UTIL_PrecacheOther("ammo_357sig"); + + // knife + UTIL_PrecacheOtherWeapon("weapon_knife"); + + UTIL_PrecacheOtherWeapon("weapon_glock18"); + UTIL_PrecacheOtherWeapon("weapon_mp5navy"); + UTIL_PrecacheOtherWeapon("weapon_tmp"); + UTIL_PrecacheOtherWeapon("weapon_elite"); + UTIL_PrecacheOther("ammo_9mm"); + + UTIL_PrecacheOtherWeapon("weapon_flashbang"); + UTIL_PrecacheOtherWeapon("weapon_hegrenade"); + UTIL_PrecacheOtherWeapon("weapon_smokegrenade"); + UTIL_PrecacheOtherWeapon("weapon_c4"); + UTIL_PrecacheOtherWeapon("weapon_galil"); + UTIL_PrecacheOtherWeapon("weapon_famas"); + + if (g_pGameRules->IsDeathmatch()) + { + // container for dropped deathmatch weapons + UTIL_PrecacheOther("weaponbox"); + } + + g_sModelIndexFireball = PRECACHE_MODEL("sprites/zerogxplode.spr"); // fireball + g_sModelIndexWExplosion = PRECACHE_MODEL("sprites/WXplo1.spr"); // underwater fireball + g_sModelIndexSmoke = PRECACHE_MODEL("sprites/steam1.spr"); // smoke + g_sModelIndexBubbles = PRECACHE_MODEL("sprites/bubble.spr"); // bubbles + g_sModelIndexBloodSpray = PRECACHE_MODEL("sprites/bloodspray.spr"); // initial blood + g_sModelIndexBloodDrop = PRECACHE_MODEL("sprites/blood.spr"); // splattered blood + + g_sModelIndexSmokePuff = PRECACHE_MODEL("sprites/smokepuff.spr"); + g_sModelIndexFireball2 = PRECACHE_MODEL("sprites/eexplo.spr"); + g_sModelIndexFireball3 = PRECACHE_MODEL("sprites/fexplo.spr"); + g_sModelIndexFireball4 = PRECACHE_MODEL("sprites/fexplo1.spr"); + g_sModelIndexRadio = PRECACHE_MODEL("sprites/radio.spr"); + + g_sModelIndexCTGhost = PRECACHE_MODEL("sprites/b-tele1.spr"); + g_sModelIndexTGhost = PRECACHE_MODEL("sprites/c-tele1.spr"); + g_sModelIndexC4Glow = PRECACHE_MODEL("sprites/ledglow.spr"); + + g_sModelIndexLaser = PRECACHE_MODEL("sprites/laserbeam.spr"); + g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr"); + + // used by explosions + PRECACHE_MODEL("models/grenade.mdl"); + PRECACHE_MODEL("sprites/explode1.spr"); + + PRECACHE_SOUND("weapons/debris1.wav"); // explosion aftermaths + PRECACHE_SOUND("weapons/debris2.wav"); // explosion aftermaths + PRECACHE_SOUND("weapons/debris3.wav"); // explosion aftermaths + + PRECACHE_SOUND("weapons/grenade_hit1.wav"); // grenade + PRECACHE_SOUND("weapons/grenade_hit2.wav"); // grenade + PRECACHE_SOUND("weapons/grenade_hit3.wav"); // grenade + + PRECACHE_SOUND("weapons/bullet_hit1.wav"); // hit by bullet + PRECACHE_SOUND("weapons/bullet_hit2.wav"); // hit by bullet + + PRECACHE_SOUND("items/weapondrop1.wav"); // weapon falls to the ground + PRECACHE_SOUND("weapons/generic_reload.wav"); +} + +ItemInfo CBasePlayerItem::m_ItemInfoArray[MAX_WEAPONS]; +AmmoInfo CBasePlayerItem::m_AmmoInfoArray[MAX_AMMO_SLOTS]; + +TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] = +{ + DEFINE_FIELD(CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR), + DEFINE_FIELD(CBasePlayerItem, m_pNext, FIELD_CLASSPTR), + DEFINE_FIELD(CBasePlayerItem, m_iId, FIELD_INTEGER), +}; + +IMPLEMENT_SAVERESTORE(CBasePlayerItem, CBaseAnimating) + +TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] = +{ + DEFINE_FIELD(CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME), + DEFINE_FIELD(CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME), + DEFINE_FIELD(CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME), + DEFINE_FIELD(CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER), + DEFINE_FIELD(CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER), + DEFINE_FIELD(CBasePlayerWeapon, m_iClip, FIELD_INTEGER), + DEFINE_FIELD(CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER), +}; + +IMPLEMENT_SAVERESTORE(CBasePlayerWeapon, CBasePlayerItem) + +void CBasePlayerItem::SetObjectCollisionBox() +{ + pev->absmin = pev->origin + Vector(-24, -24, 0); + pev->absmax = pev->origin + Vector(24, 24, 16); +} + +// Sets up movetype, size, solidtype for a new weapon. +void CBasePlayerItem::FallInit() +{ + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_BBOX; + + UTIL_SetOrigin(pev, pev->origin); + + // pointsize until it lands on the ground. + UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); + + SetTouch(&CBasePlayerItem::DefaultTouch); + SetThink(&CBasePlayerItem::FallThink); + + pev->nextthink = gpGlobals->time + 0.1f; +} + +// FallThink - Items that have just spawned run this think +// to catch them when they hit the ground. Once we're sure +// that the object is grounded, we change its solid type +// to trigger and set it in a large box that helps the +// player get it. +void CBasePlayerItem::FallThink() +{ + pev->nextthink = gpGlobals->time + 0.1f; + + if (pev->flags & FL_ONGROUND) + { + // clatter if we have an owner (i.e., dropped by someone) + // don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!) + if (!FNullEnt(pev->owner)) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", VOL_NORM, ATTN_NORM, 0, RANDOM_LONG(0, 29) + 95); + } + + // lie flat + pev->angles.x = 0.0f; + pev->angles.z = 0.0f; + + Materialize(); + } +} + +// Materialize - make a CBasePlayerItem visible and tangible +void CBasePlayerItem::Materialize() +{ + if (pev->effects & EF_NODRAW) + { + // changing from invisible state to visible. + if (g_pGameRules->IsMultiplayer()) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", VOL_NORM, ATTN_NORM, 0, 150); + } + + pev->effects &= ~EF_NODRAW; + pev->effects |= EF_MUZZLEFLASH; + } + + pev->solid = SOLID_TRIGGER; + + // link into world. + UTIL_SetOrigin(pev, pev->origin); + SetTouch(&CBasePlayerItem::DefaultTouch); + + if (g_pGameRules->IsMultiplayer()) + { + if (!CanDrop()) + { + SetTouch(nullptr); + } + + SetThink(&CBaseEntity::SUB_Remove); + pev->nextthink = gpGlobals->time + 1.0f; + } + else + { + SetThink(nullptr); + } +} + +// AttemptToMaterialize - the item is trying to rematerialize, +// should it do so now or wait longer? +void CBasePlayerItem::AttemptToMaterialize() +{ + float time = g_pGameRules->FlWeaponTryRespawn(this); + + if (time == 0) + { + Materialize(); + return; + } + + pev->nextthink = gpGlobals->time + time; +} + +// CheckRespawn - a player is taking this weapon, should +// it respawn? +void CBasePlayerItem::CheckRespawn() +{ + switch (g_pGameRules->WeaponShouldRespawn(this)) + { + case GR_WEAPON_RESPAWN_YES: + return; + case GR_WEAPON_RESPAWN_NO: + return; + } +} + +// Respawn - this item is already in the world, but it is +// invisible and intangible. Make it visible and tangible. +CBaseEntity *CBasePlayerItem::Respawn() +{ + // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code + // will decide when to make the weapon visible and touchable. + CBaseEntity *pNewWeapon = CBaseEntity::Create(pev->classname, g_pGameRules->VecWeaponRespawnSpot(this), pev->angles, pev->owner); + + if (pNewWeapon) + { + // invisible for now + pNewWeapon->pev->effects |= EF_NODRAW; + + // no touch + pNewWeapon->SetTouch(nullptr); + pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize); + + DROP_TO_FLOOR(ENT(pev)); + + // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement, + // but when it should respawn is based on conditions belonging to the weapon that was taken. + pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime(this); + } + else + { + ALERT(at_console, "Respawn failed to create %s!\n", STRING(pev->classname)); + } + + return pNewWeapon; +} + +// whats going on here is that if the player drops this weapon, they shouldn't take it back themselves +// for a little while. But if they throw it at someone else, the other player should get it immediately. +void CBasePlayerItem::DefaultTouch(CBaseEntity *pOther) +{ + // if it's not a player, ignore + if (!pOther->IsPlayer()) + { + return; + } + + CBasePlayer *pPlayer = static_cast(pOther); + + if (pPlayer->m_bIsVIP + && m_iId != WEAPON_USP + && m_iId != WEAPON_GLOCK18 + && m_iId != WEAPON_P228 + && m_iId != WEAPON_DEAGLE + && m_iId != WEAPON_KNIFE) + { + return; + } + + // can I have this? + if (!g_pGameRules->CanHavePlayerItem(pPlayer, this)) + { + if (gEvilImpulse101) + { + UTIL_Remove(this); + } + + return; + } + + if (pOther->AddPlayerItem(this)) + { + AttachToPlayer(pPlayer); +#ifndef REGAMEDLL_FIXES + SetThink(nullptr); +#endif + EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM); + } + + SUB_UseTargets(pOther, USE_TOGGLE, 0); +} + +void CBasePlayerWeapon::SetPlayerShieldAnim() +{ + if (!m_pPlayer->HasShield()) + return; + + if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) + { + Q_strcpy(m_pPlayer->m_szAnimExtention, "shield"); + } + else + { + Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + } +} + +void CBasePlayerWeapon::ResetPlayerShieldAnim() +{ + if (m_pPlayer->HasShield()) + { + if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) + { + Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + } + } +} + +void CBasePlayerWeapon::EjectBrassLate() +{ + int soundType; + Vector vecUp, vecRight, vecShellVelocity; + + UTIL_MakeVectors(m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle); + + vecUp = RANDOM_FLOAT(100, 150) * gpGlobals->v_up; + vecRight = RANDOM_FLOAT(50, 70) * gpGlobals->v_right; + + vecShellVelocity = (m_pPlayer->pev->velocity + vecRight + vecUp) + gpGlobals->v_forward * 25; + soundType = (m_iId == WEAPON_XM1014 || m_iId == WEAPON_M3) ? 2 : 1; + + EjectBrass(pev->origin + m_pPlayer->pev->view_ofs + gpGlobals->v_up * -9 + gpGlobals->v_forward * 16, gpGlobals->v_right * -9, + vecShellVelocity, pev->angles.y, m_iShellId, soundType, m_pPlayer->entindex()); +} + +bool CBasePlayerWeapon::ShieldSecondaryFire(int iUpAnim, int iDownAnim) +{ + if (!m_pPlayer->HasShield()) + return false; + + if (m_iWeaponState & WPNSTATE_SHIELD_DRAWN) + { + m_iWeaponState &= ~WPNSTATE_SHIELD_DRAWN; + SendWeaponAnim(iDownAnim, UseDecrement() != FALSE); + Q_strcpy(m_pPlayer->m_szAnimExtention, "shieldgun"); + m_fMaxSpeed = 250.0f; + m_pPlayer->m_bShieldDrawn = false; + } + else + { + m_iWeaponState |= WPNSTATE_SHIELD_DRAWN; + SendWeaponAnim(iUpAnim, UseDecrement() != FALSE); + Q_strcpy(m_pPlayer->m_szAnimExtention, "shielded"); + m_fMaxSpeed = 180.0f; + m_pPlayer->m_bShieldDrawn = true; + } + + m_pPlayer->UpdateShieldCrosshair((m_iWeaponState & WPNSTATE_SHIELD_DRAWN) != WPNSTATE_SHIELD_DRAWN); + m_pPlayer->ResetMaxSpeed(); + + m_flNextSecondaryAttack = 0.4f; + m_flNextPrimaryAttack = 0.4f; + m_flTimeWeaponIdle = 0.6f; + + return true; +} + +void CBasePlayerWeapon::KickBack(float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change) +{ + real_t flKickUp; + float flKickLateral; + + if (m_iShotsFired == 1) + { + flKickUp = up_base; + flKickLateral = lateral_base; + } + else + { + flKickUp = m_iShotsFired * up_modifier + up_base; + flKickLateral = m_iShotsFired * lateral_modifier + lateral_base; + } + + m_pPlayer->pev->punchangle.x -= flKickUp; + + if (m_pPlayer->pev->punchangle.x < -up_max) + { + m_pPlayer->pev->punchangle.x = -up_max; + } + + if (m_iDirection == 1) + { + m_pPlayer->pev->punchangle.y += flKickLateral; + + if (m_pPlayer->pev->punchangle.y > lateral_max) + m_pPlayer->pev->punchangle.y = lateral_max; + } + else + { + m_pPlayer->pev->punchangle.y -= flKickLateral; + + if (m_pPlayer->pev->punchangle.y < -lateral_max) + m_pPlayer->pev->punchangle.y = -lateral_max; + } + + if (!RANDOM_LONG(0, direction_change)) + { + m_iDirection = !m_iDirection; + } +} + +void CBasePlayerWeapon::FireRemaining(int &shotsFired, float &shootTime, BOOL bIsGlock) +{ + float nexttime = 0.1f; + if (--m_iClip < 0) + { + m_iClip = 0; + shotsFired = 3; + shootTime = 0; + return; + } + + UTIL_MakeVectors(m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle); + + Vector vecSrc = m_pPlayer->GetGunPosition(); + Vector vecDir; + + int flag; +#ifdef CLIENT_WEAPONS + flag = FEV_NOTHOST; +#else + flag = 0; +#endif + + if (bIsGlock) + { + vecDir = m_pPlayer->FireBullets3(vecSrc, gpGlobals->v_forward, 0.05, 8192, 1, BULLET_PLAYER_9MM, 18, 0.9, m_pPlayer->pev, true, m_pPlayer->random_seed); + --m_pPlayer->ammo_9mm; + + PLAYBACK_EVENT_FULL(flag, m_pPlayer->edict(), m_usFireGlock18, 0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, + int(m_pPlayer->pev->punchangle.x * 10000), int(m_pPlayer->pev->punchangle.y * 10000), m_iClip == 0, FALSE); + } + else + { + + vecDir = m_pPlayer->FireBullets3(vecSrc, gpGlobals->v_forward, m_fBurstSpread, 8192, 2, BULLET_PLAYER_556MM, 30, 0.96, m_pPlayer->pev, false, m_pPlayer->random_seed); + --m_pPlayer->ammo_556nato; + + PLAYBACK_EVENT_FULL(flag, m_pPlayer->edict(), m_usFireFamas, 0, (float *)&g_vecZero, (float *)&g_vecZero, vecDir.x, vecDir.y, + int(m_pPlayer->pev->punchangle.x * 10000000), int(m_pPlayer->pev->punchangle.y * 10000000), FALSE, FALSE); + } + + m_pPlayer->pev->effects |= EF_MUZZLEFLASH; + m_pPlayer->SetAnimation(PLAYER_ATTACK1); + + if (++shotsFired != 3) + { + shootTime = gpGlobals->time + nexttime; + } + else + shootTime = 0; +} + +BOOL CanAttack(float attack_time, float curtime, BOOL isPredicted) +{ +#ifdef CLIENT_WEAPONS + if (!isPredicted) +#else + if (1) +#endif + { + return (attack_time <= curtime) ? TRUE : FALSE; + } + else + { + return (attack_time <= 0.0f) ? TRUE : FALSE; + } +} + +bool CBasePlayerWeapon::HasSecondaryAttack() +{ + if (m_pPlayer && m_pPlayer->HasShield()) + { + return true; + } + +#ifdef REGAMEDLL_API + if (CSPlayerWeapon()->m_bHasSecondaryAttack) + { + return true; + } +#endif + + switch (m_iId) + { + case WEAPON_AK47: + case WEAPON_XM1014: + case WEAPON_MAC10: + case WEAPON_ELITE: + case WEAPON_FIVESEVEN: + case WEAPON_MP5N: +#ifdef BUILD_LATEST_FIXES + case WEAPON_UMP45: +#endif + case WEAPON_M249: + case WEAPON_M3: + case WEAPON_TMP: + case WEAPON_DEAGLE: + case WEAPON_P228: + case WEAPON_P90: + case WEAPON_C4: + case WEAPON_GALIL: + return false; + default: + break; + } + + return true; +} + +void CBasePlayerWeapon::HandleInfiniteAmmo() +{ + int nInfiniteAmmo = 0; + +#ifdef REGAMEDLL_API + nInfiniteAmmo = m_pPlayer->CSPlayer()->m_iWeaponInfiniteAmmo; +#endif + + if (!nInfiniteAmmo) + nInfiniteAmmo = static_cast(infiniteAmmo.value); + + if (nInfiniteAmmo == WPNMODE_INFINITE_CLIP && !IsGrenadeWeapon(m_iId)) + { + m_iClip = iMaxClip(); + } + else if ((nInfiniteAmmo == WPNMODE_INFINITE_BPAMMO && +#ifdef REGAMEDLL_API + ((m_pPlayer->CSPlayer()->m_iWeaponInfiniteIds & (1 << m_iId)) || (m_pPlayer->CSPlayer()->m_iWeaponInfiniteIds <= 0 && !IsGrenadeWeapon(m_iId))) +#endif + ) + || (IsGrenadeWeapon(m_iId) && infiniteGrenades.value == 1.0f)) + { + if (pszAmmo1()) + { + m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] = iMaxAmmo1(); + } + + if (pszAmmo2()) + { + m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] = iMaxAmmo2(); + } + } +} + +void CBasePlayerWeapon::ItemPostFrame() +{ + int usableButtons = m_pPlayer->pev->button; + + if (!HasSecondaryAttack()) + { + usableButtons &= ~IN_ATTACK2; + } + + if (m_flGlock18Shoot != 0) + { + FireRemaining(m_iGlock18ShotsFired, m_flGlock18Shoot, TRUE); + } + else if (gpGlobals->time > m_flFamasShoot && m_flFamasShoot != 0) + { + FireRemaining(m_iFamasShotsFired, m_flFamasShoot, FALSE); + } + + // Return zoom level back to previous zoom level before we fired a shot. + // This is used only for the AWP and Scout + if (m_flNextPrimaryAttack <= UTIL_WeaponTimeBase()) + { + if (m_pPlayer->m_bResumeZoom) + { + m_pPlayer->m_iFOV = m_pPlayer->m_iLastZoom; + m_pPlayer->pev->fov = m_pPlayer->m_iFOV; + + if (m_pPlayer->m_iFOV == m_pPlayer->m_iLastZoom) + { + // return the fade level in zoom. + m_pPlayer->m_bResumeZoom = false; + } + } + } + + if (m_pPlayer->m_flEjectBrass != 0 && m_pPlayer->m_flEjectBrass <= gpGlobals->time) + { + m_pPlayer->m_flEjectBrass = 0; + EjectBrassLate(); + } + + if (!(m_pPlayer->pev->button & IN_ATTACK)) + { + m_flLastFireTime = 0; + } + + if (m_pPlayer->HasShield()) + { + if (m_fInReload && (m_pPlayer->pev->button & IN_ATTACK2)) + { + SecondaryAttack(); + m_pPlayer->pev->button &= ~IN_ATTACK2; + m_fInReload = FALSE; + m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase(); + } + } + + if (m_fInReload && m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase()) + { + // complete the reload. + int j = Q_min(iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + + // Add them to the clip + m_iClip += j; + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; + + m_pPlayer->TabulateAmmo(); + m_fInReload = FALSE; + } + + if ((usableButtons & IN_ATTACK2) && CanAttack(m_flNextSecondaryAttack, UTIL_WeaponTimeBase(), UseDecrement()) +#ifdef REGAMEDLL_FIXES + && !m_pPlayer->m_bIsDefusing // In-line: I think it's fine to block secondary attack, when defusing. It's better then blocking speed resets in weapons. +#endif + ) + { + if (pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()]) + { + m_fFireOnEmpty = TRUE; + } + + SecondaryAttack(); + m_pPlayer->pev->button &= ~IN_ATTACK2; + } + else if ((m_pPlayer->pev->button & IN_ATTACK) && CanAttack(m_flNextPrimaryAttack, UTIL_WeaponTimeBase(), UseDecrement())) + { + if ((m_iClip == 0 && pszAmmo1()) || (iMaxClip() == WEAPON_NOCLIP && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()])) + { + m_fFireOnEmpty = TRUE; + } + + m_pPlayer->TabulateAmmo(); + + // Can't shoot during the freeze period + // Always allow firing in single player + if ((m_pPlayer->m_bCanShoot && g_pGameRules->IsMultiplayer() && !g_pGameRules->IsFreezePeriod() && !m_pPlayer->m_bIsDefusing) || !g_pGameRules->IsMultiplayer()) + { + PrimaryAttack(); + } + } + else if ((m_pPlayer->pev->button & IN_RELOAD) && iMaxClip() != WEAPON_NOCLIP && !m_fInReload && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + if (m_flFamasShoot == 0 && m_flGlock18Shoot == 0) + { + if (!(m_iWeaponState & WPNSTATE_SHIELD_DRAWN)) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + Reload(); + } + } + } + else if (!(usableButtons & (IN_ATTACK | IN_ATTACK2))) + { + // no fire buttons down + + // The following code prevents the player from tapping the firebutton repeatedly + // to simulate full auto and retaining the single shot accuracy of single fire + if (m_bDelayFire) + { + m_bDelayFire = false; + + if (m_iShotsFired > 15) + { + m_iShotsFired = 15; + } + + m_flDecreaseShotsFired = gpGlobals->time + 0.4f; + } + + m_fFireOnEmpty = FALSE; + + // if it's a pistol then set the shots fired to 0 after the player releases a button + if (IsSecondaryWeapon(m_iId)) + { + m_iShotsFired = 0; + } + else + { + if (m_iShotsFired > 0 && m_flDecreaseShotsFired < gpGlobals->time) + { + m_flDecreaseShotsFired = gpGlobals->time + 0.0225f; + m_iShotsFired--; + } + } + + if (!IsUseable() && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) + { +#if 0 + // weapon isn't useable, switch. + if (!(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon(m_pPlayer, this)) + { + m_flNextPrimaryAttack = UTIL_WeaponTimeBase() + 0.3f; + return; + } +#endif + } + else + { + if (!(m_iWeaponState & WPNSTATE_SHIELD_DRAWN)) + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if (!m_iClip && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < UTIL_WeaponTimeBase()) + { + if (m_flFamasShoot == 0 && m_flGlock18Shoot == 0) + { + Reload(); + return; + } + } + } + } + +#ifdef BUILD_LATEST + HandleInfiniteAmmo(); +#endif + + WeaponIdle(); + return; + } + +#ifdef BUILD_LATEST + HandleInfiniteAmmo(); +#endif + + // catch all + if (ShouldWeaponIdle()) + { + WeaponIdle(); + } +} + +void CBasePlayerItem::DestroyItem() +{ + if (m_pPlayer) + { + // if attached to a player, remove. + m_pPlayer->RemovePlayerItem(this); + } + + Kill(); +} + +int CBasePlayerItem::AddToPlayer(CBasePlayer *pPlayer) +{ + m_pPlayer = pPlayer; + + MESSAGE_BEGIN(MSG_ONE, gmsgWeapPickup, nullptr, pPlayer->pev); + WRITE_BYTE(m_iId); + MESSAGE_END(); + + return TRUE; +} + +void CBasePlayerItem::Drop() +{ + SetTouch(nullptr); + SetThink(&CBaseEntity::SUB_Remove); + pev->nextthink = gpGlobals->time + 0.1f; +} + +void CBasePlayerItem::Kill() +{ + SetTouch(nullptr); + SetThink(&CBaseEntity::SUB_Remove); + pev->nextthink = gpGlobals->time + 0.1f; +} + +void CBasePlayerItem::Holster(int skiplocal) +{ + m_pPlayer->pev->viewmodel = 0; + m_pPlayer->pev->weaponmodel = 0; +} + +void CBasePlayerItem::AttachToPlayer(CBasePlayer *pPlayer) +{ + pev->movetype = MOVETYPE_FOLLOW; + pev->solid = SOLID_NOT; + pev->aiment = pPlayer->edict(); + pev->effects = EF_NODRAW; + + // server won't send down to clients if modelindex == 0 + pev->modelindex = 0; + pev->model = 0; + pev->owner = pPlayer->edict(); + +#ifndef REGAMEDLL_FIXES + pev->nextthink = gpGlobals->time + 0.1f; +#else + // Remove think - prevents futher attempts to materialize + pev->nextthink = 0; + SetThink(nullptr); +#endif + + SetTouch(nullptr); +} + +void CBasePlayerWeapon::Spawn() +{ + ItemInfo info; + Q_memset(&info, 0, sizeof(info)); + + if (GetItemInfo(&info)) { + CSPlayerItem()->SetItemInfo(&info); + } + + CSPlayerWeapon()->m_bHasSecondaryAttack = HasSecondaryAttack(); +} + +// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal +int CBasePlayerWeapon::AddDuplicate(CBasePlayerItem *pOriginal) +{ + if (m_iDefaultAmmo) + { + return ExtractAmmo((CBasePlayerWeapon *)pOriginal); + } + else + { + // a dead player dropped this. + return ExtractClipAmmo((CBasePlayerWeapon *)pOriginal); + } +} + +int CBasePlayerWeapon::AddToPlayer(CBasePlayer *pPlayer) +{ + m_pPlayer = pPlayer; + pPlayer->pev->weapons |= (1 << m_iId); + + if (!m_iPrimaryAmmoType) + { + m_iPrimaryAmmoType = pPlayer->GetAmmoIndex(pszAmmo1()); + m_iSecondaryAmmoType = pPlayer->GetAmmoIndex(pszAmmo2()); + } + + if (AddWeapon()) + { + return CBasePlayerItem::AddToPlayer(pPlayer); + } + + return FALSE; +} + +int CBasePlayerWeapon::UpdateClientData(CBasePlayer *pPlayer) +{ + bool bSend = false; + int state = 0; + + if (pPlayer->m_pActiveItem == this) + { + if (pPlayer->m_fOnTarget) + state = WEAPON_IS_ONTARGET; + else + state = 1; + } + + if (!pPlayer->m_fWeapon) + bSend = true; + + if (this == pPlayer->m_pActiveItem || this == pPlayer->m_pClientActiveItem) + { + if (pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem) + bSend = true; + } + + if (m_iClip != m_iClientClip || state != m_iClientWeaponState || pPlayer->m_iFOV != pPlayer->m_iClientFOV) + bSend = true; + + if (bSend) + { + MESSAGE_BEGIN(MSG_ONE, gmsgCurWeapon, nullptr, pPlayer->pev); + WRITE_BYTE(state); + WRITE_BYTE(m_iId); + WRITE_BYTE(m_iClip); + MESSAGE_END(); + + m_iClientClip = m_iClip; + m_iClientWeaponState = state; + pPlayer->m_fWeapon = TRUE; + } + + if (m_pNext) + { + m_pNext->UpdateClientData(pPlayer); + } + + return 1; +} + +void CBasePlayerWeapon::SendWeaponAnim(int iAnim, int skiplocal) +{ + m_pPlayer->pev->weaponanim = iAnim; + +#ifdef CLIENT_WEAPONS + if (skiplocal && ENGINE_CANSKIP(m_pPlayer->edict())) + return; +#endif + + MESSAGE_BEGIN(MSG_ONE, SVC_WEAPONANIM, nullptr, m_pPlayer->pev); + WRITE_BYTE(iAnim); // sequence number + WRITE_BYTE(pev->body); // weaponmodel bodygroup. + MESSAGE_END(); +} + +BOOL CBasePlayerWeapon::AddPrimaryAmmo(int iCount, char *szName, int iMaxClip, int iMaxCarry) +{ + int iIdAmmo; + + if (iMaxClip < 1) + { + m_iClip = -1; + iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMaxCarry); + } + else if (m_iClip == 0) + { + int i; + i = Q_min(m_iClip + iCount, iMaxClip); + m_iClip += i; + + iIdAmmo = m_pPlayer->GiveAmmo(iCount - i, szName, iMaxCarry); + } + else + { + iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMaxCarry); + } + + if (iIdAmmo > 0) + { + m_iPrimaryAmmoType = iIdAmmo; + if (m_pPlayer->HasPlayerItem(this)) + { + // play the "got ammo" sound only if we gave some ammo to a player that already had this gun. + // if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us. + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); + } + } + + return iIdAmmo > 0 ? TRUE : FALSE; +} + +BOOL CBasePlayerWeapon::AddSecondaryAmmo(int iCount, char *szName, int iMax) +{ + int iIdAmmo = m_pPlayer->GiveAmmo(iCount, szName, iMax); + + if (iIdAmmo > 0) + { + m_iSecondaryAmmoType = iIdAmmo; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", VOL_NORM, ATTN_NORM); + } + + return iIdAmmo > 0 ? TRUE : FALSE; +} + +// IsUseable - this function determines whether or not a +// weapon is useable by the player in its current state. +// (does it have ammo loaded? do I have any ammo for the +// weapon?, etc) +BOOL CBasePlayerWeapon::IsUseable() +{ + if (m_iClip <= 0) + { + if (m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] <= 0 && iMaxAmmo1() != -1) + { + // clip is empty (or nonexistant) and the player has no more ammo of this type. + return FALSE; + } + } + + return TRUE; +} + +BOOL CBasePlayerWeapon::CanDeploy() +{ + return TRUE; +} + +BOOL CBasePlayerWeapon::DefaultDeploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal) +{ + if (!CanDeploy()) + return FALSE; + + m_pPlayer->TabulateAmmo(); + m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel); + m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel); + model_name = m_pPlayer->pev->viewmodel; + Q_strcpy(m_pPlayer->m_szAnimExtention, szAnimExt); + SendWeaponAnim(iAnim, skiplocal); + + m_pPlayer->m_flNextAttack = 0.75f; + m_flTimeWeaponIdle = 1.5f; + m_flLastFireTime = 0.0f; + m_flDecreaseShotsFired = gpGlobals->time; + + m_pPlayer->m_iFOV = DEFAULT_FOV; + m_pPlayer->pev->fov = DEFAULT_FOV; + m_pPlayer->m_iLastZoom = DEFAULT_FOV; + m_pPlayer->m_bResumeZoom = false; + + return TRUE; +} + +void CBasePlayerWeapon::ReloadSound() +{ + CBasePlayer *pPlayer = nullptr; + while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) + { + if (pPlayer->IsDormant()) + break; + + if (pPlayer == m_pPlayer) + continue; + + float distance = (m_pPlayer->pev->origin - pPlayer->pev->origin).Length(); + if (distance <= MAX_DIST_RELOAD_SOUND) + { + MESSAGE_BEGIN(MSG_ONE, gmsgReloadSound, nullptr, pPlayer->pev); + WRITE_BYTE(int((1.0f - (distance / MAX_DIST_RELOAD_SOUND)) * 255.0f)); + if (!Q_strcmp(STRING(pev->classname), "weapon_m3") || !Q_strcmp(STRING(pev->classname), "weapon_xm1014")) + WRITE_BYTE(0); + else + WRITE_BYTE(1); + MESSAGE_END(); + } + } +} + +int CBasePlayerWeapon::DefaultReload(int iClipSize, int iAnim, float fDelay) +{ + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return FALSE; + + int j = Q_min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + if (!j) + { + return FALSE; + } + + m_pPlayer->m_flNextAttack = fDelay; + + ReloadSound(); + SendWeaponAnim(iAnim, UseDecrement() ? 1 : 0); + + m_fInReload = TRUE; + m_flTimeWeaponIdle = fDelay + 0.5f; + + return TRUE; +} + +BOOL CBasePlayerWeapon::PlayEmptySound() +{ + if (m_iPlayEmptySound) + { + switch (m_iId) + { + case WEAPON_USP: + case WEAPON_GLOCK18: + case WEAPON_P228: + case WEAPON_DEAGLE: + case WEAPON_ELITE: + case WEAPON_FIVESEVEN: + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/dryfire_pistol.wav", 0.8, ATTN_NORM); + break; + default: + EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/dryfire_rifle.wav", 0.8, ATTN_NORM); + break; + } + } + + return FALSE; +} + +void CBasePlayerWeapon::ResetEmptySound() +{ + m_iPlayEmptySound = 1; +} + +int CBasePlayerWeapon::PrimaryAmmoIndex() +{ + return m_iPrimaryAmmoType; +} + +int CBasePlayerWeapon::SecondaryAmmoIndex() +{ + return -1; +} + +void CBasePlayerWeapon::Holster(int skiplocal) +{ + // cancel any reload in progress. + m_fInReload = FALSE; + m_pPlayer->pev->viewmodel = 0; + m_pPlayer->pev->weaponmodel = 0; +} + +// called by the new item with the existing item as parameter +// +// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for +// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it. +// if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in +// the weapon clip comes along. +int CBasePlayerWeapon::ExtractAmmo(CBasePlayerWeapon *pWeapon) +{ + int res = 0; + if (pszAmmo1()) + { + // blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero, + // we only get the ammo in the weapon's clip, which is what we want. + res = pWeapon->AddPrimaryAmmo(m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1()); + m_iDefaultAmmo = 0; + } + + if (pszAmmo2()) + { + res = AddSecondaryAmmo(0, (char *)pszAmmo2(), iMaxAmmo2()); + } + + return res; +} + +// called by the new item's class with the existing item as parameter +int CBasePlayerWeapon::ExtractClipAmmo(CBasePlayerWeapon *pWeapon) +{ + int iAmmo; + if (m_iClip == WEAPON_NOCLIP) + { + // guns with no clips always come empty if they are second-hand + iAmmo = 0; + } + else + { + iAmmo = m_iClip; + } + + return pWeapon->m_pPlayer->GiveAmmo(iAmmo, (char *)pszAmmo1(), iMaxAmmo1()); +} + +// RetireWeapon - no more ammo for this gun, put it away. +void CBasePlayerWeapon::RetireWeapon() +{ + // first, no viewmodel at all. + m_pPlayer->pev->viewmodel = iStringNull; + m_pPlayer->pev->weaponmodel = iStringNull; + + g_pGameRules->GetNextBestWeapon(m_pPlayer, this); +} + +// GetNextAttackDelay - An accurate way of calcualting the next attack time. +float CBasePlayerWeapon::GetNextAttackDelay(float delay) +{ +#ifndef REGAMEDLL_FIXES + if (m_flLastFireTime == 0.0f || m_flNextPrimaryAttack == -1.0f) + { + // At this point, we are assuming that the client has stopped firing + // and we are going to reset our book keeping variables. + m_flPrevPrimaryAttack = delay; + m_flLastFireTime = gpGlobals->time; + } +#endif + +#ifdef REGAMEDLL_BUILD_6153 + + // TODO: Build 6xxx + // at build 6153 beta this removed + // maybe it was initiated due to the delay of the shot + + // calculate the time between this shot and the previous + float flTimeBetweenFires = gpGlobals->time - m_flLastFireTime; + float flCreep = 0.0f; + + if (flTimeBetweenFires > 0) + { + flCreep = flTimeBetweenFires - m_flPrevPrimaryAttack; + } + + float flNextAttack = UTIL_WeaponTimeBase() + delay - flCreep; +#else + float flNextAttack = UTIL_WeaponTimeBase() + delay; +#endif + + // save the last fire time + m_flLastFireTime = gpGlobals->time; + + // we need to remember what the m_flNextPrimaryAttack time is set to for each shot, + // store it as m_flPrevPrimaryAttack. + m_flPrevPrimaryAttack = flNextAttack - UTIL_WeaponTimeBase(); + + return flNextAttack; +} + +// true - keep the amount of bpammo +// false - let take away bpammo +void CBasePlayerWeapon::InstantReload(bool bCanRefillBPAmmo) +{ + // if you already reload + //if (m_fInReload) + // return; + + if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) + return; + + m_fInReload = FALSE; + m_pPlayer->m_flNextAttack = 0; + + // complete the reload. + int j = Q_min(iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]); + if (j == 0) + return; + + // Add them to the clip + m_iClip += j; + + if (!bCanRefillBPAmmo) { + m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j; + } + + m_pPlayer->TabulateAmmo(); +} + +TYPEDESCRIPTION CWeaponBox::m_SaveData[] = +{ + DEFINE_ARRAY(CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS), + DEFINE_ARRAY(CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS), + DEFINE_ARRAY(CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES), + DEFINE_FIELD(CWeaponBox, m_cAmmoTypes, FIELD_INTEGER), +}; + +LINK_ENTITY_TO_CLASS(weaponbox, CWeaponBox, CCSWeaponBox) +IMPLEMENT_SAVERESTORE(CWeaponBox, CBaseEntity) + +void CWeaponBox::Precache() +{ + PRECACHE_MODEL("models/w_weaponbox.mdl"); +} + +void CWeaponBox::KeyValue(KeyValueData *pkvd) +{ + if (m_cAmmoTypes >= MAX_AMMO_SLOTS) + { + ALERT(at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS); + return; + } + + PackAmmo(ALLOC_STRING(pkvd->szKeyName), Q_atoi(pkvd->szValue)); + + // count this new ammo type. + m_cAmmoTypes++; + pkvd->fHandled = TRUE; +} + +void CWeaponBox::BombThink() +{ + if (!m_bIsBomb) + return; + + CBasePlayer *pPlayer = nullptr; + while ((pPlayer = UTIL_FindEntityByClassname(pPlayer, "player"))) + { + if (FNullEnt(pPlayer->edict())) + break; + + if (!pPlayer->IsPlayer() || pPlayer->IsDormant()) + continue; + + CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pPlayer->pev); + + if (pTempPlayer->pev->deadflag == DEAD_NO && pTempPlayer->m_iTeam == TERRORIST) + { + MESSAGE_BEGIN(MSG_ONE, gmsgBombDrop, nullptr, pTempPlayer->edict()); + WRITE_COORD(pev->origin.x); + WRITE_COORD(pev->origin.y); + WRITE_COORD(pev->origin.z); + WRITE_BYTE(BOMB_FLAG_DROPPED); + MESSAGE_END(); + } + } + + pev->nextthink = gpGlobals->time + 1.0f; +} + +LINK_HOOK_CLASS_VOID_CHAIN(CWeaponBox, SetModel, (const char *pszModelName), pszModelName) + +void CWeaponBox::__API_HOOK(SetModel)(const char *pszModelName) +{ + SET_MODEL(ENT(pev), pszModelName); +} + +void CWeaponBox::Spawn() +{ + Precache(); + + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_TRIGGER; + + m_bIsBomb = false; + + UTIL_SetSize(pev, g_vecZero, g_vecZero); + SET_MODEL(ENT(pev), "models/w_weaponbox.mdl"); +} + +// The think function that removes the box from the world. +void CWeaponBox::Kill() +{ + // destroy the weapons + for (int i = 0; i < MAX_ITEM_TYPES; i++) + { + CBasePlayerItem *pWeapon = m_rgpPlayerItems[i]; + while (pWeapon) + { + pWeapon->SetThink(&CBaseEntity::SUB_Remove); + pWeapon->pev->nextthink = gpGlobals->time + 0.1f; + pWeapon = pWeapon->m_pNext; + } + } + + // remove the box + UTIL_Remove(this); +} + +// Try to add my contents to the toucher if the toucher is a player. +void CWeaponBox::Touch(CBaseEntity *pOther) +{ + if (!(pev->flags & FL_ONGROUND)) + { + return; + } + + if (!pOther->IsPlayer()) + { + // only players may touch a weaponbox. + return; + } + + if (!pOther->IsAlive()) + { + // no dead guys. + return; + } + + CBasePlayer *pPlayer = static_cast(pOther); + + if (pPlayer->m_bIsVIP || pPlayer->m_bShieldDrawn) + return; + + pPlayer->OnTouchingWeapon(this); + + bool bRemove = true; + bool bEmitSound = false; + + // go through my weapons and try to give the usable ones to the player. + // it's important the the player be given ammo first, so the weapons code doesn't refuse + // to deploy a better weapon that the player may pick up because he has no ammo for it. + for (int i = 0; i < MAX_ITEM_TYPES; i++) + { + if (!m_rgpPlayerItems[i]) + continue; + + CBasePlayerItem *pItem = m_rgpPlayerItems[i]; + + // have at least one weapon in this slot + while (pItem) + { + if ((pPlayer->HasShield() && pItem->m_iId == WEAPON_ELITE) + || (pPlayer->IsBot() && TheCSBots() && !TheCSBots()->IsWeaponUseable(pItem))) + { + return; + } + +#ifdef REGAMEDLL_ADD + if (pPlayer->HasRestrictItem((pItem->m_iId == WEAPON_SHIELDGUN) ? ITEM_SHIELDGUN : (ItemID)pItem->m_iId, ITEM_TYPE_TOUCHED)) + return; +#endif + + if (FClassnameIs(pItem->pev, "weapon_c4")) + { +#ifdef REGAMEDLL_FIXES + if (pPlayer->m_iTeam != TERRORIST) + return; +#else + if (pPlayer->m_iTeam != TERRORIST || pPlayer->pev->deadflag != DEAD_NO) + return; +#endif + + if (pPlayer->m_bShowHints && !(pPlayer->m_flDisplayHistory & DHF_BOMB_RETRIEVED)) + { + pPlayer->m_flDisplayHistory |= DHF_BOMB_RETRIEVED; + pPlayer->HintMessage("#Hint_you_have_the_bomb"); + } + else + ClientPrint(pPlayer->pev, HUD_PRINTCENTER, "#Got_bomb"); + + UTIL_LogPrintf("\"%s<%i><%s>\" triggered \"Got_The_Bomb\"\n", + STRING(pPlayer->pev->netname), + GETPLAYERUSERID(pPlayer->edict()), + GETPLAYERAUTHID(pPlayer->edict())); + + g_pGameRules->m_bBombDropped = FALSE; + + MESSAGE_BEGIN(MSG_SPEC, SVC_DIRECTOR); + WRITE_BYTE(9); + WRITE_BYTE(DRC_CMD_EVENT); + WRITE_SHORT(ENTINDEX(pPlayer->edict())); + WRITE_SHORT(ENTINDEX(edict())); + WRITE_LONG(6); + MESSAGE_END(); + + pPlayer->m_bHasC4 = true; + pPlayer->SetBombIcon(FALSE); + pPlayer->pev->body = 1; + + CBaseEntity *pEntity = nullptr; + while ((pEntity = UTIL_FindEntityByClassname(pEntity, "player"))) + { + if (FNullEnt(pEntity->edict())) + break; + + if (!pEntity->IsPlayer()) + continue; + + if (pEntity->pev->flags == FL_DORMANT) + continue; + + CBasePlayer *pTempPlayer = GetClassPtr((CBasePlayer *)pEntity->pev); + + if (pTempPlayer->pev->deadflag == DEAD_NO && pTempPlayer->m_iTeam == TERRORIST) + { + if (pTempPlayer != pPlayer) + { + ClientPrint(pTempPlayer->pev, HUD_PRINTCENTER, "#Game_bomb_pickup", STRING(pPlayer->pev->netname)); + } + + MESSAGE_BEGIN(MSG_ONE, gmsgBombPickup, nullptr, pTempPlayer->pev); + MESSAGE_END(); + } + } + + if (TheCSBots()) + { + TheCSBots()->SetLooseBomb(nullptr); + } + + if (TheBots) + { + TheBots->OnEvent(EVENT_BOMB_PICKED_UP, pPlayer); + } + } + + if (i >= PRIMARY_WEAPON_SLOT && i <= PISTOL_SLOT && pPlayer->m_rgpPlayerItems[i]) + { + // ... + } + else if (i == GRENADE_SLOT) + { + CBasePlayerWeapon *pGrenade = static_cast(m_rgpPlayerItems[i]); + if (pGrenade && pGrenade->IsWeapon()) + { + int playerGrenades = pPlayer->m_rgAmmo[pGrenade->m_iPrimaryAmmoType]; + +#ifdef REGAMEDLL_FIXES + auto info = GetWeaponInfo(pGrenade->m_iId); + if (info && playerGrenades < info->maxRounds) + { + auto pNext = m_rgpPlayerItems[i]->m_pNext; + if (pPlayer->AddPlayerItem(pItem)) + { + pItem->AttachToPlayer(pPlayer); + bEmitSound = true; + } + + // unlink this weapon from the box + m_rgpPlayerItems[i] = pItem = pNext; + continue; + } +#else + + int maxGrenades = 0; + const char *grenadeName = nullptr; + + switch (pGrenade->m_iId) + { + case WEAPON_HEGRENADE: + grenadeName = "weapon_hegrenade"; + maxGrenades = 1; + break; + case WEAPON_SMOKEGRENADE: + grenadeName = "weapon_smokegrenade"; + maxGrenades = 1; + break; + case WEAPON_FLASHBANG: + grenadeName = "weapon_flashbang"; + maxGrenades = 2; + break; + } + + if (playerGrenades < maxGrenades && grenadeName) + { + // CRITICAL BUG: since gives a new entity using GiveNamedItem, + // but the entity is packaged in a weaponbox still exists and will never used or removed. It's leak! + // How reproduced: Drop your grenade on the ground, check output of command `entity_dump`, + // there we will see only get one grenade. Next step - pick it up, do check again `entity_dump`, + // but this time we'll see them x2. + + bEmitSound = true; + pPlayer->GiveNamedItem(grenadeName); + + // unlink this weapon from the box + pItem = m_rgpPlayerItems[i]->m_pNext; + m_rgpPlayerItems[i] = pItem; + continue; + } +#endif + + } + } + else if (pPlayer->HasShield() && i == PRIMARY_WEAPON_SLOT) + { + // ... + } + else + { + auto pNext = m_rgpPlayerItems[i]->m_pNext; + if (pPlayer->AddPlayerItem(pItem)) + { + pItem->AttachToPlayer(pPlayer); + bEmitSound = true; + } + + // unlink this weapon from the box + m_rgpPlayerItems[i] = pItem = pNext; + continue; + } + + bRemove = false; + pItem = m_rgpPlayerItems[i]->m_pNext; + } + } + + if (bRemove) + { + // dole out ammo + for (int n = 0; n < MAX_AMMO_SLOTS; n++) + { + if (!FStringNull(m_rgiszAmmo[n])) + { + // there's some ammo of this type. + pPlayer->GiveAmmo(m_rgAmmo[n], (char *)STRING(m_rgiszAmmo[n]), MaxAmmoCarry(m_rgiszAmmo[n])); + + // now empty the ammo from the weaponbox since we just gave it to the player + m_rgiszAmmo[n] = iStringNull; + m_rgAmmo[n] = 0; + } + } + } + + if (bEmitSound) + { + EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM); + } + + if (bRemove) + { + SetTouch(nullptr); + UTIL_Remove(this); + } +} + +// Add this weapon to the box +BOOL CWeaponBox::PackWeapon(CBasePlayerItem *pWeapon) +{ + // is one of these weapons already packed in this box? + if (HasWeapon(pWeapon)) + { + // box can only hold one of each weapon type + return FALSE; + } + + if (pWeapon->m_pPlayer) + { + if (pWeapon->m_pPlayer->m_pActiveItem == pWeapon) + { + pWeapon->Holster(); + } + + if (!pWeapon->m_pPlayer->RemovePlayerItem(pWeapon)) + { + // failed to unhook the weapon from the player! + return FALSE; + } + } + + int iWeaponSlot = pWeapon->iItemSlot(); + if (m_rgpPlayerItems[iWeaponSlot]) + { + // there's already one weapon in this slot, so link this into the slot's column + pWeapon->m_pNext = m_rgpPlayerItems[iWeaponSlot]; + m_rgpPlayerItems[iWeaponSlot] = pWeapon; + } + else + { + // first weapon we have for this slot + m_rgpPlayerItems[iWeaponSlot] = pWeapon; + pWeapon->m_pNext = nullptr; + } + + // never respawn + pWeapon->pev->spawnflags |= SF_NORESPAWN; + pWeapon->pev->movetype = MOVETYPE_NONE; + pWeapon->pev->solid = SOLID_NOT; + pWeapon->pev->effects = EF_NODRAW; + pWeapon->pev->modelindex = 0; + pWeapon->pev->model = 0; + pWeapon->pev->owner = ENT(pev); + pWeapon->SetThink(nullptr); + pWeapon->SetTouch(nullptr); + pWeapon->m_pPlayer = nullptr; + + return TRUE; +} + +BOOL CWeaponBox::PackAmmo(string_t iszName, int iCount) +{ + if (!iszName) + { + // error here + ALERT(at_console, "NULL String in PackAmmo!\n"); + return FALSE; + } + + int iMaxCarry = MaxAmmoCarry(iszName); + if (iMaxCarry != -1 && iCount > 0) + { + GiveAmmo(iCount, (char *)STRING(iszName), iMaxCarry); + return TRUE; + } + + return FALSE; +} + +int CWeaponBox::GiveAmmo(int iCount, char *szName, int iMax, int *pIndex) +{ + int i; + for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull(m_rgiszAmmo[i]); i++) + { + if (!Q_stricmp(szName, STRING(m_rgiszAmmo[i]))) + { + if (pIndex) + *pIndex = i; + + int iAdd = Q_min(iCount, iMax - m_rgAmmo[i]); + if (iCount == 0 || iAdd > 0) + { + m_rgAmmo[i] += iAdd; + return i; + } + + return -1; + } + } + + if (i < MAX_AMMO_SLOTS) + { + if (pIndex) + *pIndex = i; + + m_rgiszAmmo[i] = MAKE_STRING(szName); + m_rgAmmo[i] = iCount; + + return i; + } + + ALERT(at_console, "out of named ammo slots\n"); + return i; +} + +// Is a weapon of this type already packed in this box? +BOOL CWeaponBox::HasWeapon(CBasePlayerItem *pCheckItem) +{ + CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()]; + while (pItem) + { + if (FClassnameIs(pItem->pev, pCheckItem->pev->classname)) { + return TRUE; + } + + pItem = pItem->m_pNext; + } + + return FALSE; +} + +// Is there anything in this box? +BOOL CWeaponBox::IsEmpty() +{ + int i; + for (i = 0; i < MAX_ITEM_TYPES; i++) + { + if (m_rgpPlayerItems[i]) + { + return FALSE; + } + } + + for (i = 0; i < MAX_AMMO_SLOTS; i++) + { + if (m_rgiszAmmo[i]) + { + // still have a bit of this type of ammo + return FALSE; + } + } + + return TRUE; +} + +void CWeaponBox::SetObjectCollisionBox() +{ + pev->absmin = pev->origin + Vector(-16, -16, 0); + pev->absmax = pev->origin + Vector(16, 16, 16); +} + +char *CArmoury::m_ItemModels[] = { + "models/w_mp5.mdl", + "models/w_tmp.mdl", + "models/w_p90.mdl", + "models/w_mac10.mdl", + "models/w_ak47.mdl", + "models/w_sg552.mdl", + "models/w_m4a1.mdl", + "models/w_aug.mdl", + "models/w_scout.mdl", + "models/w_g3sg1.mdl", + "models/w_awp.mdl", + "models/w_m3.mdl", + "models/w_xm1014.mdl", + "models/w_m249.mdl", + "models/w_flashbang.mdl", + "models/w_hegrenade.mdl", + "models/w_kevlar.mdl", + "models/w_assault.mdl", + "models/w_smokegrenade.mdl", + +#ifdef REGAMEDLL_ADD + "models/w_shield.mdl", + "models/w_famas.mdl", + "models/w_sg550.mdl", + "models/w_galil.mdl", + "models/w_ump45.mdl", + "models/w_glock18.mdl", + "models/w_usp.mdl", + "models/w_elite.mdl", + "models/w_fiveseven.mdl", + "models/w_p228.mdl", + "models/w_deagle.mdl" +#endif + +}; + +void CArmoury::Spawn() +{ + Precache(); + + pev->movetype = MOVETYPE_TOSS; + pev->solid = SOLID_TRIGGER; + + UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16)); + UTIL_SetOrigin(pev, pev->origin); + + SetTouch(&CArmoury::ArmouryTouch); + + if (m_iItem < ARRAYSIZE(m_ItemModels)) + { + SET_MODEL(ENT(pev), m_ItemModels[m_iItem]); + } + + if (m_iCount <= 0) + { + m_iCount = 1; + } + +#ifdef REGAMEDLL_FIXES + // Cache the placed origin of source point + pev->oldorigin = pev->origin; +#endif + + m_bAlreadyCounted = false; + m_iInitialCount = m_iCount; +} + +void CArmoury::Restart() +{ +#ifdef REGAMEDLL_FIXES + // This code refers to the mode of Escape. (Because there is relationship to the team Terrorists) + if (CSGameRules()->m_bMapHasEscapeZone) +#endif + { + if (m_iItem == ARMOURY_FLASHBANG || m_iItem == ARMOURY_HEGRENADE) + { + if (!m_bAlreadyCounted) + { + m_bAlreadyCounted = true; + CSGameRules()->m_iTotalGrenadeCount += m_iInitialCount; + m_iCount = m_iInitialCount; + Draw(); + return; + } + + float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalGrenadeCount) * real_t(CSGameRules()->m_iNumTerrorist) * 1.75; + m_iCount = int(flRatio); + } + else if (m_iItem == ARMOURY_KEVLAR || m_iItem == ARMOURY_ASSAULT) + { + if (!m_bAlreadyCounted) + { + m_bAlreadyCounted = true; + CSGameRules()->m_iTotalArmourCount += m_iInitialCount; + m_iCount = m_iInitialCount; + Draw(); + return; + } + + float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalArmourCount) * real_t(CSGameRules()->m_iNumTerrorist); + m_iCount = int(flRatio); + } + else + { + if (!m_bAlreadyCounted) + { + m_bAlreadyCounted = true; + CSGameRules()->m_iTotalGunCount += m_iInitialCount; + m_iCount = m_iInitialCount; + Draw(); + return; + } + + float flRatio = real_t(m_iInitialCount / CSGameRules()->m_iTotalGunCount) * real_t(CSGameRules()->m_iNumTerrorist) * 0.85; + m_iCount = int(flRatio); + } + } + +#ifdef REGAMEDLL_FIXES + else + { + m_iCount = m_iInitialCount; + } +#endif + + if (m_iCount < 1) + m_iCount = 1; + + Draw(); + +#ifdef REGAMEDLL_FIXES + // Restored origin from the cache + UTIL_SetOrigin(pev, pev->oldorigin); + DROP_TO_FLOOR(edict()); +#endif +} + +void CArmoury::Precache() +{ + if (m_iItem < ARRAYSIZE(m_ItemModels)) + { + PRECACHE_MODEL(m_ItemModels[m_iItem]); + } +} + +void CArmoury::Draw() +{ + pev->effects &= ~EF_NODRAW; + +#ifdef REGAMEDLL_FIXES + pev->solid = SOLID_TRIGGER; +#endif +} + +void CArmoury::Hide() +{ + pev->effects |= EF_NODRAW; + +#ifdef REGAMEDLL_FIXES + // more not to touch with the world. + pev->solid = SOLID_NOT; +#endif +} + +struct ArmouryItemStruct +{ + const char *entityName; + char *ammoName; + int giveAmount; + int maxRounds; +} armouryItemInfo[] = { + { "weapon_mp5navy", "9mm", 60, MAX_AMMO_9MM }, // ARMOURY_MP5NAVY + { "weapon_tmp", "9mm", 60, MAX_AMMO_9MM }, // ARMOURY_TMP + { "weapon_p90", "57mm", 50, MAX_AMMO_57MM }, // ARMOURY_P90 + { "weapon_mac10", "45acp", 60, MAX_AMMO_45ACP }, // ARMOURY_MAC10 + { "weapon_ak47", "762Nato", 60, MAX_AMMO_762NATO }, // ARMOURY_AK47 + { "weapon_sg552", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_SG552 + { "weapon_m4a1", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_M4A1 + { "weapon_aug", "556Nato", 60, MAX_AMMO_556NATO }, // ARMOURY_AUG + { "weapon_scout", "762Nato", 30, MAX_AMMO_762NATO }, // ARMOURY_SCOUT + { "weapon_g3sg1", "762Nato", 30, MAX_AMMO_762NATO }, // ARMOURY_G3SG1 + { "weapon_awp", "338Magnum", 20, MAX_AMMO_338MAGNUM }, // ARMOURY_AWP + { "weapon_m3", "buckshot", 24, MAX_AMMO_BUCKSHOT }, // ARMOURY_M3 + { "weapon_xm1014", "buckshot", 24, MAX_AMMO_BUCKSHOT }, // ARMOURY_XM1014 + { "weapon_m249", "556NatoBox", 60, MAX_AMMO_556NATOBOX }, // ARMOURY_M249 + { nullptr, nullptr, 0, 0 }, // ARMOURY_FLASHBANG + { nullptr, nullptr, 0, 0 }, // ARMOURY_HEGRENADE + { nullptr, nullptr, 0, 0 }, // ARMOURY_KEVLAR + { nullptr, nullptr, 0, 0 }, // ARMOURY_ASSAULT + { nullptr, nullptr, 0, 0 }, // ARMOURY_SMOKEGRENADE + { nullptr, nullptr, 0, 0 }, // ARMOURY_SHIELD + { "weapon_famas", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_FAMAS + { "weapon_sg550", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_SG550 + { "weapon_galil", "556Nato", 90, MAX_AMMO_556NATO }, // ARMOURY_GALIL + { "weapon_ump45", "45acp", 100, MAX_AMMO_45ACP }, // ARMOURY_UMP45 + { "weapon_glock18", "9mm", 120, MAX_AMMO_9MM }, // ARMOURY_GLOCK18 + { "weapon_usp", "45acp", 100, MAX_AMMO_45ACP }, // ARMOURY_USP + { "weapon_elite", "9mm", 120, MAX_AMMO_9MM }, // ARMOURY_ELITE + { "weapon_fiveseven", "57mm", 100, MAX_AMMO_57MM }, // ARMOURY_FIVESEVEN + { "weapon_p228", "357SIG", 52, MAX_AMMO_357SIG }, // ARMOURY_P228 + { "weapon_deagle", "50AE", 35, MAX_AMMO_50AE }, // ARMOURY_DEAGLE +}; + +void CArmoury::ArmouryTouch(CBaseEntity *pOther) +{ + if (!pOther->IsPlayer()) + return; + + CBasePlayer *pToucher = static_cast(pOther); + + if (pToucher->m_bIsVIP) + return; + +#ifdef REGAMEDLL_ADD + if (pToucher->HasRestrictItem(GetItemIdByArmoury(m_iItem), ITEM_TYPE_TOUCHED)) + return; +#endif + +#ifdef REGAMEDLL_FIXES + if (pToucher->IsBot() && TheCSBots() && !TheCSBots()->IsWeaponUseable(m_iItem)) + return; +#endif + + // primary weapons + if (m_iCount > 0 && (m_iItem <= ARMOURY_M249 +#ifdef REGAMEDLL_ADD + || (m_iItem >= ARMOURY_FAMAS && m_iItem <= ARMOURY_UMP45) +#endif +)) + { + if (pToucher->m_bHasPrimary) + return; + + m_iCount--; + auto item = &armouryItemInfo[m_iItem]; + +#ifdef REGAMEDLL_FIXES + pToucher->GiveNamedItemEx(item->entityName); +#else + pToucher->GiveNamedItem(item->entityName); +#endif + + pToucher->GiveAmmo(item->giveAmount, item->ammoName, item->maxRounds); + } +#ifdef REGAMEDLL_ADD + // secondary weapons (pistols) + else if (m_iCount > 0 && m_iItem >= ARMOURY_GLOCK18) + { + if (pToucher->m_rgpPlayerItems[PISTOL_SLOT]) + return; + + if (pToucher->HasShield() && m_iItem == ARMOURY_ELITE) + return; + + m_iCount--; + auto item = &armouryItemInfo[m_iItem]; + + pToucher->GiveNamedItemEx(item->entityName); + pToucher->GiveAmmo(item->giveAmount, item->ammoName, item->maxRounds); + } +#endif + // items & grenades + else if (m_iCount > 0 && m_iItem >= ARMOURY_FLASHBANG) + { + switch (m_iItem) + { + case ARMOURY_FLASHBANG: + { + if (pToucher->AmmoInventory(AMMO_FLASHBANG) >= MaxAmmoCarry(WEAPON_FLASHBANG)) + return; + + pToucher->GiveNamedItem("weapon_flashbang"); + m_iCount--; + break; + } + case ARMOURY_HEGRENADE: + { + if (pToucher->AmmoInventory(AMMO_HEGRENADE) >= MaxAmmoCarry(WEAPON_HEGRENADE)) + return; + + pToucher->GiveNamedItem("weapon_hegrenade"); + m_iCount--; + break; + } + case ARMOURY_KEVLAR: + { + if (pToucher->m_iKevlar == ARMOR_KEVLAR) + return; + + pToucher->GiveNamedItem("item_kevlar"); + m_iCount--; + break; + } + case ARMOURY_ASSAULT: + { + if (pToucher->m_iKevlar == ARMOR_VESTHELM) + return; + + pToucher->GiveNamedItem("item_assaultsuit"); + m_iCount--; + break; + } + case ARMOURY_SMOKEGRENADE: + { + if (pToucher->AmmoInventory(AMMO_SMOKEGRENADE) >= MaxAmmoCarry(WEAPON_SMOKEGRENADE)) + return; + + pToucher->GiveNamedItem("weapon_smokegrenade"); + m_iCount--; + break; + } +#ifdef REGAMEDLL_ADD + case ARMOURY_SHIELD: + { + if (pToucher->m_bHasPrimary || (pToucher->m_rgpPlayerItems[PISTOL_SLOT] && pToucher->GetItemById(WEAPON_ELITE))) + return; + + pToucher->GiveNamedItemEx("weapon_shield"); + m_iCount--; + break; + } +#endif + } + } + + if (!m_iCount) + Hide(); +} + +void CArmoury::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "item")) + { + m_iItem = (ArmouryItemPack)Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "count")) + { + m_iCount = Q_atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CBaseEntity::KeyValue(pkvd); + } +} + +#ifdef REGAMEDLL_FIXES +void CArmoury::SetObjectCollisionBox() +{ + pev->absmin = pev->origin + Vector(-16, -16, 0); + pev->absmax = pev->origin + Vector(16, 16, 16); +} +#endif + +LINK_ENTITY_TO_CLASS(armoury_entity, CArmoury, CCSArmoury) + +#ifdef REGAMEDLL_API +#define m_ItemInfoEx CSPlayerItem()->m_ItemInfo +#else +#define m_ItemInfoEx m_ItemInfoArray[m_iId] +#endif + +const char *CBasePlayerItem::pszAmmo1() const +{ + return m_ItemInfoEx.pszAmmo1; +} + +int CBasePlayerItem::iMaxAmmo1() const +{ + return m_ItemInfoEx.iMaxAmmo1; +} + +const char *CBasePlayerItem::pszAmmo2() const +{ + return m_ItemInfoEx.pszAmmo2; +} + +int CBasePlayerItem::iMaxAmmo2() const +{ + return m_ItemInfoEx.iMaxAmmo2; +} + +const char *CBasePlayerItem::pszName() const +{ + return m_ItemInfoEx.pszName; +} + +int CBasePlayerItem::iMaxClip() const +{ + return m_ItemInfoEx.iMaxClip; +} + +int CBasePlayerItem::iWeight() const +{ + return m_ItemInfoEx.iWeight; +} + +int CBasePlayerItem::iFlags() const +{ + return m_ItemInfoEx.iFlags; +} diff --git a/regamedll/dlls/weapons.h b/regamedll/dlls/weapons.h index 8286bcbe..9f8e85ae 100644 --- a/regamedll/dlls/weapons.h +++ b/regamedll/dlls/weapons.h @@ -1,2021 +1,2021 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -class CBasePlayer; - -const float MAX_NORMAL_BATTERY = 100.0f; -const float MAX_DIST_RELOAD_SOUND = 512.0f; - -#define MAX_WEAPONS 32 - -#define ITEM_FLAG_SELECTONEMPTY 1 -#define ITEM_FLAG_NOAUTORELOAD 2 -#define ITEM_FLAG_NOAUTOSWITCHEMPTY 4 -#define ITEM_FLAG_LIMITINWORLD 8 -#define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon - -#define WEAPON_IS_ONTARGET 0x40 - -// the maximum amount of ammo each weapon's clip can hold -#define WEAPON_NOCLIP -1 - -#define LOUD_GUN_VOLUME 1000 -#define NORMAL_GUN_VOLUME 600 -#define QUIET_GUN_VOLUME 200 - -#define BRIGHT_GUN_FLASH 512 -#define NORMAL_GUN_FLASH 256 -#define DIM_GUN_FLASH 128 - -#define BIG_EXPLOSION_VOLUME 2048 -#define NORMAL_EXPLOSION_VOLUME 1024 -#define SMALL_EXPLOSION_VOLUME 512 - -#define WEAPON_ACTIVITY_VOLUME 64 - -// spawn flags -#define SF_DETONATE BIT(0) // Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges - -// custom enum -enum ArmorType -{ - ARMOR_NONE, // No armor - ARMOR_KEVLAR, // Body vest only - ARMOR_VESTHELM, // Vest and helmet -}; - -enum ArmouryItemPack -{ - ARMOURY_MP5NAVY, - ARMOURY_TMP, - ARMOURY_P90, - ARMOURY_MAC10, - ARMOURY_AK47, - ARMOURY_SG552, - ARMOURY_M4A1, - ARMOURY_AUG, - ARMOURY_SCOUT, - ARMOURY_G3SG1, - ARMOURY_AWP, - ARMOURY_M3, - ARMOURY_XM1014, - ARMOURY_M249, - ARMOURY_FLASHBANG, - ARMOURY_HEGRENADE, - ARMOURY_KEVLAR, - ARMOURY_ASSAULT, - ARMOURY_SMOKEGRENADE, - ARMOURY_SHIELD, - ARMOURY_FAMAS, - ARMOURY_SG550, - ARMOURY_GALIL, - ARMOURY_UMP45, - ARMOURY_GLOCK18, - ARMOURY_USP, - ARMOURY_ELITE, - ARMOURY_FIVESEVEN, - ARMOURY_P228, - ARMOURY_DEAGLE, -}; - -struct ItemInfo -{ - int iSlot; - int iPosition; - const char *pszAmmo1; - int iMaxAmmo1; - const char *pszAmmo2; - int iMaxAmmo2; - const char *pszName; - int iMaxClip; - int iId; - int iFlags; - int iWeight; -}; - -struct AmmoInfo -{ - const char *pszName; - int iId; -}; - -struct MULTIDAMAGE -{ - CBaseEntity *pEntity; - float amount; - int type; -}; - -#include "weapontype.h" -#include "items.h" - -class CArmoury: public CBaseEntity -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual void Restart(); - virtual void KeyValue(KeyValueData *pkvd); -#ifdef REGAMEDLL_FIXES - virtual void SetObjectCollisionBox(); -#endif - -public: - void EXPORT ArmouryTouch(CBaseEntity *pOther); - -private: - void Draw(); - void Hide(); - -public: - static char *m_ItemModels[]; - - ArmouryItemPack m_iItem; - int m_iCount; - int m_iInitialCount; - bool m_bAlreadyCounted; -}; - -class CGrenade: public CBaseMonster -{ -public: - virtual void Spawn(); - virtual int Save(CSave &save); - virtual int Restore(CRestore &restore); - virtual int ObjectCaps() { return m_bIsC4 ? FCAP_CONTINUOUS_USE : 0; } - virtual void Killed(entvars_t *pevAttacker, int iGib); - virtual int BloodColor() { return DONT_BLEED; } - virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); - virtual void BounceSound(); - -public: - enum SATCHELCODE - { - SATCHEL_DETONATE, - SATCHEL_RELEASE, - }; -public: - void DefuseBombStart(CBasePlayer *pPlayer); - void DefuseBombEnd(CBasePlayer *pPlayer, bool bDefused); - - static CGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time); - static CGrenade *ShootTimed2(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, int iTeam, unsigned short usEvent); - static CGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); - static CGrenade *ShootSmokeGrenade(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, unsigned short usEvent); - static CGrenade *ShootSatchelCharge(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); - static void UseSatchelCharges(entvars_t *pevOwner, SATCHELCODE code); -public: - void Explode(Vector vecSrc, Vector vecAim); - void Explode(TraceResult *pTrace, int bitsDamageType); - void Explode2(TraceResult *pTrace, int bitsDamageType); - void Explode3(TraceResult *pTrace, int bitsDamageType); - void SG_Explode(TraceResult *pTrace, int bitsDamageType); - -#ifdef REGAMEDLL_API - static CGrenade *ShootTimed_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time); - static CGrenade *ShootTimed2_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time, int iTeam, unsigned short usEvent); - static CGrenade *ShootSmokeGrenade_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time, unsigned short usEvent); - static CGrenade *ShootSatchelCharge_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity); - - void DefuseBombStart_OrigFunc(CBasePlayer *pPlayer); - void DefuseBombEnd_OrigFunc(CBasePlayer *pPlayer, bool bDefused); - - void Explode_OrigFunc(TraceResult *pTrace, int bitsDamageType); - void Explode3_OrigFunc(TraceResult *pTrace, int bitsDamageType); - void Explode2_OrigFunc(TraceResult *pTrace, int bitsDamageType); - void SG_Detonate_OrigFunc(); -#endif - - void EXPORT Smoke(); - void EXPORT Smoke2(); - void EXPORT Smoke3_A(); - void EXPORT Smoke3_B(); - void EXPORT Smoke3_C(); - void EXPORT SG_Smoke(); - void EXPORT BounceTouch(CBaseEntity *pOther); - void EXPORT SlideTouch(CBaseEntity *pOther); - void EXPORT C4Touch(CBaseEntity *pOther); - void EXPORT ExplodeTouch(CBaseEntity *pOther); - void EXPORT DangerSoundThink(); - void EXPORT PreDetonate(); - void EXPORT Detonate(); - void EXPORT SG_Detonate(); - void EXPORT Detonate2(); - void EXPORT Detonate3(); - void EXPORT DetonateUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); - void EXPORT TumbleThink(); - void EXPORT SG_TumbleThink(); - void EXPORT C4Think(); - -public: - static TYPEDESCRIPTION m_SaveData[]; - - bool m_bStartDefuse; - bool m_bIsC4; - EntityHandle m_pBombDefuser; - float m_flDefuseCountDown; - float m_flC4Blow; - float m_flNextFreqInterval; - float m_flNextBeep; - float m_flNextFreq; - char *m_sBeepName; - float m_fAttenu; - float m_flNextBlink; - float m_fNextDefuse; - bool m_bJustBlew; - int m_iTeam; - int m_iCurWave; - edict_t *m_pentCurBombTarget; - int m_SGSmoke; - int m_angle; - unsigned short m_usEvent; - bool m_bLightSmoke; - bool m_bDetonated; - Vector m_vSmokeDetonate; - int m_iBounceCount; - BOOL m_fRegisteredSound; -}; - -// Items that the player has in their inventory that they can use -class CCSPlayerItem; -class CBasePlayerItem: public CBaseAnimating -{ -public: - virtual int Save(CSave &save); - virtual int Restore(CRestore &restore); - virtual void SetObjectCollisionBox(); - virtual CBaseEntity *Respawn(); - virtual int AddToPlayer(CBasePlayer *pPlayer); // return TRUE if the item you want the item added to the player inventory - virtual int AddDuplicate(CBasePlayerItem *pItem) { return FALSE; } // return TRUE if you want your duplicate removed from world - virtual int GetItemInfo(ItemInfo *p) { return 0; } // returns 0 if struct not filled out - virtual BOOL CanDeploy() { return TRUE; } - virtual BOOL CanDrop() { return TRUE; } // returns is deploy was successful - virtual BOOL Deploy() { return TRUE; } - virtual BOOL IsWeapon() { return FALSE; } - virtual BOOL CanHolster() { return TRUE; } // can this weapon be put away right now? - virtual void Holster(int skiplocal = 0); - virtual void UpdateItemInfo() {} - virtual void ItemPreFrame() {} // called each frame by the player PreThink - virtual void ItemPostFrame() {} // called each frame by the player PostThink - virtual void Drop(); - virtual void Kill(); - virtual void AttachToPlayer(CBasePlayer *pPlayer); - virtual int PrimaryAmmoIndex() { return -1; } - virtual int SecondaryAmmoIndex() { return -1; } - virtual int UpdateClientData(CBasePlayer *pPlayer) { return 0; } - virtual CBasePlayerItem *GetWeaponPtr() { return nullptr; } - virtual float GetMaxSpeed() { return 260.0f; } - virtual int iItemSlot() { return 0; } // return 0 to MAX_ITEMS_SLOTS, used in hud - -public: - void EXPORT DestroyItem(); - void EXPORT DefaultTouch(CBaseEntity *pOther); - void EXPORT FallThink(); - void EXPORT Materialize(); - void EXPORT AttemptToMaterialize(); - - void FallInit(); - void CheckRespawn(); - -public: -#ifdef REGAMEDLL_API - CCSPlayerItem *CSPlayerItem() const; -#endif - - const char *pszAmmo1() const; - int iMaxAmmo1() const; - const char *pszAmmo2() const; - int iMaxAmmo2() const; - const char *pszName() const; - int iMaxClip() const; - int iWeight() const; - int iFlags() const; - -public: - static TYPEDESCRIPTION m_SaveData[]; - static ItemInfo m_ItemInfoArray[MAX_WEAPONS]; - static AmmoInfo m_AmmoInfoArray[MAX_AMMO_SLOTS]; - - CBasePlayer *m_pPlayer; - CBasePlayerItem *m_pNext; - int m_iId; // WEAPON_??? -}; - -#ifdef REGAMEDLL_API -inline CCSPlayerItem *CBasePlayerItem::CSPlayerItem() const -{ - return reinterpret_cast(this->m_pEntity); -} -#endif - -// inventory items that -class CCSPlayerWeapon; -class CBasePlayerWeapon: public CBasePlayerItem -{ -public: - virtual void Spawn(); - virtual int Save(CSave &save); - virtual int Restore(CRestore &restore); - - // generic weapon versions of CBasePlayerItem calls - virtual int AddToPlayer(CBasePlayer *pPlayer); - virtual int AddDuplicate(CBasePlayerItem *pItem); - virtual BOOL CanDeploy(); - virtual BOOL IsWeapon() { return TRUE; } - virtual void Holster(int skiplocal = 0); - virtual void UpdateItemInfo() {}; - virtual void ItemPostFrame(); - virtual int PrimaryAmmoIndex(); - virtual int SecondaryAmmoIndex(); - virtual int UpdateClientData(CBasePlayer *pPlayer); - virtual CBasePlayerItem *GetWeaponPtr() { return (CBasePlayerItem *)this; } - virtual int ExtractAmmo(CBasePlayerWeapon *pWeapon); - virtual int ExtractClipAmmo(CBasePlayerWeapon *pWeapon); - virtual int AddWeapon() - { - ExtractAmmo(this); - return 1; - } - virtual BOOL PlayEmptySound(); - virtual void ResetEmptySound(); - virtual void SendWeaponAnim(int iAnim, int skiplocal = 0); - virtual BOOL IsUseable(); - virtual void PrimaryAttack() {}; - virtual void SecondaryAttack() {}; - virtual void Reload() {}; - virtual void WeaponIdle() {}; - virtual void RetireWeapon(); - virtual BOOL ShouldWeaponIdle() { return FALSE; } - virtual BOOL UseDecrement() { return FALSE; } - -public: - BOOL AddPrimaryAmmo(int iCount, char *szName, int iMaxClip, int iMaxCarry); - BOOL AddSecondaryAmmo(int iCount, char *szName, int iMaxCarry); - BOOL DefaultDeploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal = 0); - int DefaultReload(int iClipSize, int iAnim, float fDelay); - void FireRemaining(int &shotsFired, float &shootTime, BOOL isGlock18); - void KickBack(float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change); - void EjectBrassLate(); - void MakeBeam(); - void BeamUpdate(); - void ReloadSound(); - float GetNextAttackDelay(float delay); - float GetNextAttackDelay2(float delay); - bool HasSecondaryAttack(); - BOOL IsPistol() { return (m_iId == WEAPON_USP || m_iId == WEAPON_GLOCK18 || m_iId == WEAPON_P228 || m_iId == WEAPON_DEAGLE || m_iId == WEAPON_ELITE || m_iId == WEAPON_FIVESEVEN); } - void SetPlayerShieldAnim(); - void ResetPlayerShieldAnim(); - bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); - void HandleInfiniteAmmo(); - void InstantReload(bool bCanRefillBPAmmo = false); - -#ifdef REGAMEDLL_API - CCSPlayerWeapon *CSPlayerWeapon() const; -#endif - -public: - static TYPEDESCRIPTION m_SaveData[]; - - int m_iPlayEmptySound; - int m_fFireOnEmpty; - float m_flNextPrimaryAttack; // soonest time ItemPostFrame will call PrimaryAttack - float m_flNextSecondaryAttack; // soonest time ItemPostFrame will call SecondaryAttack - float m_flTimeWeaponIdle; // soonest time ItemPostFrame will call WeaponIdle - int m_iPrimaryAmmoType; // "primary" ammo index into players m_rgAmmo[] - int m_iSecondaryAmmoType; // "secondary" ammo index into players m_rgAmmo[] - int m_iClip; // number of shots left in the primary weapon clip, -1 it not used - int m_iClientClip; // the last version of m_iClip sent to hud dll - int m_iClientWeaponState; // the last version of the weapon state sent to hud dll (is current weapon, is on target) - int m_fInReload; // Are we in the middle of a reload; - int m_fInSpecialReload; // Are we in the middle of a reload for the shotguns - int m_iDefaultAmmo; // how much ammo you get when you pick up this weapon as placed by a level designer. - int m_iShellId; - float m_fMaxSpeed; - bool m_bDelayFire; - int m_iDirection; - bool m_bSecondarySilencerOn; - float m_flAccuracy; - float m_flLastFire; - int m_iShotsFired; - Vector m_vVecAiming; - string_t model_name; - float m_flGlock18Shoot; // time to shoot the remaining bullets of the glock18 burst fire - int m_iGlock18ShotsFired; // used to keep track of the shots fired during the Glock18 burst fire mode. - float m_flFamasShoot; - int m_iFamasShotsFired; - float m_fBurstSpread; - int m_iWeaponState; - float m_flNextReload; - float m_flDecreaseShotsFired; - unsigned short m_usFireGlock18; - unsigned short m_usFireFamas; - - // hle time creep vars - float m_flPrevPrimaryAttack; - float m_flLastFireTime; -}; - -#ifdef REGAMEDLL_API -inline CCSPlayerWeapon *CBasePlayerWeapon::CSPlayerWeapon() const -{ - return reinterpret_cast(this->m_pEntity); -} -#endif - -class CWeaponBox: public CBaseEntity -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual void KeyValue(KeyValueData *pkvd); - virtual int Save(CSave &save); - virtual int Restore(CRestore &restore); - virtual void SetObjectCollisionBox(); - virtual void Touch(CBaseEntity *pOther); - -public: - BOOL IsEmpty(); - int GiveAmmo(int iCount, char *szName, int iMax, int *pIndex = nullptr); - - void EXPORT Kill(); - void EXPORT BombThink(); - void SetModel(const char *pszModelName); - - BOOL HasWeapon(CBasePlayerItem *pCheckItem); - BOOL PackWeapon(CBasePlayerItem *pWeapon); - BOOL PackAmmo(string_t iszName, int iCount); - -#ifdef REGAMEDLL_API - void SetModel_OrigFunc(const char *pszModelName); -#endif - -public: - static TYPEDESCRIPTION m_SaveData[]; - - CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES]; - string_t m_rgiszAmmo[MAX_AMMO_SLOTS]; - int m_rgAmmo[MAX_AMMO_SLOTS]; - int m_cAmmoTypes; - bool m_bIsBomb; -}; - - -const float USP_MAX_SPEED = 250.0f; -const float USP_DAMAGE = 34.0f; -const float USP_DAMAGE_SIL = 30.0f; -const float USP_RANGE_MODIFER = 0.79f; -const float USP_RELOAD_TIME = 2.7f; - -enum usp_e -{ - USP_IDLE, - USP_SHOOT1, - USP_SHOOT2, - USP_SHOOT3, - USP_SHOOT_EMPTY, - USP_RELOAD, - USP_DRAW, - USP_ATTACH_SILENCER, - USP_UNSIL_IDLE, - USP_UNSIL_SHOOT1, - USP_UNSIL_SHOOT2, - USP_UNSIL_SHOOT3, - USP_UNSIL_SHOOT_EMPTY, - USP_UNSIL_RELOAD, - USP_UNSIL_DRAW, - USP_DETACH_SILENCER, -}; - -enum usp_shield_e -{ - USP_SHIELD_IDLE, - USP_SHIELD_SHOOT1, - USP_SHIELD_SHOOT2, - USP_SHIELD_SHOOT_EMPTY, - USP_SHIELD_RELOAD, - USP_SHIELD_DRAW, - USP_SHIELD_UP_IDLE, - USP_SHIELD_UP, - USP_SHIELD_DOWN, -}; - -class CUSP: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void USPFire(float flSpread, float flCycleTime, BOOL fUseSemi); - void MakeBeam(); - void BeamUpdate(); - int m_iShell; - -private: - unsigned short m_usFireUSP; - -#ifdef REGAMEDLL_API - float m_flBaseDamageSil; -#endif -}; - - -const float MP5N_MAX_SPEED = 250.0f; -const float MP5N_DAMAGE = 26.0f; -const float MP5N_RANGE_MODIFER = 0.84f; -const float MP5N_RELOAD_TIME = 2.63f; - -enum mp5n_e -{ - MP5N_IDLE1, - MP5N_RELOAD, - MP5N_DRAW, - MP5N_SHOOT1, - MP5N_SHOOT2, - MP5N_SHOOT3, -}; - -class CMP5N: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return MP5N_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void MP5NFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireMP5N; -}; - - -const float SG552_MAX_SPEED = 235.0f; -const float SG552_MAX_SPEED_ZOOM = 200.0f; -const float SG552_DAMAGE = 33.0f; -const float SG552_RANGE_MODIFER = 0.955f; -const float SG552_RELOAD_TIME = 3.0f; - -enum sg552_e -{ - SG552_IDLE1, - SG552_RELOAD, - SG552_DRAW, - SG552_SHOOT1, - SG552_SHOOT2, - SG552_SHOOT3, -}; - -class CSG552: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void SG552Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireSG552; -}; - - -const float AK47_MAX_SPEED = 221.0f; -const float AK47_DAMAGE = 36.0f; -const float AK47_RANGE_MODIFER = 0.98f; -const float AK47_RELOAD_TIME = 2.45f; - -enum ak47_e -{ - AK47_IDLE1, - AK47_RELOAD, - AK47_DRAW, - AK47_SHOOT1, - AK47_SHOOT2, - AK47_SHOOT3, -}; - -class CAK47: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return AK47_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void AK47Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireAK47; -}; - - -const float AUG_MAX_SPEED = 240.0f; -const float AUG_DAMAGE = 32.0f; -const float AUG_RANGE_MODIFER = 0.96f; -const float AUG_RELOAD_TIME = 3.3f; - -enum aug_e -{ - AUG_IDLE1, - AUG_RELOAD, - AUG_DRAW, - AUG_SHOOT1, - AUG_SHOOT2, - AUG_SHOOT3, -}; - -class CAUG: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return AUG_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void AUGFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireAug; -}; - - -const float AWP_MAX_SPEED = 210.0f; -const float AWP_MAX_SPEED_ZOOM = 150.0f; -const float AWP_DAMAGE = 115.0f; -const float AWP_RANGE_MODIFER = 0.99f; -const float AWP_RELOAD_TIME = 2.5f; - -enum awp_e -{ - AWP_IDLE, - AWP_SHOOT, - AWP_SHOOT2, - AWP_SHOOT3, - AWP_RELOAD, - AWP_DRAW, -}; - -class CAWP: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void AWPFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - -private: - unsigned short m_usFireAWP; -}; - - -// for usermsg BombDrop -#define BOMB_FLAG_DROPPED 0 // if the bomb was dropped due to voluntary dropping or death/disconnect -#define BOMB_FLAG_PLANTED 1 // if the bomb has been planted will also trigger the round timer to hide will also show where the dropped bomb on the Terrorist team's radar. - -const float C4_MAX_AMMO = 1.0f; -const float C4_MAX_SPEED = 250.0f; -const float C4_ARMING_ON_TIME = 3.0f; - -enum c4_e -{ - C4_IDLE1, - C4_DRAW, - C4_DROP, - C4_ARM, -}; - -class CC4: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual void KeyValue(KeyValueData *pkvd); - virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual void Holster(int skiplocal); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return C4_SLOT; } - virtual void PrimaryAttack(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - bool m_bStartedArming; - bool m_bBombPlacedAnimation; - float m_fArmedTime; - -private: - bool m_bHasShield; -}; - - -const float DEAGLE_MAX_SPEED = 250.0f; -const float DEAGLE_DAMAGE = 54.0f; -const float DEAGLE_RANGE_MODIFER = 0.81f; -const float DEAGLE_RELOAD_TIME = 2.2f; - -enum deagle_e -{ - DEAGLE_IDLE1, - DEAGLE_SHOOT1, - DEAGLE_SHOOT2, - DEAGLE_SHOOT_EMPTY, - DEAGLE_RELOAD, - DEAGLE_DRAW, -}; - -class CDEAGLE: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void DEAGLEFire(float flSpread, float flCycleTime, BOOL fUseSemi); - - int m_iShell; - -private: - unsigned short m_usFireDeagle; -}; - - -const float FLASHBANG_MAX_SPEED = 250.0f; -const float FLASHBANG_MAX_SPEED_SHIELD = 180.0f; - -enum flashbang_e -{ - FLASHBANG_IDLE, - FLASHBANG_PULLPIN, - FLASHBANG_THROW, - FLASHBANG_DRAW, -}; - -class CFlashbang: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL CanDeploy(); - virtual BOOL CanDrop() { return FALSE; } - virtual BOOL Deploy(); - virtual void Holster(int skiplocal); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return GRENADE_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() - { - #ifdef REGAMEDLL_FIXES - return FALSE; - #else - // TODO: why the object flashbang is IsPistol? - return TRUE; - #endif - } - -public: - bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); - void SetPlayerShieldAnim(); - void ResetPlayerShieldAnim(); -}; - - -const float G3SG1_MAX_SPEED = 210.0f; -const float G3SG1_MAX_SPEED_ZOOM = 150.0f; -const float G3SG1_DAMAGE = 80.0f; -const float G3SG1_RANGE_MODIFER = 0.98f; -const float G3SG1_RELOAD_TIME = 3.5f; - -enum g3sg1_e -{ - G3SG1_IDLE, - G3SG1_SHOOT, - G3SG1_SHOOT2, - G3SG1_RELOAD, - G3SG1_DRAW, -}; - -class CG3SG1: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void G3SG1Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - -private: - unsigned short m_usFireG3SG1; -}; - - -const float GLOCK18_MAX_SPEED = 250.0f; -const float GLOCK18_DAMAGE = 25.0f; -const float GLOCK18_RANGE_MODIFER = 0.75f; -const float GLOCK18_RELOAD_TIME = 2.2f; - -enum glock18_e -{ - GLOCK18_IDLE1, - GLOCK18_IDLE2, - GLOCK18_IDLE3, - GLOCK18_SHOOT, - GLOCK18_SHOOT2, - GLOCK18_SHOOT3, - GLOCK18_SHOOT_EMPTY, - GLOCK18_RELOAD, - GLOCK18_DRAW, - GLOCK18_HOLSTER, - GLOCK18_ADD_SILENCER, - GLOCK18_DRAW2, - GLOCK18_RELOAD2, -}; - -enum glock18_shield_e -{ - GLOCK18_SHIELD_IDLE1, - GLOCK18_SHIELD_SHOOT, - GLOCK18_SHIELD_SHOOT2, - GLOCK18_SHIELD_SHOOT_EMPTY, - GLOCK18_SHIELD_RELOAD, - GLOCK18_SHIELD_DRAW, - GLOCK18_SHIELD_IDLE, - GLOCK18_SHIELD_UP, - GLOCK18_SHIELD_DOWN, -}; - -class CGLOCK18: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void GLOCK18Fire(float flSpread, float flCycleTime, BOOL bFireBurst); - -public: - int m_iShell; - bool m_bBurstFire; -}; - - -const float HEGRENADE_MAX_SPEED = 250.0f; -const float HEGRENADE_MAX_SPEED_SHIELD = 180.0f; - -enum hegrenade_e -{ - HEGRENADE_IDLE, - HEGRENADE_PULLPIN, - HEGRENADE_THROW, - HEGRENADE_DRAW, -}; - -class CHEGrenade: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL CanDeploy(); - virtual BOOL CanDrop() { return FALSE; } - virtual BOOL Deploy(); - virtual void Holster(int skiplocal); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return GRENADE_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); - void SetPlayerShieldAnim(); - void ResetPlayerShieldAnim(); - -public: - unsigned short m_usCreateExplosion; -}; - - -const float KNIFE_BODYHIT_VOLUME = 128.0f; -const float KNIFE_WALLHIT_VOLUME = 512.0f; -const float KNIFE_MAX_SPEED = 250.0f; -const float KNIFE_MAX_SPEED_SHIELD = 180.0f; - -enum knife_e -{ - KNIFE_IDLE, - KNIFE_ATTACK1HIT, - KNIFE_ATTACK2HIT, - KNIFE_DRAW, - KNIFE_STABHIT, - KNIFE_STABMISS, - KNIFE_MIDATTACK1HIT, - KNIFE_MIDATTACK2HIT, -}; - -enum knife_shield_e -{ - KNIFE_SHIELD_IDLE, - KNIFE_SHIELD_SLASH, - KNIFE_SHIELD_ATTACKHIT, - KNIFE_SHIELD_DRAW, - KNIFE_SHIELD_UPIDLE, - KNIFE_SHIELD_UP, - KNIFE_SHIELD_DOWN, -}; - -class CKnife: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL CanDrop() { return FALSE; } - virtual BOOL Deploy(); - virtual void Holster(int skiplocal); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return KNIFE_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual void WeaponIdle(); - -public: - void EXPORT SwingAgain(); - void EXPORT Smack(); - - void WeaponAnimation(int iAnimation); - BOOL Stab(BOOL fFirst); - BOOL Swing(BOOL fFirst); - -public: - bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); - void SetPlayerShieldAnim(); - void ResetPlayerShieldAnim(); - -public: - TraceResult m_trHit; - unsigned short m_usKnife; -}; - - -const float M249_MAX_SPEED = 220.0f; -const float M249_DAMAGE = 32.0f; -const float M249_RANGE_MODIFER = 0.97f; -const float M249_RELOAD_TIME = 4.7f; - -enum m249_e -{ - M249_IDLE1, - M249_SHOOT1, - M249_SHOOT2, - M249_RELOAD, - M249_DRAW, -}; - -class CM249: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return M249_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void M249Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireM249; -}; - - -const float M3_MAX_SPEED = 230.0f; -const float M3_DAMAGE = 20.0f; -const Vector M3_CONE_VECTOR = Vector(0.0675, 0.0675, 0.0); // special shotgun spreads - -enum m3_e -{ - M3_IDLE, - M3_FIRE1, - M3_FIRE2, - M3_RELOAD, - M3_PUMP, - M3_START_RELOAD, - M3_DRAW, - M3_HOLSTER, -}; - -class CM3: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return M3_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual BOOL PlayEmptySound(); - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - int m_iShell; - float m_flPumpTime; - -private: - unsigned short m_usFireM3; -}; - - -const float M4A1_MAX_SPEED = 230.0f; -const float M4A1_DAMAGE = 32.0f; -const float M4A1_DAMAGE_SIL = 33.0f; -const float M4A1_RANGE_MODIFER = 0.97f; -const float M4A1_RANGE_MODIFER_SIL = 0.95f; -const float M4A1_RELOAD_TIME = 3.05f; - -enum m4a1_e -{ - M4A1_IDLE, - M4A1_SHOOT1, - M4A1_SHOOT2, - M4A1_SHOOT3, - M4A1_RELOAD, - M4A1_DRAW, - M4A1_ATTACH_SILENCER, - M4A1_UNSIL_IDLE, - M4A1_UNSIL_SHOOT1, - M4A1_UNSIL_SHOOT2, - M4A1_UNSIL_SHOOT3, - M4A1_UNSIL_RELOAD, - M4A1_UNSIL_DRAW, - M4A1_DETACH_SILENCER, -}; - -class CM4A1: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void M4A1Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireM4A1; - -#ifdef REGAMEDLL_API - float m_flBaseDamageSil; -#endif -}; - - -const float MAC10_MAX_SPEED = 250.0f; -const float MAC10_DAMAGE = 29.0f; -const float MAC10_RANGE_MODIFER = 0.82f; -const float MAC10_RELOAD_TIME = 3.15f; - -enum mac10_e -{ - MAC10_IDLE1, - MAC10_RELOAD, - MAC10_DRAW, - MAC10_SHOOT1, - MAC10_SHOOT2, - MAC10_SHOOT3, -}; - -class CMAC10: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return MAC10_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void MAC10Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireMAC10; -}; - - -const float P228_MAX_SPEED = 250.0f; -const float P228_DAMAGE = 32.0f; -const float P228_RANGE_MODIFER = 0.8f; -const float P228_RELOAD_TIME = 2.7f; - -enum p228_e -{ - P228_IDLE, - P228_SHOOT1, - P228_SHOOT2, - P228_SHOOT3, - P228_SHOOT_EMPTY, - P228_RELOAD, - P228_DRAW, -}; - -enum p228_shield_e -{ - P228_SHIELD_IDLE, - P228_SHIELD_SHOOT1, - P228_SHIELD_SHOOT2, - P228_SHIELD_SHOOT_EMPTY, - P228_SHIELD_RELOAD, - P228_SHIELD_DRAW, - P228_SHIELD_IDLE_UP, - P228_SHIELD_UP, - P228_SHIELD_DOWN, -}; - -class CP228: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void P228Fire(float flSpread, float flCycleTime, BOOL fUseSemi); - void MakeBeam(); - void BeamUpdate(); - -public: - int m_iShell; - -private: - unsigned short m_usFireP228; -}; - - -const float P90_MAX_SPEED = 245.0f; -const float P90_DAMAGE = 21.0f; -const float P90_RANGE_MODIFER = 0.885f; -const float P90_RELOAD_TIME = 3.4f; - -enum p90_e -{ - P90_IDLE1, - P90_RELOAD, - P90_DRAW, - P90_SHOOT1, - P90_SHOOT2, - P90_SHOOT3, -}; - -class CP90: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void P90Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireP90; -}; - - -const float SCOUT_MAX_SPEED = 260.0f; -const float SCOUT_MAX_SPEED_ZOOM = 220.0f; -const float SCOUT_DAMAGE = 75.0f; -const float SCOUT_RANGE_MODIFER = 0.98f; -const float SCOUT_RELOAD_TIME = 2.0f; - -enum scout_e -{ - SCOUT_IDLE, - SCOUT_SHOOT, - SCOUT_SHOOT2, - SCOUT_RELOAD, - SCOUT_DRAW, -}; - -class CSCOUT: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void SCOUTFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - int m_iShell; - -private: - unsigned short m_usFireScout; -}; - - -const float SMOKEGRENADE_MAX_SPEED = 250.0f; -const float SMOKEGRENADE_MAX_SPEED_SHIELD = 180.0f; - -enum smokegrenade_e -{ - SMOKEGRENADE_IDLE, - SMOKEGRENADE_PINPULL, - SMOKEGRENADE_THROW, - SMOKEGRENADE_DRAW, -}; - -class CSmokeGrenade: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL CanDeploy(); - virtual BOOL CanDrop() { return FALSE; } - virtual BOOL Deploy(); - virtual void Holster(int skiplocal); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return GRENADE_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); - void SetPlayerShieldAnim(); - void ResetPlayerShieldAnim(); - -public: - unsigned short m_usCreateSmoke; -}; - - -const float TMP_MAX_SPEED = 250.0f; -const float TMP_DAMAGE = 20.0f; -const float TMP_RANGE_MODIFER = 0.85f; -const float TMP_RELOAD_TIME = 2.12f; - -enum tmp_e -{ - TMP_IDLE1, - TMP_RELOAD, - TMP_DRAW, - TMP_SHOOT1, - TMP_SHOOT2, - TMP_SHOOT3, -}; - -class CTMP: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return TMP_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void TMPFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - -public: - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireTMP; -}; - - -const float XM1014_MAX_SPEED = 240.0f; -const float XM1014_DAMAGE = 20.0f; -const Vector XM1014_CONE_VECTOR = Vector(0.0725, 0.0725, 0.0); // special shotgun spreads - -enum xm1014_e -{ - XM1014_IDLE, - XM1014_FIRE1, - XM1014_FIRE2, - XM1014_RELOAD, - XM1014_PUMP, - XM1014_START_RELOAD, - XM1014_DRAW, -}; - -class CXM1014: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return XM1014_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual BOOL PlayEmptySound(); - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - int m_iShell; - float m_flPumpTime; - -private: - unsigned short m_usFireXM1014; -}; - - -const float ELITE_MAX_SPEED = 250.0f; -const float ELITE_RELOAD_TIME = 4.5f; -const float ELITE_DAMAGE = 36.0f; -const float ELITE_RANGE_MODIFER = 0.75f; - -enum elite_e -{ - ELITE_IDLE, - ELITE_IDLE_LEFTEMPTY, - ELITE_SHOOTLEFT1, - ELITE_SHOOTLEFT2, - ELITE_SHOOTLEFT3, - ELITE_SHOOTLEFT4, - ELITE_SHOOTLEFT5, - ELITE_SHOOTLEFTLAST, - ELITE_SHOOTRIGHT1, - ELITE_SHOOTRIGHT2, - ELITE_SHOOTRIGHT3, - ELITE_SHOOTRIGHT4, - ELITE_SHOOTRIGHT5, - ELITE_SHOOTRIGHTLAST, - ELITE_RELOAD, - ELITE_DRAW, -}; - -class CELITE: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return ELITE_MAX_SPEED; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void ELITEFire(float flSpread, float flCycleTime, BOOL fUseSemi); - -public: - int m_iShell; -private: - unsigned short m_usFireELITE_LEFT; - unsigned short m_usFireELITE_RIGHT; -}; - - -const float FIVESEVEN_MAX_SPEED = 250.0f; -const float FIVESEVEN_DAMAGE = 20.0f; -const float FIVESEVEN_RANGE_MODIFER = 0.885f; -const float FIVESEVEN_RELOAD_TIME = 2.7f; - -enum fiveseven_e -{ - FIVESEVEN_IDLE, - FIVESEVEN_SHOOT1, - FIVESEVEN_SHOOT2, - FIVESEVEN_SHOOT_EMPTY, - FIVESEVEN_RELOAD, - FIVESEVEN_DRAW, -}; - -class CFiveSeven: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return m_fMaxSpeed; } - virtual int iItemSlot() { return PISTOL_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - virtual BOOL IsPistol() { return TRUE; } - -public: - void FiveSevenFire(float flSpread, float flCycleTime, BOOL fUseSemi); - void MakeBeam(); - void BeamUpdate(); - -public: - int m_iShell; - -private: - unsigned short m_usFireFiveSeven; -}; - - -const float UMP45_MAX_SPEED = 250.0f; -const float UMP45_DAMAGE = 30.0f; -const float UMP45_RANGE_MODIFER = 0.82f; -const float UMP45_RELOAD_TIME = 3.5f; - -enum ump45_e -{ - UMP45_IDLE1, - UMP45_RELOAD, - UMP45_DRAW, - UMP45_SHOOT1, - UMP45_SHOOT2, - UMP45_SHOOT3, -}; - -class CUMP45: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return UMP45_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void UMP45Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireUMP45; -}; - - -const float SG550_MAX_SPEED = 210.0f; -const float SG550_MAX_SPEED_ZOOM = 150.0f; -const float SG550_DAMAGE = 70.0f; -const float SG550_RANGE_MODIFER = 0.98f; -const float SG550_RELOAD_TIME = 3.35f; - -enum sg550_e -{ - SG550_IDLE, - SG550_SHOOT, - SG550_SHOOT2, - SG550_RELOAD, - SG550_DRAW, -}; - -class CSG550: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed(); - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void SG550Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - int m_iShell; - -private: - unsigned short m_usFireSG550; -}; - - -const float GALIL_MAX_SPEED = 240.0f; -const float GALIL_DAMAGE = 30.0f; -const float GALIL_RANGE_MODIFER = 0.98f; -const float GALIL_RELOAD_TIME = 2.45f; - -enum galil_e -{ - GALIL_IDLE1, - GALIL_RELOAD, - GALIL_DRAW, - GALIL_SHOOT1, - GALIL_SHOOT2, - GALIL_SHOOT3, -}; - -class CGalil: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return GALIL_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void GalilFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); - -public: - int m_iShell; - int iShellOn; - -private: - unsigned short m_usFireGalil; -}; - - -const float FAMAS_MAX_SPEED = 240.0f; -const float FAMAS_RELOAD_TIME = 3.3f; -const float FAMAS_DAMAGE = 30.0f; -const float FAMAS_DAMAGE_BURST = 34.0f; -const float FAMAS_RANGE_MODIFER = 0.96f; - -enum famas_e -{ - FAMAS_IDLE1, - FAMAS_RELOAD, - FAMAS_DRAW, - FAMAS_SHOOT1, - FAMAS_SHOOT2, - FAMAS_SHOOT3, -}; - -class CFamas: public CBasePlayerWeapon -{ -public: - virtual void Spawn(); - virtual void Precache(); - virtual int GetItemInfo(ItemInfo *p); - virtual BOOL Deploy(); - virtual float GetMaxSpeed() { return FAMAS_MAX_SPEED; } - virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } - virtual void PrimaryAttack(); - virtual void SecondaryAttack(); - virtual void Reload(); - virtual void WeaponIdle(); - virtual BOOL UseDecrement() - { - #ifdef CLIENT_WEAPONS - return TRUE; - #else - return FALSE; - #endif - } - -public: - void FamasFire(float flSpread, float flCycleTime, BOOL fUseAutoAim, BOOL bFireBurst); - -public: - int m_iShell; - int iShellOn; - -#ifdef REGAMEDLL_API - float m_flBaseDamageBurst; -#endif -}; - -extern short g_sModelIndexLaser; -extern short g_sModelIndexLaserDot; - -extern short g_sModelIndexFireball; -extern short g_sModelIndexSmoke; -extern short g_sModelIndexWExplosion; -extern short g_sModelIndexBubbles; -extern short g_sModelIndexBloodDrop; -extern short g_sModelIndexBloodSpray; -extern short g_sModelIndexSmokePuff; -extern short g_sModelIndexFireball2; -extern short g_sModelIndexFireball3; -extern short g_sModelIndexFireball4; -extern short g_sModelIndexCTGhost; -extern short g_sModelIndexTGhost; -extern short g_sModelIndexC4Glow; - -extern short g_sModelIndexRadio; -extern MULTIDAMAGE gMultiDamage; - -void WeaponsPrecache(); -void FindHullIntersection(const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity); -void AnnounceFlashInterval(float interval, float offset = 0); - -int MaxAmmoCarry(const char *szName); -int MaxAmmoCarry(WeaponIdType ammoType); - -void ClearMultiDamage(); -void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker); -void AddMultiDamage(entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType); -void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage); -int DamageDecal(CBaseEntity *pEntity, int bitsDamageType); -void DecalGunshot(TraceResult *pTrace, int iBulletType, bool ClientOnly, entvars_t *pShooter, bool bHitMetal); -void EjectBrass(const Vector &vecOrigin, const Vector &vecLeft, const Vector &vecVelocity, float rotation, int model, int soundtype, int entityIndex); -void EjectBrass2(const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype, entvars_t *pev); -void AddAmmoNameToAmmoRegistry(const char *szAmmoname); -void UTIL_PrecacheOtherWeapon(const char *szClassname); -BOOL CanAttack(float attack_time, float curtime, BOOL isPredicted); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class CBasePlayer; + +const float MAX_NORMAL_BATTERY = 100.0f; +const float MAX_DIST_RELOAD_SOUND = 512.0f; + +#define MAX_WEAPONS 32 + +#define ITEM_FLAG_SELECTONEMPTY 1 +#define ITEM_FLAG_NOAUTORELOAD 2 +#define ITEM_FLAG_NOAUTOSWITCHEMPTY 4 +#define ITEM_FLAG_LIMITINWORLD 8 +#define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon + +#define WEAPON_IS_ONTARGET 0x40 + +// the maximum amount of ammo each weapon's clip can hold +#define WEAPON_NOCLIP -1 + +#define LOUD_GUN_VOLUME 1000 +#define NORMAL_GUN_VOLUME 600 +#define QUIET_GUN_VOLUME 200 + +#define BRIGHT_GUN_FLASH 512 +#define NORMAL_GUN_FLASH 256 +#define DIM_GUN_FLASH 128 + +#define BIG_EXPLOSION_VOLUME 2048 +#define NORMAL_EXPLOSION_VOLUME 1024 +#define SMALL_EXPLOSION_VOLUME 512 + +#define WEAPON_ACTIVITY_VOLUME 64 + +// spawn flags +#define SF_DETONATE BIT(0) // Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges + +// custom enum +enum ArmorType +{ + ARMOR_NONE, // No armor + ARMOR_KEVLAR, // Body vest only + ARMOR_VESTHELM, // Vest and helmet +}; + +enum ArmouryItemPack +{ + ARMOURY_MP5NAVY, + ARMOURY_TMP, + ARMOURY_P90, + ARMOURY_MAC10, + ARMOURY_AK47, + ARMOURY_SG552, + ARMOURY_M4A1, + ARMOURY_AUG, + ARMOURY_SCOUT, + ARMOURY_G3SG1, + ARMOURY_AWP, + ARMOURY_M3, + ARMOURY_XM1014, + ARMOURY_M249, + ARMOURY_FLASHBANG, + ARMOURY_HEGRENADE, + ARMOURY_KEVLAR, + ARMOURY_ASSAULT, + ARMOURY_SMOKEGRENADE, + ARMOURY_SHIELD, + ARMOURY_FAMAS, + ARMOURY_SG550, + ARMOURY_GALIL, + ARMOURY_UMP45, + ARMOURY_GLOCK18, + ARMOURY_USP, + ARMOURY_ELITE, + ARMOURY_FIVESEVEN, + ARMOURY_P228, + ARMOURY_DEAGLE, +}; + +struct ItemInfo +{ + int iSlot; + int iPosition; + const char *pszAmmo1; + int iMaxAmmo1; + const char *pszAmmo2; + int iMaxAmmo2; + const char *pszName; + int iMaxClip; + int iId; + int iFlags; + int iWeight; +}; + +struct AmmoInfo +{ + const char *pszName; + int iId; +}; + +struct MULTIDAMAGE +{ + CBaseEntity *pEntity; + float amount; + int type; +}; + +#include "weapontype.h" +#include "items.h" + +class CArmoury: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual void Restart(); + virtual void KeyValue(KeyValueData *pkvd); +#ifdef REGAMEDLL_FIXES + virtual void SetObjectCollisionBox(); +#endif + +public: + void EXPORT ArmouryTouch(CBaseEntity *pOther); + +private: + void Draw(); + void Hide(); + +public: + static char *m_ItemModels[]; + + ArmouryItemPack m_iItem; + int m_iCount; + int m_iInitialCount; + bool m_bAlreadyCounted; +}; + +class CGrenade: public CBaseMonster +{ +public: + virtual void Spawn(); + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + virtual int ObjectCaps() { return m_bIsC4 ? FCAP_CONTINUOUS_USE : 0; } + virtual void Killed(entvars_t *pevAttacker, int iGib); + virtual int BloodColor() { return DONT_BLEED; } + virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); + virtual void BounceSound(); + +public: + enum SATCHELCODE + { + SATCHEL_DETONATE, + SATCHEL_RELEASE, + }; +public: + void DefuseBombStart(CBasePlayer *pPlayer); + void DefuseBombEnd(CBasePlayer *pPlayer, bool bDefused); + + static CGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time); + static CGrenade *ShootTimed2(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, int iTeam, unsigned short usEvent); + static CGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); + static CGrenade *ShootSmokeGrenade(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time, unsigned short usEvent); + static CGrenade *ShootSatchelCharge(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); + static void UseSatchelCharges(entvars_t *pevOwner, SATCHELCODE code); +public: + void Explode(Vector vecSrc, Vector vecAim); + void Explode(TraceResult *pTrace, int bitsDamageType); + void Explode2(TraceResult *pTrace, int bitsDamageType); + void Explode3(TraceResult *pTrace, int bitsDamageType); + void SG_Explode(TraceResult *pTrace, int bitsDamageType); + +#ifdef REGAMEDLL_API + static CGrenade *ShootTimed_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time); + static CGrenade *ShootTimed2_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time, int iTeam, unsigned short usEvent); + static CGrenade *ShootSmokeGrenade_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity, float time, unsigned short usEvent); + static CGrenade *ShootSatchelCharge_OrigFunc(entvars_t *pevOwner, VectorRef vecStart, VectorRef vecVelocity); + + void DefuseBombStart_OrigFunc(CBasePlayer *pPlayer); + void DefuseBombEnd_OrigFunc(CBasePlayer *pPlayer, bool bDefused); + + void Explode_OrigFunc(TraceResult *pTrace, int bitsDamageType); + void Explode3_OrigFunc(TraceResult *pTrace, int bitsDamageType); + void Explode2_OrigFunc(TraceResult *pTrace, int bitsDamageType); + void SG_Detonate_OrigFunc(); +#endif + + void EXPORT Smoke(); + void EXPORT Smoke2(); + void EXPORT Smoke3_A(); + void EXPORT Smoke3_B(); + void EXPORT Smoke3_C(); + void EXPORT SG_Smoke(); + void EXPORT BounceTouch(CBaseEntity *pOther); + void EXPORT SlideTouch(CBaseEntity *pOther); + void EXPORT C4Touch(CBaseEntity *pOther); + void EXPORT ExplodeTouch(CBaseEntity *pOther); + void EXPORT DangerSoundThink(); + void EXPORT PreDetonate(); + void EXPORT Detonate(); + void EXPORT SG_Detonate(); + void EXPORT Detonate2(); + void EXPORT Detonate3(); + void EXPORT DetonateUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); + void EXPORT TumbleThink(); + void EXPORT SG_TumbleThink(); + void EXPORT C4Think(); + +public: + static TYPEDESCRIPTION m_SaveData[]; + + bool m_bStartDefuse; + bool m_bIsC4; + EntityHandle m_pBombDefuser; + float m_flDefuseCountDown; + float m_flC4Blow; + float m_flNextFreqInterval; + float m_flNextBeep; + float m_flNextFreq; + char *m_sBeepName; + float m_fAttenu; + float m_flNextBlink; + float m_fNextDefuse; + bool m_bJustBlew; + int m_iTeam; + int m_iCurWave; + edict_t *m_pentCurBombTarget; + int m_SGSmoke; + int m_angle; + unsigned short m_usEvent; + bool m_bLightSmoke; + bool m_bDetonated; + Vector m_vSmokeDetonate; + int m_iBounceCount; + BOOL m_fRegisteredSound; +}; + +// Items that the player has in their inventory that they can use +class CCSPlayerItem; +class CBasePlayerItem: public CBaseAnimating +{ +public: + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + virtual void SetObjectCollisionBox(); + virtual CBaseEntity *Respawn(); + virtual int AddToPlayer(CBasePlayer *pPlayer); // return TRUE if the item you want the item added to the player inventory + virtual int AddDuplicate(CBasePlayerItem *pItem) { return FALSE; } // return TRUE if you want your duplicate removed from world + virtual int GetItemInfo(ItemInfo *p) { return 0; } // returns 0 if struct not filled out + virtual BOOL CanDeploy() { return TRUE; } + virtual BOOL CanDrop() { return TRUE; } // returns is deploy was successful + virtual BOOL Deploy() { return TRUE; } + virtual BOOL IsWeapon() { return FALSE; } + virtual BOOL CanHolster() { return TRUE; } // can this weapon be put away right now? + virtual void Holster(int skiplocal = 0); + virtual void UpdateItemInfo() {} + virtual void ItemPreFrame() {} // called each frame by the player PreThink + virtual void ItemPostFrame() {} // called each frame by the player PostThink + virtual void Drop(); + virtual void Kill(); + virtual void AttachToPlayer(CBasePlayer *pPlayer); + virtual int PrimaryAmmoIndex() { return -1; } + virtual int SecondaryAmmoIndex() { return -1; } + virtual int UpdateClientData(CBasePlayer *pPlayer) { return 0; } + virtual CBasePlayerItem *GetWeaponPtr() { return nullptr; } + virtual float GetMaxSpeed() { return 260.0f; } + virtual int iItemSlot() { return 0; } // return 0 to MAX_ITEMS_SLOTS, used in hud + +public: + void EXPORT DestroyItem(); + void EXPORT DefaultTouch(CBaseEntity *pOther); + void EXPORT FallThink(); + void EXPORT Materialize(); + void EXPORT AttemptToMaterialize(); + + void FallInit(); + void CheckRespawn(); + +public: +#ifdef REGAMEDLL_API + CCSPlayerItem *CSPlayerItem() const; +#endif + + const char *pszAmmo1() const; + int iMaxAmmo1() const; + const char *pszAmmo2() const; + int iMaxAmmo2() const; + const char *pszName() const; + int iMaxClip() const; + int iWeight() const; + int iFlags() const; + +public: + static TYPEDESCRIPTION m_SaveData[]; + static ItemInfo m_ItemInfoArray[MAX_WEAPONS]; + static AmmoInfo m_AmmoInfoArray[MAX_AMMO_SLOTS]; + + CBasePlayer *m_pPlayer; + CBasePlayerItem *m_pNext; + int m_iId; // WEAPON_??? +}; + +#ifdef REGAMEDLL_API +inline CCSPlayerItem *CBasePlayerItem::CSPlayerItem() const +{ + return reinterpret_cast(this->m_pEntity); +} +#endif + +// inventory items that +class CCSPlayerWeapon; +class CBasePlayerWeapon: public CBasePlayerItem +{ +public: + virtual void Spawn(); + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + + // generic weapon versions of CBasePlayerItem calls + virtual int AddToPlayer(CBasePlayer *pPlayer); + virtual int AddDuplicate(CBasePlayerItem *pItem); + virtual BOOL CanDeploy(); + virtual BOOL IsWeapon() { return TRUE; } + virtual void Holster(int skiplocal = 0); + virtual void UpdateItemInfo() {}; + virtual void ItemPostFrame(); + virtual int PrimaryAmmoIndex(); + virtual int SecondaryAmmoIndex(); + virtual int UpdateClientData(CBasePlayer *pPlayer); + virtual CBasePlayerItem *GetWeaponPtr() { return (CBasePlayerItem *)this; } + virtual int ExtractAmmo(CBasePlayerWeapon *pWeapon); + virtual int ExtractClipAmmo(CBasePlayerWeapon *pWeapon); + virtual int AddWeapon() + { + ExtractAmmo(this); + return 1; + } + virtual BOOL PlayEmptySound(); + virtual void ResetEmptySound(); + virtual void SendWeaponAnim(int iAnim, int skiplocal = 0); + virtual BOOL IsUseable(); + virtual void PrimaryAttack() {}; + virtual void SecondaryAttack() {}; + virtual void Reload() {}; + virtual void WeaponIdle() {}; + virtual void RetireWeapon(); + virtual BOOL ShouldWeaponIdle() { return FALSE; } + virtual BOOL UseDecrement() { return FALSE; } + +public: + BOOL AddPrimaryAmmo(int iCount, char *szName, int iMaxClip, int iMaxCarry); + BOOL AddSecondaryAmmo(int iCount, char *szName, int iMaxCarry); + BOOL DefaultDeploy(char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal = 0); + int DefaultReload(int iClipSize, int iAnim, float fDelay); + void FireRemaining(int &shotsFired, float &shootTime, BOOL isGlock18); + void KickBack(float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change); + void EjectBrassLate(); + void MakeBeam(); + void BeamUpdate(); + void ReloadSound(); + float GetNextAttackDelay(float delay); + float GetNextAttackDelay2(float delay); + bool HasSecondaryAttack(); + BOOL IsPistol() { return (m_iId == WEAPON_USP || m_iId == WEAPON_GLOCK18 || m_iId == WEAPON_P228 || m_iId == WEAPON_DEAGLE || m_iId == WEAPON_ELITE || m_iId == WEAPON_FIVESEVEN); } + void SetPlayerShieldAnim(); + void ResetPlayerShieldAnim(); + bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); + void HandleInfiniteAmmo(); + void InstantReload(bool bCanRefillBPAmmo = false); + +#ifdef REGAMEDLL_API + CCSPlayerWeapon *CSPlayerWeapon() const; +#endif + +public: + static TYPEDESCRIPTION m_SaveData[]; + + int m_iPlayEmptySound; + int m_fFireOnEmpty; + float m_flNextPrimaryAttack; // soonest time ItemPostFrame will call PrimaryAttack + float m_flNextSecondaryAttack; // soonest time ItemPostFrame will call SecondaryAttack + float m_flTimeWeaponIdle; // soonest time ItemPostFrame will call WeaponIdle + int m_iPrimaryAmmoType; // "primary" ammo index into players m_rgAmmo[] + int m_iSecondaryAmmoType; // "secondary" ammo index into players m_rgAmmo[] + int m_iClip; // number of shots left in the primary weapon clip, -1 it not used + int m_iClientClip; // the last version of m_iClip sent to hud dll + int m_iClientWeaponState; // the last version of the weapon state sent to hud dll (is current weapon, is on target) + int m_fInReload; // Are we in the middle of a reload; + int m_fInSpecialReload; // Are we in the middle of a reload for the shotguns + int m_iDefaultAmmo; // how much ammo you get when you pick up this weapon as placed by a level designer. + int m_iShellId; + float m_fMaxSpeed; + bool m_bDelayFire; + int m_iDirection; + bool m_bSecondarySilencerOn; + float m_flAccuracy; + float m_flLastFire; + int m_iShotsFired; + Vector m_vVecAiming; + string_t model_name; + float m_flGlock18Shoot; // time to shoot the remaining bullets of the glock18 burst fire + int m_iGlock18ShotsFired; // used to keep track of the shots fired during the Glock18 burst fire mode. + float m_flFamasShoot; + int m_iFamasShotsFired; + float m_fBurstSpread; + int m_iWeaponState; + float m_flNextReload; + float m_flDecreaseShotsFired; + unsigned short m_usFireGlock18; + unsigned short m_usFireFamas; + + // hle time creep vars + float m_flPrevPrimaryAttack; + float m_flLastFireTime; +}; + +#ifdef REGAMEDLL_API +inline CCSPlayerWeapon *CBasePlayerWeapon::CSPlayerWeapon() const +{ + return reinterpret_cast(this->m_pEntity); +} +#endif + +class CWeaponBox: public CBaseEntity +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual void KeyValue(KeyValueData *pkvd); + virtual int Save(CSave &save); + virtual int Restore(CRestore &restore); + virtual void SetObjectCollisionBox(); + virtual void Touch(CBaseEntity *pOther); + +public: + BOOL IsEmpty(); + int GiveAmmo(int iCount, char *szName, int iMax, int *pIndex = nullptr); + + void EXPORT Kill(); + void EXPORT BombThink(); + void SetModel(const char *pszModelName); + + BOOL HasWeapon(CBasePlayerItem *pCheckItem); + BOOL PackWeapon(CBasePlayerItem *pWeapon); + BOOL PackAmmo(string_t iszName, int iCount); + +#ifdef REGAMEDLL_API + void SetModel_OrigFunc(const char *pszModelName); +#endif + +public: + static TYPEDESCRIPTION m_SaveData[]; + + CBasePlayerItem *m_rgpPlayerItems[MAX_ITEM_TYPES]; + string_t m_rgiszAmmo[MAX_AMMO_SLOTS]; + int m_rgAmmo[MAX_AMMO_SLOTS]; + int m_cAmmoTypes; + bool m_bIsBomb; +}; + + +const float USP_MAX_SPEED = 250.0f; +const float USP_DAMAGE = 34.0f; +const float USP_DAMAGE_SIL = 30.0f; +const float USP_RANGE_MODIFER = 0.79f; +const float USP_RELOAD_TIME = 2.7f; + +enum usp_e +{ + USP_IDLE, + USP_SHOOT1, + USP_SHOOT2, + USP_SHOOT3, + USP_SHOOT_EMPTY, + USP_RELOAD, + USP_DRAW, + USP_ATTACH_SILENCER, + USP_UNSIL_IDLE, + USP_UNSIL_SHOOT1, + USP_UNSIL_SHOOT2, + USP_UNSIL_SHOOT3, + USP_UNSIL_SHOOT_EMPTY, + USP_UNSIL_RELOAD, + USP_UNSIL_DRAW, + USP_DETACH_SILENCER, +}; + +enum usp_shield_e +{ + USP_SHIELD_IDLE, + USP_SHIELD_SHOOT1, + USP_SHIELD_SHOOT2, + USP_SHIELD_SHOOT_EMPTY, + USP_SHIELD_RELOAD, + USP_SHIELD_DRAW, + USP_SHIELD_UP_IDLE, + USP_SHIELD_UP, + USP_SHIELD_DOWN, +}; + +class CUSP: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void USPFire(float flSpread, float flCycleTime, BOOL fUseSemi); + void MakeBeam(); + void BeamUpdate(); + int m_iShell; + +private: + unsigned short m_usFireUSP; + +#ifdef REGAMEDLL_API + float m_flBaseDamageSil; +#endif +}; + + +const float MP5N_MAX_SPEED = 250.0f; +const float MP5N_DAMAGE = 26.0f; +const float MP5N_RANGE_MODIFER = 0.84f; +const float MP5N_RELOAD_TIME = 2.63f; + +enum mp5n_e +{ + MP5N_IDLE1, + MP5N_RELOAD, + MP5N_DRAW, + MP5N_SHOOT1, + MP5N_SHOOT2, + MP5N_SHOOT3, +}; + +class CMP5N: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return MP5N_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void MP5NFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireMP5N; +}; + + +const float SG552_MAX_SPEED = 235.0f; +const float SG552_MAX_SPEED_ZOOM = 200.0f; +const float SG552_DAMAGE = 33.0f; +const float SG552_RANGE_MODIFER = 0.955f; +const float SG552_RELOAD_TIME = 3.0f; + +enum sg552_e +{ + SG552_IDLE1, + SG552_RELOAD, + SG552_DRAW, + SG552_SHOOT1, + SG552_SHOOT2, + SG552_SHOOT3, +}; + +class CSG552: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void SG552Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireSG552; +}; + + +const float AK47_MAX_SPEED = 221.0f; +const float AK47_DAMAGE = 36.0f; +const float AK47_RANGE_MODIFER = 0.98f; +const float AK47_RELOAD_TIME = 2.45f; + +enum ak47_e +{ + AK47_IDLE1, + AK47_RELOAD, + AK47_DRAW, + AK47_SHOOT1, + AK47_SHOOT2, + AK47_SHOOT3, +}; + +class CAK47: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return AK47_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void AK47Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireAK47; +}; + + +const float AUG_MAX_SPEED = 240.0f; +const float AUG_DAMAGE = 32.0f; +const float AUG_RANGE_MODIFER = 0.96f; +const float AUG_RELOAD_TIME = 3.3f; + +enum aug_e +{ + AUG_IDLE1, + AUG_RELOAD, + AUG_DRAW, + AUG_SHOOT1, + AUG_SHOOT2, + AUG_SHOOT3, +}; + +class CAUG: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return AUG_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void AUGFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireAug; +}; + + +const float AWP_MAX_SPEED = 210.0f; +const float AWP_MAX_SPEED_ZOOM = 150.0f; +const float AWP_DAMAGE = 115.0f; +const float AWP_RANGE_MODIFER = 0.99f; +const float AWP_RELOAD_TIME = 2.5f; + +enum awp_e +{ + AWP_IDLE, + AWP_SHOOT, + AWP_SHOOT2, + AWP_SHOOT3, + AWP_RELOAD, + AWP_DRAW, +}; + +class CAWP: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void AWPFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + +private: + unsigned short m_usFireAWP; +}; + + +// for usermsg BombDrop +#define BOMB_FLAG_DROPPED 0 // if the bomb was dropped due to voluntary dropping or death/disconnect +#define BOMB_FLAG_PLANTED 1 // if the bomb has been planted will also trigger the round timer to hide will also show where the dropped bomb on the Terrorist team's radar. + +const float C4_MAX_AMMO = 1.0f; +const float C4_MAX_SPEED = 250.0f; +const float C4_ARMING_ON_TIME = 3.0f; + +enum c4_e +{ + C4_IDLE1, + C4_DRAW, + C4_DROP, + C4_ARM, +}; + +class CC4: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual void KeyValue(KeyValueData *pkvd); + virtual void Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual void Holster(int skiplocal); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return C4_SLOT; } + virtual void PrimaryAttack(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + bool m_bStartedArming; + bool m_bBombPlacedAnimation; + float m_fArmedTime; + +private: + bool m_bHasShield; +}; + + +const float DEAGLE_MAX_SPEED = 250.0f; +const float DEAGLE_DAMAGE = 54.0f; +const float DEAGLE_RANGE_MODIFER = 0.81f; +const float DEAGLE_RELOAD_TIME = 2.2f; + +enum deagle_e +{ + DEAGLE_IDLE1, + DEAGLE_SHOOT1, + DEAGLE_SHOOT2, + DEAGLE_SHOOT_EMPTY, + DEAGLE_RELOAD, + DEAGLE_DRAW, +}; + +class CDEAGLE: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void DEAGLEFire(float flSpread, float flCycleTime, BOOL fUseSemi); + + int m_iShell; + +private: + unsigned short m_usFireDeagle; +}; + + +const float FLASHBANG_MAX_SPEED = 250.0f; +const float FLASHBANG_MAX_SPEED_SHIELD = 180.0f; + +enum flashbang_e +{ + FLASHBANG_IDLE, + FLASHBANG_PULLPIN, + FLASHBANG_THROW, + FLASHBANG_DRAW, +}; + +class CFlashbang: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL CanDeploy(); + virtual BOOL CanDrop() { return FALSE; } + virtual BOOL Deploy(); + virtual void Holster(int skiplocal); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return GRENADE_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() + { + #ifdef REGAMEDLL_FIXES + return FALSE; + #else + // TODO: why the object flashbang is IsPistol? + return TRUE; + #endif + } + +public: + bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); + void SetPlayerShieldAnim(); + void ResetPlayerShieldAnim(); +}; + + +const float G3SG1_MAX_SPEED = 210.0f; +const float G3SG1_MAX_SPEED_ZOOM = 150.0f; +const float G3SG1_DAMAGE = 80.0f; +const float G3SG1_RANGE_MODIFER = 0.98f; +const float G3SG1_RELOAD_TIME = 3.5f; + +enum g3sg1_e +{ + G3SG1_IDLE, + G3SG1_SHOOT, + G3SG1_SHOOT2, + G3SG1_RELOAD, + G3SG1_DRAW, +}; + +class CG3SG1: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void G3SG1Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + +private: + unsigned short m_usFireG3SG1; +}; + + +const float GLOCK18_MAX_SPEED = 250.0f; +const float GLOCK18_DAMAGE = 25.0f; +const float GLOCK18_RANGE_MODIFER = 0.75f; +const float GLOCK18_RELOAD_TIME = 2.2f; + +enum glock18_e +{ + GLOCK18_IDLE1, + GLOCK18_IDLE2, + GLOCK18_IDLE3, + GLOCK18_SHOOT, + GLOCK18_SHOOT2, + GLOCK18_SHOOT3, + GLOCK18_SHOOT_EMPTY, + GLOCK18_RELOAD, + GLOCK18_DRAW, + GLOCK18_HOLSTER, + GLOCK18_ADD_SILENCER, + GLOCK18_DRAW2, + GLOCK18_RELOAD2, +}; + +enum glock18_shield_e +{ + GLOCK18_SHIELD_IDLE1, + GLOCK18_SHIELD_SHOOT, + GLOCK18_SHIELD_SHOOT2, + GLOCK18_SHIELD_SHOOT_EMPTY, + GLOCK18_SHIELD_RELOAD, + GLOCK18_SHIELD_DRAW, + GLOCK18_SHIELD_IDLE, + GLOCK18_SHIELD_UP, + GLOCK18_SHIELD_DOWN, +}; + +class CGLOCK18: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void GLOCK18Fire(float flSpread, float flCycleTime, BOOL bFireBurst); + +public: + int m_iShell; + bool m_bBurstFire; +}; + + +const float HEGRENADE_MAX_SPEED = 250.0f; +const float HEGRENADE_MAX_SPEED_SHIELD = 180.0f; + +enum hegrenade_e +{ + HEGRENADE_IDLE, + HEGRENADE_PULLPIN, + HEGRENADE_THROW, + HEGRENADE_DRAW, +}; + +class CHEGrenade: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL CanDeploy(); + virtual BOOL CanDrop() { return FALSE; } + virtual BOOL Deploy(); + virtual void Holster(int skiplocal); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return GRENADE_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); + void SetPlayerShieldAnim(); + void ResetPlayerShieldAnim(); + +public: + unsigned short m_usCreateExplosion; +}; + + +const float KNIFE_BODYHIT_VOLUME = 128.0f; +const float KNIFE_WALLHIT_VOLUME = 512.0f; +const float KNIFE_MAX_SPEED = 250.0f; +const float KNIFE_MAX_SPEED_SHIELD = 180.0f; + +enum knife_e +{ + KNIFE_IDLE, + KNIFE_ATTACK1HIT, + KNIFE_ATTACK2HIT, + KNIFE_DRAW, + KNIFE_STABHIT, + KNIFE_STABMISS, + KNIFE_MIDATTACK1HIT, + KNIFE_MIDATTACK2HIT, +}; + +enum knife_shield_e +{ + KNIFE_SHIELD_IDLE, + KNIFE_SHIELD_SLASH, + KNIFE_SHIELD_ATTACKHIT, + KNIFE_SHIELD_DRAW, + KNIFE_SHIELD_UPIDLE, + KNIFE_SHIELD_UP, + KNIFE_SHIELD_DOWN, +}; + +class CKnife: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL CanDrop() { return FALSE; } + virtual BOOL Deploy(); + virtual void Holster(int skiplocal); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return KNIFE_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual void WeaponIdle(); + +public: + void EXPORT SwingAgain(); + void EXPORT Smack(); + + void WeaponAnimation(int iAnimation); + BOOL Stab(BOOL fFirst); + BOOL Swing(BOOL fFirst); + +public: + bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); + void SetPlayerShieldAnim(); + void ResetPlayerShieldAnim(); + +public: + TraceResult m_trHit; + unsigned short m_usKnife; +}; + + +const float M249_MAX_SPEED = 220.0f; +const float M249_DAMAGE = 32.0f; +const float M249_RANGE_MODIFER = 0.97f; +const float M249_RELOAD_TIME = 4.7f; + +enum m249_e +{ + M249_IDLE1, + M249_SHOOT1, + M249_SHOOT2, + M249_RELOAD, + M249_DRAW, +}; + +class CM249: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return M249_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void M249Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireM249; +}; + + +const float M3_MAX_SPEED = 230.0f; +const float M3_DAMAGE = 20.0f; +const Vector M3_CONE_VECTOR = Vector(0.0675, 0.0675, 0.0); // special shotgun spreads + +enum m3_e +{ + M3_IDLE, + M3_FIRE1, + M3_FIRE2, + M3_RELOAD, + M3_PUMP, + M3_START_RELOAD, + M3_DRAW, + M3_HOLSTER, +}; + +class CM3: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return M3_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual BOOL PlayEmptySound(); + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + int m_iShell; + float m_flPumpTime; + +private: + unsigned short m_usFireM3; +}; + + +const float M4A1_MAX_SPEED = 230.0f; +const float M4A1_DAMAGE = 32.0f; +const float M4A1_DAMAGE_SIL = 33.0f; +const float M4A1_RANGE_MODIFER = 0.97f; +const float M4A1_RANGE_MODIFER_SIL = 0.95f; +const float M4A1_RELOAD_TIME = 3.05f; + +enum m4a1_e +{ + M4A1_IDLE, + M4A1_SHOOT1, + M4A1_SHOOT2, + M4A1_SHOOT3, + M4A1_RELOAD, + M4A1_DRAW, + M4A1_ATTACH_SILENCER, + M4A1_UNSIL_IDLE, + M4A1_UNSIL_SHOOT1, + M4A1_UNSIL_SHOOT2, + M4A1_UNSIL_SHOOT3, + M4A1_UNSIL_RELOAD, + M4A1_UNSIL_DRAW, + M4A1_DETACH_SILENCER, +}; + +class CM4A1: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void M4A1Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireM4A1; + +#ifdef REGAMEDLL_API + float m_flBaseDamageSil; +#endif +}; + + +const float MAC10_MAX_SPEED = 250.0f; +const float MAC10_DAMAGE = 29.0f; +const float MAC10_RANGE_MODIFER = 0.82f; +const float MAC10_RELOAD_TIME = 3.15f; + +enum mac10_e +{ + MAC10_IDLE1, + MAC10_RELOAD, + MAC10_DRAW, + MAC10_SHOOT1, + MAC10_SHOOT2, + MAC10_SHOOT3, +}; + +class CMAC10: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return MAC10_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void MAC10Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireMAC10; +}; + + +const float P228_MAX_SPEED = 250.0f; +const float P228_DAMAGE = 32.0f; +const float P228_RANGE_MODIFER = 0.8f; +const float P228_RELOAD_TIME = 2.7f; + +enum p228_e +{ + P228_IDLE, + P228_SHOOT1, + P228_SHOOT2, + P228_SHOOT3, + P228_SHOOT_EMPTY, + P228_RELOAD, + P228_DRAW, +}; + +enum p228_shield_e +{ + P228_SHIELD_IDLE, + P228_SHIELD_SHOOT1, + P228_SHIELD_SHOOT2, + P228_SHIELD_SHOOT_EMPTY, + P228_SHIELD_RELOAD, + P228_SHIELD_DRAW, + P228_SHIELD_IDLE_UP, + P228_SHIELD_UP, + P228_SHIELD_DOWN, +}; + +class CP228: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void P228Fire(float flSpread, float flCycleTime, BOOL fUseSemi); + void MakeBeam(); + void BeamUpdate(); + +public: + int m_iShell; + +private: + unsigned short m_usFireP228; +}; + + +const float P90_MAX_SPEED = 245.0f; +const float P90_DAMAGE = 21.0f; +const float P90_RANGE_MODIFER = 0.885f; +const float P90_RELOAD_TIME = 3.4f; + +enum p90_e +{ + P90_IDLE1, + P90_RELOAD, + P90_DRAW, + P90_SHOOT1, + P90_SHOOT2, + P90_SHOOT3, +}; + +class CP90: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void P90Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireP90; +}; + + +const float SCOUT_MAX_SPEED = 260.0f; +const float SCOUT_MAX_SPEED_ZOOM = 220.0f; +const float SCOUT_DAMAGE = 75.0f; +const float SCOUT_RANGE_MODIFER = 0.98f; +const float SCOUT_RELOAD_TIME = 2.0f; + +enum scout_e +{ + SCOUT_IDLE, + SCOUT_SHOOT, + SCOUT_SHOOT2, + SCOUT_RELOAD, + SCOUT_DRAW, +}; + +class CSCOUT: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void SCOUTFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + int m_iShell; + +private: + unsigned short m_usFireScout; +}; + + +const float SMOKEGRENADE_MAX_SPEED = 250.0f; +const float SMOKEGRENADE_MAX_SPEED_SHIELD = 180.0f; + +enum smokegrenade_e +{ + SMOKEGRENADE_IDLE, + SMOKEGRENADE_PINPULL, + SMOKEGRENADE_THROW, + SMOKEGRENADE_DRAW, +}; + +class CSmokeGrenade: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL CanDeploy(); + virtual BOOL CanDrop() { return FALSE; } + virtual BOOL Deploy(); + virtual void Holster(int skiplocal); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return GRENADE_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); + void SetPlayerShieldAnim(); + void ResetPlayerShieldAnim(); + +public: + unsigned short m_usCreateSmoke; +}; + + +const float TMP_MAX_SPEED = 250.0f; +const float TMP_DAMAGE = 20.0f; +const float TMP_RANGE_MODIFER = 0.85f; +const float TMP_RELOAD_TIME = 2.12f; + +enum tmp_e +{ + TMP_IDLE1, + TMP_RELOAD, + TMP_DRAW, + TMP_SHOOT1, + TMP_SHOOT2, + TMP_SHOOT3, +}; + +class CTMP: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return TMP_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void TMPFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + +public: + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireTMP; +}; + + +const float XM1014_MAX_SPEED = 240.0f; +const float XM1014_DAMAGE = 20.0f; +const Vector XM1014_CONE_VECTOR = Vector(0.0725, 0.0725, 0.0); // special shotgun spreads + +enum xm1014_e +{ + XM1014_IDLE, + XM1014_FIRE1, + XM1014_FIRE2, + XM1014_RELOAD, + XM1014_PUMP, + XM1014_START_RELOAD, + XM1014_DRAW, +}; + +class CXM1014: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return XM1014_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual BOOL PlayEmptySound(); + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + int m_iShell; + float m_flPumpTime; + +private: + unsigned short m_usFireXM1014; +}; + + +const float ELITE_MAX_SPEED = 250.0f; +const float ELITE_RELOAD_TIME = 4.5f; +const float ELITE_DAMAGE = 36.0f; +const float ELITE_RANGE_MODIFER = 0.75f; + +enum elite_e +{ + ELITE_IDLE, + ELITE_IDLE_LEFTEMPTY, + ELITE_SHOOTLEFT1, + ELITE_SHOOTLEFT2, + ELITE_SHOOTLEFT3, + ELITE_SHOOTLEFT4, + ELITE_SHOOTLEFT5, + ELITE_SHOOTLEFTLAST, + ELITE_SHOOTRIGHT1, + ELITE_SHOOTRIGHT2, + ELITE_SHOOTRIGHT3, + ELITE_SHOOTRIGHT4, + ELITE_SHOOTRIGHT5, + ELITE_SHOOTRIGHTLAST, + ELITE_RELOAD, + ELITE_DRAW, +}; + +class CELITE: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return ELITE_MAX_SPEED; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void ELITEFire(float flSpread, float flCycleTime, BOOL fUseSemi); + +public: + int m_iShell; +private: + unsigned short m_usFireELITE_LEFT; + unsigned short m_usFireELITE_RIGHT; +}; + + +const float FIVESEVEN_MAX_SPEED = 250.0f; +const float FIVESEVEN_DAMAGE = 20.0f; +const float FIVESEVEN_RANGE_MODIFER = 0.885f; +const float FIVESEVEN_RELOAD_TIME = 2.7f; + +enum fiveseven_e +{ + FIVESEVEN_IDLE, + FIVESEVEN_SHOOT1, + FIVESEVEN_SHOOT2, + FIVESEVEN_SHOOT_EMPTY, + FIVESEVEN_RELOAD, + FIVESEVEN_DRAW, +}; + +class CFiveSeven: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return m_fMaxSpeed; } + virtual int iItemSlot() { return PISTOL_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + virtual BOOL IsPistol() { return TRUE; } + +public: + void FiveSevenFire(float flSpread, float flCycleTime, BOOL fUseSemi); + void MakeBeam(); + void BeamUpdate(); + +public: + int m_iShell; + +private: + unsigned short m_usFireFiveSeven; +}; + + +const float UMP45_MAX_SPEED = 250.0f; +const float UMP45_DAMAGE = 30.0f; +const float UMP45_RANGE_MODIFER = 0.82f; +const float UMP45_RELOAD_TIME = 3.5f; + +enum ump45_e +{ + UMP45_IDLE1, + UMP45_RELOAD, + UMP45_DRAW, + UMP45_SHOOT1, + UMP45_SHOOT2, + UMP45_SHOOT3, +}; + +class CUMP45: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return UMP45_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void UMP45Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireUMP45; +}; + + +const float SG550_MAX_SPEED = 210.0f; +const float SG550_MAX_SPEED_ZOOM = 150.0f; +const float SG550_DAMAGE = 70.0f; +const float SG550_RANGE_MODIFER = 0.98f; +const float SG550_RELOAD_TIME = 3.35f; + +enum sg550_e +{ + SG550_IDLE, + SG550_SHOOT, + SG550_SHOOT2, + SG550_RELOAD, + SG550_DRAW, +}; + +class CSG550: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed(); + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void SG550Fire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + int m_iShell; + +private: + unsigned short m_usFireSG550; +}; + + +const float GALIL_MAX_SPEED = 240.0f; +const float GALIL_DAMAGE = 30.0f; +const float GALIL_RANGE_MODIFER = 0.98f; +const float GALIL_RELOAD_TIME = 2.45f; + +enum galil_e +{ + GALIL_IDLE1, + GALIL_RELOAD, + GALIL_DRAW, + GALIL_SHOOT1, + GALIL_SHOOT2, + GALIL_SHOOT3, +}; + +class CGalil: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return GALIL_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void GalilFire(float flSpread, float flCycleTime, BOOL fUseAutoAim); + +public: + int m_iShell; + int iShellOn; + +private: + unsigned short m_usFireGalil; +}; + + +const float FAMAS_MAX_SPEED = 240.0f; +const float FAMAS_RELOAD_TIME = 3.3f; +const float FAMAS_DAMAGE = 30.0f; +const float FAMAS_DAMAGE_BURST = 34.0f; +const float FAMAS_RANGE_MODIFER = 0.96f; + +enum famas_e +{ + FAMAS_IDLE1, + FAMAS_RELOAD, + FAMAS_DRAW, + FAMAS_SHOOT1, + FAMAS_SHOOT2, + FAMAS_SHOOT3, +}; + +class CFamas: public CBasePlayerWeapon +{ +public: + virtual void Spawn(); + virtual void Precache(); + virtual int GetItemInfo(ItemInfo *p); + virtual BOOL Deploy(); + virtual float GetMaxSpeed() { return FAMAS_MAX_SPEED; } + virtual int iItemSlot() { return PRIMARY_WEAPON_SLOT; } + virtual void PrimaryAttack(); + virtual void SecondaryAttack(); + virtual void Reload(); + virtual void WeaponIdle(); + virtual BOOL UseDecrement() + { + #ifdef CLIENT_WEAPONS + return TRUE; + #else + return FALSE; + #endif + } + +public: + void FamasFire(float flSpread, float flCycleTime, BOOL fUseAutoAim, BOOL bFireBurst); + +public: + int m_iShell; + int iShellOn; + +#ifdef REGAMEDLL_API + float m_flBaseDamageBurst; +#endif +}; + +extern short g_sModelIndexLaser; +extern short g_sModelIndexLaserDot; + +extern short g_sModelIndexFireball; +extern short g_sModelIndexSmoke; +extern short g_sModelIndexWExplosion; +extern short g_sModelIndexBubbles; +extern short g_sModelIndexBloodDrop; +extern short g_sModelIndexBloodSpray; +extern short g_sModelIndexSmokePuff; +extern short g_sModelIndexFireball2; +extern short g_sModelIndexFireball3; +extern short g_sModelIndexFireball4; +extern short g_sModelIndexCTGhost; +extern short g_sModelIndexTGhost; +extern short g_sModelIndexC4Glow; + +extern short g_sModelIndexRadio; +extern MULTIDAMAGE gMultiDamage; + +void WeaponsPrecache(); +void FindHullIntersection(const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity); +void AnnounceFlashInterval(float interval, float offset = 0); + +int MaxAmmoCarry(const char *szName); +int MaxAmmoCarry(WeaponIdType ammoType); + +void ClearMultiDamage(); +void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker); +void AddMultiDamage(entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType); +void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage); +int DamageDecal(CBaseEntity *pEntity, int bitsDamageType); +void DecalGunshot(TraceResult *pTrace, int iBulletType, bool ClientOnly, entvars_t *pShooter, bool bHitMetal); +void EjectBrass(const Vector &vecOrigin, const Vector &vecLeft, const Vector &vecVelocity, float rotation, int model, int soundtype, int entityIndex); +void EjectBrass2(const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype, entvars_t *pev); +void AddAmmoNameToAmmoRegistry(const char *szAmmoname); +void UTIL_PrecacheOtherWeapon(const char *szClassname); +BOOL CanAttack(float attack_time, float curtime, BOOL isPredicted); diff --git a/regamedll/dlls/weapontype.h b/regamedll/dlls/weapontype.h index a6f91817..7b842c0c 100644 --- a/regamedll/dlls/weapontype.h +++ b/regamedll/dlls/weapontype.h @@ -1,464 +1,464 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#pragma once - -enum WeaponIdType -{ - WEAPON_NONE, - WEAPON_P228, - WEAPON_GLOCK, - WEAPON_SCOUT, - WEAPON_HEGRENADE, - WEAPON_XM1014, - WEAPON_C4, - WEAPON_MAC10, - WEAPON_AUG, - WEAPON_SMOKEGRENADE, - WEAPON_ELITE, - WEAPON_FIVESEVEN, - WEAPON_UMP45, - WEAPON_SG550, - WEAPON_GALIL, - WEAPON_FAMAS, - WEAPON_USP, - WEAPON_GLOCK18, - WEAPON_AWP, - WEAPON_MP5N, - WEAPON_M249, - WEAPON_M3, - WEAPON_M4A1, - WEAPON_TMP, - WEAPON_G3SG1, - WEAPON_FLASHBANG, - WEAPON_DEAGLE, - WEAPON_SG552, - WEAPON_AK47, - WEAPON_KNIFE, - WEAPON_P90, - WEAPON_SHIELDGUN = 99 -}; - -enum AutoBuyClassType -{ - AUTOBUYCLASS_NONE = 0, - AUTOBUYCLASS_PRIMARY = BIT(0), - AUTOBUYCLASS_SECONDARY = BIT(1), - AUTOBUYCLASS_AMMO = BIT(2), - AUTOBUYCLASS_ARMOR = BIT(3), - AUTOBUYCLASS_DEFUSER = BIT(4), - AUTOBUYCLASS_PISTOL = BIT(5), - AUTOBUYCLASS_SMG = BIT(6), - AUTOBUYCLASS_RIFLE = BIT(7), - AUTOBUYCLASS_SNIPERRIFLE = BIT(8), - AUTOBUYCLASS_SHOTGUN = BIT(9), - AUTOBUYCLASS_MACHINEGUN = BIT(10), - AUTOBUYCLASS_GRENADE = BIT(11), - AUTOBUYCLASS_NIGHTVISION = BIT(12), - AUTOBUYCLASS_SHIELD = BIT(13), -}; - -enum ItemCostType -{ - ASSAULTSUIT_PRICE = 1000, - FLASHBANG_PRICE = 200, - HEGRENADE_PRICE = 300, - SMOKEGRENADE_PRICE = 300, - KEVLAR_PRICE = 650, - HELMET_PRICE = 350, - NVG_PRICE = 1250, - DEFUSEKIT_PRICE = 200, -}; - -enum AmmoCostType -{ - AMMO_338MAG_PRICE = 125, - AMMO_357SIG_PRICE = 50, - AMMO_45ACP_PRICE = 25, - AMMO_50AE_PRICE = 40, - AMMO_556MM_PRICE = 60, - AMMO_57MM_PRICE = 50, - AMMO_762MM_PRICE = 80, - AMMO_9MM_PRICE = 20, - AMMO_BUCKSHOT_PRICE = 65, - AMMO_FLASHBANG_PRICE = FLASHBANG_PRICE, - AMMO_HEGRENADE_PRICE = HEGRENADE_PRICE, - AMMO_SMOKEGRENADE_PRICE = SMOKEGRENADE_PRICE, -}; - -enum WeaponCostType -{ - AK47_PRICE = 2500, - AWP_PRICE = 4750, - DEAGLE_PRICE = 650, - G3SG1_PRICE = 5000, - SG550_PRICE = 4200, - GLOCK18_PRICE = 400, - M249_PRICE = 5750, - M3_PRICE = 1700, - M4A1_PRICE = 3100, - AUG_PRICE = 3500, - MP5NAVY_PRICE = 1500, - P228_PRICE = 600, - P90_PRICE = 2350, - UMP45_PRICE = 1700, - MAC10_PRICE = 1400, - SCOUT_PRICE = 2750, - SG552_PRICE = 3500, - TMP_PRICE = 1250, - USP_PRICE = 500, - ELITE_PRICE = 800, - FIVESEVEN_PRICE = 750, - XM1014_PRICE = 3000, - GALIL_PRICE = 2000, - FAMAS_PRICE = 2250, - SHIELDGUN_PRICE = 2200, -}; - -enum WeaponState -{ - WPNSTATE_USP_SILENCED = BIT(0), - WPNSTATE_GLOCK18_BURST_MODE = BIT(1), - WPNSTATE_M4A1_SILENCED = BIT(2), - WPNSTATE_ELITE_LEFT = BIT(3), - WPNSTATE_FAMAS_BURST_MODE = BIT(4), - WPNSTATE_SHIELD_DRAWN = BIT(5), -}; - -// custom enum -// the default amount of ammo that comes with each gun when it spawns -enum ClipGiveDefault -{ - P228_DEFAULT_GIVE = 13, - GLOCK18_DEFAULT_GIVE = 20, - SCOUT_DEFAULT_GIVE = 10, - HEGRENADE_DEFAULT_GIVE = 1, - XM1014_DEFAULT_GIVE = 7, - C4_DEFAULT_GIVE = 1, - MAC10_DEFAULT_GIVE = 30, - AUG_DEFAULT_GIVE = 30, - SMOKEGRENADE_DEFAULT_GIVE = 1, - ELITE_DEFAULT_GIVE = 30, - FIVESEVEN_DEFAULT_GIVE = 20, - UMP45_DEFAULT_GIVE = 25, - SG550_DEFAULT_GIVE = 30, - GALIL_DEFAULT_GIVE = 35, - FAMAS_DEFAULT_GIVE = 25, - USP_DEFAULT_GIVE = 12, - AWP_DEFAULT_GIVE = 10, - MP5NAVY_DEFAULT_GIVE = 30, - M249_DEFAULT_GIVE = 100, - M3_DEFAULT_GIVE = 8, - M4A1_DEFAULT_GIVE = 30, - TMP_DEFAULT_GIVE = 30, - G3SG1_DEFAULT_GIVE = 20, - FLASHBANG_DEFAULT_GIVE = 1, - DEAGLE_DEFAULT_GIVE = 7, - SG552_DEFAULT_GIVE = 30, - AK47_DEFAULT_GIVE = 30, - //KNIFE_DEFAULT_GIVE = 1, - P90_DEFAULT_GIVE = 50, -}; - -enum ClipSizeType -{ - P228_MAX_CLIP = 13, - GLOCK18_MAX_CLIP = 20, - SCOUT_MAX_CLIP = 10, - XM1014_MAX_CLIP = 7, - MAC10_MAX_CLIP = 30, - AUG_MAX_CLIP = 30, - ELITE_MAX_CLIP = 30, - FIVESEVEN_MAX_CLIP = 20, - UMP45_MAX_CLIP = 25, - SG550_MAX_CLIP = 30, - GALIL_MAX_CLIP = 35, - FAMAS_MAX_CLIP = 25, - USP_MAX_CLIP = 12, - AWP_MAX_CLIP = 10, - MP5N_MAX_CLIP = 30, - M249_MAX_CLIP = 100, - M3_MAX_CLIP = 8, - M4A1_MAX_CLIP = 30, - TMP_MAX_CLIP = 30, - G3SG1_MAX_CLIP = 20, - DEAGLE_MAX_CLIP = 7, - SG552_MAX_CLIP = 30, - AK47_MAX_CLIP = 30, - P90_MAX_CLIP = 50, -}; - -enum WeightWeapon -{ - P228_WEIGHT = 5, - GLOCK18_WEIGHT = 5, - SCOUT_WEIGHT = 30, - HEGRENADE_WEIGHT = 2, - XM1014_WEIGHT = 20, - C4_WEIGHT = 3, - MAC10_WEIGHT = 25, - AUG_WEIGHT = 25, - SMOKEGRENADE_WEIGHT = 1, - ELITE_WEIGHT = 5, - FIVESEVEN_WEIGHT = 5, - UMP45_WEIGHT = 25, - SG550_WEIGHT = 20, - GALIL_WEIGHT = 25, - FAMAS_WEIGHT = 75, - USP_WEIGHT = 5, - AWP_WEIGHT = 30, - MP5NAVY_WEIGHT = 25, - M249_WEIGHT = 25, - M3_WEIGHT = 20, - M4A1_WEIGHT = 25, - TMP_WEIGHT = 25, - G3SG1_WEIGHT = 20, - FLASHBANG_WEIGHT = 1, - DEAGLE_WEIGHT = 7, - SG552_WEIGHT = 25, - AK47_WEIGHT = 25, - P90_WEIGHT = 26, - KNIFE_WEIGHT = 0, -}; - -enum MaxAmmoType -{ - MAX_AMMO_BUCKSHOT = 32, - MAX_AMMO_9MM = 120, - MAX_AMMO_556NATO = 90, - MAX_AMMO_556NATOBOX = 200, - MAX_AMMO_762NATO = 90, - MAX_AMMO_45ACP = 100, - MAX_AMMO_50AE = 35, - MAX_AMMO_338MAGNUM = 30, - MAX_AMMO_57MM = 100, - MAX_AMMO_357SIG = 52, - - // custom - MAX_AMMO_SMOKEGRENADE = 1, - MAX_AMMO_HEGRENADE = 1, - MAX_AMMO_FLASHBANG = 2, -}; - -enum AmmoType -{ - AMMO_NONE, - AMMO_338MAGNUM, - AMMO_762NATO, - AMMO_556NATOBOX, - AMMO_556NATO, - AMMO_BUCKSHOT, - AMMO_45ACP, - AMMO_57MM, - AMMO_50AE, - AMMO_357SIG, - AMMO_9MM, - AMMO_FLASHBANG, - AMMO_HEGRENADE, - AMMO_SMOKEGRENADE, - AMMO_C4, - - AMMO_MAX_TYPES -}; - -enum WeaponClassType -{ - WEAPONCLASS_NONE, - WEAPONCLASS_KNIFE, - WEAPONCLASS_PISTOL, - WEAPONCLASS_GRENADE, - WEAPONCLASS_SUBMACHINEGUN, - WEAPONCLASS_SHOTGUN, - WEAPONCLASS_MACHINEGUN, - WEAPONCLASS_RIFLE, - WEAPONCLASS_SNIPERRIFLE, - WEAPONCLASS_MAX, -}; - -enum AmmoBuyAmount -{ - AMMO_338MAG_BUY = 10, - AMMO_357SIG_BUY = 13, - AMMO_45ACP_BUY = 12, - AMMO_50AE_BUY = 7, - AMMO_556NATO_BUY = 30, - AMMO_556NATOBOX_BUY = 30, - AMMO_57MM_BUY = 50, - AMMO_762NATO_BUY = 30, - AMMO_9MM_BUY = 30, - AMMO_BUCKSHOT_BUY = 8, - AMMO_FLASHBANG_BUY = 1, - AMMO_HEGRENADE_BUY = 1, - AMMO_SMOKEGRENADE_BUY = 1, -}; - -enum shieldgun_e -{ - SHIELDGUN_IDLE, - SHIELDGUN_SHOOT1, - SHIELDGUN_SHOOT2, - SHIELDGUN_SHOOT_EMPTY, - SHIELDGUN_RELOAD, - SHIELDGUN_DRAW, - SHIELDGUN_DRAWN_IDLE, - SHIELDGUN_UP, - SHIELDGUN_DOWN, -}; - -// custom -enum shieldgren_e -{ - SHIELDREN_IDLE = 4, - SHIELDREN_UP, - SHIELDREN_DOWN -}; - -enum InventorySlotType -{ - NONE_SLOT, - PRIMARY_WEAPON_SLOT, - PISTOL_SLOT, - KNIFE_SLOT, - GRENADE_SLOT, - C4_SLOT, -}; - -enum Bullet -{ - BULLET_NONE, - BULLET_PLAYER_9MM, - BULLET_PLAYER_MP5, - BULLET_PLAYER_357, - BULLET_PLAYER_BUCKSHOT, - BULLET_PLAYER_CROWBAR, - BULLET_MONSTER_9MM, - BULLET_MONSTER_MP5, - BULLET_MONSTER_12MM, - BULLET_PLAYER_45ACP, - BULLET_PLAYER_338MAG, - BULLET_PLAYER_762MM, - BULLET_PLAYER_556MM, - BULLET_PLAYER_50AE, - BULLET_PLAYER_57MM, - BULLET_PLAYER_357SIG, -}; - -struct WeaponStruct -{ - int m_type; - int m_price; - int m_side; - int m_slot; - int m_ammoPrice; -}; - -struct AutoBuyInfoStruct -{ - int m_class; - char *m_command; - char *m_classname; -}; - -struct WeaponAliasInfo -{ - char *alias; - WeaponIdType id; -}; - -struct WeaponBuyAliasInfo -{ - char *alias; - WeaponIdType id; - char *failName; -}; - -struct WeaponClassAliasInfo -{ - char *alias; - WeaponClassType id; -}; - -struct WeaponInfoStruct -{ - int id; - int cost; - int clipCost; - int buyClipSize; - int gunClipSize; - int maxRounds; - AmmoType ammoType; - char *entityName; - - // custom - const char *ammoName1; - const char *ammoName2; -}; - -struct AmmoInfoStruct -{ - AmmoType ammoType; - - int clipCost; - int buyClipSize; - int maxRounds; - - const char *ammoName1; - const char *ammoName2; -}; - -struct WeaponSlotInfo -{ - WeaponIdType id; - InventorySlotType slot; - const char *weaponName; -}; - -extern AutoBuyInfoStruct g_autoBuyInfo[35]; -extern WeaponStruct g_weaponStruct[MAX_WEAPONS]; - -// WeaponType -WeaponIdType AliasToWeaponID(const char *alias); -const char *BuyAliasToWeaponID(const char *alias, WeaponIdType &id); -const char *WeaponIDToAlias(int id); -WeaponClassType AliasToWeaponClass(const char *alias); -WeaponClassType WeaponIDToWeaponClass(int id); -WeaponClassType WeaponIDToWeaponClass(ArmouryItemPack id); -bool IsPrimaryWeapon(int id); -bool IsSecondaryWeapon(int id); -bool IsGrenadeWeapon(int id); -bool CanBuyWeaponByMaptype(int playerTeam, WeaponIdType weaponID, bool useAssasinationRestrictions); -void WeaponInfoReset(); - -WeaponInfoStruct *GetWeaponInfo(int weaponID); -WeaponInfoStruct *GetWeaponInfo(const char *weaponName); - -AmmoInfoStruct *GetAmmoInfo(AmmoType ammoID); -AmmoInfoStruct *GetAmmoInfo(const char *ammoName); - -WeaponSlotInfo *GetWeaponSlot(WeaponIdType weaponID); -WeaponSlotInfo *GetWeaponSlot(const char *weaponName); +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +enum WeaponIdType +{ + WEAPON_NONE, + WEAPON_P228, + WEAPON_GLOCK, + WEAPON_SCOUT, + WEAPON_HEGRENADE, + WEAPON_XM1014, + WEAPON_C4, + WEAPON_MAC10, + WEAPON_AUG, + WEAPON_SMOKEGRENADE, + WEAPON_ELITE, + WEAPON_FIVESEVEN, + WEAPON_UMP45, + WEAPON_SG550, + WEAPON_GALIL, + WEAPON_FAMAS, + WEAPON_USP, + WEAPON_GLOCK18, + WEAPON_AWP, + WEAPON_MP5N, + WEAPON_M249, + WEAPON_M3, + WEAPON_M4A1, + WEAPON_TMP, + WEAPON_G3SG1, + WEAPON_FLASHBANG, + WEAPON_DEAGLE, + WEAPON_SG552, + WEAPON_AK47, + WEAPON_KNIFE, + WEAPON_P90, + WEAPON_SHIELDGUN = 99 +}; + +enum AutoBuyClassType +{ + AUTOBUYCLASS_NONE = 0, + AUTOBUYCLASS_PRIMARY = BIT(0), + AUTOBUYCLASS_SECONDARY = BIT(1), + AUTOBUYCLASS_AMMO = BIT(2), + AUTOBUYCLASS_ARMOR = BIT(3), + AUTOBUYCLASS_DEFUSER = BIT(4), + AUTOBUYCLASS_PISTOL = BIT(5), + AUTOBUYCLASS_SMG = BIT(6), + AUTOBUYCLASS_RIFLE = BIT(7), + AUTOBUYCLASS_SNIPERRIFLE = BIT(8), + AUTOBUYCLASS_SHOTGUN = BIT(9), + AUTOBUYCLASS_MACHINEGUN = BIT(10), + AUTOBUYCLASS_GRENADE = BIT(11), + AUTOBUYCLASS_NIGHTVISION = BIT(12), + AUTOBUYCLASS_SHIELD = BIT(13), +}; + +enum ItemCostType +{ + ASSAULTSUIT_PRICE = 1000, + FLASHBANG_PRICE = 200, + HEGRENADE_PRICE = 300, + SMOKEGRENADE_PRICE = 300, + KEVLAR_PRICE = 650, + HELMET_PRICE = 350, + NVG_PRICE = 1250, + DEFUSEKIT_PRICE = 200, +}; + +enum AmmoCostType +{ + AMMO_338MAG_PRICE = 125, + AMMO_357SIG_PRICE = 50, + AMMO_45ACP_PRICE = 25, + AMMO_50AE_PRICE = 40, + AMMO_556MM_PRICE = 60, + AMMO_57MM_PRICE = 50, + AMMO_762MM_PRICE = 80, + AMMO_9MM_PRICE = 20, + AMMO_BUCKSHOT_PRICE = 65, + AMMO_FLASHBANG_PRICE = FLASHBANG_PRICE, + AMMO_HEGRENADE_PRICE = HEGRENADE_PRICE, + AMMO_SMOKEGRENADE_PRICE = SMOKEGRENADE_PRICE, +}; + +enum WeaponCostType +{ + AK47_PRICE = 2500, + AWP_PRICE = 4750, + DEAGLE_PRICE = 650, + G3SG1_PRICE = 5000, + SG550_PRICE = 4200, + GLOCK18_PRICE = 400, + M249_PRICE = 5750, + M3_PRICE = 1700, + M4A1_PRICE = 3100, + AUG_PRICE = 3500, + MP5NAVY_PRICE = 1500, + P228_PRICE = 600, + P90_PRICE = 2350, + UMP45_PRICE = 1700, + MAC10_PRICE = 1400, + SCOUT_PRICE = 2750, + SG552_PRICE = 3500, + TMP_PRICE = 1250, + USP_PRICE = 500, + ELITE_PRICE = 800, + FIVESEVEN_PRICE = 750, + XM1014_PRICE = 3000, + GALIL_PRICE = 2000, + FAMAS_PRICE = 2250, + SHIELDGUN_PRICE = 2200, +}; + +enum WeaponState +{ + WPNSTATE_USP_SILENCED = BIT(0), + WPNSTATE_GLOCK18_BURST_MODE = BIT(1), + WPNSTATE_M4A1_SILENCED = BIT(2), + WPNSTATE_ELITE_LEFT = BIT(3), + WPNSTATE_FAMAS_BURST_MODE = BIT(4), + WPNSTATE_SHIELD_DRAWN = BIT(5), +}; + +// custom enum +// the default amount of ammo that comes with each gun when it spawns +enum ClipGiveDefault +{ + P228_DEFAULT_GIVE = 13, + GLOCK18_DEFAULT_GIVE = 20, + SCOUT_DEFAULT_GIVE = 10, + HEGRENADE_DEFAULT_GIVE = 1, + XM1014_DEFAULT_GIVE = 7, + C4_DEFAULT_GIVE = 1, + MAC10_DEFAULT_GIVE = 30, + AUG_DEFAULT_GIVE = 30, + SMOKEGRENADE_DEFAULT_GIVE = 1, + ELITE_DEFAULT_GIVE = 30, + FIVESEVEN_DEFAULT_GIVE = 20, + UMP45_DEFAULT_GIVE = 25, + SG550_DEFAULT_GIVE = 30, + GALIL_DEFAULT_GIVE = 35, + FAMAS_DEFAULT_GIVE = 25, + USP_DEFAULT_GIVE = 12, + AWP_DEFAULT_GIVE = 10, + MP5NAVY_DEFAULT_GIVE = 30, + M249_DEFAULT_GIVE = 100, + M3_DEFAULT_GIVE = 8, + M4A1_DEFAULT_GIVE = 30, + TMP_DEFAULT_GIVE = 30, + G3SG1_DEFAULT_GIVE = 20, + FLASHBANG_DEFAULT_GIVE = 1, + DEAGLE_DEFAULT_GIVE = 7, + SG552_DEFAULT_GIVE = 30, + AK47_DEFAULT_GIVE = 30, + //KNIFE_DEFAULT_GIVE = 1, + P90_DEFAULT_GIVE = 50, +}; + +enum ClipSizeType +{ + P228_MAX_CLIP = 13, + GLOCK18_MAX_CLIP = 20, + SCOUT_MAX_CLIP = 10, + XM1014_MAX_CLIP = 7, + MAC10_MAX_CLIP = 30, + AUG_MAX_CLIP = 30, + ELITE_MAX_CLIP = 30, + FIVESEVEN_MAX_CLIP = 20, + UMP45_MAX_CLIP = 25, + SG550_MAX_CLIP = 30, + GALIL_MAX_CLIP = 35, + FAMAS_MAX_CLIP = 25, + USP_MAX_CLIP = 12, + AWP_MAX_CLIP = 10, + MP5N_MAX_CLIP = 30, + M249_MAX_CLIP = 100, + M3_MAX_CLIP = 8, + M4A1_MAX_CLIP = 30, + TMP_MAX_CLIP = 30, + G3SG1_MAX_CLIP = 20, + DEAGLE_MAX_CLIP = 7, + SG552_MAX_CLIP = 30, + AK47_MAX_CLIP = 30, + P90_MAX_CLIP = 50, +}; + +enum WeightWeapon +{ + P228_WEIGHT = 5, + GLOCK18_WEIGHT = 5, + SCOUT_WEIGHT = 30, + HEGRENADE_WEIGHT = 2, + XM1014_WEIGHT = 20, + C4_WEIGHT = 3, + MAC10_WEIGHT = 25, + AUG_WEIGHT = 25, + SMOKEGRENADE_WEIGHT = 1, + ELITE_WEIGHT = 5, + FIVESEVEN_WEIGHT = 5, + UMP45_WEIGHT = 25, + SG550_WEIGHT = 20, + GALIL_WEIGHT = 25, + FAMAS_WEIGHT = 75, + USP_WEIGHT = 5, + AWP_WEIGHT = 30, + MP5NAVY_WEIGHT = 25, + M249_WEIGHT = 25, + M3_WEIGHT = 20, + M4A1_WEIGHT = 25, + TMP_WEIGHT = 25, + G3SG1_WEIGHT = 20, + FLASHBANG_WEIGHT = 1, + DEAGLE_WEIGHT = 7, + SG552_WEIGHT = 25, + AK47_WEIGHT = 25, + P90_WEIGHT = 26, + KNIFE_WEIGHT = 0, +}; + +enum MaxAmmoType +{ + MAX_AMMO_BUCKSHOT = 32, + MAX_AMMO_9MM = 120, + MAX_AMMO_556NATO = 90, + MAX_AMMO_556NATOBOX = 200, + MAX_AMMO_762NATO = 90, + MAX_AMMO_45ACP = 100, + MAX_AMMO_50AE = 35, + MAX_AMMO_338MAGNUM = 30, + MAX_AMMO_57MM = 100, + MAX_AMMO_357SIG = 52, + + // custom + MAX_AMMO_SMOKEGRENADE = 1, + MAX_AMMO_HEGRENADE = 1, + MAX_AMMO_FLASHBANG = 2, +}; + +enum AmmoType +{ + AMMO_NONE, + AMMO_338MAGNUM, + AMMO_762NATO, + AMMO_556NATOBOX, + AMMO_556NATO, + AMMO_BUCKSHOT, + AMMO_45ACP, + AMMO_57MM, + AMMO_50AE, + AMMO_357SIG, + AMMO_9MM, + AMMO_FLASHBANG, + AMMO_HEGRENADE, + AMMO_SMOKEGRENADE, + AMMO_C4, + + AMMO_MAX_TYPES +}; + +enum WeaponClassType +{ + WEAPONCLASS_NONE, + WEAPONCLASS_KNIFE, + WEAPONCLASS_PISTOL, + WEAPONCLASS_GRENADE, + WEAPONCLASS_SUBMACHINEGUN, + WEAPONCLASS_SHOTGUN, + WEAPONCLASS_MACHINEGUN, + WEAPONCLASS_RIFLE, + WEAPONCLASS_SNIPERRIFLE, + WEAPONCLASS_MAX, +}; + +enum AmmoBuyAmount +{ + AMMO_338MAG_BUY = 10, + AMMO_357SIG_BUY = 13, + AMMO_45ACP_BUY = 12, + AMMO_50AE_BUY = 7, + AMMO_556NATO_BUY = 30, + AMMO_556NATOBOX_BUY = 30, + AMMO_57MM_BUY = 50, + AMMO_762NATO_BUY = 30, + AMMO_9MM_BUY = 30, + AMMO_BUCKSHOT_BUY = 8, + AMMO_FLASHBANG_BUY = 1, + AMMO_HEGRENADE_BUY = 1, + AMMO_SMOKEGRENADE_BUY = 1, +}; + +enum shieldgun_e +{ + SHIELDGUN_IDLE, + SHIELDGUN_SHOOT1, + SHIELDGUN_SHOOT2, + SHIELDGUN_SHOOT_EMPTY, + SHIELDGUN_RELOAD, + SHIELDGUN_DRAW, + SHIELDGUN_DRAWN_IDLE, + SHIELDGUN_UP, + SHIELDGUN_DOWN, +}; + +// custom +enum shieldgren_e +{ + SHIELDREN_IDLE = 4, + SHIELDREN_UP, + SHIELDREN_DOWN +}; + +enum InventorySlotType +{ + NONE_SLOT, + PRIMARY_WEAPON_SLOT, + PISTOL_SLOT, + KNIFE_SLOT, + GRENADE_SLOT, + C4_SLOT, +}; + +enum Bullet +{ + BULLET_NONE, + BULLET_PLAYER_9MM, + BULLET_PLAYER_MP5, + BULLET_PLAYER_357, + BULLET_PLAYER_BUCKSHOT, + BULLET_PLAYER_CROWBAR, + BULLET_MONSTER_9MM, + BULLET_MONSTER_MP5, + BULLET_MONSTER_12MM, + BULLET_PLAYER_45ACP, + BULLET_PLAYER_338MAG, + BULLET_PLAYER_762MM, + BULLET_PLAYER_556MM, + BULLET_PLAYER_50AE, + BULLET_PLAYER_57MM, + BULLET_PLAYER_357SIG, +}; + +struct WeaponStruct +{ + int m_type; + int m_price; + int m_side; + int m_slot; + int m_ammoPrice; +}; + +struct AutoBuyInfoStruct +{ + int m_class; + char *m_command; + char *m_classname; +}; + +struct WeaponAliasInfo +{ + char *alias; + WeaponIdType id; +}; + +struct WeaponBuyAliasInfo +{ + char *alias; + WeaponIdType id; + char *failName; +}; + +struct WeaponClassAliasInfo +{ + char *alias; + WeaponClassType id; +}; + +struct WeaponInfoStruct +{ + int id; + int cost; + int clipCost; + int buyClipSize; + int gunClipSize; + int maxRounds; + AmmoType ammoType; + char *entityName; + + // custom + const char *ammoName1; + const char *ammoName2; +}; + +struct AmmoInfoStruct +{ + AmmoType ammoType; + + int clipCost; + int buyClipSize; + int maxRounds; + + const char *ammoName1; + const char *ammoName2; +}; + +struct WeaponSlotInfo +{ + WeaponIdType id; + InventorySlotType slot; + const char *weaponName; +}; + +extern AutoBuyInfoStruct g_autoBuyInfo[35]; +extern WeaponStruct g_weaponStruct[MAX_WEAPONS]; + +// WeaponType +WeaponIdType AliasToWeaponID(const char *alias); +const char *BuyAliasToWeaponID(const char *alias, WeaponIdType &id); +const char *WeaponIDToAlias(int id); +WeaponClassType AliasToWeaponClass(const char *alias); +WeaponClassType WeaponIDToWeaponClass(int id); +WeaponClassType WeaponIDToWeaponClass(ArmouryItemPack id); +bool IsPrimaryWeapon(int id); +bool IsSecondaryWeapon(int id); +bool IsGrenadeWeapon(int id); +bool CanBuyWeaponByMaptype(int playerTeam, WeaponIdType weaponID, bool useAssasinationRestrictions); +void WeaponInfoReset(); + +WeaponInfoStruct *GetWeaponInfo(int weaponID); +WeaponInfoStruct *GetWeaponInfo(const char *weaponName); + +AmmoInfoStruct *GetAmmoInfo(AmmoType ammoID); +AmmoInfoStruct *GetAmmoInfo(const char *ammoName); + +WeaponSlotInfo *GetWeaponSlot(WeaponIdType weaponID); +WeaponSlotInfo *GetWeaponSlot(const char *weaponName); diff --git a/regamedll/engine/eiface.h b/regamedll/engine/eiface.h index cb696646..8e9e989f 100644 --- a/regamedll/engine/eiface.h +++ b/regamedll/engine/eiface.h @@ -1,541 +1,541 @@ -/*** -* -* Copyright (c) 1999, Valve LLC. All rights reserved. -* -* This product contains software technology licensed from Id -* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. -* All Rights Reserved. -* -* Use, distribution, and modification of this source code and/or resulting -* object code is restricted to non-commercial enhancements to products from -* Valve LLC. All other use, distribution, or modification is prohibited -* without written permission from Valve LLC. -* -****/ -#pragma once - -#include "archtypes.h" // DAL - -#ifdef HLDEMO_BUILD -#define INTERFACE_VERSION 001 -#else // !HLDEMO_BUILD, i.e., regular version of HL -#define INTERFACE_VERSION 140 -#endif // !HLDEMO_BUILD - -#include -#include "custom.h" -#include "cvardef.h" -#include "Sequence.h" -// -// Defines entity interface between engine and DLLs. -// This header file included by engine files and DLL files. -// -// Before including this header, DLLs must: -// include progdefs.h -// This is conveniently done for them in extdll.h -// - -/* -#ifdef _WIN32 -#define DLLEXPORT __stdcall EXT_FUNC -#else -#define DLLEXPORT __attribute__ ((visibility("default"))) EXT_FUNC -#endif -*/ - -enum ALERT_TYPE -{ - at_notice, - at_console, // same as at_notice, but forces a ConPrintf, not a message box - at_aiconsole, // same as at_console, but only shown if developer level is 2! - at_warning, - at_error, - at_logged // Server print to console ( only in multiplayer games ). -}; - -// 4-22-98 JOHN: added for use in pfnClientPrintf -enum PRINT_TYPE -{ - print_console, - print_center, - print_chat, -}; - -// For integrity checking of content on clients -enum FORCE_TYPE -{ - force_exactfile, // File on client must exactly match server's file - force_model_samebounds, // For model files only, the geometry must fit in the same bbox - force_model_specifybounds, // For model files only, the geometry must fit in the specified bbox - force_model_specifybounds_if_avail, // For Steam model files only, the geometry must fit in the specified bbox (if the file is available) -}; - -// Returned by TraceLine -struct TraceResult -{ - int fAllSolid; // if true, plane is not valid - int fStartSolid; // if true, the initial point was in a solid area - int fInOpen; - int fInWater; - float flFraction; // time completed, 1.0 = didn't hit anything - vec3_t vecEndPos; // final position - float flPlaneDist; - vec3_t vecPlaneNormal; // surface normal at impact - edict_t *pHit; // entity the surface is on - int iHitgroup; // 0 == generic, non zero is specific body part -}; - -// CD audio status -typedef struct -{ - int fPlaying;// is sound playing right now? - int fWasPlaying;// if not, CD is paused if WasPlaying is true. - int fInitialized; - int fEnabled; - int fPlayLooping; - float cdvolume; - //BYTE remap[100]; - int fCDRom; - int fPlayTrack; -} CDStatus; - -#include "../common/crc.h" - - -// Engine hands this to DLLs for functionality callbacks -typedef struct enginefuncs_s -{ - int (*pfnPrecacheModel) (const char* s); - int (*pfnPrecacheSound) (const char* s); - void (*pfnSetModel) (edict_t *e, const char *m); - int (*pfnModelIndex) (const char *m); - int (*pfnModelFrames) (int modelIndex); - void (*pfnSetSize) (edict_t *e, const float *rgflMin, const float *rgflMax); - void (*pfnChangeLevel) (const char* s1, const char* s2); - void (*pfnGetSpawnParms) (edict_t *ent); - void (*pfnSaveSpawnParms) (edict_t *ent); - float (*pfnVecToYaw) (const float *rgflVector); - void (*pfnVecToAngles) (const float *rgflVectorIn, float *rgflVectorOut); - void (*pfnMoveToOrigin) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); - void (*pfnChangeYaw) (edict_t* ent); - void (*pfnChangePitch) (edict_t* ent); - edict_t* (*pfnFindEntityByString) (edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue); - int (*pfnGetEntityIllum) (edict_t* pEnt); - edict_t* (*pfnFindEntityInSphere) (edict_t *pEdictStartSearchAfter, const float *org, float rad); - edict_t* (*pfnFindClientInPVS) (edict_t *pEdict); - edict_t* (*pfnEntitiesInPVS) (edict_t *pplayer); - void (*pfnMakeVectors) (const float *rgflVector); - void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up); - edict_t* (*pfnCreateEntity) (void); - void (*pfnRemoveEntity) (edict_t* e); - edict_t* (*pfnCreateNamedEntity) (int className); - void (*pfnMakeStatic) (edict_t *ent); - int (*pfnEntIsOnFloor) (edict_t *e); - int (*pfnDropToFloor) (edict_t* e); - int (*pfnWalkMove) (edict_t *ent, float yaw, float dist, int iMode); - void (*pfnSetOrigin) (edict_t *e, const float *rgflOrigin); - void (*pfnEmitSound) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch); - void (*pfnEmitAmbientSound) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); - void (*pfnTraceLine) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceToss) (edict_t* pent, edict_t* pentToIgnore, TraceResult *ptr); - int (*pfnTraceMonsterHull) (edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceHull) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnTraceModel) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); - const char *(*pfnTraceTexture) (edict_t *pTextureEntity, const float *v1, const float *v2 ); - void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); - void (*pfnGetAimVector) (edict_t* ent, float speed, float *rgflReturn); - void (*pfnServerCommand) (const char* str); - void (*pfnServerExecute) (void); - void (*pfnClientCommand) (edict_t* pEdict, const char* szFmt, ...); - void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); - void (*pfnLightStyle) (int style, const char* val); - int (*pfnDecalIndex) (const char *name); - int (*pfnPointContents) (const float *rgflVector); - void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); - void (*pfnMessageEnd) (void); - void (*pfnWriteByte) (int iValue); - void (*pfnWriteChar) (int iValue); - void (*pfnWriteShort) (int iValue); - void (*pfnWriteLong) (int iValue); - void (*pfnWriteAngle) (float flValue); - void (*pfnWriteCoord) (float flValue); - void (*pfnWriteString) (const char *sz); - void (*pfnWriteEntity) (int iValue); - void (*pfnCVarRegister) (cvar_t *pCvar); - float (*pfnCVarGetFloat) (const char *szVarName); - const char* (*pfnCVarGetString) (const char *szVarName); - void (*pfnCVarSetFloat) (const char *szVarName, float flValue); - void (*pfnCVarSetString) (const char *szVarName, const char *szValue); - void (*pfnAlertMessage) (ALERT_TYPE atype, const char *szFmt, ...); - void (*pfnEngineFprintf) (void *pfile, const char *szFmt, ...); - void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, int32 cb); - void* (*pfnPvEntPrivateData) (edict_t *pEdict); - void (*pfnFreeEntPrivateData) (edict_t *pEdict); - const char* (*pfnSzFromIndex) (int iString); - int (*pfnAllocString) (const char *szValue); - struct entvars_s* (*pfnGetVarsOfEnt) (edict_t *pEdict); - edict_t* (*pfnPEntityOfEntOffset) (int iEntOffset); - int (*pfnEntOffsetOfPEntity) (const edict_t *pEdict); - int (*pfnIndexOfEdict) (const edict_t *pEdict); - edict_t* (*pfnPEntityOfEntIndex) (int iEntIndex); - edict_t* (*pfnFindEntityByVars) (struct entvars_s* pvars); - void* (*pfnGetModelPtr) (edict_t* pEdict); - int (*pfnRegUserMsg) (const char *pszName, int iSize); - void (*pfnAnimationAutomove) (const edict_t* pEdict, float flTime); - void (*pfnGetBonePosition) (const edict_t* pEdict, int iBone, float *rgflOrigin, float *rgflAngles ); - uint32 (*pfnFunctionFromName) ( const char *pName ); - const char *(*pfnNameForFunction) ( uint32 function ); - void (*pfnClientPrintf) ( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg ); // JOHN: engine callbacks so game DLL can print messages to individual clients - void (*pfnServerPrint) ( const char *szMsg ); - const char *(*pfnCmd_Args) ( void ); // these 3 added - const char *(*pfnCmd_Argv) ( int argc ); // so game DLL can easily - int (*pfnCmd_Argc) ( void ); // access client 'cmd' strings - void (*pfnGetAttachment) (const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles ); - void (*pfnCRC32_Init) (CRC32_t *pulCRC); - void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len); - void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, unsigned char ch); - CRC32_t (*pfnCRC32_Final) (CRC32_t pulCRC); - int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); - float (*pfnRandomFloat) (float flLow, float flHigh); - void (*pfnSetView) (const edict_t *pClient, const edict_t *pViewent ); - float (*pfnTime) ( void ); - void (*pfnCrosshairAngle) (const edict_t *pClient, float pitch, float yaw); - byte * (*pfnLoadFileForMe) (const char *filename, int *pLength); - void (*pfnFreeFile) (void *buffer); - void (*pfnEndSection) (const char *pszSectionName); // trigger_endsection - int (*pfnCompareFileTime) (char *filename1, char *filename2, int *iCompare); - void (*pfnGetGameDir) (char *szGetGameDir); - void (*pfnCvar_RegisterVariable) (cvar_t *variable); - void (*pfnFadeClientVolume) (const edict_t *pEdict, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); - void (*pfnSetClientMaxspeed) (edict_t *pEdict, float fNewMaxspeed); - edict_t * (*pfnCreateFakeClient) (const char *netname); // returns NULL if fake client can't be created - void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, byte msec ); - int (*pfnNumberOfEntities) (void); - char* (*pfnGetInfoKeyBuffer) (edict_t *e); // passing in NULL gets the serverinfo - char* (*pfnInfoKeyValue) (char *infobuffer, const char *key); - void (*pfnSetKeyValue) (char *infobuffer, const char *key, const char *value); - void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, const char *key, const char *value); - int (*pfnIsMapValid) (const char *filename); - void (*pfnStaticDecal) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); - int (*pfnPrecacheGeneric) (const char* s); - int (*pfnGetPlayerUserId) (edict_t *e ); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients - void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); - int (*pfnIsDedicatedServer) (void);// is this a dedicated server? - cvar_t *(*pfnCVarGetPointer) (const char *szVarName); - unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients - - // YWB 8/1/99 TFF Physics additions - void (*pfnInfo_RemoveKey) ( char *s, const char *key ); - const char *(*pfnGetPhysicsKeyValue) ( const edict_t *pClient, const char *key ); - void (*pfnSetPhysicsKeyValue) ( const edict_t *pClient, const char *key, const char *value ); - const char *(*pfnGetPhysicsInfoString) ( const edict_t *pClient ); - unsigned short (*pfnPrecacheEvent) ( int type, const char*psz ); - void (*pfnPlaybackEvent) ( int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); - - unsigned char *(*pfnSetFatPVS) ( float *org ); - unsigned char *(*pfnSetFatPAS) ( float *org ); - - int (*pfnCheckVisibility ) ( edict_t *entity, unsigned char *pset ); - - void (*pfnDeltaSetField) ( struct delta_s *pFields, const char *fieldname ); - void (*pfnDeltaUnsetField) ( struct delta_s *pFields, const char *fieldname ); - void (*pfnDeltaAddEncoder) ( const char *name, void (*conditionalencode)( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) ); - int (*pfnGetCurrentPlayer) ( void ); - int (*pfnCanSkipPlayer) ( const edict_t *player ); - int (*pfnDeltaFindField) ( struct delta_s *pFields, const char *fieldname ); - void (*pfnDeltaSetFieldByIndex) ( struct delta_s *pFields, int fieldNumber ); - void (*pfnDeltaUnsetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); - - void (*pfnSetGroupMask) ( int mask, int op ); - - int (*pfnCreateInstancedBaseline) ( int classname, struct entity_state_s *baseline ); - void (*pfnCvar_DirectSet) ( struct cvar_s *var, const char *value ); - - // Forces the client and server to be running with the same version of the specified file - // ( e.g., a player model ). - // Calling this has no effect in single player - void (*pfnForceUnmodified) ( FORCE_TYPE type, float *mins, float *maxs, const char *filename ); - - void (*pfnGetPlayerStats) ( const edict_t *pClient, int *ping, int *packet_loss ); - - void (*pfnAddServerCommand) ( const char *cmd_name, void (*function) (void) ); - - // For voice communications, set which clients hear eachother. - // NOTE: these functions take player entity indices (starting at 1). - qboolean (*pfnVoice_GetClientListening)(int iReceiver, int iSender); - qboolean (*pfnVoice_SetClientListening)(int iReceiver, int iSender, qboolean bListen); - - const char *(*pfnGetPlayerAuthId) ( edict_t *e ); - - // PSV: Added for CZ training map -// const char *(*pfnKeyNameForBinding) ( const char* pBinding ); - - sequenceEntry_s* (*pfnSequenceGet) ( const char* fileName, const char* entryName ); - sentenceEntry_s* (*pfnSequencePickSentence) ( const char* groupName, int pickMethod, int *picked ); - - // LH: Give access to filesize via filesystem - int (*pfnGetFileSize) ( const char *filename ); - - unsigned int (*pfnGetApproxWavePlayLen) (const char *filepath); - // MDC: Added for CZ career-mode - int (*pfnIsCareerMatch) ( void ); - - // BGC: return the number of characters of the localized string referenced by using "label" - int (*pfnGetLocalizedStringLength) (const char *label); - - // BGC: added to facilitate persistent storage of tutor message decay values for - // different career game profiles. Also needs to persist regardless of mp.dll being - // destroyed and recreated. - void (*pfnRegisterTutorMessageShown) (int mid); - int (*pfnGetTimesTutorMessageShown) (int mid); - void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength); - void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength); - void (*pfnResetTutorMessageDecayData) ( void ); - - // Added 2005/08/11 (no SDK update): - void(*pfnQueryClientCvarValue) (const edict_t *player, const char *cvarName); - - // Added 2005/11/21 (no SDK update): - void(*pfnQueryClientCvarValue2) (const edict_t *player, const char *cvarName, int requestID); - - // Added 2009/06/19 (no SDK update): - int(*pfnEngCheckParm) (const char *pchCmdLineToken, char **ppnext); - - // Added 2019/06/26 (no SDK update): - // Commented out this for backward compatibility - //edict_t *(*pfnPEntityOfEntIndexAllEntities)(int iEntIndex); - -} enginefuncs_t; - - -// ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 - -// Passed to pfnKeyValue -typedef struct KeyValueData_s -{ - char *szClassName; // in: entity classname - char *szKeyName; // in: name of key - char *szValue; // in: value of key - qboolean fHandled; // out: DLL sets to true if key-value pair was understood -} KeyValueData; - - -typedef struct -{ - char mapName[ 32 ]; - char landmarkName[ 32 ]; - edict_t *pentLandmark; - vec3_t vecLandmarkOrigin; -} LEVELLIST; -#define MAX_LEVEL_CONNECTIONS 16 // These are encoded in the lower 16bits of ENTITYTABLE->flags - -typedef struct -{ - int id; // Ordinal ID of this entity (used for entity <--> pointer conversions) - edict_t *pent; // Pointer to the in-game entity - - int location; // Offset from the base data of this entity - int size; // Byte size of this entity's data - int flags; // This could be a short -- bit mask of transitions that this entity is in the PVS of - string_t classname; // entity class name - -} ENTITYTABLE; - -#define FENTTABLE_PLAYER 0x80000000 -#define FENTTABLE_REMOVED 0x40000000 -#define FENTTABLE_MOVEABLE 0x20000000 -#define FENTTABLE_GLOBAL 0x10000000 - -typedef struct saverestore_s SAVERESTOREDATA; - -#ifdef _WIN32 -typedef -#endif -struct saverestore_s -{ - char *pBaseData; // Start of all entity save data - char *pCurrentData; // Current buffer pointer for sequential access - int size; // Current data size - int bufferSize; // Total space for data - int tokenSize; // Size of the linear list of tokens - int tokenCount; // Number of elements in the pTokens table - char **pTokens; // Hash table of entity strings (sparse) - int currentIndex; // Holds a global entity table ID - int tableCount; // Number of elements in the entity table - int connectionCount;// Number of elements in the levelList[] - ENTITYTABLE *pTable; // Array of ENTITYTABLE elements (1 for each entity) - LEVELLIST levelList[ MAX_LEVEL_CONNECTIONS ]; // List of connections from this level - - // smooth transition - int fUseLandmark; - char szLandmarkName[20];// landmark we'll spawn near in next level - vec3_t vecLandmarkOffset;// for landmark transitions - float time; - char szCurrentMapName[32]; // To check global entities - -} -#ifdef _WIN32 -SAVERESTOREDATA -#endif -; - -typedef enum _fieldtypes -{ - FIELD_FLOAT = 0, // Any floating point value - FIELD_STRING, // A string ID (return from ALLOC_STRING) - FIELD_ENTITY, // An entity offset (EOFFSET) - FIELD_CLASSPTR, // CBaseEntity * - FIELD_EHANDLE, // Entity handle - FIELD_EVARS, // EVARS * - FIELD_EDICT, // edict_t *, or edict_t * (same thing) - FIELD_VECTOR, // Any vector - FIELD_POSITION_VECTOR, // A world coordinate (these are fixed up across level transitions automagically) - FIELD_POINTER, // Arbitrary data pointer... to be removed, use an array of FIELD_CHARACTER - FIELD_INTEGER, // Any integer or enum - FIELD_FUNCTION, // A class function pointer (Think, Use, etc) - FIELD_BOOLEAN, // boolean, implemented as an int, I may use this as a hint for compression - FIELD_SHORT, // 2 byte integer - FIELD_CHARACTER, // a byte - FIELD_TIME, // a floating point time (these are fixed up automatically too!) - FIELD_MODELNAME, // Engine string that is a model name (needs precache) - FIELD_SOUNDNAME, // Engine string that is a sound name (needs precache) - - FIELD_TYPECOUNT, // MUST BE LAST -} FIELDTYPE; - -#if !defined(offsetof) && !defined(GNUC) -#define offsetof(s,m) (size_t)&(((s *)0)->m) -#endif - -#define _FIELD(type,name,fieldtype,count,flags) { fieldtype, #name, offsetof(type, name), count, flags } -#define DEFINE_FIELD(type,name,fieldtype) _FIELD(type, name, fieldtype, 1, 0) -#define DEFINE_ARRAY(type,name,fieldtype,count) _FIELD(type, name, fieldtype, count, 0) -#define DEFINE_ENTITY_FIELD(name,fieldtype) _FIELD(entvars_t, name, fieldtype, 1, 0 ) -#define DEFINE_ENTITY_GLOBAL_FIELD(name,fieldtype) _FIELD(entvars_t, name, fieldtype, 1, FTYPEDESC_GLOBAL ) -#define DEFINE_GLOBAL_FIELD(type,name,fieldtype) _FIELD(type, name, fieldtype, 1, FTYPEDESC_GLOBAL ) - - -#define FTYPEDESC_GLOBAL 0x0001 // This field is masked for global entity save/restore - -typedef struct -{ - FIELDTYPE fieldType; - char *fieldName; - int fieldOffset; - short fieldSize; - short flags; -} TYPEDESCRIPTION; - -#ifndef ARRAYSIZE -#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) -#endif - -typedef struct -{ - // Initialize/shutdown the game (one-time call after loading of game .dll ) - void (*pfnGameInit) ( void ); - int (*pfnSpawn) ( edict_t *pent ); - void (*pfnThink) ( edict_t *pent ); - void (*pfnUse) ( edict_t *pentUsed, edict_t *pentOther ); - void (*pfnTouch) ( edict_t *pentTouched, edict_t *pentOther ); - void (*pfnBlocked) ( edict_t *pentBlocked, edict_t *pentOther ); - void (*pfnKeyValue) ( edict_t *pentKeyvalue, KeyValueData *pkvd ); - void (*pfnSave) ( edict_t *pent, SAVERESTOREDATA *pSaveData ); - int (*pfnRestore) ( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); - void (*pfnSetAbsBox) ( edict_t *pent ); - - void (*pfnSaveWriteFields) ( SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int ); - void (*pfnSaveReadFields) ( SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int ); - - void (*pfnSaveGlobalState) ( SAVERESTOREDATA * ); - void (*pfnRestoreGlobalState) ( SAVERESTOREDATA * ); - void (*pfnResetGlobalState) ( void ); - - qboolean (*pfnClientConnect) ( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); - - void (*pfnClientDisconnect) ( edict_t *pEntity ); - void (*pfnClientKill) ( edict_t *pEntity ); - void (*pfnClientPutInServer) ( edict_t *pEntity ); - void (*pfnClientCommand) ( edict_t *pEntity ); - void (*pfnClientUserInfoChanged)( edict_t *pEntity, char *infobuffer ); - - void (*pfnServerActivate) ( edict_t *pEdictList, int edictCount, int clientMax ); - void (*pfnServerDeactivate) ( void ); - - void (*pfnPlayerPreThink) ( edict_t *pEntity ); - void (*pfnPlayerPostThink) ( edict_t *pEntity ); - - void (*pfnStartFrame) ( void ); - void (*pfnParmsNewLevel) ( void ); - void (*pfnParmsChangeLevel) ( void ); - - // Returns string describing current .dll. E.g., TeamFotrress 2, Half-Life - const char *(*pfnGetGameDescription)( void ); - - // Notify dll about a player customization. - void (*pfnPlayerCustomization) ( edict_t *pEntity, customization_t *pCustom ); - - // Spectator funcs - void (*pfnSpectatorConnect) ( edict_t *pEntity ); - void (*pfnSpectatorDisconnect) ( edict_t *pEntity ); - void (*pfnSpectatorThink) ( edict_t *pEntity ); - - // Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. - void (*pfnSys_Error) ( const char *error_string ); - - void (*pfnPM_Move) ( struct playermove_s *ppmove, qboolean server ); - void (*pfnPM_Init) ( struct playermove_s *ppmove ); - char (*pfnPM_FindTextureType)( char *name ); - void (*pfnSetupVisibility)( struct edict_s *pViewEntity, struct edict_s *pClient, unsigned char **pvs, unsigned char **pas ); - void (*pfnUpdateClientData) ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); - int (*pfnAddToFullPack)( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); - void (*pfnCreateBaseline) ( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); - void (*pfnRegisterEncoders) ( void ); - int (*pfnGetWeaponData) ( struct edict_s *player, struct weapon_data_s *info ); - - void (*pfnCmdStart) ( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); - void (*pfnCmdEnd) ( const edict_t *player ); - - // Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max - // size of the response_buffer, so you must zero it out if you choose not to respond. - int (*pfnConnectionlessPacket ) ( const struct netadr_s *net_from_, const char *args, char *response_buffer, int *response_buffer_size ); - - // Enumerates player hulls. Returns 0 if the hull number doesn't exist, 1 otherwise - int (*pfnGetHullBounds) ( int hullnumber, float *mins, float *maxs ); - - // Create baselines for certain "unplaced" items. - void (*pfnCreateInstancedBaselines) ( void ); - - // One of the pfnForceUnmodified files failed the consistency check for the specified player - // Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) - int (*pfnInconsistentFile)( const struct edict_s *player, const char *filename, char *disconnect_message ); - - // The game .dll should return 1 if lag compensation should be allowed ( could also just set - // the sv_unlag cvar. - // Most games right now should return 0, until client-side weapon prediction code is written - // and tested for them. - int (*pfnAllowLagCompensation)( void ); -} DLL_FUNCTIONS; - -extern DLL_FUNCTIONS gEntityInterface; - -// Current version. -#define NEW_DLL_FUNCTIONS_VERSION 1 - -typedef struct -{ - // Called right before the object's memory is freed. - // Calls its destructor. - void (*pfnOnFreeEntPrivateData)(edict_t *pEnt); - void (*pfnGameShutdown)(void); - int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther ); - void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); - void (*pfnCvarValue2)( const edict_t *pEnt, int requestID, const char *cvarName, const char *value ); -} NEW_DLL_FUNCTIONS; -typedef int(*NEW_DLL_FUNCTIONS_FN)(NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); - -// Pointers will be null if the game DLL doesn't support this API. -extern NEW_DLL_FUNCTIONS gNewDLLFunctions; - -typedef int(*APIFUNCTION)(DLL_FUNCTIONS *pFunctionTable, int interfaceVersion); -typedef int(*APIFUNCTION2)(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); +/*** +* +* Copyright (c) 1999, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +****/ +#pragma once + +#include "archtypes.h" // DAL + +#ifdef HLDEMO_BUILD +#define INTERFACE_VERSION 001 +#else // !HLDEMO_BUILD, i.e., regular version of HL +#define INTERFACE_VERSION 140 +#endif // !HLDEMO_BUILD + +#include +#include "custom.h" +#include "cvardef.h" +#include "Sequence.h" +// +// Defines entity interface between engine and DLLs. +// This header file included by engine files and DLL files. +// +// Before including this header, DLLs must: +// include progdefs.h +// This is conveniently done for them in extdll.h +// + +/* +#ifdef _WIN32 +#define DLLEXPORT __stdcall EXT_FUNC +#else +#define DLLEXPORT __attribute__ ((visibility("default"))) EXT_FUNC +#endif +*/ + +enum ALERT_TYPE +{ + at_notice, + at_console, // same as at_notice, but forces a ConPrintf, not a message box + at_aiconsole, // same as at_console, but only shown if developer level is 2! + at_warning, + at_error, + at_logged // Server print to console ( only in multiplayer games ). +}; + +// 4-22-98 JOHN: added for use in pfnClientPrintf +enum PRINT_TYPE +{ + print_console, + print_center, + print_chat, +}; + +// For integrity checking of content on clients +enum FORCE_TYPE +{ + force_exactfile, // File on client must exactly match server's file + force_model_samebounds, // For model files only, the geometry must fit in the same bbox + force_model_specifybounds, // For model files only, the geometry must fit in the specified bbox + force_model_specifybounds_if_avail, // For Steam model files only, the geometry must fit in the specified bbox (if the file is available) +}; + +// Returned by TraceLine +struct TraceResult +{ + int fAllSolid; // if true, plane is not valid + int fStartSolid; // if true, the initial point was in a solid area + int fInOpen; + int fInWater; + float flFraction; // time completed, 1.0 = didn't hit anything + vec3_t vecEndPos; // final position + float flPlaneDist; + vec3_t vecPlaneNormal; // surface normal at impact + edict_t *pHit; // entity the surface is on + int iHitgroup; // 0 == generic, non zero is specific body part +}; + +// CD audio status +typedef struct +{ + int fPlaying;// is sound playing right now? + int fWasPlaying;// if not, CD is paused if WasPlaying is true. + int fInitialized; + int fEnabled; + int fPlayLooping; + float cdvolume; + //BYTE remap[100]; + int fCDRom; + int fPlayTrack; +} CDStatus; + +#include "../common/crc.h" + + +// Engine hands this to DLLs for functionality callbacks +typedef struct enginefuncs_s +{ + int (*pfnPrecacheModel) (const char* s); + int (*pfnPrecacheSound) (const char* s); + void (*pfnSetModel) (edict_t *e, const char *m); + int (*pfnModelIndex) (const char *m); + int (*pfnModelFrames) (int modelIndex); + void (*pfnSetSize) (edict_t *e, const float *rgflMin, const float *rgflMax); + void (*pfnChangeLevel) (const char* s1, const char* s2); + void (*pfnGetSpawnParms) (edict_t *ent); + void (*pfnSaveSpawnParms) (edict_t *ent); + float (*pfnVecToYaw) (const float *rgflVector); + void (*pfnVecToAngles) (const float *rgflVectorIn, float *rgflVectorOut); + void (*pfnMoveToOrigin) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); + void (*pfnChangeYaw) (edict_t* ent); + void (*pfnChangePitch) (edict_t* ent); + edict_t* (*pfnFindEntityByString) (edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue); + int (*pfnGetEntityIllum) (edict_t* pEnt); + edict_t* (*pfnFindEntityInSphere) (edict_t *pEdictStartSearchAfter, const float *org, float rad); + edict_t* (*pfnFindClientInPVS) (edict_t *pEdict); + edict_t* (*pfnEntitiesInPVS) (edict_t *pplayer); + void (*pfnMakeVectors) (const float *rgflVector); + void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up); + edict_t* (*pfnCreateEntity) (void); + void (*pfnRemoveEntity) (edict_t* e); + edict_t* (*pfnCreateNamedEntity) (int className); + void (*pfnMakeStatic) (edict_t *ent); + int (*pfnEntIsOnFloor) (edict_t *e); + int (*pfnDropToFloor) (edict_t* e); + int (*pfnWalkMove) (edict_t *ent, float yaw, float dist, int iMode); + void (*pfnSetOrigin) (edict_t *e, const float *rgflOrigin); + void (*pfnEmitSound) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch); + void (*pfnEmitAmbientSound) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); + void (*pfnTraceLine) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceToss) (edict_t* pent, edict_t* pentToIgnore, TraceResult *ptr); + int (*pfnTraceMonsterHull) (edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceHull) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnTraceModel) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); + const char *(*pfnTraceTexture) (edict_t *pTextureEntity, const float *v1, const float *v2 ); + void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); + void (*pfnGetAimVector) (edict_t* ent, float speed, float *rgflReturn); + void (*pfnServerCommand) (const char* str); + void (*pfnServerExecute) (void); + void (*pfnClientCommand) (edict_t* pEdict, const char* szFmt, ...); + void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); + void (*pfnLightStyle) (int style, const char* val); + int (*pfnDecalIndex) (const char *name); + int (*pfnPointContents) (const float *rgflVector); + void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + void (*pfnMessageEnd) (void); + void (*pfnWriteByte) (int iValue); + void (*pfnWriteChar) (int iValue); + void (*pfnWriteShort) (int iValue); + void (*pfnWriteLong) (int iValue); + void (*pfnWriteAngle) (float flValue); + void (*pfnWriteCoord) (float flValue); + void (*pfnWriteString) (const char *sz); + void (*pfnWriteEntity) (int iValue); + void (*pfnCVarRegister) (cvar_t *pCvar); + float (*pfnCVarGetFloat) (const char *szVarName); + const char* (*pfnCVarGetString) (const char *szVarName); + void (*pfnCVarSetFloat) (const char *szVarName, float flValue); + void (*pfnCVarSetString) (const char *szVarName, const char *szValue); + void (*pfnAlertMessage) (ALERT_TYPE atype, const char *szFmt, ...); + void (*pfnEngineFprintf) (void *pfile, const char *szFmt, ...); + void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, int32 cb); + void* (*pfnPvEntPrivateData) (edict_t *pEdict); + void (*pfnFreeEntPrivateData) (edict_t *pEdict); + const char* (*pfnSzFromIndex) (int iString); + int (*pfnAllocString) (const char *szValue); + struct entvars_s* (*pfnGetVarsOfEnt) (edict_t *pEdict); + edict_t* (*pfnPEntityOfEntOffset) (int iEntOffset); + int (*pfnEntOffsetOfPEntity) (const edict_t *pEdict); + int (*pfnIndexOfEdict) (const edict_t *pEdict); + edict_t* (*pfnPEntityOfEntIndex) (int iEntIndex); + edict_t* (*pfnFindEntityByVars) (struct entvars_s* pvars); + void* (*pfnGetModelPtr) (edict_t* pEdict); + int (*pfnRegUserMsg) (const char *pszName, int iSize); + void (*pfnAnimationAutomove) (const edict_t* pEdict, float flTime); + void (*pfnGetBonePosition) (const edict_t* pEdict, int iBone, float *rgflOrigin, float *rgflAngles ); + uint32 (*pfnFunctionFromName) ( const char *pName ); + const char *(*pfnNameForFunction) ( uint32 function ); + void (*pfnClientPrintf) ( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg ); // JOHN: engine callbacks so game DLL can print messages to individual clients + void (*pfnServerPrint) ( const char *szMsg ); + const char *(*pfnCmd_Args) ( void ); // these 3 added + const char *(*pfnCmd_Argv) ( int argc ); // so game DLL can easily + int (*pfnCmd_Argc) ( void ); // access client 'cmd' strings + void (*pfnGetAttachment) (const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles ); + void (*pfnCRC32_Init) (CRC32_t *pulCRC); + void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len); + void (*pfnCRC32_ProcessByte) (CRC32_t *pulCRC, unsigned char ch); + CRC32_t (*pfnCRC32_Final) (CRC32_t pulCRC); + int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); + float (*pfnRandomFloat) (float flLow, float flHigh); + void (*pfnSetView) (const edict_t *pClient, const edict_t *pViewent ); + float (*pfnTime) ( void ); + void (*pfnCrosshairAngle) (const edict_t *pClient, float pitch, float yaw); + byte * (*pfnLoadFileForMe) (const char *filename, int *pLength); + void (*pfnFreeFile) (void *buffer); + void (*pfnEndSection) (const char *pszSectionName); // trigger_endsection + int (*pfnCompareFileTime) (char *filename1, char *filename2, int *iCompare); + void (*pfnGetGameDir) (char *szGetGameDir); + void (*pfnCvar_RegisterVariable) (cvar_t *variable); + void (*pfnFadeClientVolume) (const edict_t *pEdict, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); + void (*pfnSetClientMaxspeed) (edict_t *pEdict, float fNewMaxspeed); + edict_t * (*pfnCreateFakeClient) (const char *netname); // returns NULL if fake client can't be created + void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, byte msec ); + int (*pfnNumberOfEntities) (void); + char* (*pfnGetInfoKeyBuffer) (edict_t *e); // passing in NULL gets the serverinfo + char* (*pfnInfoKeyValue) (char *infobuffer, const char *key); + void (*pfnSetKeyValue) (char *infobuffer, const char *key, const char *value); + void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, const char *key, const char *value); + int (*pfnIsMapValid) (const char *filename); + void (*pfnStaticDecal) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); + int (*pfnPrecacheGeneric) (const char* s); + int (*pfnGetPlayerUserId) (edict_t *e ); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients + void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + int (*pfnIsDedicatedServer) (void);// is this a dedicated server? + cvar_t *(*pfnCVarGetPointer) (const char *szVarName); + unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients + + // YWB 8/1/99 TFF Physics additions + void (*pfnInfo_RemoveKey) ( char *s, const char *key ); + const char *(*pfnGetPhysicsKeyValue) ( const edict_t *pClient, const char *key ); + void (*pfnSetPhysicsKeyValue) ( const edict_t *pClient, const char *key, const char *value ); + const char *(*pfnGetPhysicsInfoString) ( const edict_t *pClient ); + unsigned short (*pfnPrecacheEvent) ( int type, const char*psz ); + void (*pfnPlaybackEvent) ( int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); + + unsigned char *(*pfnSetFatPVS) ( float *org ); + unsigned char *(*pfnSetFatPAS) ( float *org ); + + int (*pfnCheckVisibility ) ( edict_t *entity, unsigned char *pset ); + + void (*pfnDeltaSetField) ( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaUnsetField) ( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaAddEncoder) ( const char *name, void (*conditionalencode)( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) ); + int (*pfnGetCurrentPlayer) ( void ); + int (*pfnCanSkipPlayer) ( const edict_t *player ); + int (*pfnDeltaFindField) ( struct delta_s *pFields, const char *fieldname ); + void (*pfnDeltaSetFieldByIndex) ( struct delta_s *pFields, int fieldNumber ); + void (*pfnDeltaUnsetFieldByIndex)( struct delta_s *pFields, int fieldNumber ); + + void (*pfnSetGroupMask) ( int mask, int op ); + + int (*pfnCreateInstancedBaseline) ( int classname, struct entity_state_s *baseline ); + void (*pfnCvar_DirectSet) ( struct cvar_s *var, const char *value ); + + // Forces the client and server to be running with the same version of the specified file + // ( e.g., a player model ). + // Calling this has no effect in single player + void (*pfnForceUnmodified) ( FORCE_TYPE type, float *mins, float *maxs, const char *filename ); + + void (*pfnGetPlayerStats) ( const edict_t *pClient, int *ping, int *packet_loss ); + + void (*pfnAddServerCommand) ( const char *cmd_name, void (*function) (void) ); + + // For voice communications, set which clients hear eachother. + // NOTE: these functions take player entity indices (starting at 1). + qboolean (*pfnVoice_GetClientListening)(int iReceiver, int iSender); + qboolean (*pfnVoice_SetClientListening)(int iReceiver, int iSender, qboolean bListen); + + const char *(*pfnGetPlayerAuthId) ( edict_t *e ); + + // PSV: Added for CZ training map +// const char *(*pfnKeyNameForBinding) ( const char* pBinding ); + + sequenceEntry_s* (*pfnSequenceGet) ( const char* fileName, const char* entryName ); + sentenceEntry_s* (*pfnSequencePickSentence) ( const char* groupName, int pickMethod, int *picked ); + + // LH: Give access to filesize via filesystem + int (*pfnGetFileSize) ( const char *filename ); + + unsigned int (*pfnGetApproxWavePlayLen) (const char *filepath); + // MDC: Added for CZ career-mode + int (*pfnIsCareerMatch) ( void ); + + // BGC: return the number of characters of the localized string referenced by using "label" + int (*pfnGetLocalizedStringLength) (const char *label); + + // BGC: added to facilitate persistent storage of tutor message decay values for + // different career game profiles. Also needs to persist regardless of mp.dll being + // destroyed and recreated. + void (*pfnRegisterTutorMessageShown) (int mid); + int (*pfnGetTimesTutorMessageShown) (int mid); + void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength); + void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength); + void (*pfnResetTutorMessageDecayData) ( void ); + + // Added 2005/08/11 (no SDK update): + void(*pfnQueryClientCvarValue) (const edict_t *player, const char *cvarName); + + // Added 2005/11/21 (no SDK update): + void(*pfnQueryClientCvarValue2) (const edict_t *player, const char *cvarName, int requestID); + + // Added 2009/06/19 (no SDK update): + int(*pfnEngCheckParm) (const char *pchCmdLineToken, char **ppnext); + + // Added 2019/06/26 (no SDK update): + // Commented out this for backward compatibility + //edict_t *(*pfnPEntityOfEntIndexAllEntities)(int iEntIndex); + +} enginefuncs_t; + + +// ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 + +// Passed to pfnKeyValue +typedef struct KeyValueData_s +{ + char *szClassName; // in: entity classname + char *szKeyName; // in: name of key + char *szValue; // in: value of key + qboolean fHandled; // out: DLL sets to true if key-value pair was understood +} KeyValueData; + + +typedef struct +{ + char mapName[ 32 ]; + char landmarkName[ 32 ]; + edict_t *pentLandmark; + vec3_t vecLandmarkOrigin; +} LEVELLIST; +#define MAX_LEVEL_CONNECTIONS 16 // These are encoded in the lower 16bits of ENTITYTABLE->flags + +typedef struct +{ + int id; // Ordinal ID of this entity (used for entity <--> pointer conversions) + edict_t *pent; // Pointer to the in-game entity + + int location; // Offset from the base data of this entity + int size; // Byte size of this entity's data + int flags; // This could be a short -- bit mask of transitions that this entity is in the PVS of + string_t classname; // entity class name + +} ENTITYTABLE; + +#define FENTTABLE_PLAYER 0x80000000 +#define FENTTABLE_REMOVED 0x40000000 +#define FENTTABLE_MOVEABLE 0x20000000 +#define FENTTABLE_GLOBAL 0x10000000 + +typedef struct saverestore_s SAVERESTOREDATA; + +#ifdef _WIN32 +typedef +#endif +struct saverestore_s +{ + char *pBaseData; // Start of all entity save data + char *pCurrentData; // Current buffer pointer for sequential access + int size; // Current data size + int bufferSize; // Total space for data + int tokenSize; // Size of the linear list of tokens + int tokenCount; // Number of elements in the pTokens table + char **pTokens; // Hash table of entity strings (sparse) + int currentIndex; // Holds a global entity table ID + int tableCount; // Number of elements in the entity table + int connectionCount;// Number of elements in the levelList[] + ENTITYTABLE *pTable; // Array of ENTITYTABLE elements (1 for each entity) + LEVELLIST levelList[ MAX_LEVEL_CONNECTIONS ]; // List of connections from this level + + // smooth transition + int fUseLandmark; + char szLandmarkName[20];// landmark we'll spawn near in next level + vec3_t vecLandmarkOffset;// for landmark transitions + float time; + char szCurrentMapName[32]; // To check global entities + +} +#ifdef _WIN32 +SAVERESTOREDATA +#endif +; + +typedef enum _fieldtypes +{ + FIELD_FLOAT = 0, // Any floating point value + FIELD_STRING, // A string ID (return from ALLOC_STRING) + FIELD_ENTITY, // An entity offset (EOFFSET) + FIELD_CLASSPTR, // CBaseEntity * + FIELD_EHANDLE, // Entity handle + FIELD_EVARS, // EVARS * + FIELD_EDICT, // edict_t *, or edict_t * (same thing) + FIELD_VECTOR, // Any vector + FIELD_POSITION_VECTOR, // A world coordinate (these are fixed up across level transitions automagically) + FIELD_POINTER, // Arbitrary data pointer... to be removed, use an array of FIELD_CHARACTER + FIELD_INTEGER, // Any integer or enum + FIELD_FUNCTION, // A class function pointer (Think, Use, etc) + FIELD_BOOLEAN, // boolean, implemented as an int, I may use this as a hint for compression + FIELD_SHORT, // 2 byte integer + FIELD_CHARACTER, // a byte + FIELD_TIME, // a floating point time (these are fixed up automatically too!) + FIELD_MODELNAME, // Engine string that is a model name (needs precache) + FIELD_SOUNDNAME, // Engine string that is a sound name (needs precache) + + FIELD_TYPECOUNT, // MUST BE LAST +} FIELDTYPE; + +#if !defined(offsetof) && !defined(GNUC) +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif + +#define _FIELD(type,name,fieldtype,count,flags) { fieldtype, #name, offsetof(type, name), count, flags } +#define DEFINE_FIELD(type,name,fieldtype) _FIELD(type, name, fieldtype, 1, 0) +#define DEFINE_ARRAY(type,name,fieldtype,count) _FIELD(type, name, fieldtype, count, 0) +#define DEFINE_ENTITY_FIELD(name,fieldtype) _FIELD(entvars_t, name, fieldtype, 1, 0 ) +#define DEFINE_ENTITY_GLOBAL_FIELD(name,fieldtype) _FIELD(entvars_t, name, fieldtype, 1, FTYPEDESC_GLOBAL ) +#define DEFINE_GLOBAL_FIELD(type,name,fieldtype) _FIELD(type, name, fieldtype, 1, FTYPEDESC_GLOBAL ) + + +#define FTYPEDESC_GLOBAL 0x0001 // This field is masked for global entity save/restore + +typedef struct +{ + FIELDTYPE fieldType; + char *fieldName; + int fieldOffset; + short fieldSize; + short flags; +} TYPEDESCRIPTION; + +#ifndef ARRAYSIZE +#define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) +#endif + +typedef struct +{ + // Initialize/shutdown the game (one-time call after loading of game .dll ) + void (*pfnGameInit) ( void ); + int (*pfnSpawn) ( edict_t *pent ); + void (*pfnThink) ( edict_t *pent ); + void (*pfnUse) ( edict_t *pentUsed, edict_t *pentOther ); + void (*pfnTouch) ( edict_t *pentTouched, edict_t *pentOther ); + void (*pfnBlocked) ( edict_t *pentBlocked, edict_t *pentOther ); + void (*pfnKeyValue) ( edict_t *pentKeyvalue, KeyValueData *pkvd ); + void (*pfnSave) ( edict_t *pent, SAVERESTOREDATA *pSaveData ); + int (*pfnRestore) ( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); + void (*pfnSetAbsBox) ( edict_t *pent ); + + void (*pfnSaveWriteFields) ( SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int ); + void (*pfnSaveReadFields) ( SAVERESTOREDATA *, const char *, void *, TYPEDESCRIPTION *, int ); + + void (*pfnSaveGlobalState) ( SAVERESTOREDATA * ); + void (*pfnRestoreGlobalState) ( SAVERESTOREDATA * ); + void (*pfnResetGlobalState) ( void ); + + qboolean (*pfnClientConnect) ( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[ 128 ] ); + + void (*pfnClientDisconnect) ( edict_t *pEntity ); + void (*pfnClientKill) ( edict_t *pEntity ); + void (*pfnClientPutInServer) ( edict_t *pEntity ); + void (*pfnClientCommand) ( edict_t *pEntity ); + void (*pfnClientUserInfoChanged)( edict_t *pEntity, char *infobuffer ); + + void (*pfnServerActivate) ( edict_t *pEdictList, int edictCount, int clientMax ); + void (*pfnServerDeactivate) ( void ); + + void (*pfnPlayerPreThink) ( edict_t *pEntity ); + void (*pfnPlayerPostThink) ( edict_t *pEntity ); + + void (*pfnStartFrame) ( void ); + void (*pfnParmsNewLevel) ( void ); + void (*pfnParmsChangeLevel) ( void ); + + // Returns string describing current .dll. E.g., TeamFotrress 2, Half-Life + const char *(*pfnGetGameDescription)( void ); + + // Notify dll about a player customization. + void (*pfnPlayerCustomization) ( edict_t *pEntity, customization_t *pCustom ); + + // Spectator funcs + void (*pfnSpectatorConnect) ( edict_t *pEntity ); + void (*pfnSpectatorDisconnect) ( edict_t *pEntity ); + void (*pfnSpectatorThink) ( edict_t *pEntity ); + + // Notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint. + void (*pfnSys_Error) ( const char *error_string ); + + void (*pfnPM_Move) ( struct playermove_s *ppmove, qboolean server ); + void (*pfnPM_Init) ( struct playermove_s *ppmove ); + char (*pfnPM_FindTextureType)( char *name ); + void (*pfnSetupVisibility)( struct edict_s *pViewEntity, struct edict_s *pClient, unsigned char **pvs, unsigned char **pas ); + void (*pfnUpdateClientData) ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); + int (*pfnAddToFullPack)( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); + void (*pfnCreateBaseline) ( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); + void (*pfnRegisterEncoders) ( void ); + int (*pfnGetWeaponData) ( struct edict_s *player, struct weapon_data_s *info ); + + void (*pfnCmdStart) ( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); + void (*pfnCmdEnd) ( const edict_t *player ); + + // Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max + // size of the response_buffer, so you must zero it out if you choose not to respond. + int (*pfnConnectionlessPacket ) ( const struct netadr_s *net_from_, const char *args, char *response_buffer, int *response_buffer_size ); + + // Enumerates player hulls. Returns 0 if the hull number doesn't exist, 1 otherwise + int (*pfnGetHullBounds) ( int hullnumber, float *mins, float *maxs ); + + // Create baselines for certain "unplaced" items. + void (*pfnCreateInstancedBaselines) ( void ); + + // One of the pfnForceUnmodified files failed the consistency check for the specified player + // Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters ) + int (*pfnInconsistentFile)( const struct edict_s *player, const char *filename, char *disconnect_message ); + + // The game .dll should return 1 if lag compensation should be allowed ( could also just set + // the sv_unlag cvar. + // Most games right now should return 0, until client-side weapon prediction code is written + // and tested for them. + int (*pfnAllowLagCompensation)( void ); +} DLL_FUNCTIONS; + +extern DLL_FUNCTIONS gEntityInterface; + +// Current version. +#define NEW_DLL_FUNCTIONS_VERSION 1 + +typedef struct +{ + // Called right before the object's memory is freed. + // Calls its destructor. + void (*pfnOnFreeEntPrivateData)(edict_t *pEnt); + void (*pfnGameShutdown)(void); + int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther ); + void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); + void (*pfnCvarValue2)( const edict_t *pEnt, int requestID, const char *cvarName, const char *value ); +} NEW_DLL_FUNCTIONS; +typedef int(*NEW_DLL_FUNCTIONS_FN)(NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); + +// Pointers will be null if the game DLL doesn't support this API. +extern NEW_DLL_FUNCTIONS gNewDLLFunctions; + +typedef int(*APIFUNCTION)(DLL_FUNCTIONS *pFunctionTable, int interfaceVersion); +typedef int(*APIFUNCTION2)(DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); diff --git a/regamedll/engine/maintypes.h b/regamedll/engine/maintypes.h index 2b63299f..2ad3d371 100644 --- a/regamedll/engine/maintypes.h +++ b/regamedll/engine/maintypes.h @@ -1,76 +1,76 @@ -/* -* -* This program is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License as published by the -* Free Software Foundation; either version 2 of the License, or (at -* your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software Foundation, -* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* In addition, as a special exception, the author gives permission to -* link the code of this program with the Half-Life Game Engine ("HL -* Engine") and Modified Game Libraries ("MODs") developed by Valve, -* L.L.C ("Valve"). You must obey the GNU General Public License in all -* respects for all of the code used other than the HL Engine and MODs -* from Valve. If you modify this file, you may extend this exception -* to your version of the file, but you are not obligated to do so. If -* you do not wish to do so, delete this exception statement from your -* version. -* -*/ - -#ifndef MAINTYPES_H -#define MAINTYPES_H -#ifdef _WIN32 -#pragma once -#endif - -#include "osconfig.h" -#include "mathlib.h" - -// Has no references on multiplayer library CS. -#define NOXREF -// Function body is not implemented. -#define NOBODY -// Function is not tested at all. -#define UNTESTED -// Function is doubt reversed -#define TODOBODY - -#define BIT(n) (1<<(n)) - -#ifdef HAVE_STRONG_TYPEDEF -enum class string_t: unsigned int {}; -#else -typedef unsigned int string_t; -#endif - -typedef int EOFFSET; -typedef int BOOL; -typedef unsigned char byte; -typedef unsigned short word; -#define _DEF_BYTE_ - -#ifndef __cplusplus - #undef true - #undef false - typedef enum {false, true} qboolean; -#else - typedef int qboolean; -#endif // #ifndef __cplusplus - -// From engine/server.h -typedef enum sv_delta_s -{ - sv_packet_nodelta, - sv_packet_delta, -} sv_delta_t; - -#endif // MAINTYPES_H +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#ifndef MAINTYPES_H +#define MAINTYPES_H +#ifdef _WIN32 +#pragma once +#endif + +#include "osconfig.h" +#include "mathlib.h" + +// Has no references on multiplayer library CS. +#define NOXREF +// Function body is not implemented. +#define NOBODY +// Function is not tested at all. +#define UNTESTED +// Function is doubt reversed +#define TODOBODY + +#define BIT(n) (1<<(n)) + +#ifdef HAVE_STRONG_TYPEDEF +enum class string_t: unsigned int {}; +#else +typedef unsigned int string_t; +#endif + +typedef int EOFFSET; +typedef int BOOL; +typedef unsigned char byte; +typedef unsigned short word; +#define _DEF_BYTE_ + +#ifndef __cplusplus + #undef true + #undef false + typedef enum {false, true} qboolean; +#else + typedef int qboolean; +#endif // #ifndef __cplusplus + +// From engine/server.h +typedef enum sv_delta_s +{ + sv_packet_nodelta, + sv_packet_delta, +} sv_delta_t; + +#endif // MAINTYPES_H diff --git a/regamedll/game_shared/bot/bot.cpp b/regamedll/game_shared/bot/bot.cpp index 706740fe..e5849b9b 100644 --- a/regamedll/game_shared/bot/bot.cpp +++ b/regamedll/game_shared/bot/bot.cpp @@ -1,508 +1,508 @@ -#include "precompiled.h" - -// Nasty Hack. See client.cpp/ClientCommand() -const char *BotArgs[4] = {}; -bool UseBotArgs = false; - -CBot::CBot() -{ - // the profile will be attached after this instance is constructed - m_profile = nullptr; - - // assign this bot a unique ID - static unsigned int nextID = 1; - - // wraparound (highly unlikely) - if (nextID == 0) - ++nextID; - - m_id = nextID++; - m_postureStackIndex = 0; -} - -// Prepare bot for action -bool CBot::Initialize(const BotProfile *profile) -{ - m_profile = profile; - return true; -} - -void CBot::Spawn() -{ - // Let CBasePlayer set some things up - CBasePlayer::Spawn(); - - // Make sure everyone knows we are a bot - pev->flags |= (FL_CLIENT | FL_FAKECLIENT); - - // Bots use their own thinking mechanism - SetThink(nullptr); - pev->nextthink = -1; - - m_flNextBotThink = gpGlobals->time + g_flBotCommandInterval; - m_flNextFullBotThink = gpGlobals->time + g_flBotFullThinkInterval; - m_flPreviousCommandTime = gpGlobals->time; - - m_isRunning = true; - m_isCrouching = false; - m_postureStackIndex = 0; - - m_jumpTimestamp = 0.0f; - - // Command interface variable initialization - ResetCommand(); - - // Allow derived classes to setup at spawn time - SpawnBot(); -} - -Vector CBot::GetAutoaimVector(float flDelta) -{ - UTIL_MakeVectors(pev->v_angle + pev->punchangle); - return gpGlobals->v_forward; -} - -void CBot::BotThink() -{ - if (gpGlobals->time >= m_flNextBotThink) - { - m_flNextBotThink = gpGlobals->time + g_flBotCommandInterval; - - Upkeep(); - - if (gpGlobals->time >= m_flNextFullBotThink) - { - m_flNextFullBotThink = gpGlobals->time + g_flBotFullThinkInterval; - - ResetCommand(); - Update(); - } - ExecuteCommand(); - } -} - -void CBot::MoveForward() -{ - m_forwardSpeed = GetMoveSpeed(); - m_buttonFlags |= IN_FORWARD; - - // make mutually exclusive - m_buttonFlags &= ~IN_BACK; -} - -void CBot::MoveBackward() -{ - m_forwardSpeed = -GetMoveSpeed(); - m_buttonFlags |= IN_BACK; - - // make mutually exclusive - m_buttonFlags &= ~IN_FORWARD; -} - -void CBot::StrafeLeft() -{ - m_strafeSpeed = -GetMoveSpeed(); - m_buttonFlags |= IN_MOVELEFT; - - // make mutually exclusive - m_buttonFlags &= ~IN_MOVERIGHT; -} - -void CBot::StrafeRight() -{ - m_strafeSpeed = GetMoveSpeed(); - m_buttonFlags |= IN_MOVERIGHT; - - // make mutually exclusive - m_buttonFlags &= ~IN_MOVELEFT; -} - -bool CBot::Jump(bool mustJump) -{ - if (IsJumping() || IsCrouching()) - return false; - - if (!mustJump) - { - const float minJumpInterval = 0.9f; // 1.5f; - if (gpGlobals->time - m_jumpTimestamp < minJumpInterval) - return false; - } - - // still need sanity check for jumping frequency - const float sanityInterval = 0.3f; - if (gpGlobals->time - m_jumpTimestamp < sanityInterval) - return false; - - // jump - m_buttonFlags |= IN_JUMP; - m_jumpTimestamp = gpGlobals->time; - return true; -} - -// Zero any MoveForward(), Jump(), etc -void CBot::ClearMovement() -{ - ResetCommand(); -} - -// Returns true if we are in the midst of a jump -bool CBot::IsJumping() -{ - // if long time after last jump, we can't be jumping - if (gpGlobals->time - m_jumpTimestamp > 3.0f) - return false; - - // if we just jumped, we're still jumping - if (gpGlobals->time - m_jumpTimestamp < 1.0f) - return true; - - // a little after our jump, we're jumping until we hit the ground - if (pev->flags & FL_ONGROUND) - return false; - - return true; -} - -void CBot::Crouch() -{ - m_isCrouching = true; -} - -void CBot::StandUp() -{ - m_isCrouching = false; -} - -void CBot::UseEnvironment() -{ - m_buttonFlags |= IN_USE; -} - -void CBot::PrimaryAttack() -{ - m_buttonFlags |= IN_ATTACK; -} - -void CBot::ClearPrimaryAttack() -{ - m_buttonFlags &= ~IN_ATTACK; -} - -void CBot::TogglePrimaryAttack() -{ - m_buttonFlags ^= IN_ATTACK; -} - -void CBot::SecondaryAttack() -{ - m_buttonFlags |= IN_ATTACK2; -} - -void CBot::Reload() -{ - m_buttonFlags |= IN_RELOAD; -} - -// Returns ratio of ammo left to max ammo (1 = full clip, 0 = empty) -float CBot::GetActiveWeaponAmmoRatio() const -{ - CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); - if (!pCurrentWeapon) - return 0.0f; - - // Weapons with no ammo are always full - if (pCurrentWeapon->m_iClip < 0) - return 1.0f; - - return float(pCurrentWeapon->m_iClip) / float(pCurrentWeapon->iMaxClip()); -} - -// Return true if active weapon has an empty clip -bool CBot::IsActiveWeaponClipEmpty() const -{ - CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); - if (pCurrentWeapon && pCurrentWeapon->m_iClip == 0) - return true; - - return false; -} - -// Return true if active weapon has no ammo at all -bool CBot::IsActiveWeaponOutOfAmmo() const -{ - CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); - if (!pCurrentWeapon) - return true; - - if (pCurrentWeapon->m_iClip < 0) - return false; - - if (pCurrentWeapon->m_iClip == 0 && m_rgAmmo[pCurrentWeapon->m_iPrimaryAmmoType] <= 0) - return true; - - return false; -} - -// Return true if looking thru weapon's scope -bool CBot::IsUsingScope() const -{ - // if our field of view is less than 90, we're looking thru a scope (maybe only true for CS...) - if (m_iFOV < 90.0f) - return true; - - return false; -} - -void CBot::ExecuteCommand() -{ - byte adjustedMSec; - - // Adjust msec to command time interval - adjustedMSec = ThrottledMsec(); - - // player model is "munged" - pev->angles = pev->v_angle; - pev->angles.x /= -3.0f; - - // save the command time - m_flPreviousCommandTime = gpGlobals->time; - - if (IsCrouching()) - { - m_buttonFlags |= IN_DUCK; - } - -#ifdef REGAMEDLL_FIXES - // don't move if frozen state present - if (pev->flags & FL_FROZEN) - { - adjustedMSec = 0; - ResetCommand(); - } -#endif - - // Run the command - PLAYER_RUN_MOVE(edict(), pev->v_angle, m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0, adjustedMSec); -} - -void CBot::ResetCommand() -{ - m_forwardSpeed = 0.0f; - m_strafeSpeed = 0.0f; - m_verticalSpeed = 0.0f; - m_buttonFlags = 0; -} - -byte CBot::ThrottledMsec() const -{ - int iNewMsec; - - // Estimate Msec to use for this command based on time passed from the previous command - iNewMsec = int((gpGlobals->time - m_flPreviousCommandTime) * 1000); - - // Doh, bots are going to be slower than they should if this happens. - // Upgrade that CPU or use less bots! - if (iNewMsec > 255) - iNewMsec = 255; - - return byte(iNewMsec); -} - -#ifndef REGAMEDLL_FIXES -// Do a "client command" - useful for invoking menu choices, etc. -void CBot::ClientCommand(const char *cmd, const char *arg1, const char *arg2, const char *arg3) -{ - BotArgs[0] = cmd; - BotArgs[1] = arg1; - BotArgs[2] = arg2; - BotArgs[3] = arg3; - - UseBotArgs = true; - ::ClientCommand_(ENT(pev)); - UseBotArgs = false; -} -#endif - -// Returns TRUE if given entity is our enemy -bool CBot::IsEnemy(CBaseEntity *pEntity) const -{ - // only Players (real and AI) can be enemies - if (!pEntity->IsPlayer()) - return false; - - // corpses are no threat - if (!pEntity->IsAlive()) - return false; - - CBasePlayer *pPlayer = static_cast(pEntity); - - // if they are on our team, they are our friends - if (BotRelationship(pPlayer) == BOT_TEAMMATE) - return false; - - // yep, we hate 'em - return true; -} - -// Return number of enemies left alive -int CBot::GetEnemiesRemaining() const -{ - int count = 0; - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) - continue; - - if (FStrEq(STRING(pPlayer->pev->netname), "")) - continue; - - if (!IsEnemy(pPlayer)) - continue; - - if (!pPlayer->IsAlive()) - continue; - - count++; - } - - return count; -} - -// Return number of friends left alive -int CBot::GetFriendsRemaining() const -{ - int count = 0; - for (int i = 1; i <= gpGlobals->maxClients; i++) - { - CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); - if (!pPlayer) - continue; - - if (FNullEnt(pPlayer->pev)) - continue; - - if (FStrEq(STRING(pPlayer->pev->netname), "")) - continue; - - if (IsEnemy(pPlayer)) - continue; - - if (!pPlayer->IsAlive()) - continue; - - if (pPlayer == static_cast(const_cast(this))) - continue; - - count++; - } - - return count; -} - -bool CBot::IsLocalPlayerWatchingMe() const -{ - // avoid crash during spawn - if (!pev) - return false; - - int myIndex = const_cast(this)->entindex(); - - CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); - if (!pPlayer) - return false; - - if (((pPlayer->pev->flags & FL_SPECTATOR) || pPlayer->m_iTeam == SPECTATOR) && pPlayer->pev->iuser2 == myIndex) - { - switch (pPlayer->pev->iuser1) - { - case OBS_CHASE_LOCKED: - case OBS_CHASE_FREE: - case OBS_IN_EYE: - return true; - } - } - - return false; -} - -NOXREF void CBot::Print(char *format, ...) const -{ - va_list varg; - char buffer[1024]; - - // prefix the message with the bot's name - Q_sprintf(buffer, "%s: ", STRING(pev->netname)); - SERVER_PRINT(buffer); - - va_start(varg, format); - vsprintf(buffer, format, varg); - va_end(varg); - - SERVER_PRINT(buffer); -} - -void CBot::PrintIfWatched(char *format, ...) const -{ - if (!cv_bot_debug.value) - return; - - if ((IsLocalPlayerWatchingMe() && (cv_bot_debug.value == 1 || cv_bot_debug.value == 3)) - || (cv_bot_debug.value == 2 || cv_bot_debug.value == 4)) - { - va_list varg; - char buffer[1024]; - - // prefix the message with the bot's name (this can be NULL if bot was just added) - const char *name = pev ? STRING(pev->netname) : "(NULL pev)"; - Q_sprintf(buffer, "%s: ", name ? name : "(NULL netname)"); - - SERVER_PRINT(buffer); - - va_start(varg, format); - vsprintf(buffer, format, varg); - va_end(varg); - - SERVER_PRINT(buffer); - } -} - -ActiveGrenade::ActiveGrenade(int weaponID, CGrenade *grenadeEntity) -{ - m_id = weaponID; - m_entity = grenadeEntity; - m_detonationPosition = grenadeEntity->pev->origin; - m_dieTimestamp = 0; -} - -void ActiveGrenade::OnEntityGone() -{ - if (m_id == WEAPON_SMOKEGRENADE) - { - // smoke lingers after grenade is gone - const float smokeLingerTime = 4.0f; - m_dieTimestamp = gpGlobals->time + smokeLingerTime; - } - - m_entity = nullptr; -} - -bool ActiveGrenade::IsValid() const -{ - if (!m_entity) - { - if (gpGlobals->time > m_dieTimestamp) - return false; - } - - return true; -} - -const Vector *ActiveGrenade::GetPosition() const -{ - return &m_entity->pev->origin; -} +#include "precompiled.h" + +// Nasty Hack. See client.cpp/ClientCommand() +const char *BotArgs[4] = {}; +bool UseBotArgs = false; + +CBot::CBot() +{ + // the profile will be attached after this instance is constructed + m_profile = nullptr; + + // assign this bot a unique ID + static unsigned int nextID = 1; + + // wraparound (highly unlikely) + if (nextID == 0) + ++nextID; + + m_id = nextID++; + m_postureStackIndex = 0; +} + +// Prepare bot for action +bool CBot::Initialize(const BotProfile *profile) +{ + m_profile = profile; + return true; +} + +void CBot::Spawn() +{ + // Let CBasePlayer set some things up + CBasePlayer::Spawn(); + + // Make sure everyone knows we are a bot + pev->flags |= (FL_CLIENT | FL_FAKECLIENT); + + // Bots use their own thinking mechanism + SetThink(nullptr); + pev->nextthink = -1; + + m_flNextBotThink = gpGlobals->time + g_flBotCommandInterval; + m_flNextFullBotThink = gpGlobals->time + g_flBotFullThinkInterval; + m_flPreviousCommandTime = gpGlobals->time; + + m_isRunning = true; + m_isCrouching = false; + m_postureStackIndex = 0; + + m_jumpTimestamp = 0.0f; + + // Command interface variable initialization + ResetCommand(); + + // Allow derived classes to setup at spawn time + SpawnBot(); +} + +Vector CBot::GetAutoaimVector(float flDelta) +{ + UTIL_MakeVectors(pev->v_angle + pev->punchangle); + return gpGlobals->v_forward; +} + +void CBot::BotThink() +{ + if (gpGlobals->time >= m_flNextBotThink) + { + m_flNextBotThink = gpGlobals->time + g_flBotCommandInterval; + + Upkeep(); + + if (gpGlobals->time >= m_flNextFullBotThink) + { + m_flNextFullBotThink = gpGlobals->time + g_flBotFullThinkInterval; + + ResetCommand(); + Update(); + } + ExecuteCommand(); + } +} + +void CBot::MoveForward() +{ + m_forwardSpeed = GetMoveSpeed(); + m_buttonFlags |= IN_FORWARD; + + // make mutually exclusive + m_buttonFlags &= ~IN_BACK; +} + +void CBot::MoveBackward() +{ + m_forwardSpeed = -GetMoveSpeed(); + m_buttonFlags |= IN_BACK; + + // make mutually exclusive + m_buttonFlags &= ~IN_FORWARD; +} + +void CBot::StrafeLeft() +{ + m_strafeSpeed = -GetMoveSpeed(); + m_buttonFlags |= IN_MOVELEFT; + + // make mutually exclusive + m_buttonFlags &= ~IN_MOVERIGHT; +} + +void CBot::StrafeRight() +{ + m_strafeSpeed = GetMoveSpeed(); + m_buttonFlags |= IN_MOVERIGHT; + + // make mutually exclusive + m_buttonFlags &= ~IN_MOVELEFT; +} + +bool CBot::Jump(bool mustJump) +{ + if (IsJumping() || IsCrouching()) + return false; + + if (!mustJump) + { + const float minJumpInterval = 0.9f; // 1.5f; + if (gpGlobals->time - m_jumpTimestamp < minJumpInterval) + return false; + } + + // still need sanity check for jumping frequency + const float sanityInterval = 0.3f; + if (gpGlobals->time - m_jumpTimestamp < sanityInterval) + return false; + + // jump + m_buttonFlags |= IN_JUMP; + m_jumpTimestamp = gpGlobals->time; + return true; +} + +// Zero any MoveForward(), Jump(), etc +void CBot::ClearMovement() +{ + ResetCommand(); +} + +// Returns true if we are in the midst of a jump +bool CBot::IsJumping() +{ + // if long time after last jump, we can't be jumping + if (gpGlobals->time - m_jumpTimestamp > 3.0f) + return false; + + // if we just jumped, we're still jumping + if (gpGlobals->time - m_jumpTimestamp < 1.0f) + return true; + + // a little after our jump, we're jumping until we hit the ground + if (pev->flags & FL_ONGROUND) + return false; + + return true; +} + +void CBot::Crouch() +{ + m_isCrouching = true; +} + +void CBot::StandUp() +{ + m_isCrouching = false; +} + +void CBot::UseEnvironment() +{ + m_buttonFlags |= IN_USE; +} + +void CBot::PrimaryAttack() +{ + m_buttonFlags |= IN_ATTACK; +} + +void CBot::ClearPrimaryAttack() +{ + m_buttonFlags &= ~IN_ATTACK; +} + +void CBot::TogglePrimaryAttack() +{ + m_buttonFlags ^= IN_ATTACK; +} + +void CBot::SecondaryAttack() +{ + m_buttonFlags |= IN_ATTACK2; +} + +void CBot::Reload() +{ + m_buttonFlags |= IN_RELOAD; +} + +// Returns ratio of ammo left to max ammo (1 = full clip, 0 = empty) +float CBot::GetActiveWeaponAmmoRatio() const +{ + CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); + if (!pCurrentWeapon) + return 0.0f; + + // Weapons with no ammo are always full + if (pCurrentWeapon->m_iClip < 0) + return 1.0f; + + return float(pCurrentWeapon->m_iClip) / float(pCurrentWeapon->iMaxClip()); +} + +// Return true if active weapon has an empty clip +bool CBot::IsActiveWeaponClipEmpty() const +{ + CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); + if (pCurrentWeapon && pCurrentWeapon->m_iClip == 0) + return true; + + return false; +} + +// Return true if active weapon has no ammo at all +bool CBot::IsActiveWeaponOutOfAmmo() const +{ + CBasePlayerWeapon *pCurrentWeapon = GetActiveWeapon(); + if (!pCurrentWeapon) + return true; + + if (pCurrentWeapon->m_iClip < 0) + return false; + + if (pCurrentWeapon->m_iClip == 0 && m_rgAmmo[pCurrentWeapon->m_iPrimaryAmmoType] <= 0) + return true; + + return false; +} + +// Return true if looking thru weapon's scope +bool CBot::IsUsingScope() const +{ + // if our field of view is less than 90, we're looking thru a scope (maybe only true for CS...) + if (m_iFOV < 90.0f) + return true; + + return false; +} + +void CBot::ExecuteCommand() +{ + byte adjustedMSec; + + // Adjust msec to command time interval + adjustedMSec = ThrottledMsec(); + + // player model is "munged" + pev->angles = pev->v_angle; + pev->angles.x /= -3.0f; + + // save the command time + m_flPreviousCommandTime = gpGlobals->time; + + if (IsCrouching()) + { + m_buttonFlags |= IN_DUCK; + } + +#ifdef REGAMEDLL_FIXES + // don't move if frozen state present + if (pev->flags & FL_FROZEN) + { + adjustedMSec = 0; + ResetCommand(); + } +#endif + + // Run the command + PLAYER_RUN_MOVE(edict(), pev->v_angle, m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0, adjustedMSec); +} + +void CBot::ResetCommand() +{ + m_forwardSpeed = 0.0f; + m_strafeSpeed = 0.0f; + m_verticalSpeed = 0.0f; + m_buttonFlags = 0; +} + +byte CBot::ThrottledMsec() const +{ + int iNewMsec; + + // Estimate Msec to use for this command based on time passed from the previous command + iNewMsec = int((gpGlobals->time - m_flPreviousCommandTime) * 1000); + + // Doh, bots are going to be slower than they should if this happens. + // Upgrade that CPU or use less bots! + if (iNewMsec > 255) + iNewMsec = 255; + + return byte(iNewMsec); +} + +#ifndef REGAMEDLL_FIXES +// Do a "client command" - useful for invoking menu choices, etc. +void CBot::ClientCommand(const char *cmd, const char *arg1, const char *arg2, const char *arg3) +{ + BotArgs[0] = cmd; + BotArgs[1] = arg1; + BotArgs[2] = arg2; + BotArgs[3] = arg3; + + UseBotArgs = true; + ::ClientCommand_(ENT(pev)); + UseBotArgs = false; +} +#endif + +// Returns TRUE if given entity is our enemy +bool CBot::IsEnemy(CBaseEntity *pEntity) const +{ + // only Players (real and AI) can be enemies + if (!pEntity->IsPlayer()) + return false; + + // corpses are no threat + if (!pEntity->IsAlive()) + return false; + + CBasePlayer *pPlayer = static_cast(pEntity); + + // if they are on our team, they are our friends + if (BotRelationship(pPlayer) == BOT_TEAMMATE) + return false; + + // yep, we hate 'em + return true; +} + +// Return number of enemies left alive +int CBot::GetEnemiesRemaining() const +{ + int count = 0; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer) + continue; + + if (FNullEnt(pPlayer->pev)) + continue; + + if (FStrEq(STRING(pPlayer->pev->netname), "")) + continue; + + if (!IsEnemy(pPlayer)) + continue; + + if (!pPlayer->IsAlive()) + continue; + + count++; + } + + return count; +} + +// Return number of friends left alive +int CBot::GetFriendsRemaining() const +{ + int count = 0; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer) + continue; + + if (FNullEnt(pPlayer->pev)) + continue; + + if (FStrEq(STRING(pPlayer->pev->netname), "")) + continue; + + if (IsEnemy(pPlayer)) + continue; + + if (!pPlayer->IsAlive()) + continue; + + if (pPlayer == static_cast(const_cast(this))) + continue; + + count++; + } + + return count; +} + +bool CBot::IsLocalPlayerWatchingMe() const +{ + // avoid crash during spawn + if (!pev) + return false; + + int myIndex = const_cast(this)->entindex(); + + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (!pPlayer) + return false; + + if (((pPlayer->pev->flags & FL_SPECTATOR) || pPlayer->m_iTeam == SPECTATOR) && pPlayer->pev->iuser2 == myIndex) + { + switch (pPlayer->pev->iuser1) + { + case OBS_CHASE_LOCKED: + case OBS_CHASE_FREE: + case OBS_IN_EYE: + return true; + } + } + + return false; +} + +NOXREF void CBot::Print(char *format, ...) const +{ + va_list varg; + char buffer[1024]; + + // prefix the message with the bot's name + Q_sprintf(buffer, "%s: ", STRING(pev->netname)); + SERVER_PRINT(buffer); + + va_start(varg, format); + vsprintf(buffer, format, varg); + va_end(varg); + + SERVER_PRINT(buffer); +} + +void CBot::PrintIfWatched(char *format, ...) const +{ + if (!cv_bot_debug.value) + return; + + if ((IsLocalPlayerWatchingMe() && (cv_bot_debug.value == 1 || cv_bot_debug.value == 3)) + || (cv_bot_debug.value == 2 || cv_bot_debug.value == 4)) + { + va_list varg; + char buffer[1024]; + + // prefix the message with the bot's name (this can be NULL if bot was just added) + const char *name = pev ? STRING(pev->netname) : "(NULL pev)"; + Q_sprintf(buffer, "%s: ", name ? name : "(NULL netname)"); + + SERVER_PRINT(buffer); + + va_start(varg, format); + vsprintf(buffer, format, varg); + va_end(varg); + + SERVER_PRINT(buffer); + } +} + +ActiveGrenade::ActiveGrenade(int weaponID, CGrenade *grenadeEntity) +{ + m_id = weaponID; + m_entity = grenadeEntity; + m_detonationPosition = grenadeEntity->pev->origin; + m_dieTimestamp = 0; +} + +void ActiveGrenade::OnEntityGone() +{ + if (m_id == WEAPON_SMOKEGRENADE) + { + // smoke lingers after grenade is gone + const float smokeLingerTime = 4.0f; + m_dieTimestamp = gpGlobals->time + smokeLingerTime; + } + + m_entity = nullptr; +} + +bool ActiveGrenade::IsValid() const +{ + if (!m_entity) + { + if (gpGlobals->time > m_dieTimestamp) + return false; + } + + return true; +} + +const Vector *ActiveGrenade::GetPosition() const +{ + return &m_entity->pev->origin; +} diff --git a/regamedll/pm_shared/pm_shared.cpp b/regamedll/pm_shared/pm_shared.cpp index be0374ba..bbe213e5 100644 --- a/regamedll/pm_shared/pm_shared.cpp +++ b/regamedll/pm_shared/pm_shared.cpp @@ -1,3247 +1,3247 @@ -#include "precompiled.h" - -BOOL pm_shared_initialized = FALSE; - -vec3_t rgv3tStuckTable[54]; -int rgStuckLast[MAX_CLIENTS][2]; - -int pm_gcTextures = 0; -char pm_grgszTextureName[MAX_TEXTURES][MAX_TEXTURENAME_LENGHT]; -char pm_grgchTextureType[MAX_TEXTURES]; - -playermove_t *pmove = nullptr; -BOOL g_onladder = FALSE; - -#ifdef CLIENT_DLL - int iJumpSpectator; - float vJumpOrigin[3]; - float vJumpAngles[3]; -#endif - -void PM_SwapTextures(int i, int j) -{ - char chTemp; - char szTemp[MAX_TEXTURENAME_LENGHT]; - - Q_strcpy(szTemp, pm_grgszTextureName[i]); - chTemp = pm_grgchTextureType[i]; - - Q_strcpy(pm_grgszTextureName[i], pm_grgszTextureName[j]); - pm_grgchTextureType[i] = pm_grgchTextureType[j]; - - Q_strcpy(pm_grgszTextureName[j], szTemp); - pm_grgchTextureType[j] = chTemp; -} - -NOXREF qboolean PM_IsThereGrassTexture() -{ - for (int i = 0; i < pm_gcTextures; i++) - { - if (pm_grgchTextureType[i] == CHAR_TEX_GRASS) - return TRUE; - } - - return FALSE; -} - -void PM_SortTextures() -{ - // Bubble sort, yuck, but this only occurs at startup and it's only 512 elements... - int i, j; - for (i = 0; i < pm_gcTextures; i++) - { - for (j = i + 1; j < pm_gcTextures; j++) - { - if (Q_stricmp(pm_grgszTextureName[i], pm_grgszTextureName[j]) > 0) - { - // Swap - PM_SwapTextures(i, j); - } - } - } -} - -void PM_InitTextureTypes() -{ - char buffer[512]; - int i, j; - byte *pMemFile; - int fileSize, filePos = 0; - static bool bTextureTypeInit = false; - - if (bTextureTypeInit) - return; - - Q_memset(&(pm_grgszTextureName[0][0]), 0, sizeof(pm_grgszTextureName)); - Q_memset(pm_grgchTextureType, 0, sizeof(pm_grgchTextureType)); - - pm_gcTextures = 0; - Q_memset(buffer, 0, sizeof(buffer)); - - pMemFile = pmove->COM_LoadFile("sound/materials.txt", 5, &fileSize); - if (!pMemFile) - return; - - // for each line in the file... - while (pmove->memfgets(pMemFile, fileSize, &filePos, buffer, sizeof(buffer) - 1) && (pm_gcTextures < MAX_TEXTURES)) - { - // skip whitespace - i = 0; - while (buffer[i] && isspace(buffer[i])) - i++; - - if (!buffer[i]) - continue; - - // skip comment lines - if (buffer[i] == '/' || !isalpha(buffer[i])) - continue; - - // get texture type - pm_grgchTextureType[pm_gcTextures] = toupper(buffer[i++]); - - // skip whitespace - while (buffer[i] && isspace(buffer[i])) - i++; - - if (!buffer[i]) - continue; - - // get sentence name - j = i; - while (buffer[j] && !isspace(buffer[j])) - j++; - - if (!buffer[j]) - continue; - - // null-terminate name and save in sentences array - j = Q_min(j, MAX_TEXTURENAME_LENGHT - 1 + i); - buffer[j] = '\0'; - - Q_strcpy(&(pm_grgszTextureName[pm_gcTextures++][0]), &(buffer[i])); - } - - // Must use engine to free since we are in a .dll - pmove->COM_FreeFile(pMemFile); - - PM_SortTextures(); - bTextureTypeInit = true; -} - -char EXT_FUNC PM_FindTextureType(char *name) -{ - int left, right, pivot; - int val; - - assert(pm_shared_initialized); - - left = 0; - right = pm_gcTextures - 1; - - while (left <= right) - { - pivot = (left + right) / 2; - - val = Q_strnicmp(name, pm_grgszTextureName[pivot], MAX_TEXTURENAME_LENGHT - 1); - - if (val == 0) - { - return pm_grgchTextureType[pivot]; - } - else if (val > 0) - { - left = pivot + 1; - } - else if (val < 0) - { - right = pivot - 1; - } - } - - return CHAR_TEX_CONCRETE; -} - -void PM_PlayStepSound(int step, float fvol) -{ - static int iSkipStep = 0; - int irand; - - pmove->iStepLeft = !pmove->iStepLeft; - - if (!pmove->runfuncs) - { - return; - } - - irand = pmove->RandomLong(0, 1) + (pmove->iStepLeft * 2); - - // FIXME mp_footsteps needs to be a movevar - if (pmove->multiplayer && !pmove->movevars->footsteps) - return; - - // irand - 0,1 for right foot, 2,3 for left foot - // used to alternate left and right foot - // FIXME, move to player state - switch (step) - { - default: - case STEP_CONCRETE: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_METAL: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_DIRT: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_VENT: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_GRATE: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_TILE: - if (!pmove->RandomLong(0, 4)) - irand = 4; - - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 4: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile5.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_SLOSH: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_WADE: - if (iSkipStep == 0) - { - iSkipStep++; - break; - } - - if (iSkipStep++ == 3) - { - iSkipStep = 0; - } - - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - - break; - case STEP_LADDER: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - case STEP_SNOW: - switch (irand) - { - // right foot - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - // left foot - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; - } - break; - } -} - -int PM_MapTextureTypeStepType(char chTextureType) -{ - switch (chTextureType) - { - default: - case CHAR_TEX_CONCRETE: return STEP_CONCRETE; - case CHAR_TEX_METAL: return STEP_METAL; - case CHAR_TEX_DIRT: return STEP_DIRT; - case CHAR_TEX_VENT: return STEP_VENT; - case CHAR_TEX_GRATE: return STEP_GRATE; - case CHAR_TEX_TILE: return STEP_TILE; - case CHAR_TEX_SLOSH: return STEP_SLOSH; - case CHAR_TEX_SNOW: return STEP_SNOW; - } -} - -void PM_CatagorizeTextureType() -{ - vec3_t start, end; - const char *pTextureName; - - VectorCopy(pmove->origin, start); - VectorCopy(pmove->origin, end); - - // Straight down - end[2] -= 64.0f; - - // Fill in default values, just in case. - pmove->sztexturename[0] = '\0'; - pmove->chtexturetype = CHAR_TEX_CONCRETE; - - pTextureName = pmove->PM_TraceTexture(pmove->onground, start, end); - - if (!pTextureName) - return; - - // strip leading '-0' or '+0~' or '{' or '!' - if (*pTextureName == '-' || *pTextureName == '+') - pTextureName += 2; - - if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') - pTextureName++; - - Q_strcpy(pmove->sztexturename, pTextureName); - pmove->sztexturename[MAX_TEXTURENAME_LENGHT - 1] = '\0'; - - // get texture type - pmove->chtexturetype = PM_FindTextureType(pmove->sztexturename); -} - -LINK_HOOK_VOID_CHAIN2(PM_UpdateStepSound); - -void EXT_FUNC __API_HOOK(PM_UpdateStepSound)() -{ - float fvol; - vec3_t knee; - vec3_t feet; - vec3_t center; - float height; - float speed; - int fLadder; - int step; - int onground; - - if (pmove->flTimeStepSound > 0) - return; - - if (pmove->flags & FL_FROZEN) - return; - - speed = Length(pmove->velocity); - - if (speed <= 150.0) - { - pmove->flTimeStepSound = 400; - return; - } - - // determine if we are on a ladder - fLadder = (pmove->movetype == MOVETYPE_FLY); - - // determine if we are not in air - onground = (pmove->onground != -1); - - // If we're on a ladder or on the ground play step sound. - if (fLadder || onground) - { - PM_CatagorizeTextureType(); - - VectorCopy(pmove->origin, center); - VectorCopy(pmove->origin, knee); - VectorCopy(pmove->origin, feet); - - height = pmove->player_maxs[pmove->usehull][2] - pmove->player_mins[pmove->usehull][2]; - - knee[2] = pmove->origin[2] - 0.3 * height; - feet[2] = pmove->origin[2] - 0.5 * height; - - // find out what we're stepping in or on... - if (fLadder) - { - step = STEP_LADDER; - fvol = 0.35; - pmove->flTimeStepSound = 350; - } - else if (pmove->PM_PointContents(knee, nullptr) == CONTENTS_WATER) - { - step = STEP_WADE; - fvol = 0.65; - pmove->flTimeStepSound = 600; - } - else if (pmove->PM_PointContents(feet, nullptr) == CONTENTS_WATER) - { - step = STEP_SLOSH; - fvol = 0.5; - pmove->flTimeStepSound = 300; - } - else - { - // find texture under player, if different from current texture, - // get material type - step = PM_MapTextureTypeStepType(pmove->chtexturetype); - - switch (pmove->chtexturetype) - { - default: - case CHAR_TEX_CONCRETE: - fvol = 0.5; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_METAL: - fvol = 0.5; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_DIRT: - fvol = 0.55; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_VENT: - fvol = 0.7; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_GRATE: - fvol = 0.5; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_TILE: - fvol = 0.5; - pmove->flTimeStepSound = 300; - break; - case CHAR_TEX_SLOSH: - fvol = 0.5; - pmove->flTimeStepSound = 300; - break; - } - } - - if ((pmove->flags & FL_DUCKING) || fLadder) - { - // slower step time if ducking - pmove->flTimeStepSound += 100; - - // play the sound - // 35% volume if ducking - if ((pmove->flags & FL_DUCKING) && pmove->flDuckTime < 950.0) - { - fvol *= 0.35; - } - } - - PM_PlayStepSound(step, fvol); - } -} - -// Add's the trace result to touch list, if contact is not already in list. -qboolean PM_AddToTouched(pmtrace_t tr, vec_t *impactvelocity) -{ - int i; - for (i = 0; i < pmove->numtouch; i++) - { - if (pmove->touchindex[i].ent == tr.ent) - break; - } - - // Already in list. - if (i != pmove->numtouch) - { - return FALSE; - } - - VectorCopy(impactvelocity, tr.deltavelocity); - - if (pmove->numtouch >= MAX_PHYSENTS) - { - pmove->Con_DPrintf("Too many entities were touched!\n"); - } - - pmove->touchindex[pmove->numtouch++] = tr; - return TRUE; -} - -void PM_CheckVelocity() -{ - int i; - - // bound velocity - for (i = 0; i < 3; i++) - { - // See if it's bogus. - if (IS_NAN(pmove->velocity[i])) - { - pmove->Con_Printf("PM Got a NaN velocity %i\n", i); - pmove->velocity[i] = 0; - } - - if (IS_NAN(pmove->origin[i])) - { - pmove->Con_Printf("PM Got a NaN origin on %i\n", i); - pmove->origin[i] = 0; - } - - // Bound it. - if (pmove->velocity[i] > pmove->movevars->maxvelocity) - { - pmove->Con_DPrintf("PM Got a velocity too high on %i\n", i); - pmove->velocity[i] = pmove->movevars->maxvelocity; - } - else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) - { - pmove->Con_DPrintf("PM Got a velocity too low on %i\n", i); - pmove->velocity[i] = -pmove->movevars->maxvelocity; - } - } -} - -// Slide off of the impacting object -// returns the blocked flags: -// 0x01 == floor -// 0x02 == step / wall -int PM_ClipVelocity(vec_t *in, vec_t *normal, vec_t *out, float overbounce) -{ - float change; - real_t angle; - real_t backoff; - int i, blocked; - - angle = normal[2]; - - // Assume unblocked. - blocked = 0x00; - - // If the plane that is blocking us has a positive z component, then assume it's a floor. - if (angle > 0) - { - blocked |= 0x01; - } - - // If the plane has no Z, it is vertical (wall/step) - if (!angle) - { - blocked |= 0x02; - } - - // Determine how far along plane to slide based on incoming direction. - // Scale by overbounce factor. - backoff = DotProduct(in, normal) * overbounce; - - for (i = 0; i < 3; i++) - { - change = in[i] - normal[i] * backoff; - out[i] = change; - - // If out velocity is too small, zero it out. - if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) - { - out[i] = 0; - } - } - - // Return blocking flags. - return blocked; -} - -void PM_AddCorrectGravity() -{ - real_t ent_gravity; - - if (pmove->waterjumptime) - return; - - if (pmove->gravity != 0.0f) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0f; - - // Add gravity so they'll be in the correct position during movement - // yes, this 0.5 looks wrong, but it's not. - pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * 0.5f * pmove->frametime); - pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; - - pmove->basevelocity[2] = 0; - - PM_CheckVelocity(); -} - -void PM_FixupGravityVelocity() -{ - real_t ent_gravity; - - if (pmove->waterjumptime) - return; - - if (pmove->gravity != 0.0) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0; - - // Get the correct velocity for the end of the dt - pmove->velocity[2] -= (pmove->movevars->gravity * pmove->frametime * ent_gravity * 0.5); - PM_CheckVelocity(); -} - -int PM_FlyMove() -{ - int bumpcount, numbumps; - vec3_t dir; - float d; - int numplanes; - vec3_t planes[MAX_CLIP_PLANES]; - vec3_t primal_velocity, original_velocity; - vec3_t new_velocity; - int i, j; - pmtrace_t trace; - vec3_t end; - float time_left, allFraction; - int blocked; - - numbumps = 4; // Bump up to four times - blocked = 0x00; // Assume not blocked - numplanes = 0; // and not sliding along any planes - - VectorCopy(pmove->velocity, original_velocity); // Store original velocity - VectorCopy(pmove->velocity, primal_velocity); - - allFraction = 0; - time_left = pmove->frametime; // Total time for this movement operation. - - for (bumpcount = 0; bumpcount < numbumps; bumpcount++) - { - if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) - break; - - // Assume we can move all the way from the current origin to the - // end point. - for (i = 0; i < 3; i++) - { - real_t flScale = time_left * pmove->velocity[i]; - - end[i] = pmove->origin[i] + flScale; - } - - // See if we can make it from origin to end point. - trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); - - allFraction += trace.fraction; - - // If we started in a solid object, or we were in solid space - // the whole way, zero out our velocity and return that we - // are blocked by floor and wall. - if (trace.allsolid) - { - // entity is trapped in another solid - VectorCopy(vec3_origin, pmove->velocity); - return 4; - } - - // If we moved some portion of the total distance, then - // copy the end position into the pmove->origin and - // zero the plane counter. - if (trace.fraction > 0.0f) - { - // actually covered some distance - VectorCopy(trace.endpos, pmove->origin); - VectorCopy(pmove->velocity, original_velocity); - - numplanes = 0; - } - - // If we covered the entire distance, we are done - // and can return. - if (trace.fraction == 1.0f) - { - // moved the entire distance - break; - } - - // Save entity that blocked us (since fraction was < 1.0) - // for contact - // Add it if it's not already in the list - PM_AddToTouched(trace, pmove->velocity); - - // If the plane we hit has a high z component in the normal, then - // it's probably a floor - if (trace.plane.normal[2] > 0.7f) - { - // floor - blocked |= 0x01; - } - - // If the plane has a zero z component in the normal, then it's a - // step or wall - if (!trace.plane.normal[2]) - { - // step / wall - blocked |= 0x02; - } - - // Reduce amount of pmove->frametime left by total time left * fraction - // that we covered. - time_left -= time_left * trace.fraction; - - // Did we run out of planes to clip against? - if (numplanes >= MAX_CLIP_PLANES) - { - // this shouldn't really happen - // Stop our movement if so. - VectorCopy(vec3_origin, pmove->velocity); - break; - } - - // Set up next clipping plane - VectorCopy(trace.plane.normal, planes[numplanes]); - numplanes++; - - // modify original_velocity so it parallels all of the clip planes - // relfect player velocity - if (numplanes == 1 && pmove->movetype == MOVETYPE_WALK && (pmove->onground == -1 || pmove->friction != 1)) - { - for (i = 0; i < numplanes; i++) - { - if (planes[i][2] > 0.7f) - { - // floor or slope - PM_ClipVelocity(original_velocity, planes[i], new_velocity, 1); - VectorCopy(new_velocity, original_velocity); - } - else - PM_ClipVelocity(original_velocity, planes[i], new_velocity, 1.0 + pmove->movevars->bounce * (1.0 - pmove->friction)); - } - - VectorCopy(new_velocity, pmove->velocity); - VectorCopy(new_velocity, original_velocity); - } - else - { - for (i = 0; i < numplanes; i++) - { - PM_ClipVelocity(original_velocity, planes[i], pmove->velocity, 1); - - for (j = 0; j < numplanes; j++) - { - if (j != i && DotProduct(pmove->velocity, planes[j]) < 0) - { - break; - } - } - - if (j == numplanes) - break; - } - - if (i == numplanes) - { - if (numplanes != 2) - { - VectorCopy(vec3_origin, pmove->velocity); - break; - } - - CrossProduct(planes[0], planes[1], dir); - d = DotProduct(dir, pmove->velocity); - VectorScale(dir, d, pmove->velocity); - } - - if (DotProduct(pmove->velocity, primal_velocity) <= 0) - { - VectorCopy(vec3_origin, pmove->velocity); - break; - } - } - } - - if (allFraction == 0.0f) - { - VectorCopy(vec3_origin, pmove->velocity); - } - - return blocked; -} - -void PM_Accelerate(vec_t *wishdir, real_t wishspeed, float accel) -{ - int i; - float addspeed; - - real_t currentspeed; - real_t accelspeed; - - // Dead player's don't accelerate - if (pmove->dead) - return; - - // If waterjumping, don't accelerate - if (pmove->waterjumptime) - return; - - // See if we are changing direction a bit - currentspeed = DotProduct(pmove->velocity, wishdir); - - // Reduce wishspeed by the amount of veer. - addspeed = wishspeed - currentspeed; - - // If not going to add any speed, done. - if (addspeed <= 0) - return; - - // Determine amount of accleration. - accelspeed = accel * pmove->frametime * wishspeed * pmove->friction; - - // Cap at addspeed - if (accelspeed > addspeed) - accelspeed = addspeed; - - // Adjust velocity. - for (i = 0; i < 3; i++) - { - pmove->velocity[i] += accelspeed * wishdir[i]; - } -} - -// Only used by players. Moves along the ground when player is a MOVETYPE_WALK. -void PM_WalkMove() -{ - int clip; - int oldonground; - int i; - - vec3_t wishvel; - real_t spd; - float fmove, smove; - vec3_t wishdir; - real_t wishspeed; - - //vec3_t start; // TODO: unused - vec3_t dest; - vec3_t original, originalvel; - vec3_t down, downvel; - float downdist, updist; - - pmtrace_t trace; - - if (pmove->fuser2 > 0.0) - { - real_t flRatio = (100 - pmove->fuser2 * 0.001 * 19) * 0.01; - - pmove->velocity[0] *= flRatio; - pmove->velocity[1] *= flRatio; - } - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - // Zero out z components of movement vectors - pmove->forward[2] = 0; - pmove->right[2] = 0; - - // Normalize remainder of vectors. - VectorNormalize(pmove->forward); - VectorNormalize(pmove->right); - - // Determine x and y parts of velocity - for (i = 0; i < 2; i++) - { - wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; - } - - // Zero out z part of velocity - wishvel[2] = 0; - - // Determine maginitude of speed of move - VectorCopy(wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // Clamp to server defined max speed - if (wishspeed > pmove->maxspeed) - { - VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - - // Set pmove velocity - pmove->velocity[2] = 0; - PM_Accelerate(wishdir, wishspeed, pmove->movevars->accelerate); - pmove->velocity[2] = 0; - - // Add in any base velocity to the current velocity. - VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); - - spd = Length(pmove->velocity); - - if (spd < 1.0) - { - VectorClear(pmove->velocity); - return; - } - - // If we are not moving, do nothing - //if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) - // return; - - oldonground = pmove->onground; - - // first try just moving to the destination - dest[0] = pmove->origin[0] + pmove->velocity[0] * pmove->frametime; - dest[1] = pmove->origin[1] + pmove->velocity[1] * pmove->frametime; - dest[2] = pmove->origin[2]; - - // first try moving directly to the next spot - // VectorCopy(dest, start); - - trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); - - // If we made it all the way, then copy trace end - // as new player position. - if (trace.fraction == 1.0f) - { - VectorCopy(trace.endpos, pmove->origin); - return; - } - - // Don't walk up stairs if not on ground. - if (oldonground == -1 && pmove->waterlevel == 0) - { - return; - } - - // If we are jumping out of water, don't do anything more. - if (pmove->waterjumptime) - return; - - // Try sliding forward both on ground and up 16 pixels - // take the move that goes farthest - - // Save out original pos & - VectorCopy(pmove->origin, original); - - // velocity. - VectorCopy(pmove->velocity, originalvel); - - // Slide move - clip = PM_FlyMove(); - - // Copy the results out - VectorCopy(pmove->origin, down); - VectorCopy(pmove->velocity, downvel); - - // Reset original values. - VectorCopy(original, pmove->origin); - VectorCopy(originalvel, pmove->velocity); - - // Start out up one stair height - VectorCopy(pmove->origin, dest); - - dest[2] += pmove->movevars->stepsize; - - trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); - - // If we started okay and made it part of the way at least, - // copy the results to the movement start position and then - // run another move try. - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy(trace.endpos, pmove->origin); - } - - // slide move the rest of the way. - clip = PM_FlyMove(); - - // Now try going back down from the end point - // press down the stepheight - VectorCopy(pmove->origin, dest); - dest[2] -= pmove->movevars->stepsize; - - trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); - - // If we are not on the ground any more then - // use the original movement attempt - if (trace.plane.normal[2] < 0.7f) - goto usedown; - - // If the trace ended up in empty space, copy the end - // over to the origin. - if (!trace.startsolid && !trace.allsolid) - { - VectorCopy(trace.endpos, pmove->origin); - } - - // Copy this origion to up. - VectorCopy(pmove->origin, pmove->up); - - // decide which one went farther - downdist = (down[0] - original[0]) * (down[0] - original[0]) + (down[1] - original[1]) * (down[1] - original[1]); - updist = (pmove->up[0] - original[0]) * (pmove->up[0] - original[0]) + (pmove->up[1] - original[1]) * (pmove->up[1] - original[1]); - - if (downdist > updist) - { -usedown: - VectorCopy(down, pmove->origin); - VectorCopy(downvel, pmove->velocity); - } - else - { - // copy z value from slide move - pmove->velocity[2] = downvel[2]; - } -} - -// Handles both ground friction and water friction -void PM_Friction() -{ - float *vel; - float speed; - real_t newspeed, control, friction, drop; - vec3_t newvel; - - // If we are in water jump cycle, don't apply friction - if (pmove->waterjumptime) - return; - - // Get velocity - vel = pmove->velocity; - - // Calculate speed - speed = Q_sqrt(real_t(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2])); - - // If too slow, return - if (speed < 0.1f) - { - return; - } - - drop = 0; - - // apply ground friction - // On an entity that is the ground - if (pmove->onground != -1) - { - vec3_t start, stop; - pmtrace_t trace; - - start[0] = stop[0] = pmove->origin[0] + vel[0] / speed * 16; - start[1] = stop[1] = pmove->origin[1] + vel[1] / speed * 16; - start[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2]; - stop[2] = start[2] - 34; - - trace = pmove->PM_PlayerTrace(start, stop, PM_NORMAL, -1); - - if (trace.fraction == 1.0f) - friction = pmove->movevars->friction * pmove->movevars->edgefriction; - else - friction = pmove->movevars->friction; - - // Grab friction value. - //friction = pmove->movevars->friction; - - // player friction? - friction *= pmove->friction; - - // Bleed off some speed, but if we have less than the bleed - // threshhold, bleed the theshold amount. - control = (speed < pmove->movevars->stopspeed) ? pmove->movevars->stopspeed : speed; - - // Add the amount to t'he drop amount. - drop += friction * (control * pmove->frametime); - } - - // apply water friction - //if (pmove->waterlevel) - //{ - // drop += speed * pmove->movevars->waterfriction * waterlevel * pmove->frametime; - //} - - // scale the velocity - newspeed = speed - drop; - - if (newspeed < 0) - { - newspeed = 0; - } - - // Determine proportion of old speed we are using. - newspeed /= speed; - - // Adjust velocity according to proportion. - newvel[0] = vel[0] * newspeed; - newvel[1] = vel[1] * float(newspeed); - newvel[2] = vel[2] * float(newspeed); - - VectorCopy(newvel, pmove->velocity); -} - -void PM_AirAccelerate(vec_t *wishdir, float wishspeed, float accel) -{ - int i; - float addspeed; - float wishspd = wishspeed; - - real_t currentspeed; - real_t accelspeed; - - if (pmove->dead || pmove->waterjumptime) - return; - - // Cap speed - if (wishspd > 30) - wishspd = 30; - - // Determine veer amount - currentspeed = DotProduct(pmove->velocity, wishdir); - - // See how much to add - addspeed = wishspd - currentspeed; - - // If not adding any, done. - if (addspeed <= 0) - return; - - // Determine acceleration speed after acceleration - accelspeed = accel * wishspeed * pmove->frametime * pmove->friction; - - // Cap it - if (accelspeed > addspeed) - accelspeed = addspeed; - - // Adjust pmove vel. - for (i = 0; i < 3; i++) - { - pmove->velocity[i] += accelspeed * wishdir[i]; - } -} - -void PM_WaterMove() -{ - int i; - vec3_t wishvel; - vec3_t wishdir; - vec3_t start, dest; - vec3_t temp; - pmtrace_t trace; - - real_t speed, accelspeed, wishspeed; - float newspeed, addspeed; - - // user intentions - for (i = 0; i < 3; i++) - { - wishvel[i] = (pmove->forward[i] * pmove->cmd.forwardmove) + (pmove->cmd.sidemove * pmove->right[i]); - } - - // Sinking after no other movement occurs - if (!pmove->cmd.forwardmove && !pmove->cmd.sidemove && !pmove->cmd.upmove) - { - // drift towards bottom - wishvel[2] -= 60.0f; - } - else - { - // Go straight up by upmove amount. - wishvel[2] += pmove->cmd.upmove; - } - - // Copy it over and determine speed - VectorCopy(wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // Cap speed. - if (wishspeed > pmove->maxspeed) - { - VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - - // Slow us down a bit. - wishspeed *= 0.8; - VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); - - // Water friction - VectorCopy(pmove->velocity, temp); - speed = VectorNormalize(temp); - - if (speed) - { - newspeed = speed - pmove->movevars->friction * pmove->friction * pmove->frametime * speed; - - if (newspeed < 0.0f) - { - newspeed = 0.0f; - } - - VectorScale(pmove->velocity, newspeed / speed, pmove->velocity); - } - else - newspeed = 0; - - // water acceleration - if (float(wishspeed) < 0.1f) - { - return; - } - - addspeed = float(wishspeed) - newspeed; - - if (addspeed > 0.0f) - { - VectorNormalize(wishvel); - accelspeed = pmove->movevars->accelerate * pmove->friction * pmove->frametime * float(wishspeed); - - if (accelspeed > addspeed) - { - accelspeed = addspeed; - } - - for (i = 0; i < 3; i++) - { - pmove->velocity[i] += accelspeed * wishvel[i]; - } - } - - // Now move - // assume it is a stair or a slope, so press down from stepheight above - VectorMA(pmove->origin, pmove->frametime, pmove->velocity, dest); - VectorCopy(dest, start); - - start[2] += pmove->movevars->stepsize + 1; - trace = pmove->PM_PlayerTrace(start, dest, PM_NORMAL, -1); - - // FIXME: check steep slope? - if (!trace.startsolid && !trace.allsolid) - { - // walked up the step, so just keep result and exit - VectorCopy(trace.endpos, pmove->origin); - return; - } - - // Try moving straight along out normal path. - PM_FlyMove(); -} - -LINK_HOOK_VOID_CHAIN(PM_AirMove, (int playerIndex = 0), pmove->player_index + 1); - -void EXT_FUNC __API_HOOK(PM_AirMove)(int playerIndex) -{ - PM_AirMove_internal(); -} - -void PM_AirMove_internal() -{ - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - float wishspeed; - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - // Zero out z components of movement vectors - pmove->forward[2] = 0; - pmove->right[2] = 0; - - // Renormalize - VectorNormalize(pmove->forward); - VectorNormalize(pmove->right); - - // Determine x and y parts of velocity - for (i = 0; i < 2; i++) - { - wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; - } - - // Zero out z part of velocity - wishvel[2] = 0; - - // Determine maginitude of speed of move - VectorCopy(wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // Clamp to server defined max speed - if (wishspeed > pmove->maxspeed) - { - VectorScale(wishvel, pmove->maxspeed/wishspeed, wishvel); - wishspeed = pmove->maxspeed; - } - - PM_AirAccelerate(wishdir, wishspeed, pmove->movevars->airaccelerate); - - // Add in any base velocity to the current velocity. - VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); - - PM_FlyMove(); -} - -qboolean PM_InWater() -{ - return (pmove->waterlevel > 1) ? TRUE : FALSE; -} - -// Sets pmove->waterlevel and pmove->watertype values. -qboolean PM_CheckWater() -{ -#ifdef REGAMEDLL_FIXES - // do not check for dead - if (pmove->dead || pmove->deadflag != DEAD_NO) - return FALSE; -#endif - - vec3_t point; - int cont; - int truecont; - float height; - float heightover2; - - // Pick a spot just above the players feet. - point[0] = pmove->origin[0] + (pmove->player_mins[pmove->usehull][0] + pmove->player_maxs[pmove->usehull][0]) * 0.5f; - point[1] = pmove->origin[1] + (pmove->player_mins[pmove->usehull][1] + pmove->player_maxs[pmove->usehull][1]) * 0.5f; - point[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; - - // Assume that we are not in water at all. - pmove->waterlevel = 0; - pmove->watertype = CONTENTS_EMPTY; - - // Grab point contents. - cont = pmove->PM_PointContents(point, &truecont); - - // Are we under water? (not solid and not empty?) - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) - { - // Set water type - pmove->watertype = cont; - - // We are at least at level one - pmove->waterlevel = 1; - - height = (pmove->player_mins[pmove->usehull][2] + pmove->player_maxs[pmove->usehull][2]); - heightover2 = height * 0.5; - - // Now check a point that is at the player hull midpoint. - point[2] = pmove->origin[2] + heightover2; - cont = pmove->PM_PointContents(point, nullptr); - - // If that point is also under water... - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) - { - // Set a higher water level. - pmove->waterlevel = 2; - - // Now check the eye position. (view_ofs is relative to the origin) - point[2] = pmove->origin[2] + pmove->view_ofs[2]; - - cont = pmove->PM_PointContents(point, nullptr); - if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) - { - // In over our eyes - pmove->waterlevel = 3; - } - } - - // Adjust velocity based on water current, if any. - if ((truecont <= CONTENTS_CURRENT_0) && (truecont >= CONTENTS_CURRENT_DOWN)) - { - // The deeper we are, the stronger the current. - static vec3_t current_table[] = - { - {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, - {0, -1, 0}, {0, 0, 1}, {0, 0, -1} - }; - - VectorMA(pmove->basevelocity, 50.0 * pmove->waterlevel, current_table[CONTENTS_CURRENT_0 - truecont], pmove->basevelocity); - } - } - - return (pmove->waterlevel > 1) ? TRUE : FALSE; -} - -void PM_CategorizePosition() -{ - vec3_t point; - pmtrace_t tr; - - // if the player hull point one unit down is solid, the player - // is on ground - - // see if standing on something solid - - // Doing this before we move may introduce a potential latency in water detection, but - // doing it after can get us stuck on the bottom in water if the amount we move up - // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call - // this several times per frame, so we really need to avoid sticking to the bottom of - // water on each call, and the converse case will correct itself if called twice. - PM_CheckWater(); - - point[0] = pmove->origin[0]; - point[1] = pmove->origin[1]; - point[2] = pmove->origin[2] - 2; - - // Shooting up really fast. Definitely not on ground. - if (pmove->velocity[2] > 180) - { - pmove->onground = -1; - return; - } - - // Try and move down. - tr = pmove->PM_PlayerTrace(pmove->origin, point, PM_NORMAL, -1); - - // If we hit a steep plane, we are not on ground - if (tr.plane.normal[2] < 0.7f) - { - // too steep - pmove->onground = -1; - } - else - { - // Otherwise, point to index of ent under us. - pmove->onground = tr.ent; - } - - // If we are on something... - if (pmove->onground != -1) - { - // Then we are not in water jump sequence - pmove->waterjumptime = 0; - - // If we could make the move, drop us down that 1 pixel - if (pmove->waterlevel < 2 && !tr.startsolid && !tr.allsolid) - { - VectorCopy(tr.endpos, pmove->origin); - } - } - - // Standing on an entity other than the world - // So signal that we are touching something. - if (tr.ent > 0) - { - PM_AddToTouched(tr, pmove->velocity); - } -} - -// When a player is stuck, it's costly to try and unstick them -// Grab a test offset for the player based on a passed in index -int PM_GetRandomStuckOffsets(int nIndex, int server, vec_t *offset) -{ - // Last time we did a full - int idx = rgStuckLast[nIndex][server]++; - VectorCopy(rgv3tStuckTable[idx % ARRAYSIZE(rgv3tStuckTable)], offset); - return (idx % ARRAYSIZE(rgv3tStuckTable)); -} - -void PM_ResetStuckOffsets(int nIndex, int server) -{ - rgStuckLast[nIndex][server] = 0; -} - -// If pmove->origin is in a solid position, -// try nudging slightly on all axis to -// allow for the cut precision of the net coordinates -qboolean PM_CheckStuck() -{ - vec3_t base; - vec3_t offset; - vec3_t test; - int hitent; - int idx; - real_t fTime; - int i; - pmtrace_t traceresult; - - // Last time we did a full - static float rgStuckCheckTime[MAX_CLIENTS][2]; - - // If position is okay, exit - hitent = pmove->PM_TestPlayerPosition(pmove->origin, &traceresult); - if (hitent == -1) - { - PM_ResetStuckOffsets(pmove->player_index, pmove->server); - return FALSE; - } - - VectorCopy(pmove->origin, base); - - // Deal with precision error in network. - if (!pmove->server) - { - // World or BSP model - if (hitent == 0 || pmove->physents[hitent].model) - { - int nReps = 0; - PM_ResetStuckOffsets(pmove->player_index, pmove->server); - do - { - i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); - - VectorAdd(base, offset, test); - if (pmove->PM_TestPlayerPosition(test, &traceresult) == -1) - { - PM_ResetStuckOffsets(pmove->player_index, pmove->server); - VectorCopy(test, pmove->origin); - return FALSE; - } - - nReps++; - } - while (nReps < ARRAYSIZE(rgv3tStuckTable)); - } - } - - // Only an issue on the client. - if (pmove->server) - idx = 0; - else - idx = 1; - - fTime = pmove->Sys_FloatTime(); - - // Too soon? - if (rgStuckCheckTime[pmove->player_index][idx] >= (fTime - PM_CHECKSTUCK_MINTIME)) - { - return TRUE; - } - - rgStuckCheckTime[pmove->player_index][idx] = fTime; - - pmove->PM_StuckTouch(hitent, &traceresult); - - i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); - - VectorAdd(base, offset, test); - if ((hitent = pmove->PM_TestPlayerPosition(test, nullptr)) == -1) - { - PM_ResetStuckOffsets(pmove->player_index, pmove->server); - - if (i >= (ARRAYSIZE(rgv3tStuckTable) / 2)) - { - VectorCopy(test, pmove->origin); - } - - return FALSE; - } - - // If player is flailing while stuck in another player (should never happen), then see - // if we can't "unstick" them forceably. - if ((pmove->cmd.buttons & (IN_JUMP | IN_DUCK | IN_ATTACK)) && pmove->physents[hitent].player != 0) - { - float x, y, z; - float xystep = 8.0; - float zstep = 18.0; - float xyminmax = xystep; - float zminmax = 4 * zstep; - - for (z = 0; z <= zminmax; z += zstep) - { - for (x = -xyminmax; x <= xyminmax; x += xystep) - { - for (y = -xyminmax; y <= xyminmax; y += xystep) - { - VectorCopy(base, test); - - test[0] += x; - test[1] += y; - test[2] += z; - - if (pmove->PM_TestPlayerPosition(test, nullptr) == -1) - { - VectorCopy(test, pmove->origin); - return FALSE; - } - } - } - } - } - - return TRUE; -} - -void PM_SpectatorMove() -{ - real_t speed, drop, friction; - real_t control, newspeed; - float currentspeed, addspeed; - real_t accelspeed; - int i; - vec3_t wishvel; - float fmove, smove; - vec3_t wishdir; - real_t wishspeed; - - // this routine keeps track of the spectators psoition - // there a two different main move types : track player or moce freely (OBS_ROAMING) - // doesn't need excate track position, only to generate PVS, so just copy - // targets position and real view position is calculated on client (saves server CPU) - if (pmove->iuser1 == OBS_ROAMING) - { -#ifdef CLIENT_DLL - if (iJumpSpectator) - { - VectorCopy(vJumpOrigin, pmove->origin); - VectorCopy(vJumpAngles, pmove->angles); - VectorCopy(vec3_origin, pmove->velocity); - iJumpSpectator = 0; - return; - } -#endif - // Move around in normal spectator method - speed = Length (pmove->velocity); - - if (speed >= 1.0) - { - drop = 0; - - // extra friction - friction = pmove->movevars->friction * 1.5; - control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; - drop += friction * (control * pmove->frametime); - - // scale the velocity - newspeed = speed - drop; - - if (newspeed < 0) - { - newspeed = 0; - } - newspeed /= speed; - - VectorScale(pmove->velocity, newspeed, pmove->velocity); - } - else - { - VectorCopy(vec3_origin, pmove->velocity); - } - - // accelerate - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - VectorNormalize(pmove->forward); - VectorNormalize(pmove->right); - - for (i = 0; i < 3; i++) - { - wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; - } - - wishvel[2] += pmove->cmd.upmove; - - VectorCopy(wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); - - // clamp to server defined max speed - if (wishspeed > pmove->movevars->spectatormaxspeed) - { - VectorScale(wishvel, pmove->movevars->spectatormaxspeed / wishspeed, wishvel); - wishspeed = pmove->movevars->spectatormaxspeed; - } - - currentspeed = DotProduct(pmove->velocity, wishdir); - - addspeed = wishspeed - currentspeed; - if (addspeed <= 0) - { - return; - } - - accelspeed = pmove->movevars->accelerate * pmove->frametime * wishspeed; - if (accelspeed > addspeed) - { - accelspeed = addspeed; - } - - for (i = 0; i < 3; i++) - { - pmove->velocity[i] += accelspeed * wishdir[i]; - } - - // move - VectorMA(pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); - } - else - { - // all other modes just track some kind of target, so spectator PVS = target PVS - int target; - - // no valid target ? - if (pmove->iuser2 <= 0) - return; - - // Find the client this player's targeting - for (target = 0; target < pmove->numphysent; target++) - { - if (pmove->physents[target].info == pmove->iuser2) - break; - } - - if (target == pmove->numphysent) - return; - - // use targets position as own origin for PVS - VectorCopy(pmove->physents[target].angles, pmove->angles); - VectorCopy(pmove->physents[target].origin, pmove->origin); - - // no velocity - VectorCopy(vec3_origin, pmove->velocity); - } -} - -// Use for ease-in, ease-out style interpolation (accel/decel) -// Used by ducking code. -float PM_SplineFraction(float value, float scale) -{ - real_t valueSquared; - - value = scale * value; - valueSquared = value * value; - - // Nice little ease-in, ease-out spline-like curve - return 3 * valueSquared - 2 * valueSquared * value; -} - -NOXREF float PM_SimpleSpline(float value) -{ - float valueSquared; - - valueSquared = value * value; - - return 3 * valueSquared - 2 * valueSquared * value; -} - -void PM_FixPlayerCrouchStuck(int direction) -{ - int hitent; - int i; - vec3_t test; - - hitent = pmove->PM_TestPlayerPosition(pmove->origin, nullptr); - - if (hitent == -1) - { - return; - } - - VectorCopy(pmove->origin, test); - - for (i = 0; i < HalfHumanHeight; i++) - { - pmove->origin[2] += direction; - hitent = pmove->PM_TestPlayerPosition(pmove->origin, nullptr); - - if (hitent == -1) - { - return; - } - } - - // Failed - VectorCopy(test, pmove->origin); -} - -void PM_UnDuck() -{ -#ifdef REGAMEDLL_ADD - if (unduck_method.value) -#endif - { -#ifdef REGAMEDLL_FIXES - // if ducking isn't finished yet, so don't unduck - if (pmove->bInDuck || !(pmove->flags & FL_DUCKING)) - { - pmove->usehull = 0; - pmove->flDuckTime = 0; - pmove->bInDuck = FALSE; - pmove->view_ofs[2] = PM_VEC_VIEW; - return; - } -#endif // #ifdef REGAMEDLL_FIXES - } - - pmtrace_t trace; - vec3_t newOrigin; - - VectorCopy(pmove->origin, newOrigin); - - if (pmove->onground != -1) - { -#ifdef REGAMEDLL_FIXES - vec3_t offset; - VectorSubtract(pmove->player_mins[1], pmove->player_mins[0], offset); - VectorAdd(newOrigin, offset, newOrigin); -#else - newOrigin[2] += 18.0; -#endif - } - - trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); - if (!trace.startsolid) - { - pmove->usehull = 0; - - // Oh, no, changing hulls stuck us into something, try unsticking downward first. - trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); - - if (trace.startsolid) - { - // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot - // Con_Printf("unstick got stuck\n"); - pmove->usehull = 1; - return; - } - - pmove->flags &= ~FL_DUCKING; - pmove->bInDuck = FALSE; - pmove->view_ofs[2] = PM_VEC_VIEW; - pmove->flDuckTime = 0; - - pmove->flTimeStepSound -= 100; - - if (pmove->flTimeStepSound < 0) - { - pmove->flTimeStepSound = 0; - } - - VectorCopy(newOrigin, pmove->origin); - - // Recatagorize position since ducking can change origin - PM_CategorizePosition(); - } -} - -void PM_Duck() -{ - int buttonsChanged = (pmove->oldbuttons ^ pmove->cmd.buttons); // These buttons have changed this frame - int nButtonPressed = buttonsChanged & pmove->cmd.buttons; // The changed ones still down are "pressed" - - int duckchange = buttonsChanged & IN_DUCK ? 1 : 0; - int duckpressed = nButtonPressed & IN_DUCK ? 1 : 0; - - if (pmove->cmd.buttons & IN_DUCK) - { - pmove->oldbuttons |= IN_DUCK; - } - else - { - pmove->oldbuttons &= ~IN_DUCK; - } - -#ifdef REGAMEDLL_ADD - // Prevent ducking if the iuser3 variable is contain PLAYER_PREVENT_DUCK - if ((pmove->iuser3 & PLAYER_PREVENT_DUCK) == PLAYER_PREVENT_DUCK) - { - // Try to unduck - if (pmove->flags & FL_DUCKING) - { - PM_UnDuck(); - } - - return; - } -#endif - - if (pmove->dead || (!(pmove->cmd.buttons & IN_DUCK) && !pmove->bInDuck && !(pmove->flags & FL_DUCKING))) - { - return; - } - - pmove->cmd.forwardmove *= PLAYER_DUCKING_MULTIPLIER; - pmove->cmd.sidemove *= PLAYER_DUCKING_MULTIPLIER; - pmove->cmd.upmove *= PLAYER_DUCKING_MULTIPLIER; - - if (pmove->cmd.buttons & IN_DUCK) - { - if ((nButtonPressed & IN_DUCK) && !(pmove->flags & FL_DUCKING)) - { - // Use 1 second so super long jump will work - pmove->flDuckTime = 1000; - pmove->bInDuck = TRUE; - } - - if (pmove->bInDuck) - { - // Finish ducking immediately if duck time is over or not on ground - if (((pmove->flDuckTime / 1000.0) <= (1.0 - TIME_TO_DUCK)) || pmove->onground == -1) - { - pmove->usehull = 1; - pmove->view_ofs[2] = PM_VEC_DUCK_VIEW; - pmove->flags |= FL_DUCKING; - pmove->bInDuck = FALSE; - - // HACKHACK - Fudge for collision bug - no time to fix this properly - if (pmove->onground != -1) - { -#ifdef REGAMEDLL_FIXES - vec3_t newOrigin; - VectorSubtract(pmove->player_mins[1], pmove->player_mins[0], newOrigin); - VectorSubtract(pmove->origin, newOrigin, pmove->origin); -#else - pmove->origin[2] = pmove->origin[2] - 18.0; -#endif - - // See if we are stuck? - PM_FixPlayerCrouchStuck(STUCK_MOVEUP); - - // Recatagorize position since ducking can change origin - PM_CategorizePosition(); - } - } - else - { - real_t duckFraction = PM_VEC_VIEW; - real_t time = (1.0 - pmove->flDuckTime / 1000.0); - - // Calc parametric time - if (time >= 0.0) { - duckFraction = PM_SplineFraction(time, (1.0 / TIME_TO_DUCK)); - } - -#ifdef REGAMEDLL_FIXES - float fMore = (pmove->player_mins[1][2] - pmove->player_mins[0][2]); -#else - float fMore = (PM_VEC_DUCK_HULL_MIN - PM_VEC_HULL_MIN); -#endif - - pmove->view_ofs[2] = ((PM_VEC_DUCK_VIEW - fMore) * duckFraction) + (PM_VEC_VIEW * (1 - duckFraction)); - } - } - } - // Try to unduck - else - { - PM_UnDuck(); - } -} - -void PM_LadderMove(physent_t *pLadder) -{ - vec3_t ladderCenter; - trace_t trace; - bool onFloor; - vec3_t floor; - vec3_t modelmins, modelmaxs; - - if (pmove->movetype == MOVETYPE_NOCLIP) - return; - - pmove->PM_GetModelBounds(pLadder->model, modelmins, modelmaxs); - - VectorAdd(modelmins, modelmaxs, ladderCenter); - VectorScale(ladderCenter, 0.5, ladderCenter); - - pmove->movetype = MOVETYPE_FLY; - - // On ladder, convert movement to be relative to the ladder - VectorCopy(pmove->origin, floor); - floor[2] += pmove->player_mins[pmove->usehull][2] - 1; - - if (pmove->PM_PointContents(floor, nullptr) == CONTENTS_SOLID) - onFloor = true; - else - onFloor = false; - - pmove->gravity = 0; - pmove->PM_TraceModel(pLadder, pmove->origin, ladderCenter, &trace); - - if (trace.fraction != 1.0f) - { - float forward = 0, right = 0; - vec3_t vpn, v_right; - float flSpeed = MAX_CLIMB_SPEED; - - // they shouldn't be able to move faster than their maxspeed - if (flSpeed > pmove->maxspeed) - { - flSpeed = pmove->maxspeed; - } - - AngleVectors(pmove->angles, vpn, v_right, nullptr); - - if (pmove->flags & FL_DUCKING) - { - flSpeed *= PLAYER_DUCKING_MULTIPLIER; - } - - if (pmove->cmd.buttons & IN_BACK) - { - forward -= flSpeed; - } - if (pmove->cmd.buttons & IN_FORWARD) - { - forward += flSpeed; - } - if (pmove->cmd.buttons & IN_MOVELEFT) - { - right -= flSpeed; - } - if (pmove->cmd.buttons & IN_MOVERIGHT) - { - right += flSpeed; - } - - if (pmove->cmd.buttons & IN_JUMP) - { - pmove->movetype = MOVETYPE_WALK; - VectorScale(trace.plane.normal, 270, pmove->velocity); - } - else - { - if (forward != 0 || right != 0) - { - vec3_t velocity, perp, cross, lateral, tmp; - float normal; - - VectorScale(vpn, forward, velocity); - VectorMA(velocity, right, v_right, velocity); - - VectorClear(tmp); - tmp[2] = 1; - - CrossProduct(tmp, trace.plane.normal, perp); - VectorNormalize(perp); - - // decompose velocity into ladder plane - normal = DotProduct(velocity, trace.plane.normal); - // This is the velocity into the face of the ladder - VectorScale(trace.plane.normal, normal, cross); - - // This is the player's additional velocity - VectorSubtract(velocity, cross, lateral); - - // This turns the velocity into the face of the ladder into velocity that - // is roughly vertically perpendicular to the face of the ladder. - // NOTE: It IS possible to face up and move down or face down and move up - // because the velocity is a sum of the directional velocity and the converted - // velocity through the face of the ladder - by design. - CrossProduct(trace.plane.normal, perp, tmp); - VectorMA(lateral, -normal, tmp, pmove->velocity); - - // On ground moving away from the ladder - if (onFloor && normal > 0) - { - VectorMA(pmove->velocity, MAX_CLIMB_SPEED, trace.plane.normal, pmove->velocity); - } - } - else - { - VectorClear(pmove->velocity); - } - } - } -} - -physent_t *PM_Ladder() -{ - int i; - physent_t *pe; - hull_t *hull; - int num; - vec3_t test; - - for (i = 0; i < pmove->nummoveent; i++) - { - pe = &pmove->moveents[i]; - - if (pe->model && (modtype_t)pmove->PM_GetModelType(pe->model) == mod_brush && pe->skin == CONTENTS_LADDER) - { - hull = (hull_t *)pmove->PM_HullForBsp(pe, test); - num = hull->firstclipnode; - - // Offset the test point appropriately for this hull. - VectorSubtract(pmove->origin, test, test); - - // Test the player's hull for intersection with this model - if (pmove->PM_HullPointContents(hull, num, test) == CONTENTS_EMPTY) - { - continue; - } - - return pe; - } - } - - return nullptr; -} - -void PM_WaterJump() -{ - if (pmove->waterjumptime > 10000) - { - pmove->waterjumptime = 10000; - } - - if (!pmove->waterjumptime) - { - return; - } - - pmove->waterjumptime -= pmove->cmd.msec; - - if (pmove->waterjumptime < 0 || !pmove->waterlevel) - { - pmove->waterjumptime = 0; - pmove->flags &= ~FL_WATERJUMP; - } - - pmove->velocity[0] = pmove->movedir[0]; - pmove->velocity[1] = pmove->movedir[1]; -} - -void PM_AddGravity() -{ - float ent_gravity; - - if (pmove->gravity != 0.0f) - ent_gravity = pmove->gravity; - else - ent_gravity = 1.0f; - - pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime); - pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; - - pmove->basevelocity[2] = 0; - PM_CheckVelocity(); -} - -// Does not change the entities velocity at all -pmtrace_t PM_PushEntity(vec_t *push) -{ - pmtrace_t trace; - vec3_t end; - - VectorAdd(pmove->origin, push, end); - - trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); - - VectorCopy(trace.endpos, pmove->origin); - - // So we can run impact function afterwards. - if (trace.fraction < 1.0f && !trace.allsolid) - { - PM_AddToTouched(trace, pmove->velocity); - } - - return trace; -} - -void PM_Physics_Toss() -{ - pmtrace_t trace; - vec3_t move; - float backoff; - - PM_CheckWater(); - - if (pmove->velocity[2] > 0) - { - pmove->onground = -1; - } - - // If on ground and not moving, return. - if (pmove->onground != -1) - { - if (VectorCompare(pmove->basevelocity, vec3_origin) && VectorCompare(pmove->velocity, vec3_origin)) - { - return; - } - } - - PM_CheckVelocity(); - - // add gravity - if (pmove->movetype != MOVETYPE_FLY && pmove->movetype != MOVETYPE_BOUNCEMISSILE && pmove->movetype != MOVETYPE_FLYMISSILE) - { - PM_AddGravity(); - } - - // move origin - // Base velocity is not properly accounted for since this entity will move again after the bounce without - // taking it into account - VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); - - PM_CheckVelocity(); - VectorScale(pmove->velocity, pmove->frametime, move); - VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); - - // Should this clear basevelocity - trace = PM_PushEntity(move); - - PM_CheckVelocity(); - - if (trace.allsolid) - { - // entity is trapped in another solid - pmove->onground = trace.ent; - VectorCopy(vec3_origin, pmove->velocity); - return; - } - - if (trace.fraction == 1.0f) - { - PM_CheckWater(); - return; - } - - if (pmove->movetype == MOVETYPE_BOUNCE) - { - backoff = 2.0f - pmove->friction; - } - else if (pmove->movetype == MOVETYPE_BOUNCEMISSILE) - { - backoff = 2.0f; - } - else - backoff = 1.0f; - - PM_ClipVelocity(pmove->velocity, trace.plane.normal, pmove->velocity, backoff); - - // stop if on ground - if (trace.plane.normal[2] > 0.7f) - { - float vel; - vec3_t base; - - VectorClear(base); - if (pmove->velocity[2] < pmove->movevars->gravity * pmove->frametime) - { - // we're rolling on the ground, add static friction. - pmove->onground = trace.ent; - pmove->velocity[2] = 0; - } - - vel = DotProduct(pmove->velocity, pmove->velocity); - - if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) - { - pmove->onground = trace.ent; - VectorCopy(vec3_origin, pmove->velocity); - } - else - { - VectorScale(pmove->velocity, (1.0f - trace.fraction) * pmove->frametime * 0.9f, move); - trace = PM_PushEntity(move); - } - - VectorSubtract(pmove->velocity, base, pmove->velocity); - } - - // check for in water - PM_CheckWater(); -} - -void PM_NoClip() -{ - int i; - vec3_t wishvel; - float fmove, smove; - - // Copy movement amounts - fmove = pmove->cmd.forwardmove; - smove = pmove->cmd.sidemove; - - VectorNormalize(pmove->forward); - VectorNormalize(pmove->right); - - // Determine x and y parts of velocity - for (i = 0; i < 3; i++) - { - wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; - } - - wishvel[2] += pmove->cmd.upmove; - - VectorMA(pmove->origin, pmove->frametime, wishvel, pmove->origin); - - // Zero out the velocity so that we don't accumulate a huge downward velocity from - // gravity, etc. - VectorClear(pmove->velocity); -} - -// Purpose: Corrects bunny jumping (where player initiates a bunny jump before other -// movement logic runs, thus making onground == -1 thus making PM_Friction get skipped and -// running PM_AirMove, which doesn't crop velocity to maxspeed like the ground / other -// movement logic does. -void PM_PreventMegaBunnyJumping() -{ - // Current player speed - real_t spd; - // If we have to crop, apply this cropping fraction to velocity - float fraction; - // Speed at which bunny jumping is limited - float maxscaledspeed; - - maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed; - - // Don't divide by zero - if (maxscaledspeed <= 0.0f) - return; - - spd = Length(pmove->velocity); - - if (spd <= maxscaledspeed) - return; - - // Returns the modifier for the velocity - fraction = (maxscaledspeed / spd) * 0.8; - - // Crop it down!. - VectorScale(pmove->velocity, fraction, pmove->velocity); -} - -void PM_Jump() -{ - if (pmove->dead) - { - // don't jump again until released - pmove->oldbuttons |= IN_JUMP; - return; - } - - // See if we are waterjumping. If so, decrement count and return. - if (pmove->waterjumptime != 0.0f) - { - pmove->waterjumptime -= pmove->cmd.msec; - - if (pmove->waterjumptime < 0) - { - pmove->waterjumptime = 0; - } - - return; - } - - // If we are in the water most of the way... - if (pmove->waterlevel >= 2) - { - // swimming, not jumping - pmove->onground = -1; - - // We move up a certain amount - if (pmove->watertype == CONTENTS_WATER) - { - pmove->velocity[2] = 100; - } - else if (pmove->watertype == CONTENTS_SLIME) - { - pmove->velocity[2] = 80; - } - else // LAVA - pmove->velocity[2] = 50; - - // play swiming sound - if (pmove->flSwimTime <= 0) - { - // Don't play sound again for 1 second - pmove->flSwimTime = 1000.0f; - - switch (pmove->RandomLong(0, 3)) - { - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - } - } - - return; - } - -#ifdef REGAMEDLL_ADD - // Prevent jumping if the iuser3 variable is contain PLAYER_PREVENT_JUMP - if ((pmove->iuser3 & PLAYER_PREVENT_JUMP) == PLAYER_PREVENT_JUMP) - { - return; - } -#endif - - // No more effect - // in air, so no effect - if (pmove->onground == -1) - { - // Flag that we jumped. - // don't jump again until released - pmove->oldbuttons |= IN_JUMP; - return; - } - - // don't pogo stick - if (pmove->oldbuttons & IN_JUMP) - { - return; - } - - if (pmove->bInDuck && (pmove->flags & FL_DUCKING)) - { - return; - } - - PM_CatagorizeTextureType(); - - // In the air now. - pmove->onground = -1; - - PM_PreventMegaBunnyJumping(); - - real_t fvel = Length(pmove->velocity); - float fvol = 1.0f; - - if (fvel >= 150.0f) - { - PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), fvol); - } - -#ifdef REGAMEDLL_ADD - // See if user can super long jump? - bool cansuperjump = (pmove->PM_Info_ValueForKey(pmove->physinfo, "slj")[0] == '1'); - - // Acclerate upward - // If we are ducking... - if (pmove->bInDuck || (pmove->flags & FL_DUCKING)) - { - // Adjust for super long jump module - // UNDONE: note this should be based on forward angles, not current velocity. - if (cansuperjump && (pmove->cmd.buttons & IN_DUCK) && pmove->flDuckTime > 0 && Length(pmove->velocity) > 50) - { - pmove->punchangle[0] = -5.0f; - - for (int i = 0; i < 2; i++) - { - pmove->velocity[i] = pmove->forward[i] * PLAYER_LONGJUMP_SPEED * 1.6f; - } - - pmove->velocity[2] = Q_sqrt(2 * 800 * 56.0f); - } - else - { - pmove->velocity[2] = Q_sqrt(2 * 800 * 45.0f); - } - } - else -#endif - { - // NOTE: don't do it in .f (float) - pmove->velocity[2] = Q_sqrt(2.0 * 800.0f * 45.0f); - } - - if (pmove->fuser2 > 0.0f) - { - // NOTE: don't do it in .f (float) - real_t flRatio = (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01; - pmove->velocity[2] *= flRatio; - } - - pmove->fuser2 = 1315.789429; - - // Decay it for simulation - PM_FixupGravityVelocity(); - - // Flag that we jumped. - // don't jump again until released - pmove->oldbuttons |= IN_JUMP; -} - -void PM_CheckWaterJump() -{ - vec3_t vecStart, vecEnd; - vec3_t flatforward; - vec3_t flatvelocity; - float curspeed; - pmtrace_t tr; - int savehull; - - // Already water jumping. - if (pmove->waterjumptime) - return; - - // Don't hop out if we just jumped in - if (pmove->velocity[2] < -180) - { - // only hop out if we are moving up - return; - } - - // See if we are backing up - flatvelocity[0] = pmove->velocity[0]; - flatvelocity[1] = pmove->velocity[1]; - flatvelocity[2] = 0; - - // Must be moving - curspeed = VectorNormalize(flatvelocity); - - // see if near an edge - flatforward[0] = pmove->forward[0]; - flatforward[1] = pmove->forward[1]; - flatforward[2] = 0; - VectorNormalize(flatforward); - - // Are we backing into water from steps or something? If so, don't pop forward - if (curspeed != 0.0 && (DotProduct(flatvelocity, flatforward) < 0.0)) - { - return; - } - - VectorCopy(pmove->origin, vecStart); - vecStart[2] += WJ_HEIGHT; - - VectorMA(vecStart, 24, flatforward, vecEnd); - - // Trace, this trace should use the point sized collision hull - savehull = pmove->usehull; - pmove->usehull = 2; - - tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); - - // Facing a near vertical wall? - if (tr.fraction < 1.0f && Q_fabs(real_t(tr.plane.normal[2])) < 0.1f) - { - vecStart[2] += pmove->player_maxs[savehull][2] - WJ_HEIGHT; - - VectorMA(vecStart, 24, flatforward, vecEnd); - VectorMA(vec3_origin, -50, tr.plane.normal, pmove->movedir); - - tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); - - if (tr.fraction == 1.0f) - { - pmove->waterjumptime = 2000.0f; - pmove->velocity[2] = 225.0f; - - pmove->oldbuttons |= IN_JUMP; - pmove->flags |= FL_WATERJUMP; - } - } - - // Reset the collision hull - pmove->usehull = savehull; -} - -void PM_CheckFalling() -{ - if (pmove->onground != -1 && !pmove->dead && pmove->flFallVelocity >= PM_PLAYER_FALL_PUNCH_THRESHHOLD) - { - float fvol = 0.5f; - - if (pmove->waterlevel <= 0) - { - if (pmove->flFallVelocity > PM_PLAYER_MAX_SAFE_FALL_SPEED) - { - fvol = 1.0f; - } - else if (pmove->flFallVelocity > PM_PLAYER_MAX_SAFE_FALL_SPEED / 2) - { - fvol = 0.85f; - } - else if (pmove->flFallVelocity < PM_PLAYER_MIN_BOUNCE_SPEED) - { - fvol = 0.0f; - } - } - - if (fvol > 0.0f) - { - PM_CatagorizeTextureType(); - - // play step sound for current texture - PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), fvol); - - pmove->flTimeStepSound = 300; - - // Knock the screen around a little bit, temporary effect - // punch z axis - pmove->punchangle[2] = pmove->flFallVelocity * 0.013; - - if (pmove->punchangle[0] > 8.0f) - { - pmove->punchangle[0] = 8.0f; - } - } - } - - if (pmove->onground != -1) - { - pmove->flFallVelocity = 0; - } -} - -void PM_PlayWaterSounds() -{ - // Did we enter or leave water? - if (pmove->oldwaterlevel != 0) - { - if (pmove->waterlevel != 0) - return; - } - else - { - if (pmove->waterlevel == 0) - return; - } - - switch (pmove->RandomLong(0, 3)) - { - case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; - } -} - -float PM_CalcRoll(vec_t *angles, vec_t *velocity, float rollangle, float rollspeed) -{ - float sign; - real_t side; - float value; - vec3_t forward, right, up; - - AngleVectors(angles, forward, right, up); - - side = DotProduct(velocity, right); - sign = side < 0 ? -1 : 1; - side = Q_fabs(side); - - value = rollangle; - - if (side < rollspeed) - { - side = side * value / rollspeed; - } - else - { - side = value; - } - - return side * sign; -} - -void PM_DropPunchAngle(vec_t *punchangle) -{ - real_t len; - - len = VectorNormalize(punchangle); - len -= (10.0 + len * 0.5) * pmove->frametime; -#ifdef PLAY_GAMEDLL - len = Q_max(len, 0.0); -#else - len = Q_max(len, 0.0f); -#endif - VectorScale(punchangle, len, punchangle); -} - -void PM_CheckParameters() -{ - float spd; - real_t maxspeed; - vec3_t v_angle; - - spd = Q_sqrt(real_t(pmove->cmd.sidemove * pmove->cmd.sidemove + pmove->cmd.forwardmove * pmove->cmd.forwardmove + pmove->cmd.upmove * pmove->cmd.upmove)); - - maxspeed = pmove->clientmaxspeed; - - if (maxspeed != 0.0f) - { - pmove->maxspeed = Q_min(maxspeed, real_t(pmove->maxspeed)); - } - - if (spd != 0.0f && spd > real_t(pmove->maxspeed)) - { - real_t fRatio = pmove->maxspeed / spd; - - pmove->cmd.forwardmove *= fRatio; - pmove->cmd.sidemove *= fRatio; - pmove->cmd.upmove *= fRatio; - } - - if ((pmove->flags & (FL_FROZEN | FL_ONTRAIN)) || pmove->dead) - { - pmove->cmd.forwardmove = 0; - pmove->cmd.sidemove = 0; - pmove->cmd.upmove = 0; - } - - PM_DropPunchAngle(pmove->punchangle); - - // Take angles from command. - if (!pmove->dead) - { - VectorCopy(pmove->cmd.viewangles, v_angle); - VectorAdd(v_angle, pmove->punchangle, v_angle); - - // Set up view angles. - pmove->angles[ROLL] = PM_CalcRoll(v_angle, pmove->velocity, pmove->movevars->rollangle, pmove->movevars->rollspeed) * 4; - pmove->angles[PITCH] = v_angle[PITCH]; - pmove->angles[YAW] = v_angle[YAW]; - } - else - { - VectorCopy(pmove->oldangles, pmove->angles); - } - - // Set dead player view_offset - if (pmove->dead) - { -#ifdef REGAMEDLL_FIXES - if (pmove->bInDuck) - { - PM_UnDuck(); - pmove->bInDuck = FALSE; - } -#endif - - pmove->view_ofs[2] = PM_DEAD_VIEWHEIGHT; - } - - // Adjust client view angles to match values used on server. - if (pmove->angles[YAW] > 180.0f) - { - pmove->angles[YAW] -= 360.0f; - } -} - -void PM_ReduceTimers() -{ - if (pmove->flTimeStepSound > 0) - { - pmove->flTimeStepSound -= pmove->cmd.msec; - - if (pmove->flTimeStepSound < 0) - { - pmove->flTimeStepSound = 0; - } - } - - if (pmove->flDuckTime > 0) - { - pmove->flDuckTime -= pmove->cmd.msec; - - if (pmove->flDuckTime < 0) - { - pmove->flDuckTime = 0; - } - } - - if (pmove->flSwimTime > 0) - { - pmove->flSwimTime -= pmove->cmd.msec; - - if (pmove->flSwimTime < 0) - { - pmove->flSwimTime = 0; - } - } - - if (pmove->fuser2 > 0.0) - { - pmove->fuser2 -= pmove->cmd.msec; - - if (pmove->fuser2 < 0.0) - { - pmove->fuser2 = 0; - } - } -} - -qboolean PM_ShouldDoSpectMode() -{ - return (pmove->iuser3 <= 0 || pmove->deadflag == DEAD_DEAD); -} - -// Returns with origin, angles, and velocity modified in place. -// Numtouch and touchindex[] will be set if any of the physents -// were contacted during the move. -void PM_PlayerMove(qboolean server) -{ - physent_t *pLadder = nullptr; - - // Are we running server code? - pmove->server = server; - - // Adjust speeds etc. - PM_CheckParameters(); - - // Assume we don't touch anything - pmove->numtouch = 0; - - // # of msec to apply movement - - //double v2 = (double)pmove->cmd.msec * 0.001; - pmove->frametime = pmove->cmd.msec * 0.001; - - PM_ReduceTimers(); - - // Convert view angles to vectors - AngleVectors(pmove->angles, pmove->forward, pmove->right, pmove->up); - - //PM_ShowClipBox(); - - // Special handling for spectator and observers. (iuser1 is set if the player's in observer mode) - if ((pmove->spectator || pmove->iuser1 > 0) && PM_ShouldDoSpectMode()) - { - PM_SpectatorMove(); - PM_CategorizePosition(); - return; - } - - // Always try and unstick us unless we are in NOCLIP mode - if (pmove->movetype != MOVETYPE_NOCLIP && pmove->movetype != MOVETYPE_NONE) - { - if (PM_CheckStuck()) - { - // Can't move, we're stuck - return; - } - } - - // Now that we are "unstuck", see where we are (waterlevel and type, pmove->onground). - PM_CategorizePosition(); - - // Store off the starting water level - pmove->oldwaterlevel = pmove->waterlevel; - - // If we are not on ground, store off how fast we are moving down - if (pmove->onground == -1) - { - pmove->flFallVelocity = -pmove->velocity[2]; - } - - g_onladder = FALSE; - - // Don't run ladder code if dead or on a train - if (!pmove->dead && !(pmove->flags & FL_ONTRAIN) -#ifdef REGAMEDLL_ADD - && !(pmove->iuser3 & PLAYER_PREVENT_CLIMB) -#endif - ) - { - pLadder = PM_Ladder(); - - if (pLadder) - { - g_onladder = TRUE; - } - } - - PM_Duck(); - PM_UpdateStepSound(); - - // Don't run ladder code if dead or on a train - if (!pmove->dead && !(pmove->flags & FL_ONTRAIN)) - { - if (pLadder) - { - PM_LadderMove(pLadder); - } - else if (pmove->movetype != MOVETYPE_WALK && pmove->movetype != MOVETYPE_NOCLIP) - { - // Clear ladder stuff unless player is noclipping - // it will be set immediately again next frame if necessary - pmove->movetype = MOVETYPE_WALK; - } - } - - // Handle movement - switch (pmove->movetype) - { - default: - pmove->Con_DPrintf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); - break; - - case MOVETYPE_NONE: - break; - - case MOVETYPE_NOCLIP: - PM_NoClip(); - break; - - case MOVETYPE_TOSS: - case MOVETYPE_BOUNCE: - PM_Physics_Toss(); - break; - - case MOVETYPE_FLY: - PM_CheckWater(); - - // Was jump button pressed? - // If so, set velocity to 270 away from ladder. This is currently wrong. - // Also, set MOVE_TYPE to walk, too. - if (pmove->cmd.buttons & IN_JUMP) - { - if (!pLadder) - { - PM_Jump(); - } - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Perform the move accounting for any base velocity. - VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); - PM_FlyMove(); - VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); - break; - - case MOVETYPE_WALK: - if (!PM_InWater()) - { - PM_AddCorrectGravity(); - } - - // If we are leaping out of the water, just update the counters. - if (pmove->waterjumptime != 0.0f) - { - PM_WaterJump(); - PM_FlyMove(); - - // Make sure waterlevel is set correctly - PM_CheckWater(); - return; - } - - // If we are swimming in the water, see if we are nudging against a place we can jump up out - // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0 - if (pmove->waterlevel >= 2) - { - if (pmove->waterlevel == 2) - { - PM_CheckWaterJump(); - } - - // If we are falling again, then we must not trying to jump out of water any more. - if (pmove->velocity[2] < 0 && pmove->waterjumptime) - { - pmove->waterjumptime = 0; - } - - // Was jump button pressed? - if (pmove->cmd.buttons & IN_JUMP) - { - PM_Jump(); - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Perform regular water movement - PM_WaterMove(); - - VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); - - // Get a final position - PM_CategorizePosition(); - } - // Not underwater - else - { - // Was jump button pressed? - if (pmove->cmd.buttons & IN_JUMP) - { - if (!pLadder) - { - PM_Jump(); - } - } - else - { - pmove->oldbuttons &= ~IN_JUMP; - } - - // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, - // we don't slow when standing still, relative to the conveyor. - if (pmove->onground != -1) - { - pmove->velocity[2] = 0; - PM_Friction(); - } - - // Make sure velocity is valid. - PM_CheckVelocity(); - - // Are we on ground now - if (pmove->onground != -1) - { - PM_WalkMove(); - } - else - { - // Take into account movement when in air. - PM_AirMove(); - } - - // Set final flags. - PM_CategorizePosition(); - - // Now pull the base velocity back out. - // Base velocity is set if you are on a moving object, like - // a conveyor (or maybe another monster?) - VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); - - // Make sure velocity is valid. - PM_CheckVelocity(); - - // Add any remaining gravitational component. - if (!PM_InWater()) - { - PM_FixupGravityVelocity(); - } - - // If we are on ground, no downward velocity. - if (pmove->onground != -1) - { - pmove->velocity[2] = 0; - } - - // See if we landed on the ground with enough force to play a landing sound. - PM_CheckFalling(); - } - - // Did we enter or leave the water? - PM_PlayWaterSounds(); - break; - } -} - -void PM_CreateStuckTable() -{ - float x, y, z; - - int idx; - int i; - float zi[3]; - - Q_memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable)); - - idx = 0; - - // Little Moves. - x = 0; - y = 0; - - // Z moves - for (z = -0.125; z <= 0.125; z += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - x = 0; - z = 0; - // Y moves - for (y = -0.125; y <= 0.125; y += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - y = 0; - z = 0; - // X moves - for (x = -0.125; x <= 0.125; x += 0.125) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - // Remaining multi axis nudges. - for (x = -0.125; x <= 0.125; x += 0.250) - { - for (y = -0.125; y <= 0.125; y += 0.250) - { - for (z = -0.125; z <= 0.125; z += 0.250) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - } - } - - // Big Moves. - x = 0; - y = 0; - - zi[0] = 0.0f; - zi[1] = 1.0f; - zi[2] = 6.0f; - - for (i = 0; i < 3; i++) - { - // Z moves - z = zi[i]; - - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - x = 0; - z = 0; - - // Y moves - for (y = -2.0f; y <= 2.0f; y += 2.0) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - y = 0; - z = 0; - - // X moves - for (x = -2.0f; x <= 2.0f; x += 2.0f) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - - // Remaining multi axis nudges. - for (i = 0; i < 3; i++) - { - z = zi[i]; - - for (x = -2.0f; x <= 2.0f; x += 2.0f) - { - for (y = -2.0f; y <= 2.0f; y += 2.0) - { - rgv3tStuckTable[idx][0] = x; - rgv3tStuckTable[idx][1] = y; - rgv3tStuckTable[idx][2] = z; - - idx++; - } - } - } -} - -LINK_HOOK_VOID_CHAIN(PM_Move, (struct playermove_s *ppmove, int server), ppmove, server); - -// This module implements the shared player physics code between any particular game and -// the engine. The same PM_Move routine is built into the game .dll and the client .dll and is -// invoked by each side as appropriate. There should be no distinction, internally, between server -// and client. This will ensure that prediction behaves appropriately. -void EXT_FUNC __API_HOOK(PM_Move)(struct playermove_s *ppmove, int server) -{ - assert(pm_shared_initialized); - - pmove = ppmove; - - PM_PlayerMove((server != 0) ? TRUE : FALSE); - - if (pmove->onground != -1) - pmove->flags |= FL_ONGROUND; - else - pmove->flags &= ~FL_ONGROUND; - - if (!pmove->multiplayer && pmove->movetype == MOVETYPE_WALK) - { - pmove->friction = 1.0f; - } -} - -NOXREF int PM_GetVisEntInfo(int ent) -{ - if (ent >= 0 && ent <= pmove->numvisent) - { - return pmove->visents[ent].info; - } - - return -1; -} - -NOXREF int PM_GetPhysEntInfo(int ent) -{ - if (ent >= 0 && ent <= pmove->numphysent) - { - return pmove->physents[ent].info; - } - - return -1; -} - -LINK_HOOK_VOID_CHAIN(PM_Init, (struct playermove_s *ppmove), ppmove); - -void EXT_FUNC __API_HOOK(PM_Init)(struct playermove_s *ppmove) -{ - assert(!pm_shared_initialized); - - pmove = ppmove; - - PM_CreateStuckTable(); - PM_InitTextureTypes(); - - pm_shared_initialized = TRUE; -} +#include "precompiled.h" + +BOOL pm_shared_initialized = FALSE; + +vec3_t rgv3tStuckTable[54]; +int rgStuckLast[MAX_CLIENTS][2]; + +int pm_gcTextures = 0; +char pm_grgszTextureName[MAX_TEXTURES][MAX_TEXTURENAME_LENGHT]; +char pm_grgchTextureType[MAX_TEXTURES]; + +playermove_t *pmove = nullptr; +BOOL g_onladder = FALSE; + +#ifdef CLIENT_DLL + int iJumpSpectator; + float vJumpOrigin[3]; + float vJumpAngles[3]; +#endif + +void PM_SwapTextures(int i, int j) +{ + char chTemp; + char szTemp[MAX_TEXTURENAME_LENGHT]; + + Q_strcpy(szTemp, pm_grgszTextureName[i]); + chTemp = pm_grgchTextureType[i]; + + Q_strcpy(pm_grgszTextureName[i], pm_grgszTextureName[j]); + pm_grgchTextureType[i] = pm_grgchTextureType[j]; + + Q_strcpy(pm_grgszTextureName[j], szTemp); + pm_grgchTextureType[j] = chTemp; +} + +NOXREF qboolean PM_IsThereGrassTexture() +{ + for (int i = 0; i < pm_gcTextures; i++) + { + if (pm_grgchTextureType[i] == CHAR_TEX_GRASS) + return TRUE; + } + + return FALSE; +} + +void PM_SortTextures() +{ + // Bubble sort, yuck, but this only occurs at startup and it's only 512 elements... + int i, j; + for (i = 0; i < pm_gcTextures; i++) + { + for (j = i + 1; j < pm_gcTextures; j++) + { + if (Q_stricmp(pm_grgszTextureName[i], pm_grgszTextureName[j]) > 0) + { + // Swap + PM_SwapTextures(i, j); + } + } + } +} + +void PM_InitTextureTypes() +{ + char buffer[512]; + int i, j; + byte *pMemFile; + int fileSize, filePos = 0; + static bool bTextureTypeInit = false; + + if (bTextureTypeInit) + return; + + Q_memset(&(pm_grgszTextureName[0][0]), 0, sizeof(pm_grgszTextureName)); + Q_memset(pm_grgchTextureType, 0, sizeof(pm_grgchTextureType)); + + pm_gcTextures = 0; + Q_memset(buffer, 0, sizeof(buffer)); + + pMemFile = pmove->COM_LoadFile("sound/materials.txt", 5, &fileSize); + if (!pMemFile) + return; + + // for each line in the file... + while (pmove->memfgets(pMemFile, fileSize, &filePos, buffer, sizeof(buffer) - 1) && (pm_gcTextures < MAX_TEXTURES)) + { + // skip whitespace + i = 0; + while (buffer[i] && isspace(buffer[i])) + i++; + + if (!buffer[i]) + continue; + + // skip comment lines + if (buffer[i] == '/' || !isalpha(buffer[i])) + continue; + + // get texture type + pm_grgchTextureType[pm_gcTextures] = toupper(buffer[i++]); + + // skip whitespace + while (buffer[i] && isspace(buffer[i])) + i++; + + if (!buffer[i]) + continue; + + // get sentence name + j = i; + while (buffer[j] && !isspace(buffer[j])) + j++; + + if (!buffer[j]) + continue; + + // null-terminate name and save in sentences array + j = Q_min(j, MAX_TEXTURENAME_LENGHT - 1 + i); + buffer[j] = '\0'; + + Q_strcpy(&(pm_grgszTextureName[pm_gcTextures++][0]), &(buffer[i])); + } + + // Must use engine to free since we are in a .dll + pmove->COM_FreeFile(pMemFile); + + PM_SortTextures(); + bTextureTypeInit = true; +} + +char EXT_FUNC PM_FindTextureType(char *name) +{ + int left, right, pivot; + int val; + + assert(pm_shared_initialized); + + left = 0; + right = pm_gcTextures - 1; + + while (left <= right) + { + pivot = (left + right) / 2; + + val = Q_strnicmp(name, pm_grgszTextureName[pivot], MAX_TEXTURENAME_LENGHT - 1); + + if (val == 0) + { + return pm_grgchTextureType[pivot]; + } + else if (val > 0) + { + left = pivot + 1; + } + else if (val < 0) + { + right = pivot - 1; + } + } + + return CHAR_TEX_CONCRETE; +} + +void PM_PlayStepSound(int step, float fvol) +{ + static int iSkipStep = 0; + int irand; + + pmove->iStepLeft = !pmove->iStepLeft; + + if (!pmove->runfuncs) + { + return; + } + + irand = pmove->RandomLong(0, 1) + (pmove->iStepLeft * 2); + + // FIXME mp_footsteps needs to be a movevar + if (pmove->multiplayer && !pmove->movevars->footsteps) + return; + + // irand - 0,1 for right foot, 2,3 for left foot + // used to alternate left and right foot + // FIXME, move to player state + switch (step) + { + default: + case STEP_CONCRETE: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_step4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_METAL: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_metal4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_DIRT: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_dirt4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_VENT: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_duct4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_GRATE: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_grate4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_TILE: + if (!pmove->RandomLong(0, 4)) + irand = 4; + + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 4: pmove->PM_PlaySound(CHAN_BODY, "player/pl_tile5.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_SLOSH: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_slosh4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_WADE: + if (iSkipStep == 0) + { + iSkipStep++; + break; + } + + if (iSkipStep++ == 3) + { + iSkipStep = 0; + } + + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + + break; + case STEP_LADDER: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_ladder4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + case STEP_SNOW: + switch (irand) + { + // right foot + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow1.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow3.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + // left foot + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow2.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_snow4.wav", fvol, ATTN_NORM, 0, PITCH_NORM); break; + } + break; + } +} + +int PM_MapTextureTypeStepType(char chTextureType) +{ + switch (chTextureType) + { + default: + case CHAR_TEX_CONCRETE: return STEP_CONCRETE; + case CHAR_TEX_METAL: return STEP_METAL; + case CHAR_TEX_DIRT: return STEP_DIRT; + case CHAR_TEX_VENT: return STEP_VENT; + case CHAR_TEX_GRATE: return STEP_GRATE; + case CHAR_TEX_TILE: return STEP_TILE; + case CHAR_TEX_SLOSH: return STEP_SLOSH; + case CHAR_TEX_SNOW: return STEP_SNOW; + } +} + +void PM_CatagorizeTextureType() +{ + vec3_t start, end; + const char *pTextureName; + + VectorCopy(pmove->origin, start); + VectorCopy(pmove->origin, end); + + // Straight down + end[2] -= 64.0f; + + // Fill in default values, just in case. + pmove->sztexturename[0] = '\0'; + pmove->chtexturetype = CHAR_TEX_CONCRETE; + + pTextureName = pmove->PM_TraceTexture(pmove->onground, start, end); + + if (!pTextureName) + return; + + // strip leading '-0' or '+0~' or '{' or '!' + if (*pTextureName == '-' || *pTextureName == '+') + pTextureName += 2; + + if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') + pTextureName++; + + Q_strcpy(pmove->sztexturename, pTextureName); + pmove->sztexturename[MAX_TEXTURENAME_LENGHT - 1] = '\0'; + + // get texture type + pmove->chtexturetype = PM_FindTextureType(pmove->sztexturename); +} + +LINK_HOOK_VOID_CHAIN2(PM_UpdateStepSound); + +void EXT_FUNC __API_HOOK(PM_UpdateStepSound)() +{ + float fvol; + vec3_t knee; + vec3_t feet; + vec3_t center; + float height; + float speed; + int fLadder; + int step; + int onground; + + if (pmove->flTimeStepSound > 0) + return; + + if (pmove->flags & FL_FROZEN) + return; + + speed = Length(pmove->velocity); + + if (speed <= 150.0) + { + pmove->flTimeStepSound = 400; + return; + } + + // determine if we are on a ladder + fLadder = (pmove->movetype == MOVETYPE_FLY); + + // determine if we are not in air + onground = (pmove->onground != -1); + + // If we're on a ladder or on the ground play step sound. + if (fLadder || onground) + { + PM_CatagorizeTextureType(); + + VectorCopy(pmove->origin, center); + VectorCopy(pmove->origin, knee); + VectorCopy(pmove->origin, feet); + + height = pmove->player_maxs[pmove->usehull][2] - pmove->player_mins[pmove->usehull][2]; + + knee[2] = pmove->origin[2] - 0.3 * height; + feet[2] = pmove->origin[2] - 0.5 * height; + + // find out what we're stepping in or on... + if (fLadder) + { + step = STEP_LADDER; + fvol = 0.35; + pmove->flTimeStepSound = 350; + } + else if (pmove->PM_PointContents(knee, nullptr) == CONTENTS_WATER) + { + step = STEP_WADE; + fvol = 0.65; + pmove->flTimeStepSound = 600; + } + else if (pmove->PM_PointContents(feet, nullptr) == CONTENTS_WATER) + { + step = STEP_SLOSH; + fvol = 0.5; + pmove->flTimeStepSound = 300; + } + else + { + // find texture under player, if different from current texture, + // get material type + step = PM_MapTextureTypeStepType(pmove->chtexturetype); + + switch (pmove->chtexturetype) + { + default: + case CHAR_TEX_CONCRETE: + fvol = 0.5; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_METAL: + fvol = 0.5; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_DIRT: + fvol = 0.55; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_VENT: + fvol = 0.7; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_GRATE: + fvol = 0.5; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_TILE: + fvol = 0.5; + pmove->flTimeStepSound = 300; + break; + case CHAR_TEX_SLOSH: + fvol = 0.5; + pmove->flTimeStepSound = 300; + break; + } + } + + if ((pmove->flags & FL_DUCKING) || fLadder) + { + // slower step time if ducking + pmove->flTimeStepSound += 100; + + // play the sound + // 35% volume if ducking + if ((pmove->flags & FL_DUCKING) && pmove->flDuckTime < 950.0) + { + fvol *= 0.35; + } + } + + PM_PlayStepSound(step, fvol); + } +} + +// Add's the trace result to touch list, if contact is not already in list. +qboolean PM_AddToTouched(pmtrace_t tr, vec_t *impactvelocity) +{ + int i; + for (i = 0; i < pmove->numtouch; i++) + { + if (pmove->touchindex[i].ent == tr.ent) + break; + } + + // Already in list. + if (i != pmove->numtouch) + { + return FALSE; + } + + VectorCopy(impactvelocity, tr.deltavelocity); + + if (pmove->numtouch >= MAX_PHYSENTS) + { + pmove->Con_DPrintf("Too many entities were touched!\n"); + } + + pmove->touchindex[pmove->numtouch++] = tr; + return TRUE; +} + +void PM_CheckVelocity() +{ + int i; + + // bound velocity + for (i = 0; i < 3; i++) + { + // See if it's bogus. + if (IS_NAN(pmove->velocity[i])) + { + pmove->Con_Printf("PM Got a NaN velocity %i\n", i); + pmove->velocity[i] = 0; + } + + if (IS_NAN(pmove->origin[i])) + { + pmove->Con_Printf("PM Got a NaN origin on %i\n", i); + pmove->origin[i] = 0; + } + + // Bound it. + if (pmove->velocity[i] > pmove->movevars->maxvelocity) + { + pmove->Con_DPrintf("PM Got a velocity too high on %i\n", i); + pmove->velocity[i] = pmove->movevars->maxvelocity; + } + else if (pmove->velocity[i] < -pmove->movevars->maxvelocity) + { + pmove->Con_DPrintf("PM Got a velocity too low on %i\n", i); + pmove->velocity[i] = -pmove->movevars->maxvelocity; + } + } +} + +// Slide off of the impacting object +// returns the blocked flags: +// 0x01 == floor +// 0x02 == step / wall +int PM_ClipVelocity(vec_t *in, vec_t *normal, vec_t *out, float overbounce) +{ + float change; + real_t angle; + real_t backoff; + int i, blocked; + + angle = normal[2]; + + // Assume unblocked. + blocked = 0x00; + + // If the plane that is blocking us has a positive z component, then assume it's a floor. + if (angle > 0) + { + blocked |= 0x01; + } + + // If the plane has no Z, it is vertical (wall/step) + if (!angle) + { + blocked |= 0x02; + } + + // Determine how far along plane to slide based on incoming direction. + // Scale by overbounce factor. + backoff = DotProduct(in, normal) * overbounce; + + for (i = 0; i < 3; i++) + { + change = in[i] - normal[i] * backoff; + out[i] = change; + + // If out velocity is too small, zero it out. + if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) + { + out[i] = 0; + } + } + + // Return blocking flags. + return blocked; +} + +void PM_AddCorrectGravity() +{ + real_t ent_gravity; + + if (pmove->waterjumptime) + return; + + if (pmove->gravity != 0.0f) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0f; + + // Add gravity so they'll be in the correct position during movement + // yes, this 0.5 looks wrong, but it's not. + pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * 0.5f * pmove->frametime); + pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; + + pmove->basevelocity[2] = 0; + + PM_CheckVelocity(); +} + +void PM_FixupGravityVelocity() +{ + real_t ent_gravity; + + if (pmove->waterjumptime) + return; + + if (pmove->gravity != 0.0) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0; + + // Get the correct velocity for the end of the dt + pmove->velocity[2] -= (pmove->movevars->gravity * pmove->frametime * ent_gravity * 0.5); + PM_CheckVelocity(); +} + +int PM_FlyMove() +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity; + vec3_t new_velocity; + int i, j; + pmtrace_t trace; + vec3_t end; + float time_left, allFraction; + int blocked; + + numbumps = 4; // Bump up to four times + blocked = 0x00; // Assume not blocked + numplanes = 0; // and not sliding along any planes + + VectorCopy(pmove->velocity, original_velocity); // Store original velocity + VectorCopy(pmove->velocity, primal_velocity); + + allFraction = 0; + time_left = pmove->frametime; // Total time for this movement operation. + + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) + { + if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) + break; + + // Assume we can move all the way from the current origin to the + // end point. + for (i = 0; i < 3; i++) + { + real_t flScale = time_left * pmove->velocity[i]; + + end[i] = pmove->origin[i] + flScale; + } + + // See if we can make it from origin to end point. + trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); + + allFraction += trace.fraction; + + // If we started in a solid object, or we were in solid space + // the whole way, zero out our velocity and return that we + // are blocked by floor and wall. + if (trace.allsolid) + { + // entity is trapped in another solid + VectorCopy(vec3_origin, pmove->velocity); + return 4; + } + + // If we moved some portion of the total distance, then + // copy the end position into the pmove->origin and + // zero the plane counter. + if (trace.fraction > 0.0f) + { + // actually covered some distance + VectorCopy(trace.endpos, pmove->origin); + VectorCopy(pmove->velocity, original_velocity); + + numplanes = 0; + } + + // If we covered the entire distance, we are done + // and can return. + if (trace.fraction == 1.0f) + { + // moved the entire distance + break; + } + + // Save entity that blocked us (since fraction was < 1.0) + // for contact + // Add it if it's not already in the list + PM_AddToTouched(trace, pmove->velocity); + + // If the plane we hit has a high z component in the normal, then + // it's probably a floor + if (trace.plane.normal[2] > 0.7f) + { + // floor + blocked |= 0x01; + } + + // If the plane has a zero z component in the normal, then it's a + // step or wall + if (!trace.plane.normal[2]) + { + // step / wall + blocked |= 0x02; + } + + // Reduce amount of pmove->frametime left by total time left * fraction + // that we covered. + time_left -= time_left * trace.fraction; + + // Did we run out of planes to clip against? + if (numplanes >= MAX_CLIP_PLANES) + { + // this shouldn't really happen + // Stop our movement if so. + VectorCopy(vec3_origin, pmove->velocity); + break; + } + + // Set up next clipping plane + VectorCopy(trace.plane.normal, planes[numplanes]); + numplanes++; + + // modify original_velocity so it parallels all of the clip planes + // relfect player velocity + if (numplanes == 1 && pmove->movetype == MOVETYPE_WALK && (pmove->onground == -1 || pmove->friction != 1)) + { + for (i = 0; i < numplanes; i++) + { + if (planes[i][2] > 0.7f) + { + // floor or slope + PM_ClipVelocity(original_velocity, planes[i], new_velocity, 1); + VectorCopy(new_velocity, original_velocity); + } + else + PM_ClipVelocity(original_velocity, planes[i], new_velocity, 1.0 + pmove->movevars->bounce * (1.0 - pmove->friction)); + } + + VectorCopy(new_velocity, pmove->velocity); + VectorCopy(new_velocity, original_velocity); + } + else + { + for (i = 0; i < numplanes; i++) + { + PM_ClipVelocity(original_velocity, planes[i], pmove->velocity, 1); + + for (j = 0; j < numplanes; j++) + { + if (j != i && DotProduct(pmove->velocity, planes[j]) < 0) + { + break; + } + } + + if (j == numplanes) + break; + } + + if (i == numplanes) + { + if (numplanes != 2) + { + VectorCopy(vec3_origin, pmove->velocity); + break; + } + + CrossProduct(planes[0], planes[1], dir); + d = DotProduct(dir, pmove->velocity); + VectorScale(dir, d, pmove->velocity); + } + + if (DotProduct(pmove->velocity, primal_velocity) <= 0) + { + VectorCopy(vec3_origin, pmove->velocity); + break; + } + } + } + + if (allFraction == 0.0f) + { + VectorCopy(vec3_origin, pmove->velocity); + } + + return blocked; +} + +void PM_Accelerate(vec_t *wishdir, real_t wishspeed, float accel) +{ + int i; + float addspeed; + + real_t currentspeed; + real_t accelspeed; + + // Dead player's don't accelerate + if (pmove->dead) + return; + + // If waterjumping, don't accelerate + if (pmove->waterjumptime) + return; + + // See if we are changing direction a bit + currentspeed = DotProduct(pmove->velocity, wishdir); + + // Reduce wishspeed by the amount of veer. + addspeed = wishspeed - currentspeed; + + // If not going to add any speed, done. + if (addspeed <= 0) + return; + + // Determine amount of accleration. + accelspeed = accel * pmove->frametime * wishspeed * pmove->friction; + + // Cap at addspeed + if (accelspeed > addspeed) + accelspeed = addspeed; + + // Adjust velocity. + for (i = 0; i < 3; i++) + { + pmove->velocity[i] += accelspeed * wishdir[i]; + } +} + +// Only used by players. Moves along the ground when player is a MOVETYPE_WALK. +void PM_WalkMove() +{ + int clip; + int oldonground; + int i; + + vec3_t wishvel; + real_t spd; + float fmove, smove; + vec3_t wishdir; + real_t wishspeed; + + //vec3_t start; // TODO: unused + vec3_t dest; + vec3_t original, originalvel; + vec3_t down, downvel; + float downdist, updist; + + pmtrace_t trace; + + if (pmove->fuser2 > 0.0) + { + real_t flRatio = (100 - pmove->fuser2 * 0.001 * 19) * 0.01; + + pmove->velocity[0] *= flRatio; + pmove->velocity[1] *= flRatio; + } + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + // Zero out z components of movement vectors + pmove->forward[2] = 0; + pmove->right[2] = 0; + + // Normalize remainder of vectors. + VectorNormalize(pmove->forward); + VectorNormalize(pmove->right); + + // Determine x and y parts of velocity + for (i = 0; i < 2; i++) + { + wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; + } + + // Zero out z part of velocity + wishvel[2] = 0; + + // Determine maginitude of speed of move + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // Clamp to server defined max speed + if (wishspeed > pmove->maxspeed) + { + VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + + // Set pmove velocity + pmove->velocity[2] = 0; + PM_Accelerate(wishdir, wishspeed, pmove->movevars->accelerate); + pmove->velocity[2] = 0; + + // Add in any base velocity to the current velocity. + VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); + + spd = Length(pmove->velocity); + + if (spd < 1.0) + { + VectorClear(pmove->velocity); + return; + } + + // If we are not moving, do nothing + //if (!pmove->velocity[0] && !pmove->velocity[1] && !pmove->velocity[2]) + // return; + + oldonground = pmove->onground; + + // first try just moving to the destination + dest[0] = pmove->origin[0] + pmove->velocity[0] * pmove->frametime; + dest[1] = pmove->origin[1] + pmove->velocity[1] * pmove->frametime; + dest[2] = pmove->origin[2]; + + // first try moving directly to the next spot + // VectorCopy(dest, start); + + trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); + + // If we made it all the way, then copy trace end + // as new player position. + if (trace.fraction == 1.0f) + { + VectorCopy(trace.endpos, pmove->origin); + return; + } + + // Don't walk up stairs if not on ground. + if (oldonground == -1 && pmove->waterlevel == 0) + { + return; + } + + // If we are jumping out of water, don't do anything more. + if (pmove->waterjumptime) + return; + + // Try sliding forward both on ground and up 16 pixels + // take the move that goes farthest + + // Save out original pos & + VectorCopy(pmove->origin, original); + + // velocity. + VectorCopy(pmove->velocity, originalvel); + + // Slide move + clip = PM_FlyMove(); + + // Copy the results out + VectorCopy(pmove->origin, down); + VectorCopy(pmove->velocity, downvel); + + // Reset original values. + VectorCopy(original, pmove->origin); + VectorCopy(originalvel, pmove->velocity); + + // Start out up one stair height + VectorCopy(pmove->origin, dest); + + dest[2] += pmove->movevars->stepsize; + + trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); + + // If we started okay and made it part of the way at least, + // copy the results to the movement start position and then + // run another move try. + if (!trace.startsolid && !trace.allsolid) + { + VectorCopy(trace.endpos, pmove->origin); + } + + // slide move the rest of the way. + clip = PM_FlyMove(); + + // Now try going back down from the end point + // press down the stepheight + VectorCopy(pmove->origin, dest); + dest[2] -= pmove->movevars->stepsize; + + trace = pmove->PM_PlayerTrace(pmove->origin, dest, PM_NORMAL, -1); + + // If we are not on the ground any more then + // use the original movement attempt + if (trace.plane.normal[2] < 0.7f) + goto usedown; + + // If the trace ended up in empty space, copy the end + // over to the origin. + if (!trace.startsolid && !trace.allsolid) + { + VectorCopy(trace.endpos, pmove->origin); + } + + // Copy this origion to up. + VectorCopy(pmove->origin, pmove->up); + + // decide which one went farther + downdist = (down[0] - original[0]) * (down[0] - original[0]) + (down[1] - original[1]) * (down[1] - original[1]); + updist = (pmove->up[0] - original[0]) * (pmove->up[0] - original[0]) + (pmove->up[1] - original[1]) * (pmove->up[1] - original[1]); + + if (downdist > updist) + { +usedown: + VectorCopy(down, pmove->origin); + VectorCopy(downvel, pmove->velocity); + } + else + { + // copy z value from slide move + pmove->velocity[2] = downvel[2]; + } +} + +// Handles both ground friction and water friction +void PM_Friction() +{ + float *vel; + float speed; + real_t newspeed, control, friction, drop; + vec3_t newvel; + + // If we are in water jump cycle, don't apply friction + if (pmove->waterjumptime) + return; + + // Get velocity + vel = pmove->velocity; + + // Calculate speed + speed = Q_sqrt(real_t(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2])); + + // If too slow, return + if (speed < 0.1f) + { + return; + } + + drop = 0; + + // apply ground friction + // On an entity that is the ground + if (pmove->onground != -1) + { + vec3_t start, stop; + pmtrace_t trace; + + start[0] = stop[0] = pmove->origin[0] + vel[0] / speed * 16; + start[1] = stop[1] = pmove->origin[1] + vel[1] / speed * 16; + start[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2]; + stop[2] = start[2] - 34; + + trace = pmove->PM_PlayerTrace(start, stop, PM_NORMAL, -1); + + if (trace.fraction == 1.0f) + friction = pmove->movevars->friction * pmove->movevars->edgefriction; + else + friction = pmove->movevars->friction; + + // Grab friction value. + //friction = pmove->movevars->friction; + + // player friction? + friction *= pmove->friction; + + // Bleed off some speed, but if we have less than the bleed + // threshhold, bleed the theshold amount. + control = (speed < pmove->movevars->stopspeed) ? pmove->movevars->stopspeed : speed; + + // Add the amount to t'he drop amount. + drop += friction * (control * pmove->frametime); + } + + // apply water friction + //if (pmove->waterlevel) + //{ + // drop += speed * pmove->movevars->waterfriction * waterlevel * pmove->frametime; + //} + + // scale the velocity + newspeed = speed - drop; + + if (newspeed < 0) + { + newspeed = 0; + } + + // Determine proportion of old speed we are using. + newspeed /= speed; + + // Adjust velocity according to proportion. + newvel[0] = vel[0] * newspeed; + newvel[1] = vel[1] * float(newspeed); + newvel[2] = vel[2] * float(newspeed); + + VectorCopy(newvel, pmove->velocity); +} + +void PM_AirAccelerate(vec_t *wishdir, float wishspeed, float accel) +{ + int i; + float addspeed; + float wishspd = wishspeed; + + real_t currentspeed; + real_t accelspeed; + + if (pmove->dead || pmove->waterjumptime) + return; + + // Cap speed + if (wishspd > 30) + wishspd = 30; + + // Determine veer amount + currentspeed = DotProduct(pmove->velocity, wishdir); + + // See how much to add + addspeed = wishspd - currentspeed; + + // If not adding any, done. + if (addspeed <= 0) + return; + + // Determine acceleration speed after acceleration + accelspeed = accel * wishspeed * pmove->frametime * pmove->friction; + + // Cap it + if (accelspeed > addspeed) + accelspeed = addspeed; + + // Adjust pmove vel. + for (i = 0; i < 3; i++) + { + pmove->velocity[i] += accelspeed * wishdir[i]; + } +} + +void PM_WaterMove() +{ + int i; + vec3_t wishvel; + vec3_t wishdir; + vec3_t start, dest; + vec3_t temp; + pmtrace_t trace; + + real_t speed, accelspeed, wishspeed; + float newspeed, addspeed; + + // user intentions + for (i = 0; i < 3; i++) + { + wishvel[i] = (pmove->forward[i] * pmove->cmd.forwardmove) + (pmove->cmd.sidemove * pmove->right[i]); + } + + // Sinking after no other movement occurs + if (!pmove->cmd.forwardmove && !pmove->cmd.sidemove && !pmove->cmd.upmove) + { + // drift towards bottom + wishvel[2] -= 60.0f; + } + else + { + // Go straight up by upmove amount. + wishvel[2] += pmove->cmd.upmove; + } + + // Copy it over and determine speed + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // Cap speed. + if (wishspeed > pmove->maxspeed) + { + VectorScale(wishvel, pmove->maxspeed / wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + + // Slow us down a bit. + wishspeed *= 0.8; + VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); + + // Water friction + VectorCopy(pmove->velocity, temp); + speed = VectorNormalize(temp); + + if (speed) + { + newspeed = speed - pmove->movevars->friction * pmove->friction * pmove->frametime * speed; + + if (newspeed < 0.0f) + { + newspeed = 0.0f; + } + + VectorScale(pmove->velocity, newspeed / speed, pmove->velocity); + } + else + newspeed = 0; + + // water acceleration + if (float(wishspeed) < 0.1f) + { + return; + } + + addspeed = float(wishspeed) - newspeed; + + if (addspeed > 0.0f) + { + VectorNormalize(wishvel); + accelspeed = pmove->movevars->accelerate * pmove->friction * pmove->frametime * float(wishspeed); + + if (accelspeed > addspeed) + { + accelspeed = addspeed; + } + + for (i = 0; i < 3; i++) + { + pmove->velocity[i] += accelspeed * wishvel[i]; + } + } + + // Now move + // assume it is a stair or a slope, so press down from stepheight above + VectorMA(pmove->origin, pmove->frametime, pmove->velocity, dest); + VectorCopy(dest, start); + + start[2] += pmove->movevars->stepsize + 1; + trace = pmove->PM_PlayerTrace(start, dest, PM_NORMAL, -1); + + // FIXME: check steep slope? + if (!trace.startsolid && !trace.allsolid) + { + // walked up the step, so just keep result and exit + VectorCopy(trace.endpos, pmove->origin); + return; + } + + // Try moving straight along out normal path. + PM_FlyMove(); +} + +LINK_HOOK_VOID_CHAIN(PM_AirMove, (int playerIndex = 0), pmove->player_index + 1); + +void EXT_FUNC __API_HOOK(PM_AirMove)(int playerIndex) +{ + PM_AirMove_internal(); +} + +void PM_AirMove_internal() +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + // Zero out z components of movement vectors + pmove->forward[2] = 0; + pmove->right[2] = 0; + + // Renormalize + VectorNormalize(pmove->forward); + VectorNormalize(pmove->right); + + // Determine x and y parts of velocity + for (i = 0; i < 2; i++) + { + wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; + } + + // Zero out z part of velocity + wishvel[2] = 0; + + // Determine maginitude of speed of move + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // Clamp to server defined max speed + if (wishspeed > pmove->maxspeed) + { + VectorScale(wishvel, pmove->maxspeed/wishspeed, wishvel); + wishspeed = pmove->maxspeed; + } + + PM_AirAccelerate(wishdir, wishspeed, pmove->movevars->airaccelerate); + + // Add in any base velocity to the current velocity. + VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); + + PM_FlyMove(); +} + +qboolean PM_InWater() +{ + return (pmove->waterlevel > 1) ? TRUE : FALSE; +} + +// Sets pmove->waterlevel and pmove->watertype values. +qboolean PM_CheckWater() +{ +#ifdef REGAMEDLL_FIXES + // do not check for dead + if (pmove->dead || pmove->deadflag != DEAD_NO) + return FALSE; +#endif + + vec3_t point; + int cont; + int truecont; + float height; + float heightover2; + + // Pick a spot just above the players feet. + point[0] = pmove->origin[0] + (pmove->player_mins[pmove->usehull][0] + pmove->player_maxs[pmove->usehull][0]) * 0.5f; + point[1] = pmove->origin[1] + (pmove->player_mins[pmove->usehull][1] + pmove->player_maxs[pmove->usehull][1]) * 0.5f; + point[2] = pmove->origin[2] + pmove->player_mins[pmove->usehull][2] + 1; + + // Assume that we are not in water at all. + pmove->waterlevel = 0; + pmove->watertype = CONTENTS_EMPTY; + + // Grab point contents. + cont = pmove->PM_PointContents(point, &truecont); + + // Are we under water? (not solid and not empty?) + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) + { + // Set water type + pmove->watertype = cont; + + // We are at least at level one + pmove->waterlevel = 1; + + height = (pmove->player_mins[pmove->usehull][2] + pmove->player_maxs[pmove->usehull][2]); + heightover2 = height * 0.5; + + // Now check a point that is at the player hull midpoint. + point[2] = pmove->origin[2] + heightover2; + cont = pmove->PM_PointContents(point, nullptr); + + // If that point is also under water... + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) + { + // Set a higher water level. + pmove->waterlevel = 2; + + // Now check the eye position. (view_ofs is relative to the origin) + point[2] = pmove->origin[2] + pmove->view_ofs[2]; + + cont = pmove->PM_PointContents(point, nullptr); + if (cont <= CONTENTS_WATER && cont > CONTENTS_TRANSLUCENT) + { + // In over our eyes + pmove->waterlevel = 3; + } + } + + // Adjust velocity based on water current, if any. + if ((truecont <= CONTENTS_CURRENT_0) && (truecont >= CONTENTS_CURRENT_DOWN)) + { + // The deeper we are, the stronger the current. + static vec3_t current_table[] = + { + {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, + {0, -1, 0}, {0, 0, 1}, {0, 0, -1} + }; + + VectorMA(pmove->basevelocity, 50.0 * pmove->waterlevel, current_table[CONTENTS_CURRENT_0 - truecont], pmove->basevelocity); + } + } + + return (pmove->waterlevel > 1) ? TRUE : FALSE; +} + +void PM_CategorizePosition() +{ + vec3_t point; + pmtrace_t tr; + + // if the player hull point one unit down is solid, the player + // is on ground + + // see if standing on something solid + + // Doing this before we move may introduce a potential latency in water detection, but + // doing it after can get us stuck on the bottom in water if the amount we move up + // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call + // this several times per frame, so we really need to avoid sticking to the bottom of + // water on each call, and the converse case will correct itself if called twice. + PM_CheckWater(); + + point[0] = pmove->origin[0]; + point[1] = pmove->origin[1]; + point[2] = pmove->origin[2] - 2; + + // Shooting up really fast. Definitely not on ground. + if (pmove->velocity[2] > 180) + { + pmove->onground = -1; + return; + } + + // Try and move down. + tr = pmove->PM_PlayerTrace(pmove->origin, point, PM_NORMAL, -1); + + // If we hit a steep plane, we are not on ground + if (tr.plane.normal[2] < 0.7f) + { + // too steep + pmove->onground = -1; + } + else + { + // Otherwise, point to index of ent under us. + pmove->onground = tr.ent; + } + + // If we are on something... + if (pmove->onground != -1) + { + // Then we are not in water jump sequence + pmove->waterjumptime = 0; + + // If we could make the move, drop us down that 1 pixel + if (pmove->waterlevel < 2 && !tr.startsolid && !tr.allsolid) + { + VectorCopy(tr.endpos, pmove->origin); + } + } + + // Standing on an entity other than the world + // So signal that we are touching something. + if (tr.ent > 0) + { + PM_AddToTouched(tr, pmove->velocity); + } +} + +// When a player is stuck, it's costly to try and unstick them +// Grab a test offset for the player based on a passed in index +int PM_GetRandomStuckOffsets(int nIndex, int server, vec_t *offset) +{ + // Last time we did a full + int idx = rgStuckLast[nIndex][server]++; + VectorCopy(rgv3tStuckTable[idx % ARRAYSIZE(rgv3tStuckTable)], offset); + return (idx % ARRAYSIZE(rgv3tStuckTable)); +} + +void PM_ResetStuckOffsets(int nIndex, int server) +{ + rgStuckLast[nIndex][server] = 0; +} + +// If pmove->origin is in a solid position, +// try nudging slightly on all axis to +// allow for the cut precision of the net coordinates +qboolean PM_CheckStuck() +{ + vec3_t base; + vec3_t offset; + vec3_t test; + int hitent; + int idx; + real_t fTime; + int i; + pmtrace_t traceresult; + + // Last time we did a full + static float rgStuckCheckTime[MAX_CLIENTS][2]; + + // If position is okay, exit + hitent = pmove->PM_TestPlayerPosition(pmove->origin, &traceresult); + if (hitent == -1) + { + PM_ResetStuckOffsets(pmove->player_index, pmove->server); + return FALSE; + } + + VectorCopy(pmove->origin, base); + + // Deal with precision error in network. + if (!pmove->server) + { + // World or BSP model + if (hitent == 0 || pmove->physents[hitent].model) + { + int nReps = 0; + PM_ResetStuckOffsets(pmove->player_index, pmove->server); + do + { + i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); + + VectorAdd(base, offset, test); + if (pmove->PM_TestPlayerPosition(test, &traceresult) == -1) + { + PM_ResetStuckOffsets(pmove->player_index, pmove->server); + VectorCopy(test, pmove->origin); + return FALSE; + } + + nReps++; + } + while (nReps < ARRAYSIZE(rgv3tStuckTable)); + } + } + + // Only an issue on the client. + if (pmove->server) + idx = 0; + else + idx = 1; + + fTime = pmove->Sys_FloatTime(); + + // Too soon? + if (rgStuckCheckTime[pmove->player_index][idx] >= (fTime - PM_CHECKSTUCK_MINTIME)) + { + return TRUE; + } + + rgStuckCheckTime[pmove->player_index][idx] = fTime; + + pmove->PM_StuckTouch(hitent, &traceresult); + + i = PM_GetRandomStuckOffsets(pmove->player_index, pmove->server, offset); + + VectorAdd(base, offset, test); + if ((hitent = pmove->PM_TestPlayerPosition(test, nullptr)) == -1) + { + PM_ResetStuckOffsets(pmove->player_index, pmove->server); + + if (i >= (ARRAYSIZE(rgv3tStuckTable) / 2)) + { + VectorCopy(test, pmove->origin); + } + + return FALSE; + } + + // If player is flailing while stuck in another player (should never happen), then see + // if we can't "unstick" them forceably. + if ((pmove->cmd.buttons & (IN_JUMP | IN_DUCK | IN_ATTACK)) && pmove->physents[hitent].player != 0) + { + float x, y, z; + float xystep = 8.0; + float zstep = 18.0; + float xyminmax = xystep; + float zminmax = 4 * zstep; + + for (z = 0; z <= zminmax; z += zstep) + { + for (x = -xyminmax; x <= xyminmax; x += xystep) + { + for (y = -xyminmax; y <= xyminmax; y += xystep) + { + VectorCopy(base, test); + + test[0] += x; + test[1] += y; + test[2] += z; + + if (pmove->PM_TestPlayerPosition(test, nullptr) == -1) + { + VectorCopy(test, pmove->origin); + return FALSE; + } + } + } + } + } + + return TRUE; +} + +void PM_SpectatorMove() +{ + real_t speed, drop, friction; + real_t control, newspeed; + float currentspeed, addspeed; + real_t accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + real_t wishspeed; + + // this routine keeps track of the spectators psoition + // there a two different main move types : track player or moce freely (OBS_ROAMING) + // doesn't need excate track position, only to generate PVS, so just copy + // targets position and real view position is calculated on client (saves server CPU) + if (pmove->iuser1 == OBS_ROAMING) + { +#ifdef CLIENT_DLL + if (iJumpSpectator) + { + VectorCopy(vJumpOrigin, pmove->origin); + VectorCopy(vJumpAngles, pmove->angles); + VectorCopy(vec3_origin, pmove->velocity); + iJumpSpectator = 0; + return; + } +#endif + // Move around in normal spectator method + speed = Length (pmove->velocity); + + if (speed >= 1.0) + { + drop = 0; + + // extra friction + friction = pmove->movevars->friction * 1.5; + control = speed < pmove->movevars->stopspeed ? pmove->movevars->stopspeed : speed; + drop += friction * (control * pmove->frametime); + + // scale the velocity + newspeed = speed - drop; + + if (newspeed < 0) + { + newspeed = 0; + } + newspeed /= speed; + + VectorScale(pmove->velocity, newspeed, pmove->velocity); + } + else + { + VectorCopy(vec3_origin, pmove->velocity); + } + + // accelerate + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize(pmove->forward); + VectorNormalize(pmove->right); + + for (i = 0; i < 3; i++) + { + wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; + } + + wishvel[2] += pmove->cmd.upmove; + + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); + + // clamp to server defined max speed + if (wishspeed > pmove->movevars->spectatormaxspeed) + { + VectorScale(wishvel, pmove->movevars->spectatormaxspeed / wishspeed, wishvel); + wishspeed = pmove->movevars->spectatormaxspeed; + } + + currentspeed = DotProduct(pmove->velocity, wishdir); + + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + { + return; + } + + accelspeed = pmove->movevars->accelerate * pmove->frametime * wishspeed; + if (accelspeed > addspeed) + { + accelspeed = addspeed; + } + + for (i = 0; i < 3; i++) + { + pmove->velocity[i] += accelspeed * wishdir[i]; + } + + // move + VectorMA(pmove->origin, pmove->frametime, pmove->velocity, pmove->origin); + } + else + { + // all other modes just track some kind of target, so spectator PVS = target PVS + int target; + + // no valid target ? + if (pmove->iuser2 <= 0) + return; + + // Find the client this player's targeting + for (target = 0; target < pmove->numphysent; target++) + { + if (pmove->physents[target].info == pmove->iuser2) + break; + } + + if (target == pmove->numphysent) + return; + + // use targets position as own origin for PVS + VectorCopy(pmove->physents[target].angles, pmove->angles); + VectorCopy(pmove->physents[target].origin, pmove->origin); + + // no velocity + VectorCopy(vec3_origin, pmove->velocity); + } +} + +// Use for ease-in, ease-out style interpolation (accel/decel) +// Used by ducking code. +float PM_SplineFraction(float value, float scale) +{ + real_t valueSquared; + + value = scale * value; + valueSquared = value * value; + + // Nice little ease-in, ease-out spline-like curve + return 3 * valueSquared - 2 * valueSquared * value; +} + +NOXREF float PM_SimpleSpline(float value) +{ + float valueSquared; + + valueSquared = value * value; + + return 3 * valueSquared - 2 * valueSquared * value; +} + +void PM_FixPlayerCrouchStuck(int direction) +{ + int hitent; + int i; + vec3_t test; + + hitent = pmove->PM_TestPlayerPosition(pmove->origin, nullptr); + + if (hitent == -1) + { + return; + } + + VectorCopy(pmove->origin, test); + + for (i = 0; i < HalfHumanHeight; i++) + { + pmove->origin[2] += direction; + hitent = pmove->PM_TestPlayerPosition(pmove->origin, nullptr); + + if (hitent == -1) + { + return; + } + } + + // Failed + VectorCopy(test, pmove->origin); +} + +void PM_UnDuck() +{ +#ifdef REGAMEDLL_ADD + if (unduck_method.value) +#endif + { +#ifdef REGAMEDLL_FIXES + // if ducking isn't finished yet, so don't unduck + if (pmove->bInDuck || !(pmove->flags & FL_DUCKING)) + { + pmove->usehull = 0; + pmove->flDuckTime = 0; + pmove->bInDuck = FALSE; + pmove->view_ofs[2] = PM_VEC_VIEW; + return; + } +#endif // #ifdef REGAMEDLL_FIXES + } + + pmtrace_t trace; + vec3_t newOrigin; + + VectorCopy(pmove->origin, newOrigin); + + if (pmove->onground != -1) + { +#ifdef REGAMEDLL_FIXES + vec3_t offset; + VectorSubtract(pmove->player_mins[1], pmove->player_mins[0], offset); + VectorAdd(newOrigin, offset, newOrigin); +#else + newOrigin[2] += 18.0; +#endif + } + + trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); + if (!trace.startsolid) + { + pmove->usehull = 0; + + // Oh, no, changing hulls stuck us into something, try unsticking downward first. + trace = pmove->PM_PlayerTrace(newOrigin, newOrigin, PM_NORMAL, -1); + + if (trace.startsolid) + { + // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot + // Con_Printf("unstick got stuck\n"); + pmove->usehull = 1; + return; + } + + pmove->flags &= ~FL_DUCKING; + pmove->bInDuck = FALSE; + pmove->view_ofs[2] = PM_VEC_VIEW; + pmove->flDuckTime = 0; + + pmove->flTimeStepSound -= 100; + + if (pmove->flTimeStepSound < 0) + { + pmove->flTimeStepSound = 0; + } + + VectorCopy(newOrigin, pmove->origin); + + // Recatagorize position since ducking can change origin + PM_CategorizePosition(); + } +} + +void PM_Duck() +{ + int buttonsChanged = (pmove->oldbuttons ^ pmove->cmd.buttons); // These buttons have changed this frame + int nButtonPressed = buttonsChanged & pmove->cmd.buttons; // The changed ones still down are "pressed" + + int duckchange = buttonsChanged & IN_DUCK ? 1 : 0; + int duckpressed = nButtonPressed & IN_DUCK ? 1 : 0; + + if (pmove->cmd.buttons & IN_DUCK) + { + pmove->oldbuttons |= IN_DUCK; + } + else + { + pmove->oldbuttons &= ~IN_DUCK; + } + +#ifdef REGAMEDLL_ADD + // Prevent ducking if the iuser3 variable is contain PLAYER_PREVENT_DUCK + if ((pmove->iuser3 & PLAYER_PREVENT_DUCK) == PLAYER_PREVENT_DUCK) + { + // Try to unduck + if (pmove->flags & FL_DUCKING) + { + PM_UnDuck(); + } + + return; + } +#endif + + if (pmove->dead || (!(pmove->cmd.buttons & IN_DUCK) && !pmove->bInDuck && !(pmove->flags & FL_DUCKING))) + { + return; + } + + pmove->cmd.forwardmove *= PLAYER_DUCKING_MULTIPLIER; + pmove->cmd.sidemove *= PLAYER_DUCKING_MULTIPLIER; + pmove->cmd.upmove *= PLAYER_DUCKING_MULTIPLIER; + + if (pmove->cmd.buttons & IN_DUCK) + { + if ((nButtonPressed & IN_DUCK) && !(pmove->flags & FL_DUCKING)) + { + // Use 1 second so super long jump will work + pmove->flDuckTime = 1000; + pmove->bInDuck = TRUE; + } + + if (pmove->bInDuck) + { + // Finish ducking immediately if duck time is over or not on ground + if (((pmove->flDuckTime / 1000.0) <= (1.0 - TIME_TO_DUCK)) || pmove->onground == -1) + { + pmove->usehull = 1; + pmove->view_ofs[2] = PM_VEC_DUCK_VIEW; + pmove->flags |= FL_DUCKING; + pmove->bInDuck = FALSE; + + // HACKHACK - Fudge for collision bug - no time to fix this properly + if (pmove->onground != -1) + { +#ifdef REGAMEDLL_FIXES + vec3_t newOrigin; + VectorSubtract(pmove->player_mins[1], pmove->player_mins[0], newOrigin); + VectorSubtract(pmove->origin, newOrigin, pmove->origin); +#else + pmove->origin[2] = pmove->origin[2] - 18.0; +#endif + + // See if we are stuck? + PM_FixPlayerCrouchStuck(STUCK_MOVEUP); + + // Recatagorize position since ducking can change origin + PM_CategorizePosition(); + } + } + else + { + real_t duckFraction = PM_VEC_VIEW; + real_t time = (1.0 - pmove->flDuckTime / 1000.0); + + // Calc parametric time + if (time >= 0.0) { + duckFraction = PM_SplineFraction(time, (1.0 / TIME_TO_DUCK)); + } + +#ifdef REGAMEDLL_FIXES + float fMore = (pmove->player_mins[1][2] - pmove->player_mins[0][2]); +#else + float fMore = (PM_VEC_DUCK_HULL_MIN - PM_VEC_HULL_MIN); +#endif + + pmove->view_ofs[2] = ((PM_VEC_DUCK_VIEW - fMore) * duckFraction) + (PM_VEC_VIEW * (1 - duckFraction)); + } + } + } + // Try to unduck + else + { + PM_UnDuck(); + } +} + +void PM_LadderMove(physent_t *pLadder) +{ + vec3_t ladderCenter; + trace_t trace; + bool onFloor; + vec3_t floor; + vec3_t modelmins, modelmaxs; + + if (pmove->movetype == MOVETYPE_NOCLIP) + return; + + pmove->PM_GetModelBounds(pLadder->model, modelmins, modelmaxs); + + VectorAdd(modelmins, modelmaxs, ladderCenter); + VectorScale(ladderCenter, 0.5, ladderCenter); + + pmove->movetype = MOVETYPE_FLY; + + // On ladder, convert movement to be relative to the ladder + VectorCopy(pmove->origin, floor); + floor[2] += pmove->player_mins[pmove->usehull][2] - 1; + + if (pmove->PM_PointContents(floor, nullptr) == CONTENTS_SOLID) + onFloor = true; + else + onFloor = false; + + pmove->gravity = 0; + pmove->PM_TraceModel(pLadder, pmove->origin, ladderCenter, &trace); + + if (trace.fraction != 1.0f) + { + float forward = 0, right = 0; + vec3_t vpn, v_right; + float flSpeed = MAX_CLIMB_SPEED; + + // they shouldn't be able to move faster than their maxspeed + if (flSpeed > pmove->maxspeed) + { + flSpeed = pmove->maxspeed; + } + + AngleVectors(pmove->angles, vpn, v_right, nullptr); + + if (pmove->flags & FL_DUCKING) + { + flSpeed *= PLAYER_DUCKING_MULTIPLIER; + } + + if (pmove->cmd.buttons & IN_BACK) + { + forward -= flSpeed; + } + if (pmove->cmd.buttons & IN_FORWARD) + { + forward += flSpeed; + } + if (pmove->cmd.buttons & IN_MOVELEFT) + { + right -= flSpeed; + } + if (pmove->cmd.buttons & IN_MOVERIGHT) + { + right += flSpeed; + } + + if (pmove->cmd.buttons & IN_JUMP) + { + pmove->movetype = MOVETYPE_WALK; + VectorScale(trace.plane.normal, 270, pmove->velocity); + } + else + { + if (forward != 0 || right != 0) + { + vec3_t velocity, perp, cross, lateral, tmp; + float normal; + + VectorScale(vpn, forward, velocity); + VectorMA(velocity, right, v_right, velocity); + + VectorClear(tmp); + tmp[2] = 1; + + CrossProduct(tmp, trace.plane.normal, perp); + VectorNormalize(perp); + + // decompose velocity into ladder plane + normal = DotProduct(velocity, trace.plane.normal); + // This is the velocity into the face of the ladder + VectorScale(trace.plane.normal, normal, cross); + + // This is the player's additional velocity + VectorSubtract(velocity, cross, lateral); + + // This turns the velocity into the face of the ladder into velocity that + // is roughly vertically perpendicular to the face of the ladder. + // NOTE: It IS possible to face up and move down or face down and move up + // because the velocity is a sum of the directional velocity and the converted + // velocity through the face of the ladder - by design. + CrossProduct(trace.plane.normal, perp, tmp); + VectorMA(lateral, -normal, tmp, pmove->velocity); + + // On ground moving away from the ladder + if (onFloor && normal > 0) + { + VectorMA(pmove->velocity, MAX_CLIMB_SPEED, trace.plane.normal, pmove->velocity); + } + } + else + { + VectorClear(pmove->velocity); + } + } + } +} + +physent_t *PM_Ladder() +{ + int i; + physent_t *pe; + hull_t *hull; + int num; + vec3_t test; + + for (i = 0; i < pmove->nummoveent; i++) + { + pe = &pmove->moveents[i]; + + if (pe->model && (modtype_t)pmove->PM_GetModelType(pe->model) == mod_brush && pe->skin == CONTENTS_LADDER) + { + hull = (hull_t *)pmove->PM_HullForBsp(pe, test); + num = hull->firstclipnode; + + // Offset the test point appropriately for this hull. + VectorSubtract(pmove->origin, test, test); + + // Test the player's hull for intersection with this model + if (pmove->PM_HullPointContents(hull, num, test) == CONTENTS_EMPTY) + { + continue; + } + + return pe; + } + } + + return nullptr; +} + +void PM_WaterJump() +{ + if (pmove->waterjumptime > 10000) + { + pmove->waterjumptime = 10000; + } + + if (!pmove->waterjumptime) + { + return; + } + + pmove->waterjumptime -= pmove->cmd.msec; + + if (pmove->waterjumptime < 0 || !pmove->waterlevel) + { + pmove->waterjumptime = 0; + pmove->flags &= ~FL_WATERJUMP; + } + + pmove->velocity[0] = pmove->movedir[0]; + pmove->velocity[1] = pmove->movedir[1]; +} + +void PM_AddGravity() +{ + float ent_gravity; + + if (pmove->gravity != 0.0f) + ent_gravity = pmove->gravity; + else + ent_gravity = 1.0f; + + pmove->velocity[2] -= (ent_gravity * pmove->movevars->gravity * pmove->frametime); + pmove->velocity[2] += pmove->basevelocity[2] * pmove->frametime; + + pmove->basevelocity[2] = 0; + PM_CheckVelocity(); +} + +// Does not change the entities velocity at all +pmtrace_t PM_PushEntity(vec_t *push) +{ + pmtrace_t trace; + vec3_t end; + + VectorAdd(pmove->origin, push, end); + + trace = pmove->PM_PlayerTrace(pmove->origin, end, PM_NORMAL, -1); + + VectorCopy(trace.endpos, pmove->origin); + + // So we can run impact function afterwards. + if (trace.fraction < 1.0f && !trace.allsolid) + { + PM_AddToTouched(trace, pmove->velocity); + } + + return trace; +} + +void PM_Physics_Toss() +{ + pmtrace_t trace; + vec3_t move; + float backoff; + + PM_CheckWater(); + + if (pmove->velocity[2] > 0) + { + pmove->onground = -1; + } + + // If on ground and not moving, return. + if (pmove->onground != -1) + { + if (VectorCompare(pmove->basevelocity, vec3_origin) && VectorCompare(pmove->velocity, vec3_origin)) + { + return; + } + } + + PM_CheckVelocity(); + + // add gravity + if (pmove->movetype != MOVETYPE_FLY && pmove->movetype != MOVETYPE_BOUNCEMISSILE && pmove->movetype != MOVETYPE_FLYMISSILE) + { + PM_AddGravity(); + } + + // move origin + // Base velocity is not properly accounted for since this entity will move again after the bounce without + // taking it into account + VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); + + PM_CheckVelocity(); + VectorScale(pmove->velocity, pmove->frametime, move); + VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); + + // Should this clear basevelocity + trace = PM_PushEntity(move); + + PM_CheckVelocity(); + + if (trace.allsolid) + { + // entity is trapped in another solid + pmove->onground = trace.ent; + VectorCopy(vec3_origin, pmove->velocity); + return; + } + + if (trace.fraction == 1.0f) + { + PM_CheckWater(); + return; + } + + if (pmove->movetype == MOVETYPE_BOUNCE) + { + backoff = 2.0f - pmove->friction; + } + else if (pmove->movetype == MOVETYPE_BOUNCEMISSILE) + { + backoff = 2.0f; + } + else + backoff = 1.0f; + + PM_ClipVelocity(pmove->velocity, trace.plane.normal, pmove->velocity, backoff); + + // stop if on ground + if (trace.plane.normal[2] > 0.7f) + { + float vel; + vec3_t base; + + VectorClear(base); + if (pmove->velocity[2] < pmove->movevars->gravity * pmove->frametime) + { + // we're rolling on the ground, add static friction. + pmove->onground = trace.ent; + pmove->velocity[2] = 0; + } + + vel = DotProduct(pmove->velocity, pmove->velocity); + + if (vel < (30 * 30) || (pmove->movetype != MOVETYPE_BOUNCE && pmove->movetype != MOVETYPE_BOUNCEMISSILE)) + { + pmove->onground = trace.ent; + VectorCopy(vec3_origin, pmove->velocity); + } + else + { + VectorScale(pmove->velocity, (1.0f - trace.fraction) * pmove->frametime * 0.9f, move); + trace = PM_PushEntity(move); + } + + VectorSubtract(pmove->velocity, base, pmove->velocity); + } + + // check for in water + PM_CheckWater(); +} + +void PM_NoClip() +{ + int i; + vec3_t wishvel; + float fmove, smove; + + // Copy movement amounts + fmove = pmove->cmd.forwardmove; + smove = pmove->cmd.sidemove; + + VectorNormalize(pmove->forward); + VectorNormalize(pmove->right); + + // Determine x and y parts of velocity + for (i = 0; i < 3; i++) + { + wishvel[i] = pmove->forward[i] * fmove + pmove->right[i] * smove; + } + + wishvel[2] += pmove->cmd.upmove; + + VectorMA(pmove->origin, pmove->frametime, wishvel, pmove->origin); + + // Zero out the velocity so that we don't accumulate a huge downward velocity from + // gravity, etc. + VectorClear(pmove->velocity); +} + +// Purpose: Corrects bunny jumping (where player initiates a bunny jump before other +// movement logic runs, thus making onground == -1 thus making PM_Friction get skipped and +// running PM_AirMove, which doesn't crop velocity to maxspeed like the ground / other +// movement logic does. +void PM_PreventMegaBunnyJumping() +{ + // Current player speed + real_t spd; + // If we have to crop, apply this cropping fraction to velocity + float fraction; + // Speed at which bunny jumping is limited + float maxscaledspeed; + + maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed; + + // Don't divide by zero + if (maxscaledspeed <= 0.0f) + return; + + spd = Length(pmove->velocity); + + if (spd <= maxscaledspeed) + return; + + // Returns the modifier for the velocity + fraction = (maxscaledspeed / spd) * 0.8; + + // Crop it down!. + VectorScale(pmove->velocity, fraction, pmove->velocity); +} + +void PM_Jump() +{ + if (pmove->dead) + { + // don't jump again until released + pmove->oldbuttons |= IN_JUMP; + return; + } + + // See if we are waterjumping. If so, decrement count and return. + if (pmove->waterjumptime != 0.0f) + { + pmove->waterjumptime -= pmove->cmd.msec; + + if (pmove->waterjumptime < 0) + { + pmove->waterjumptime = 0; + } + + return; + } + + // If we are in the water most of the way... + if (pmove->waterlevel >= 2) + { + // swimming, not jumping + pmove->onground = -1; + + // We move up a certain amount + if (pmove->watertype == CONTENTS_WATER) + { + pmove->velocity[2] = 100; + } + else if (pmove->watertype == CONTENTS_SLIME) + { + pmove->velocity[2] = 80; + } + else // LAVA + pmove->velocity[2] = 50; + + // play swiming sound + if (pmove->flSwimTime <= 0) + { + // Don't play sound again for 1 second + pmove->flSwimTime = 1000.0f; + + switch (pmove->RandomLong(0, 3)) + { + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + } + } + + return; + } + +#ifdef REGAMEDLL_ADD + // Prevent jumping if the iuser3 variable is contain PLAYER_PREVENT_JUMP + if ((pmove->iuser3 & PLAYER_PREVENT_JUMP) == PLAYER_PREVENT_JUMP) + { + return; + } +#endif + + // No more effect + // in air, so no effect + if (pmove->onground == -1) + { + // Flag that we jumped. + // don't jump again until released + pmove->oldbuttons |= IN_JUMP; + return; + } + + // don't pogo stick + if (pmove->oldbuttons & IN_JUMP) + { + return; + } + + if (pmove->bInDuck && (pmove->flags & FL_DUCKING)) + { + return; + } + + PM_CatagorizeTextureType(); + + // In the air now. + pmove->onground = -1; + + PM_PreventMegaBunnyJumping(); + + real_t fvel = Length(pmove->velocity); + float fvol = 1.0f; + + if (fvel >= 150.0f) + { + PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), fvol); + } + +#ifdef REGAMEDLL_ADD + // See if user can super long jump? + bool cansuperjump = (pmove->PM_Info_ValueForKey(pmove->physinfo, "slj")[0] == '1'); + + // Acclerate upward + // If we are ducking... + if (pmove->bInDuck || (pmove->flags & FL_DUCKING)) + { + // Adjust for super long jump module + // UNDONE: note this should be based on forward angles, not current velocity. + if (cansuperjump && (pmove->cmd.buttons & IN_DUCK) && pmove->flDuckTime > 0 && Length(pmove->velocity) > 50) + { + pmove->punchangle[0] = -5.0f; + + for (int i = 0; i < 2; i++) + { + pmove->velocity[i] = pmove->forward[i] * PLAYER_LONGJUMP_SPEED * 1.6f; + } + + pmove->velocity[2] = Q_sqrt(2 * 800 * 56.0f); + } + else + { + pmove->velocity[2] = Q_sqrt(2 * 800 * 45.0f); + } + } + else +#endif + { + // NOTE: don't do it in .f (float) + pmove->velocity[2] = Q_sqrt(2.0 * 800.0f * 45.0f); + } + + if (pmove->fuser2 > 0.0f) + { + // NOTE: don't do it in .f (float) + real_t flRatio = (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01; + pmove->velocity[2] *= flRatio; + } + + pmove->fuser2 = 1315.789429; + + // Decay it for simulation + PM_FixupGravityVelocity(); + + // Flag that we jumped. + // don't jump again until released + pmove->oldbuttons |= IN_JUMP; +} + +void PM_CheckWaterJump() +{ + vec3_t vecStart, vecEnd; + vec3_t flatforward; + vec3_t flatvelocity; + float curspeed; + pmtrace_t tr; + int savehull; + + // Already water jumping. + if (pmove->waterjumptime) + return; + + // Don't hop out if we just jumped in + if (pmove->velocity[2] < -180) + { + // only hop out if we are moving up + return; + } + + // See if we are backing up + flatvelocity[0] = pmove->velocity[0]; + flatvelocity[1] = pmove->velocity[1]; + flatvelocity[2] = 0; + + // Must be moving + curspeed = VectorNormalize(flatvelocity); + + // see if near an edge + flatforward[0] = pmove->forward[0]; + flatforward[1] = pmove->forward[1]; + flatforward[2] = 0; + VectorNormalize(flatforward); + + // Are we backing into water from steps or something? If so, don't pop forward + if (curspeed != 0.0 && (DotProduct(flatvelocity, flatforward) < 0.0)) + { + return; + } + + VectorCopy(pmove->origin, vecStart); + vecStart[2] += WJ_HEIGHT; + + VectorMA(vecStart, 24, flatforward, vecEnd); + + // Trace, this trace should use the point sized collision hull + savehull = pmove->usehull; + pmove->usehull = 2; + + tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); + + // Facing a near vertical wall? + if (tr.fraction < 1.0f && Q_fabs(real_t(tr.plane.normal[2])) < 0.1f) + { + vecStart[2] += pmove->player_maxs[savehull][2] - WJ_HEIGHT; + + VectorMA(vecStart, 24, flatforward, vecEnd); + VectorMA(vec3_origin, -50, tr.plane.normal, pmove->movedir); + + tr = pmove->PM_PlayerTrace(vecStart, vecEnd, PM_NORMAL, -1); + + if (tr.fraction == 1.0f) + { + pmove->waterjumptime = 2000.0f; + pmove->velocity[2] = 225.0f; + + pmove->oldbuttons |= IN_JUMP; + pmove->flags |= FL_WATERJUMP; + } + } + + // Reset the collision hull + pmove->usehull = savehull; +} + +void PM_CheckFalling() +{ + if (pmove->onground != -1 && !pmove->dead && pmove->flFallVelocity >= PM_PLAYER_FALL_PUNCH_THRESHHOLD) + { + float fvol = 0.5f; + + if (pmove->waterlevel <= 0) + { + if (pmove->flFallVelocity > PM_PLAYER_MAX_SAFE_FALL_SPEED) + { + fvol = 1.0f; + } + else if (pmove->flFallVelocity > PM_PLAYER_MAX_SAFE_FALL_SPEED / 2) + { + fvol = 0.85f; + } + else if (pmove->flFallVelocity < PM_PLAYER_MIN_BOUNCE_SPEED) + { + fvol = 0.0f; + } + } + + if (fvol > 0.0f) + { + PM_CatagorizeTextureType(); + + // play step sound for current texture + PM_PlayStepSound(PM_MapTextureTypeStepType(pmove->chtexturetype), fvol); + + pmove->flTimeStepSound = 300; + + // Knock the screen around a little bit, temporary effect + // punch z axis + pmove->punchangle[2] = pmove->flFallVelocity * 0.013; + + if (pmove->punchangle[0] > 8.0f) + { + pmove->punchangle[0] = 8.0f; + } + } + } + + if (pmove->onground != -1) + { + pmove->flFallVelocity = 0; + } +} + +void PM_PlayWaterSounds() +{ + // Did we enter or leave water? + if (pmove->oldwaterlevel != 0) + { + if (pmove->waterlevel != 0) + return; + } + else + { + if (pmove->waterlevel == 0) + return; + } + + switch (pmove->RandomLong(0, 3)) + { + case 0: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 1: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 2: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade3.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + case 3: pmove->PM_PlaySound(CHAN_BODY, "player/pl_wade4.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); break; + } +} + +float PM_CalcRoll(vec_t *angles, vec_t *velocity, float rollangle, float rollspeed) +{ + float sign; + real_t side; + float value; + vec3_t forward, right, up; + + AngleVectors(angles, forward, right, up); + + side = DotProduct(velocity, right); + sign = side < 0 ? -1 : 1; + side = Q_fabs(side); + + value = rollangle; + + if (side < rollspeed) + { + side = side * value / rollspeed; + } + else + { + side = value; + } + + return side * sign; +} + +void PM_DropPunchAngle(vec_t *punchangle) +{ + real_t len; + + len = VectorNormalize(punchangle); + len -= (10.0 + len * 0.5) * pmove->frametime; +#ifdef PLAY_GAMEDLL + len = Q_max(len, 0.0); +#else + len = Q_max(len, 0.0f); +#endif + VectorScale(punchangle, len, punchangle); +} + +void PM_CheckParameters() +{ + float spd; + real_t maxspeed; + vec3_t v_angle; + + spd = Q_sqrt(real_t(pmove->cmd.sidemove * pmove->cmd.sidemove + pmove->cmd.forwardmove * pmove->cmd.forwardmove + pmove->cmd.upmove * pmove->cmd.upmove)); + + maxspeed = pmove->clientmaxspeed; + + if (maxspeed != 0.0f) + { + pmove->maxspeed = Q_min(maxspeed, real_t(pmove->maxspeed)); + } + + if (spd != 0.0f && spd > real_t(pmove->maxspeed)) + { + real_t fRatio = pmove->maxspeed / spd; + + pmove->cmd.forwardmove *= fRatio; + pmove->cmd.sidemove *= fRatio; + pmove->cmd.upmove *= fRatio; + } + + if ((pmove->flags & (FL_FROZEN | FL_ONTRAIN)) || pmove->dead) + { + pmove->cmd.forwardmove = 0; + pmove->cmd.sidemove = 0; + pmove->cmd.upmove = 0; + } + + PM_DropPunchAngle(pmove->punchangle); + + // Take angles from command. + if (!pmove->dead) + { + VectorCopy(pmove->cmd.viewangles, v_angle); + VectorAdd(v_angle, pmove->punchangle, v_angle); + + // Set up view angles. + pmove->angles[ROLL] = PM_CalcRoll(v_angle, pmove->velocity, pmove->movevars->rollangle, pmove->movevars->rollspeed) * 4; + pmove->angles[PITCH] = v_angle[PITCH]; + pmove->angles[YAW] = v_angle[YAW]; + } + else + { + VectorCopy(pmove->oldangles, pmove->angles); + } + + // Set dead player view_offset + if (pmove->dead) + { +#ifdef REGAMEDLL_FIXES + if (pmove->bInDuck) + { + PM_UnDuck(); + pmove->bInDuck = FALSE; + } +#endif + + pmove->view_ofs[2] = PM_DEAD_VIEWHEIGHT; + } + + // Adjust client view angles to match values used on server. + if (pmove->angles[YAW] > 180.0f) + { + pmove->angles[YAW] -= 360.0f; + } +} + +void PM_ReduceTimers() +{ + if (pmove->flTimeStepSound > 0) + { + pmove->flTimeStepSound -= pmove->cmd.msec; + + if (pmove->flTimeStepSound < 0) + { + pmove->flTimeStepSound = 0; + } + } + + if (pmove->flDuckTime > 0) + { + pmove->flDuckTime -= pmove->cmd.msec; + + if (pmove->flDuckTime < 0) + { + pmove->flDuckTime = 0; + } + } + + if (pmove->flSwimTime > 0) + { + pmove->flSwimTime -= pmove->cmd.msec; + + if (pmove->flSwimTime < 0) + { + pmove->flSwimTime = 0; + } + } + + if (pmove->fuser2 > 0.0) + { + pmove->fuser2 -= pmove->cmd.msec; + + if (pmove->fuser2 < 0.0) + { + pmove->fuser2 = 0; + } + } +} + +qboolean PM_ShouldDoSpectMode() +{ + return (pmove->iuser3 <= 0 || pmove->deadflag == DEAD_DEAD); +} + +// Returns with origin, angles, and velocity modified in place. +// Numtouch and touchindex[] will be set if any of the physents +// were contacted during the move. +void PM_PlayerMove(qboolean server) +{ + physent_t *pLadder = nullptr; + + // Are we running server code? + pmove->server = server; + + // Adjust speeds etc. + PM_CheckParameters(); + + // Assume we don't touch anything + pmove->numtouch = 0; + + // # of msec to apply movement + + //double v2 = (double)pmove->cmd.msec * 0.001; + pmove->frametime = pmove->cmd.msec * 0.001; + + PM_ReduceTimers(); + + // Convert view angles to vectors + AngleVectors(pmove->angles, pmove->forward, pmove->right, pmove->up); + + //PM_ShowClipBox(); + + // Special handling for spectator and observers. (iuser1 is set if the player's in observer mode) + if ((pmove->spectator || pmove->iuser1 > 0) && PM_ShouldDoSpectMode()) + { + PM_SpectatorMove(); + PM_CategorizePosition(); + return; + } + + // Always try and unstick us unless we are in NOCLIP mode + if (pmove->movetype != MOVETYPE_NOCLIP && pmove->movetype != MOVETYPE_NONE) + { + if (PM_CheckStuck()) + { + // Can't move, we're stuck + return; + } + } + + // Now that we are "unstuck", see where we are (waterlevel and type, pmove->onground). + PM_CategorizePosition(); + + // Store off the starting water level + pmove->oldwaterlevel = pmove->waterlevel; + + // If we are not on ground, store off how fast we are moving down + if (pmove->onground == -1) + { + pmove->flFallVelocity = -pmove->velocity[2]; + } + + g_onladder = FALSE; + + // Don't run ladder code if dead or on a train + if (!pmove->dead && !(pmove->flags & FL_ONTRAIN) +#ifdef REGAMEDLL_ADD + && !(pmove->iuser3 & PLAYER_PREVENT_CLIMB) +#endif + ) + { + pLadder = PM_Ladder(); + + if (pLadder) + { + g_onladder = TRUE; + } + } + + PM_Duck(); + PM_UpdateStepSound(); + + // Don't run ladder code if dead or on a train + if (!pmove->dead && !(pmove->flags & FL_ONTRAIN)) + { + if (pLadder) + { + PM_LadderMove(pLadder); + } + else if (pmove->movetype != MOVETYPE_WALK && pmove->movetype != MOVETYPE_NOCLIP) + { + // Clear ladder stuff unless player is noclipping + // it will be set immediately again next frame if necessary + pmove->movetype = MOVETYPE_WALK; + } + } + + // Handle movement + switch (pmove->movetype) + { + default: + pmove->Con_DPrintf("Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", pmove->movetype, pmove->server); + break; + + case MOVETYPE_NONE: + break; + + case MOVETYPE_NOCLIP: + PM_NoClip(); + break; + + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + PM_Physics_Toss(); + break; + + case MOVETYPE_FLY: + PM_CheckWater(); + + // Was jump button pressed? + // If so, set velocity to 270 away from ladder. This is currently wrong. + // Also, set MOVE_TYPE to walk, too. + if (pmove->cmd.buttons & IN_JUMP) + { + if (!pLadder) + { + PM_Jump(); + } + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Perform the move accounting for any base velocity. + VectorAdd(pmove->velocity, pmove->basevelocity, pmove->velocity); + PM_FlyMove(); + VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); + break; + + case MOVETYPE_WALK: + if (!PM_InWater()) + { + PM_AddCorrectGravity(); + } + + // If we are leaping out of the water, just update the counters. + if (pmove->waterjumptime != 0.0f) + { + PM_WaterJump(); + PM_FlyMove(); + + // Make sure waterlevel is set correctly + PM_CheckWater(); + return; + } + + // If we are swimming in the water, see if we are nudging against a place we can jump up out + // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0 + if (pmove->waterlevel >= 2) + { + if (pmove->waterlevel == 2) + { + PM_CheckWaterJump(); + } + + // If we are falling again, then we must not trying to jump out of water any more. + if (pmove->velocity[2] < 0 && pmove->waterjumptime) + { + pmove->waterjumptime = 0; + } + + // Was jump button pressed? + if (pmove->cmd.buttons & IN_JUMP) + { + PM_Jump(); + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Perform regular water movement + PM_WaterMove(); + + VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); + + // Get a final position + PM_CategorizePosition(); + } + // Not underwater + else + { + // Was jump button pressed? + if (pmove->cmd.buttons & IN_JUMP) + { + if (!pLadder) + { + PM_Jump(); + } + } + else + { + pmove->oldbuttons &= ~IN_JUMP; + } + + // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, + // we don't slow when standing still, relative to the conveyor. + if (pmove->onground != -1) + { + pmove->velocity[2] = 0; + PM_Friction(); + } + + // Make sure velocity is valid. + PM_CheckVelocity(); + + // Are we on ground now + if (pmove->onground != -1) + { + PM_WalkMove(); + } + else + { + // Take into account movement when in air. + PM_AirMove(); + } + + // Set final flags. + PM_CategorizePosition(); + + // Now pull the base velocity back out. + // Base velocity is set if you are on a moving object, like + // a conveyor (or maybe another monster?) + VectorSubtract(pmove->velocity, pmove->basevelocity, pmove->velocity); + + // Make sure velocity is valid. + PM_CheckVelocity(); + + // Add any remaining gravitational component. + if (!PM_InWater()) + { + PM_FixupGravityVelocity(); + } + + // If we are on ground, no downward velocity. + if (pmove->onground != -1) + { + pmove->velocity[2] = 0; + } + + // See if we landed on the ground with enough force to play a landing sound. + PM_CheckFalling(); + } + + // Did we enter or leave the water? + PM_PlayWaterSounds(); + break; + } +} + +void PM_CreateStuckTable() +{ + float x, y, z; + + int idx; + int i; + float zi[3]; + + Q_memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable)); + + idx = 0; + + // Little Moves. + x = 0; + y = 0; + + // Z moves + for (z = -0.125; z <= 0.125; z += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + x = 0; + z = 0; + // Y moves + for (y = -0.125; y <= 0.125; y += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + y = 0; + z = 0; + // X moves + for (x = -0.125; x <= 0.125; x += 0.125) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + // Remaining multi axis nudges. + for (x = -0.125; x <= 0.125; x += 0.250) + { + for (y = -0.125; y <= 0.125; y += 0.250) + { + for (z = -0.125; z <= 0.125; z += 0.250) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + } + } + + // Big Moves. + x = 0; + y = 0; + + zi[0] = 0.0f; + zi[1] = 1.0f; + zi[2] = 6.0f; + + for (i = 0; i < 3; i++) + { + // Z moves + z = zi[i]; + + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + x = 0; + z = 0; + + // Y moves + for (y = -2.0f; y <= 2.0f; y += 2.0) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + y = 0; + z = 0; + + // X moves + for (x = -2.0f; x <= 2.0f; x += 2.0f) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + + // Remaining multi axis nudges. + for (i = 0; i < 3; i++) + { + z = zi[i]; + + for (x = -2.0f; x <= 2.0f; x += 2.0f) + { + for (y = -2.0f; y <= 2.0f; y += 2.0) + { + rgv3tStuckTable[idx][0] = x; + rgv3tStuckTable[idx][1] = y; + rgv3tStuckTable[idx][2] = z; + + idx++; + } + } + } +} + +LINK_HOOK_VOID_CHAIN(PM_Move, (struct playermove_s *ppmove, int server), ppmove, server); + +// This module implements the shared player physics code between any particular game and +// the engine. The same PM_Move routine is built into the game .dll and the client .dll and is +// invoked by each side as appropriate. There should be no distinction, internally, between server +// and client. This will ensure that prediction behaves appropriately. +void EXT_FUNC __API_HOOK(PM_Move)(struct playermove_s *ppmove, int server) +{ + assert(pm_shared_initialized); + + pmove = ppmove; + + PM_PlayerMove((server != 0) ? TRUE : FALSE); + + if (pmove->onground != -1) + pmove->flags |= FL_ONGROUND; + else + pmove->flags &= ~FL_ONGROUND; + + if (!pmove->multiplayer && pmove->movetype == MOVETYPE_WALK) + { + pmove->friction = 1.0f; + } +} + +NOXREF int PM_GetVisEntInfo(int ent) +{ + if (ent >= 0 && ent <= pmove->numvisent) + { + return pmove->visents[ent].info; + } + + return -1; +} + +NOXREF int PM_GetPhysEntInfo(int ent) +{ + if (ent >= 0 && ent <= pmove->numphysent) + { + return pmove->physents[ent].info; + } + + return -1; +} + +LINK_HOOK_VOID_CHAIN(PM_Init, (struct playermove_s *ppmove), ppmove); + +void EXT_FUNC __API_HOOK(PM_Init)(struct playermove_s *ppmove) +{ + assert(!pm_shared_initialized); + + pmove = ppmove; + + PM_CreateStuckTable(); + PM_InitTextureTypes(); + + pm_shared_initialized = TRUE; +}