diff --git a/src/common/const.h b/src/common/const.h index 9db7d38..9ebf665 100644 --- a/src/common/const.h +++ b/src/common/const.h @@ -1,771 +1,771 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -#ifndef CONST_H -#define CONST_H -// -// 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 (1<<0) // Changes the SV_Movestep() behavior to not need to be on ground -#define FL_SWIM (1<<1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) -#define FL_CONVEYOR (1<<2) -#define FL_CLIENT (1<<3) -#define FL_INWATER (1<<4) -#define FL_MONSTER (1<<5) -#define FL_GODMODE (1<<6) -#define FL_NOTARGET (1<<7) -#define FL_SKIPLOCALHOST (1<<8) // Don't send entity to local host, it's predicting this entity itself -#define FL_ONGROUND (1<<9) // At rest / on the ground -#define FL_PARTIALGROUND (1<<10) // not all corners are valid -#define FL_WATERJUMP (1<<11) // player jumping out of water -#define FL_FROZEN (1<<12) // Player is frozen for 3rd person camera -#define FL_FAKECLIENT (1<<13) // JAC: fake client, simulated server side; don't send network messages to them -#define FL_DUCKING (1<<14) // Player flag -- Player is fully crouched -#define FL_FLOAT (1<<15) // Apply floating force to this entity when in water -#define FL_GRAPHED (1<<16) // worldgraph has this ent listed as something that blocks a connection - -// UNDONE: Do we need these? -#define FL_IMMUNE_WATER (1<<17) -#define FL_IMMUNE_SLIME (1<<18) -#define FL_IMMUNE_LAVA (1<<19) - -#define FL_PROXY (1<<20) // This is a spectator proxy -#define FL_ALWAYSTHINK (1<<21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) -#define FL_BASEVELOCITY (1<<22) // Base velocity has been applied this frame (used to convert base velocity into momentum) -#define FL_MONSTERCLIP (1<<23) // Only collide in with monsters who have FL_MONSTERCLIP set -#define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. -#define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) -#define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc. -#define FL_CUSTOMENTITY (1<<29) // This is a custom entity -#define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time -#define FL_DORMANT (1<<31) // Entity is dormant, no updates to client - - -// Goes into globalvars_t.trace_flags -#define FTRACE_SIMPLEBOX (1<<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 -#define DEAD_DISCARDBODY 4 - -#define DAMAGE_NO 0 -#define DAMAGE_YES 1 -#define DAMAGE_AIM 2 - -// entity effects -#define EF_BRIGHTFIELD 1 // swirling cloud of particles -#define EF_MUZZLEFLASH 2 // single frame ELIGHT on entity attachment 0 -#define EF_BRIGHTLIGHT 4 // DLIGHT centered at entity origin -#define EF_DIMLIGHT 8 // player flashlight -#define EF_INVLIGHT 16 // get lighting from ceiling -#define EF_NOINTERP 32 // don't interpolate the next frame -#define EF_LIGHT 64 // rocket flare glow sprite -#define EF_NODRAW 128 // don't draw entity - -// entity flags -#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 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 -/* These additional contents constants are defined in bspfile.h -#define CONTENTS_ORIGIN -7 // removed at csg time -#define CONTENTS_CLIP -8 // changed to contents_solid -#define CONTENTS_CURRENT_0 -9 -#define CONTENTS_CURRENT_90 -10 -#define CONTENTS_CURRENT_180 -11 -#define CONTENTS_CURRENT_270 -12 -#define CONTENTS_CURRENT_UP -13 -#define CONTENTS_CURRENT_DOWN -14 - -#define CONTENTS_TRANSLUCENT -15 -*/ -#define CONTENTS_LADDER -16 - -#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). - -// attenuation values -#define ATTN_NONE 0 -#define ATTN_NORM (float)0.8 -#define ATTN_IDLE (float)2 -#define ATTN_STATIC (float)1.25 - -// 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.0 - -// plats -#define PLAT_LOW_TRIGGER 1 - -// Trains -#define SF_TRAIN_WAIT_RETRIGGER 1 -#define SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains - -// 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!) -}; - - -typedef int func_t; -typedef int string_t; - -typedef unsigned char byte; -typedef unsigned short word; -#define _DEF_BYTE_ - -#undef true -#undef false - -#ifndef __cplusplus -typedef enum {false, true} qboolean; -#else -typedef int qboolean; -#endif - -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 - +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +#ifndef CONST_H +#define CONST_H +// +// 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 (1<<0) // Changes the SV_Movestep() behavior to not need to be on ground +#define FL_SWIM (1<<1) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) +#define FL_CONVEYOR (1<<2) +#define FL_CLIENT (1<<3) +#define FL_INWATER (1<<4) +#define FL_MONSTER (1<<5) +#define FL_GODMODE (1<<6) +#define FL_NOTARGET (1<<7) +#define FL_SKIPLOCALHOST (1<<8) // Don't send entity to local host, it's predicting this entity itself +#define FL_ONGROUND (1<<9) // At rest / on the ground +#define FL_PARTIALGROUND (1<<10) // not all corners are valid +#define FL_WATERJUMP (1<<11) // player jumping out of water +#define FL_FROZEN (1<<12) // Player is frozen for 3rd person camera +#define FL_FAKECLIENT (1<<13) // JAC: fake client, simulated server side; don't send network messages to them +#define FL_DUCKING (1<<14) // Player flag -- Player is fully crouched +#define FL_FLOAT (1<<15) // Apply floating force to this entity when in water +#define FL_GRAPHED (1<<16) // worldgraph has this ent listed as something that blocks a connection + +// UNDONE: Do we need these? +#define FL_IMMUNE_WATER (1<<17) +#define FL_IMMUNE_SLIME (1<<18) +#define FL_IMMUNE_LAVA (1<<19) + +#define FL_PROXY (1<<20) // This is a spectator proxy +#define FL_ALWAYSTHINK (1<<21) // Brush model flag -- call think every frame regardless of nextthink - ltime (for constantly changing velocity/path) +#define FL_BASEVELOCITY (1<<22) // Base velocity has been applied this frame (used to convert base velocity into momentum) +#define FL_MONSTERCLIP (1<<23) // Only collide in with monsters who have FL_MONSTERCLIP set +#define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. +#define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) +#define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc. +#define FL_CUSTOMENTITY (1<<29) // This is a custom entity +#define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time +#define FL_DORMANT (1<<31) // Entity is dormant, no updates to client + + +// Goes into globalvars_t.trace_flags +#define FTRACE_SIMPLEBOX (1<<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 +#define DEAD_DISCARDBODY 4 + +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 + +// entity effects +#define EF_BRIGHTFIELD 1 // swirling cloud of particles +#define EF_MUZZLEFLASH 2 // single frame ELIGHT on entity attachment 0 +#define EF_BRIGHTLIGHT 4 // DLIGHT centered at entity origin +#define EF_DIMLIGHT 8 // player flashlight +#define EF_INVLIGHT 16 // get lighting from ceiling +#define EF_NOINTERP 32 // don't interpolate the next frame +#define EF_LIGHT 64 // rocket flare glow sprite +#define EF_NODRAW 128 // don't draw entity + +// entity flags +#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 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 +/* These additional contents constants are defined in bspfile.h +#define CONTENTS_ORIGIN -7 // removed at csg time +#define CONTENTS_CLIP -8 // changed to contents_solid +#define CONTENTS_CURRENT_0 -9 +#define CONTENTS_CURRENT_90 -10 +#define CONTENTS_CURRENT_180 -11 +#define CONTENTS_CURRENT_270 -12 +#define CONTENTS_CURRENT_UP -13 +#define CONTENTS_CURRENT_DOWN -14 + +#define CONTENTS_TRANSLUCENT -15 +*/ +#define CONTENTS_LADDER -16 + +#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). + +// attenuation values +#define ATTN_NONE 0 +#define ATTN_NORM (float)0.8 +#define ATTN_IDLE (float)2 +#define ATTN_STATIC (float)1.25 + +// 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.0 + +// plats +#define PLAT_LOW_TRIGGER 1 + +// Trains +#define SF_TRAIN_WAIT_RETRIGGER 1 +#define SF_TRAIN_PASSABLE 8 // Train is not solid -- used to make water trains + +// 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!) +}; + + +typedef int func_t; +typedef int string_t; + +typedef unsigned char byte; +typedef unsigned short word; +#define _DEF_BYTE_ + +#undef true +#undef false + +#ifndef __cplusplus +typedef enum {false, true} qboolean; +#else +typedef int qboolean; +#endif + +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 + diff --git a/src/common/crc.h b/src/common/crc.h index 1c17d62..f2f69ab 100644 --- a/src/common/crc.h +++ b/src/common/crc.h @@ -1,52 +1,52 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -/* crc.h */ -#ifndef CRC_H -#define CRC_H -#ifdef _WIN32 -#pragma once -#endif - -// MD5 Hash -typedef struct -{ - unsigned int buf[4]; - unsigned int bits[2]; - unsigned char in[64]; -} MD5Context_t; - - -typedef unsigned long CRC32_t; -void CRC32_Init(CRC32_t *pulCRC); -CRC32_t CRC32_Final(CRC32_t pulCRC); -void CRC32_ProcessBuffer(CRC32_t *pulCRC, void *p, int len); -void CRC32_ProcessByte(CRC32_t *pulCRC, unsigned char ch); -int CRC_File(CRC32_t *crcvalue, char *pszFileName); - -unsigned char COM_BlockSequenceCRCByte (unsigned char *base, int length, int sequence); - -void MD5Init(MD5Context_t *context); -void MD5Update(MD5Context_t *context, unsigned char const *buf, - unsigned int len); -void MD5Final(unsigned char digest[16], MD5Context_t *context); -void Transform(unsigned int buf[4], unsigned int const in[16]); - -int MD5_Hash_File(unsigned char digest[16], char *pszFileName, int bUsefopen, int bSeed, unsigned int seed[4]); -char *MD5_Print(unsigned char hash[16]); -int MD5_Hash_CachedFile(unsigned char digest[16], unsigned char *pCache, int nFileSize, int bSeed, unsigned int seed[4]); - -int CRC_MapFile(CRC32_t *crcvalue, char *pszFileName); - -#endif +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +/* crc.h */ +#ifndef CRC_H +#define CRC_H +#ifdef _WIN32 +#pragma once +#endif + +// MD5 Hash +typedef struct +{ + unsigned int buf[4]; + unsigned int bits[2]; + unsigned char in[64]; +} MD5Context_t; + + +typedef unsigned long CRC32_t; +void CRC32_Init(CRC32_t *pulCRC); +CRC32_t CRC32_Final(CRC32_t pulCRC); +void CRC32_ProcessBuffer(CRC32_t *pulCRC, void *p, int len); +void CRC32_ProcessByte(CRC32_t *pulCRC, unsigned char ch); +int CRC_File(CRC32_t *crcvalue, char *pszFileName); + +unsigned char COM_BlockSequenceCRCByte (unsigned char *base, int length, int sequence); + +void MD5Init(MD5Context_t *context); +void MD5Update(MD5Context_t *context, unsigned char const *buf, + unsigned int len); +void MD5Final(unsigned char digest[16], MD5Context_t *context); +void Transform(unsigned int buf[4], unsigned int const in[16]); + +int MD5_Hash_File(unsigned char digest[16], char *pszFileName, int bUsefopen, int bSeed, unsigned int seed[4]); +char *MD5_Print(unsigned char hash[16]); +int MD5_Hash_CachedFile(unsigned char digest[16], unsigned char *pCache, int nFileSize, int bSeed, unsigned int seed[4]); + +int CRC_MapFile(CRC32_t *crcvalue, char *pszFileName); + +#endif diff --git a/src/common/cvardef.h b/src/common/cvardef.h index 2e23406..2d599a3 100644 --- a/src/common/cvardef.h +++ b/src/common/cvardef.h @@ -1,36 +1,36 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -#ifndef CVARDEF_H -#define CVARDEF_H - -#define FCVAR_ARCHIVE (1<<0) // set to cause it to be saved to vars.rc -#define FCVAR_USERINFO (1<<1) // changes the client's info string -#define FCVAR_SERVER (1<<2) // notifies players when changed -#define FCVAR_EXTDLL (1<<3) // defined by external DLL -#define FCVAR_CLIENTDLL (1<<4) // defined by the client dll -#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_PRINTABLEONLY (1<<7) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). -#define FCVAR_UNLOGGED (1<<8) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log - -typedef struct cvar_s -{ - char *name; - char *string; - int flags; - float value; - struct cvar_s *next; -} cvar_t; -#endif +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +#ifndef CVARDEF_H +#define CVARDEF_H + +#define FCVAR_ARCHIVE (1<<0) // set to cause it to be saved to vars.rc +#define FCVAR_USERINFO (1<<1) // changes the client's info string +#define FCVAR_SERVER (1<<2) // notifies players when changed +#define FCVAR_EXTDLL (1<<3) // defined by external DLL +#define FCVAR_CLIENTDLL (1<<4) // defined by the client dll +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_PRINTABLEONLY (1<<7) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). +#define FCVAR_UNLOGGED (1<<8) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log + +typedef struct cvar_s +{ + char *name; + char *string; + int flags; + float value; + struct cvar_s *next; +} cvar_t; +#endif diff --git a/src/common/entity_state.h b/src/common/entity_state.h index ff84655..ee5c101 100644 --- a/src/common/entity_state.h +++ b/src/common/entity_state.h @@ -1,193 +1,193 @@ -/*** -* -* Copyright (c) 1999, 2000 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. -* -****/ -#if !defined( ENTITY_STATEH ) -#define ENTITY_STATEH -#ifdef _WIN32 -#pragma once -#endif - -// For entityType below -#define ENTITY_NORMAL (1<<0) -#define ENTITY_BEAM (1<<1) - -// Entity state is used for the baseline and for delta compression of a packet of -// entities that is sent to a client. -typedef struct entity_state_s entity_state_t; - -struct entity_state_s -{ -// Fields which are filled in by routines outside of delta compression - int entityType; - // Index into cl_entities array for this entity. - int number; - float msg_time; - - // Message number last time the player/entity state was updated. - int messagenum; - - // Fields which can be transitted and reconstructed over the network stream - vec3_t origin; - vec3_t angles; - - int modelindex; - int sequence; - float frame; - int colormap; - short skin; - short solid; - int effects; - float scale; - - byte eflags; - - // Render information - int rendermode; - int renderamt; - color24 rendercolor; - int renderfx; - - int movetype; - float animtime; - float framerate; - int body; - byte controller[4]; - byte blending[4]; - vec3_t velocity; - - // Send bbox down to client for use during prediction. - vec3_t mins; - vec3_t maxs; - - int aiment; - // If owned by a player, the index of that player ( for projectiles ). - int owner; - - // Friction, for prediction. - float friction; - // Gravity multiplier - float gravity; - -// PLAYER SPECIFIC - int team; - int playerclass; - int health; - qboolean spectator; - int weaponmodel; - int gaitsequence; - // If standing on conveyor, e.g. - vec3_t basevelocity; - // Use the crouched hull, or the regular player hull. - int usehull; - // Latched buttons last time state updated. - int oldbuttons; - // -1 = in air, else pmove entity number - int onground; - int iStepLeft; - // How fast we are falling - float flFallVelocity; - - float fov; - int weaponanim; - - // Parametric movement overrides - vec3_t startpos; - vec3_t endpos; - float impacttime; - float starttime; - - // For mods - int iuser1; - int iuser2; - int iuser3; - int iuser4; - float fuser1; - float fuser2; - float fuser3; - float fuser4; - vec3_t vuser1; - vec3_t vuser2; - vec3_t vuser3; - vec3_t vuser4; -}; - -#include "pm_info.h" - -typedef struct clientdata_s -{ - vec3_t origin; - vec3_t velocity; - - int viewmodel; - vec3_t punchangle; - int flags; - int waterlevel; - int watertype; - vec3_t view_ofs; - float health; - - int bInDuck; - - int weapons; // remove? - - int flTimeStepSound; - int flDuckTime; - int flSwimTime; - int waterjumptime; - - float maxspeed; - - float fov; - int weaponanim; - - int m_iId; - int ammo_shells; - int ammo_nails; - int ammo_cells; - int ammo_rockets; - float m_flNextAttack; - - int tfstate; - - int pushmsec; - - int deadflag; - - char physinfo[ MAX_PHYSINFO_STRING ]; - - // For mods - int iuser1; - int iuser2; - int iuser3; - int iuser4; - float fuser1; - float fuser2; - float fuser3; - float fuser4; - vec3_t vuser1; - vec3_t vuser2; - vec3_t vuser3; - vec3_t vuser4; -} clientdata_t; - -#include "weaponinfo.h" - -typedef struct local_state_s -{ - entity_state_t playerstate; - clientdata_t client; - weapon_data_t weapondata[ 32 ]; -} local_state_t; - +/*** +* +* Copyright (c) 1999, 2000 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. +* +****/ +#if !defined( ENTITY_STATEH ) +#define ENTITY_STATEH +#ifdef _WIN32 +#pragma once +#endif + +// For entityType below +#define ENTITY_NORMAL (1<<0) +#define ENTITY_BEAM (1<<1) + +// Entity state is used for the baseline and for delta compression of a packet of +// entities that is sent to a client. +typedef struct entity_state_s entity_state_t; + +struct entity_state_s +{ +// Fields which are filled in by routines outside of delta compression + int entityType; + // Index into cl_entities array for this entity. + int number; + float msg_time; + + // Message number last time the player/entity state was updated. + int messagenum; + + // Fields which can be transitted and reconstructed over the network stream + vec3_t origin; + vec3_t angles; + + int modelindex; + int sequence; + float frame; + int colormap; + short skin; + short solid; + int effects; + float scale; + + byte eflags; + + // Render information + int rendermode; + int renderamt; + color24 rendercolor; + int renderfx; + + int movetype; + float animtime; + float framerate; + int body; + byte controller[4]; + byte blending[4]; + vec3_t velocity; + + // Send bbox down to client for use during prediction. + vec3_t mins; + vec3_t maxs; + + int aiment; + // If owned by a player, the index of that player ( for projectiles ). + int owner; + + // Friction, for prediction. + float friction; + // Gravity multiplier + float gravity; + +// PLAYER SPECIFIC + int team; + int playerclass; + int health; + qboolean spectator; + int weaponmodel; + int gaitsequence; + // If standing on conveyor, e.g. + vec3_t basevelocity; + // Use the crouched hull, or the regular player hull. + int usehull; + // Latched buttons last time state updated. + int oldbuttons; + // -1 = in air, else pmove entity number + int onground; + int iStepLeft; + // How fast we are falling + float flFallVelocity; + + float fov; + int weaponanim; + + // Parametric movement overrides + vec3_t startpos; + vec3_t endpos; + float impacttime; + float starttime; + + // For mods + int iuser1; + int iuser2; + int iuser3; + int iuser4; + float fuser1; + float fuser2; + float fuser3; + float fuser4; + vec3_t vuser1; + vec3_t vuser2; + vec3_t vuser3; + vec3_t vuser4; +}; + +#include "pm_info.h" + +typedef struct clientdata_s +{ + vec3_t origin; + vec3_t velocity; + + int viewmodel; + vec3_t punchangle; + int flags; + int waterlevel; + int watertype; + vec3_t view_ofs; + float health; + + int bInDuck; + + int weapons; // remove? + + int flTimeStepSound; + int flDuckTime; + int flSwimTime; + int waterjumptime; + + float maxspeed; + + float fov; + int weaponanim; + + int m_iId; + int ammo_shells; + int ammo_nails; + int ammo_cells; + int ammo_rockets; + float m_flNextAttack; + + int tfstate; + + int pushmsec; + + int deadflag; + + char physinfo[ MAX_PHYSINFO_STRING ]; + + // For mods + int iuser1; + int iuser2; + int iuser3; + int iuser4; + float fuser1; + float fuser2; + float fuser3; + float fuser4; + vec3_t vuser1; + vec3_t vuser2; + vec3_t vuser3; + vec3_t vuser4; +} clientdata_t; + +#include "weaponinfo.h" + +typedef struct local_state_s +{ + entity_state_t playerstate; + clientdata_t client; + weapon_data_t weapondata[ 32 ]; +} local_state_t; + #endif // !ENTITY_STATEH \ No newline at end of file diff --git a/src/common/event_flags.h b/src/common/event_flags.h index 8e489a1..166ff68 100644 --- a/src/common/event_flags.h +++ b/src/common/event_flags.h @@ -1,47 +1,47 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -#if !defined( EVENT_FLAGSH ) -#define EVENT_FLAGSH -#ifdef _WIN32 -#pragma once -#endif - -// Skip local host for event send. -#define FEV_NOTHOST (1<<0) - -// Send the event reliably. You must specify the origin and angles and use -// PLAYBACK_EVENT_FULL for this to work correctly on the server for anything -// that depends on the event origin/angles. I.e., the origin/angles are not -// taken from the invoking edict for reliable events. -#define FEV_RELIABLE (1<<1) - -// Don't restrict to PAS/PVS, send this event to _everybody_ on the server ( useful for stopping CHAN_STATIC -// sounds started by client event when client is not in PVS anymore ( hwguy in TFC e.g. ). -#define FEV_GLOBAL (1<<2) - -// If this client already has one of these events in its queue, just update the event instead of sending it as a duplicate -// -#define FEV_UPDATE (1<<3) - -// Only send to entity specified as the invoker -#define FEV_HOSTONLY (1<<4) - -// Only send if the event was created on the server. -#define FEV_SERVER (1<<5) - -// Only issue event client side ( from shared code ) -#define FEV_CLIENT (1<<6) - +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +#if !defined( EVENT_FLAGSH ) +#define EVENT_FLAGSH +#ifdef _WIN32 +#pragma once +#endif + +// Skip local host for event send. +#define FEV_NOTHOST (1<<0) + +// Send the event reliably. You must specify the origin and angles and use +// PLAYBACK_EVENT_FULL for this to work correctly on the server for anything +// that depends on the event origin/angles. I.e., the origin/angles are not +// taken from the invoking edict for reliable events. +#define FEV_RELIABLE (1<<1) + +// Don't restrict to PAS/PVS, send this event to _everybody_ on the server ( useful for stopping CHAN_STATIC +// sounds started by client event when client is not in PVS anymore ( hwguy in TFC e.g. ). +#define FEV_GLOBAL (1<<2) + +// If this client already has one of these events in its queue, just update the event instead of sending it as a duplicate +// +#define FEV_UPDATE (1<<3) + +// Only send to entity specified as the invoker +#define FEV_HOSTONLY (1<<4) + +// Only send if the event was created on the server. +#define FEV_SERVER (1<<5) + +// Only issue event client side ( from shared code ) +#define FEV_CLIENT (1<<6) + #endif \ No newline at end of file diff --git a/src/common/in_buttons.h b/src/common/in_buttons.h index 8cf40ec..1894b0a 100644 --- a/src/common/in_buttons.h +++ b/src/common/in_buttons.h @@ -1,38 +1,38 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -#ifndef IN_BUTTONS_H -#define IN_BUTTONS_H -#ifdef _WIN32 -#pragma once -#endif - -#define IN_ATTACK (1 << 0) -#define IN_JUMP (1 << 1) -#define IN_DUCK (1 << 2) -#define IN_FORWARD (1 << 3) -#define IN_BACK (1 << 4) -#define IN_USE (1 << 5) -#define IN_CANCEL (1 << 6) -#define IN_LEFT (1 << 7) -#define IN_RIGHT (1 << 8) -#define IN_MOVELEFT (1 << 9) -#define IN_MOVERIGHT (1 << 10) -#define IN_ATTACK2 (1 << 11) -#define IN_RUN (1 << 12) -#define IN_RELOAD (1 << 13) -#define IN_ALT1 (1 << 14) -#define IN_SCORE (1 << 15) // Used by client.dll for when scoreboard is held down - -#endif // IN_BUTTONS_H +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +#ifndef IN_BUTTONS_H +#define IN_BUTTONS_H +#ifdef _WIN32 +#pragma once +#endif + +#define IN_ATTACK (1 << 0) +#define IN_JUMP (1 << 1) +#define IN_DUCK (1 << 2) +#define IN_FORWARD (1 << 3) +#define IN_BACK (1 << 4) +#define IN_USE (1 << 5) +#define IN_CANCEL (1 << 6) +#define IN_LEFT (1 << 7) +#define IN_RIGHT (1 << 8) +#define IN_MOVELEFT (1 << 9) +#define IN_MOVERIGHT (1 << 10) +#define IN_ATTACK2 (1 << 11) +#define IN_RUN (1 << 12) +#define IN_RELOAD (1 << 13) +#define IN_ALT1 (1 << 14) +#define IN_SCORE (1 << 15) // Used by client.dll for when scoreboard is held down + +#endif // IN_BUTTONS_H diff --git a/src/common/nowin.h b/src/common/nowin.h index e9644c8..ccfb7e7 100644 --- a/src/common/nowin.h +++ b/src/common/nowin.h @@ -1,15 +1,15 @@ -//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -#ifndef INC_NOWIN_H -#define INC_NOWIN_H -#ifndef _WIN32 - -#include - -#endif //!_WIN32 +//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#ifndef INC_NOWIN_H +#define INC_NOWIN_H +#ifndef _WIN32 + +#include + +#endif //!_WIN32 #endif //INC_NOWIN_H \ No newline at end of file diff --git a/src/common/studio_event.h b/src/common/studio_event.h index ba9d182..8fae6f5 100644 --- a/src/common/studio_event.h +++ b/src/common/studio_event.h @@ -1,29 +1,29 @@ -/*** -* -* Copyright (c) 1999, 2000, 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. -* -****/ -#if !defined( STUDIO_EVENTH ) -#define STUDIO_EVENTH -#ifdef _WIN32 -#pragma once -#endif - -typedef struct mstudioevent_s -{ - int frame; - int event; - int type; - char options[64]; -} mstudioevent_t; - -#endif // STUDIO_EVENTH +/*** +* +* Copyright (c) 1999, 2000, 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. +* +****/ +#if !defined( STUDIO_EVENTH ) +#define STUDIO_EVENTH +#ifdef _WIN32 +#pragma once +#endif + +typedef struct mstudioevent_s +{ + int frame; + int event; + int type; + char options[64]; +} mstudioevent_t; + +#endif // STUDIO_EVENTH diff --git a/src/dlls/.gitignore b/src/dlls/.gitignore index 2ca625c..b42cbff 100644 --- a/src/dlls/.gitignore +++ b/src/dlls/.gitignore @@ -1,5 +1,5 @@ -msgs/ -opt.*/ -debug.*/ -*.o +msgs/ +opt.*/ +debug.*/ +*.o *.so \ No newline at end of file diff --git a/src/dlls/AI_BaseNPC_Schedule.cpp b/src/dlls/AI_BaseNPC_Schedule.cpp index 687a5a7..de53898 100644 --- a/src/dlls/AI_BaseNPC_Schedule.cpp +++ b/src/dlls/AI_BaseNPC_Schedule.cpp @@ -1,1392 +1,1392 @@ -/*** -* -* 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// schedule.cpp - functions and data pertaining to the -// monsters' AI scheduling system. -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "animation.h" -#include "nodes.h" -#include "defaultai.h" - -extern CGraph WorldGraph; - -//========================================================= -// FHaveSchedule - Returns TRUE if monster's m_pSchedule -// is anything other than NULL. -//========================================================= -BOOL CMBaseMonster :: FHaveSchedule( void ) -{ - if ( m_pSchedule == NULL ) - { - return FALSE; - } - - return TRUE; -} - -//========================================================= -// ClearSchedule - blanks out the caller's schedule pointer -// and index. -//========================================================= -void CMBaseMonster :: ClearSchedule( void ) -{ - m_iTaskStatus = TASKSTATUS_NEW; - m_pSchedule = NULL; - m_iScheduleIndex = 0; -} - -//========================================================= -// FScheduleDone - Returns TRUE if the caller is on the -// last task in the schedule -//========================================================= -BOOL CMBaseMonster :: FScheduleDone ( void ) -{ - ASSERT( m_pSchedule != NULL ); - - if ( m_iScheduleIndex == m_pSchedule->cTasks ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// ChangeSchedule - replaces the monster's schedule pointer -// with the passed pointer, and sets the ScheduleIndex back -// to 0 -//========================================================= -void CMBaseMonster :: ChangeSchedule ( Schedule_t *pNewSchedule ) -{ - ASSERT( pNewSchedule != NULL ); - - m_pSchedule = pNewSchedule; - m_iScheduleIndex = 0; - m_iTaskStatus = TASKSTATUS_NEW; - m_afConditions = 0;// clear all of the conditions - m_failSchedule = SCHED_NONE; - -#if _DEBUG - if ( !ScheduleFromName( pNewSchedule->pName ) ) - { - ALERT( at_console, "Schedule %s not in table!!!\n", pNewSchedule->pName ); - } -#endif - -// this is very useful code if you can isolate a test case in a level with a single monster. It will notify -// you of every schedule selection the monster makes. -#if 0 - if ( strcmp( STRING(pev->model), "models/hgrunt.mdl" ) == 0 ) - { - Task_t *pTask = GetTask(); - - if ( pTask ) - { - const char *pName = NULL; - - if ( m_pSchedule ) - { - pName = m_pSchedule->pName; - } - else - { - pName = "No Schedule"; - } - - if ( !pName ) - { - pName = "Unknown"; - } - - ALERT( at_aiconsole, "%s: picked schedule %s\n", STRING( pev->classname ), pName ); - } - } -#endif// 0 - -} - -//========================================================= -// NextScheduledTask - increments the ScheduleIndex -//========================================================= -void CMBaseMonster :: NextScheduledTask ( void ) -{ - ASSERT( m_pSchedule != NULL ); - - m_iTaskStatus = TASKSTATUS_NEW; - m_iScheduleIndex++; - - if ( FScheduleDone() ) - { - // just completed last task in schedule, so make it invalid by clearing it. - SetConditions( bits_COND_SCHEDULE_DONE ); - //ClearSchedule(); - } -} - -//========================================================= -// IScheduleFlags - returns an integer with all Conditions -// bits that are currently set and also set in the current -// schedule's Interrupt mask. -//========================================================= -int CMBaseMonster :: IScheduleFlags ( void ) -{ - if( !m_pSchedule ) - { - return 0; - } - - // strip off all bits excepts the ones capable of breaking this schedule. - return m_afConditions & m_pSchedule->iInterruptMask; -} - -//========================================================= -// FScheduleValid - returns TRUE as long as the current -// schedule is still the proper schedule to be executing, -// taking into account all conditions -//========================================================= -BOOL CMBaseMonster :: FScheduleValid ( void ) -{ - if ( m_pSchedule == NULL ) - { - // schedule is empty, and therefore not valid. - return FALSE; - } - - if ( HasConditions( m_pSchedule->iInterruptMask | bits_COND_SCHEDULE_DONE | bits_COND_TASK_FAILED ) ) - { -#ifdef DEBUG - if ( HasConditions ( bits_COND_TASK_FAILED ) && m_failSchedule == SCHED_NONE ) - { - // fail! Send a visual indicator. - ALERT ( at_aiconsole, "Schedule: %s Failed\n", m_pSchedule->pName ); - -/*jlb spark - Vector tmp = pev->origin; - tmp.z = pev->absmax.z + 16; - UTIL_Sparks( tmp ); -jlb */ - } -#endif // DEBUG - - // some condition has interrupted the schedule, or the schedule is done - return FALSE; - } - - return TRUE; -} - -//========================================================= -// MaintainSchedule - does all the per-think schedule maintenance. -// ensures that the monster leaves this function with a valid -// schedule! -//========================================================= -void CMBaseMonster :: MaintainSchedule ( void ) -{ - Schedule_t *pNewSchedule; - int i; - - // UNDONE: Tune/fix this 10... This is just here so infinite loops are impossible - for ( i = 0; i < 10; i++ ) - { - if ( m_pSchedule != NULL && TaskIsComplete() ) - { - NextScheduledTask(); - } - - // validate existing schedule - if ( !FScheduleValid() || m_MonsterState != m_IdealMonsterState ) - { - // if we come into this block of code, the schedule is going to have to be changed. - // if the previous schedule was interrupted by a condition, GetIdealState will be - // called. Else, a schedule finished normally. - - // Notify the monster that his schedule is changing - ScheduleChange(); - - // Call GetIdealState if we're not dead and one or more of the following... - // - in COMBAT state with no enemy (it died?) - // - conditions bits (excluding SCHEDULE_DONE) indicate interruption, - // - schedule is done but schedule indicates it wants GetIdealState called - // after successful completion (by setting bits_COND_SCHEDULE_DONE in iInterruptMask) - // DEAD & SCRIPT are not suggestions, they are commands! - if ( m_IdealMonsterState != MONSTERSTATE_DEAD && - (m_IdealMonsterState != MONSTERSTATE_SCRIPT || m_IdealMonsterState == m_MonsterState) ) - { - if ( (m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) || - (m_pSchedule && (m_pSchedule->iInterruptMask & bits_COND_SCHEDULE_DONE)) || - ((m_MonsterState == MONSTERSTATE_COMBAT) && (m_hEnemy == NULL)) ) - { - GetIdealState(); - } - } - if ( HasConditions( bits_COND_TASK_FAILED ) && m_MonsterState == m_IdealMonsterState ) - { - if ( m_failSchedule != SCHED_NONE ) - pNewSchedule = GetScheduleOfType( m_failSchedule ); - else - pNewSchedule = GetScheduleOfType( SCHED_FAIL ); - // schedule was invalid because the current task failed to start or complete - ALERT ( at_aiconsole, "Schedule Failed at %d!\n", m_iScheduleIndex ); - ChangeSchedule( pNewSchedule ); - } - else - { - SetState( m_IdealMonsterState ); - if ( m_MonsterState == MONSTERSTATE_SCRIPT || m_MonsterState == MONSTERSTATE_DEAD ) - pNewSchedule = CMBaseMonster::GetSchedule(); - else - pNewSchedule = GetSchedule(); - ChangeSchedule( pNewSchedule ); - } - } - - if ( m_iTaskStatus == TASKSTATUS_NEW ) - { - Task_t *pTask = GetTask(); - ASSERT( pTask != NULL ); - TaskBegin(); - StartTask( pTask ); - } - - // UNDONE: Twice?!!! - if ( m_Activity != m_IdealActivity ) - { - SetActivity ( m_IdealActivity ); - } - - if ( !TaskIsComplete() && m_iTaskStatus != TASKSTATUS_NEW ) - break; - } - - if ( TaskIsRunning() ) - { - Task_t *pTask = GetTask(); - ASSERT( pTask != NULL ); - RunTask( pTask ); - } - - // UNDONE: We have to do this so that we have an animation set to blend to if RunTask changes the animation - // RunTask() will always change animations at the end of a script! - // Don't do this twice - if ( m_Activity != m_IdealActivity ) - { - SetActivity ( m_IdealActivity ); - } -} - -//========================================================= -// RunTask -//========================================================= -void CMBaseMonster :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_TURN_RIGHT: - case TASK_TURN_LEFT: - { - ChangeYaw( pev->yaw_speed ); - - if ( FacingIdeal() ) - { - TaskComplete(); - } - break; - } - - case TASK_PLAY_SEQUENCE_FACE_ENEMY: - case TASK_PLAY_SEQUENCE_FACE_TARGET: - { - edict_t *pTarget; - - if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET ) - pTarget = m_hTargetEnt; - else - pTarget = m_hEnemy; - if ( pTarget ) - { - pev->ideal_yaw = UTIL_VecToYaw( pTarget->v.origin - pev->origin ); - ChangeYaw( pev->yaw_speed ); - } - if ( m_fSequenceFinished ) - TaskComplete(); - } - break; - - case TASK_PLAY_SEQUENCE: - case TASK_PLAY_ACTIVE_IDLE: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - break; - } - - - case TASK_FACE_ENEMY: - { - MakeIdealYaw( m_vecEnemyLKP ); - - ChangeYaw( pev->yaw_speed ); - - if ( FacingIdeal() ) - { - TaskComplete(); - } - break; - } - case TASK_FACE_HINTNODE: - case TASK_FACE_LASTPOSITION: - case TASK_FACE_TARGET: - case TASK_FACE_IDEAL: - case TASK_FACE_ROUTE: - { - ChangeYaw( pev->yaw_speed ); - - if ( FacingIdeal() ) - { - TaskComplete(); - } - break; - } - case TASK_WAIT_PVS: - { - if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) ) - { - TaskComplete(); - } - break; - } - case TASK_WAIT_INDEFINITE: - { - // don't do anything. - break; - } - case TASK_WAIT: - case TASK_WAIT_RANDOM: - { - if ( gpGlobals->time >= m_flWaitFinished ) - { - TaskComplete(); - } - break; - } - case TASK_WAIT_FACE_ENEMY: - { - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if ( gpGlobals->time >= m_flWaitFinished ) - { - TaskComplete(); - } - break; - } - case TASK_MOVE_TO_TARGET_RANGE: - { - float distance; - - if ( m_hTargetEnt == NULL ) - TaskFail(); - else - { - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - // Re-evaluate when you think your finished, or the target has moved too far - if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->v.origin).Length() > pTask->flData * 0.5 ) - { - m_vecMoveGoal = m_hTargetEnt->v.origin; - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - FRefreshRoute(); - } - - // Set the appropriate activity based on an overlapping range - // overlap the range to prevent oscillation - if ( distance < pTask->flData ) - { - TaskComplete(); - RouteClear(); // Stop moving - } - else if ( distance < 190 && m_movementActivity != ACT_WALK ) - m_movementActivity = ACT_WALK; - else if ( distance >= 270 && m_movementActivity != ACT_RUN ) - m_movementActivity = ACT_RUN; - } - - break; - } - case TASK_WAIT_FOR_MOVEMENT: - { - if (MovementIsComplete()) - { - TaskComplete(); - RouteClear(); // Stop moving - } - break; - } - case TASK_DIE: - { - if ( m_fSequenceFinished && pev->frame >= 255 ) - { - pev->deadflag = DEAD_DEAD; - - SetThink ( NULL ); - StopAnimation(); - - if ( !BBoxFlat() ) - { - // a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will - // block the player on a slope or stairs, the corpse is made nonsolid. -// pev->solid = SOLID_NOT; - UTIL_SetSize ( pev, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) ); - } - else // !!!HACKHACK - put monster in a thin, wide bounding box until we fix the solid type/bounding volume problem - UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + 1 ) ); - - if ( ShouldFadeOnDeath() ) - { - // this monster was created by a monstermaker... fade the corpse out. - SUB_StartFadeOut(); - } - } - break; - } - case TASK_RANGE_ATTACK1_NOTURN: - case TASK_MELEE_ATTACK1_NOTURN: - case TASK_MELEE_ATTACK2_NOTURN: - case TASK_RANGE_ATTACK2_NOTURN: - case TASK_RELOAD_NOTURN: - { - if ( m_fSequenceFinished ) - { - m_Activity = ACT_RESET; - TaskComplete(); - } - break; - } - case TASK_RANGE_ATTACK1: - case TASK_MELEE_ATTACK1: - case TASK_MELEE_ATTACK2: - case TASK_RANGE_ATTACK2: - case TASK_SPECIAL_ATTACK1: - case TASK_SPECIAL_ATTACK2: - case TASK_RELOAD: - { - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw ( pev->yaw_speed ); - - if ( m_fSequenceFinished ) - { - m_Activity = ACT_RESET; - TaskComplete(); - } - break; - } - case TASK_SMALL_FLINCH: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - } - break; - } -} - -//========================================================= -// SetTurnActivity - measures the difference between the way -// the monster is facing and determines whether or not to -// select one of the 180 turn animations. -//========================================================= -void CMBaseMonster :: SetTurnActivity ( void ) -{ - float flYD; - flYD = FlYawDiff(); - - if ( flYD <= -45 && LookupActivity ( ACT_TURN_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) - {// big right turn - m_IdealActivity = ACT_TURN_RIGHT; - } - else if ( flYD > 45 && LookupActivity ( ACT_TURN_LEFT ) != ACTIVITY_NOT_AVAILABLE ) - {// big left turn - m_IdealActivity = ACT_TURN_LEFT; - } -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. -//========================================================= -void CMBaseMonster :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_TURN_RIGHT: - { - float flCurrentYaw; - - flCurrentYaw = UTIL_AngleMod( pev->angles.y ); - pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw - pTask->flData ); - SetTurnActivity(); - break; - } - case TASK_TURN_LEFT: - { - float flCurrentYaw; - - flCurrentYaw = UTIL_AngleMod( pev->angles.y ); - pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw + pTask->flData ); - SetTurnActivity(); - break; - } - case TASK_REMEMBER: - { - Remember ( (int)pTask->flData ); - TaskComplete(); - break; - } - case TASK_FORGET: - { - Forget ( (int)pTask->flData ); - TaskComplete(); - break; - } - case TASK_FIND_HINTNODE: - { - m_iHintNode = FindHintNode(); - - if ( m_iHintNode != NO_NODE ) - { - TaskComplete(); - } - else - { - TaskFail(); - } - break; - } - case TASK_STORE_LASTPOSITION: - { - m_vecLastPosition = pev->origin; - TaskComplete(); - break; - } - case TASK_CLEAR_LASTPOSITION: - { - m_vecLastPosition = g_vecZero; - TaskComplete(); - break; - } - case TASK_CLEAR_HINTNODE: - { - m_iHintNode = NO_NODE; - TaskComplete(); - break; - } - case TASK_STOP_MOVING: - { - if ( m_IdealActivity == m_movementActivity ) - { - m_IdealActivity = GetStoppedActivity(); - } - - RouteClear(); - TaskComplete(); - break; - } - case TASK_PLAY_SEQUENCE_FACE_ENEMY: - case TASK_PLAY_SEQUENCE_FACE_TARGET: - case TASK_PLAY_SEQUENCE: - { - m_IdealActivity = ( Activity )( int )pTask->flData; - break; - } - case TASK_PLAY_ACTIVE_IDLE: - { - // monsters verify that they have a sequence for the node's activity BEFORE - // moving towards the node, so it's ok to just set the activity without checking here. - m_IdealActivity = ( Activity )WorldGraph.m_pNodes[ m_iHintNode ].m_sHintActivity; - break; - } - case TASK_SET_SCHEDULE: - { - Schedule_t *pNewSchedule; - - pNewSchedule = GetScheduleOfType( (int)pTask->flData ); - - if ( pNewSchedule ) - { - ChangeSchedule( pNewSchedule ); - } - else - { - TaskFail(); - } - - break; - } - case TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY: - { - if ( m_hEnemy == NULL ) - { - TaskFail(); - return; - } - - if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, 0, pTask->flData ) ) - { - // try for cover farther than the FLData from the schedule. - TaskComplete(); - } - else - { - // no coverwhatsoever. - TaskFail(); - } - break; - } - case TASK_FIND_FAR_NODE_COVER_FROM_ENEMY: - { - if ( m_hEnemy == NULL ) - { - TaskFail(); - return; - } - - if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, pTask->flData, CoverRadius() ) ) - { - // try for cover farther than the FLData from the schedule. - TaskComplete(); - } - else - { - // no coverwhatsoever. - TaskFail(); - } - break; - } - case TASK_FIND_NODE_COVER_FROM_ENEMY: - { - if ( m_hEnemy == NULL ) - { - TaskFail(); - return; - } - - if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, 0, CoverRadius() ) ) - { - // try for cover farther than the FLData from the schedule. - TaskComplete(); - } - else - { - // no coverwhatsoever. - TaskFail(); - } - break; - } - case TASK_FIND_COVER_FROM_ENEMY: - { - entvars_t *pevCover; - - if ( m_hEnemy == NULL ) - { - // Find cover from self if no enemy available - pevCover = pev; -// TaskFail(); -// return; - } - else - { - edict_t *pEdict = m_hEnemy; - pevCover = VARS(pEdict); - } - - if ( FindLateralCover( pevCover->origin, pevCover->view_ofs ) ) - { - // try lateral first - m_flMoveWaitFinished = gpGlobals->time + pTask->flData; - TaskComplete(); - } - else if ( FindCover( pevCover->origin, pevCover->view_ofs, 0, CoverRadius() ) ) - { - // then try for plain ole cover - m_flMoveWaitFinished = gpGlobals->time + pTask->flData; - TaskComplete(); - } - else - { - // no coverwhatsoever. - TaskFail(); - } - break; - } - case TASK_FIND_COVER_FROM_ORIGIN: - { - if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) - { - // then try for plain ole cover - m_flMoveWaitFinished = gpGlobals->time + pTask->flData; - TaskComplete(); - } - else - { - // no cover! - TaskFail(); - } - } - break; - case TASK_FIND_COVER_FROM_BEST_SOUND: - { - { - // no coverwhatsoever. or no sound in list - TaskFail(); - } - break; - } - case TASK_FACE_HINTNODE: - { - pev->ideal_yaw = WorldGraph.m_pNodes[ m_iHintNode ].m_flHintYaw; - SetTurnActivity(); - break; - } - - case TASK_FACE_LASTPOSITION: - MakeIdealYaw ( m_vecLastPosition ); - SetTurnActivity(); - break; - - case TASK_FACE_TARGET: - if ( m_hTargetEnt != NULL ) - { - MakeIdealYaw ( m_hTargetEnt->v.origin ); - SetTurnActivity(); - } - else - TaskFail(); - break; - case TASK_FACE_ENEMY: - { - MakeIdealYaw ( m_vecEnemyLKP ); - SetTurnActivity(); - break; - } - case TASK_FACE_IDEAL: - { - SetTurnActivity(); - break; - } - case TASK_FACE_ROUTE: - { - if (FRouteClear()) - { - ALERT(at_aiconsole, "No route to face!\n"); - TaskFail(); - } - else - { - MakeIdealYaw(m_Route[m_iRouteIndex].vecLocation); - SetTurnActivity(); - } - break; - } - case TASK_WAIT_PVS: - case TASK_WAIT_INDEFINITE: - { - // don't do anything. - break; - } - case TASK_WAIT: - case TASK_WAIT_FACE_ENEMY: - {// set a future time that tells us when the wait is over. - m_flWaitFinished = gpGlobals->time + pTask->flData; - break; - } - case TASK_WAIT_RANDOM: - {// set a future time that tells us when the wait is over. - m_flWaitFinished = gpGlobals->time + RANDOM_FLOAT( 0.1, pTask->flData ); - break; - } - case TASK_MOVE_TO_TARGET_RANGE: - { - if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) - TaskComplete(); - else - { - m_vecMoveGoal = m_hTargetEnt->v.origin; - if ( !MoveToTarget( ACT_WALK, 2 ) ) - TaskFail(); - } - break; - } - case TASK_RUN_TO_TARGET: - case TASK_WALK_TO_TARGET: - { - Activity newActivity; - - if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) - TaskComplete(); - else - { - if ( pTask->iTask == TASK_WALK_TO_TARGET ) - newActivity = ACT_WALK; - else - newActivity = ACT_RUN; - // This monster can't do this! - if ( LookupActivity( newActivity ) == ACTIVITY_NOT_AVAILABLE ) - TaskComplete(); - else - { - if ( m_hTargetEnt == NULL || !MoveToTarget( newActivity, 2 ) ) - { - TaskFail(); - ALERT( at_aiconsole, "%s Failed to reach target!!!\n", STRING(pev->classname) ); - RouteClear(); - } - } - } - TaskComplete(); - break; - } - case TASK_CLEAR_MOVE_WAIT: - { - m_flMoveWaitFinished = gpGlobals->time; - TaskComplete(); - break; - } - case TASK_MELEE_ATTACK1_NOTURN: - case TASK_MELEE_ATTACK1: - { - m_IdealActivity = ACT_MELEE_ATTACK1; - break; - } - case TASK_MELEE_ATTACK2_NOTURN: - case TASK_MELEE_ATTACK2: - { - m_IdealActivity = ACT_MELEE_ATTACK2; - break; - } - case TASK_RANGE_ATTACK1_NOTURN: - case TASK_RANGE_ATTACK1: - { - m_IdealActivity = ACT_RANGE_ATTACK1; - break; - } - case TASK_RANGE_ATTACK2_NOTURN: - case TASK_RANGE_ATTACK2: - { - m_IdealActivity = ACT_RANGE_ATTACK2; - break; - } - case TASK_RELOAD_NOTURN: - case TASK_RELOAD: - { - m_IdealActivity = ACT_RELOAD; - break; - } - case TASK_SPECIAL_ATTACK1: - { - m_IdealActivity = ACT_SPECIAL_ATTACK1; - break; - } - case TASK_SPECIAL_ATTACK2: - { - m_IdealActivity = ACT_SPECIAL_ATTACK2; - break; - } - case TASK_SET_ACTIVITY: - { - m_IdealActivity = (Activity)(int)pTask->flData; - TaskComplete(); - break; - } - case TASK_GET_PATH_TO_ENEMY_LKP: - { - if ( BuildRoute ( m_vecEnemyLKP, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, 0, (m_vecEnemyLKP - pev->origin).Length() )) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_ENEMY: - { - edict_t *pEnemy = m_hEnemy; - - if ( pEnemy == NULL ) - { - TaskFail(); - return; - } - - if ( BuildRoute ( pEnemy->v.origin, bits_MF_TO_ENEMY, pEnemy ) ) - { - TaskComplete(); - } - else if (BuildNearestRoute( pEnemy->v.origin, pEnemy->v.view_ofs, 0, (pEnemy->v.origin - pev->origin).Length() )) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_ENEMY_CORPSE: - { - UTIL_MakeVectors( pev->angles ); - if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 64, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else - { - ALERT ( at_aiconsole, "GetPathToEnemyCorpse failed!!\n" ); - TaskFail(); - } - } - break; - case TASK_GET_PATH_TO_SPOT: - { - edict_t *pPlayer = FIND_ENTITY_BY_CLASSNAME( NULL, "player" ); - if ( BuildRoute ( m_vecMoveGoal, bits_MF_TO_LOCATION, pPlayer ) ) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); - TaskFail(); - } - break; - } - - case TASK_GET_PATH_TO_TARGET: - { - RouteClear(); - if ( m_hTargetEnt != NULL && MoveToTarget( m_movementActivity, 1 ) ) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_HINTNODE:// for active idles! - { - if ( MoveToLocation( m_movementActivity, 2, WorldGraph.m_pNodes[ m_iHintNode ].m_vecOrigin ) ) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToHintNode failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_LASTPOSITION: - { - m_vecMoveGoal = m_vecLastPosition; - - if ( MoveToLocation( m_movementActivity, 2, m_vecMoveGoal ) ) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToLastPosition failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_BESTSOUND: - { - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToBestSound failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_BESTSCENT: - { - if ( m_hEnemy != NULL ) - { -/*jlb - m_hTargetEnt = m_hEnemy; - if ( MoveToTarget( m_movementActivity, 2 ) ) - { - TaskComplete(); - } - else -jlb*/ - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToBestScent failed!!\n" ); - TaskFail(); - } - } - break; - } - case TASK_RUN_PATH: - { - // UNDONE: This is in some default AI and some monsters can't run? -- walk instead? - if ( LookupActivity( ACT_RUN ) != ACTIVITY_NOT_AVAILABLE ) - { - m_movementActivity = ACT_RUN; - } - else - { - m_movementActivity = ACT_WALK; - } - TaskComplete(); - break; - } - case TASK_WALK_PATH: - { - if ( pev->movetype == MOVETYPE_FLY ) - { - m_movementActivity = ACT_FLY; - } - if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE ) - { - m_movementActivity = ACT_WALK; - } - else - { - m_movementActivity = ACT_RUN; - } - TaskComplete(); - break; - } - case TASK_STRAFE_PATH: - { - Vector2D vec2DirToPoint; - Vector2D vec2RightSide; - - // to start strafing, we have to first figure out if the target is on the left side or right side - UTIL_MakeVectors ( pev->angles ); - - vec2DirToPoint = ( m_Route[ 0 ].vecLocation - pev->origin ).Make2D().Normalize(); - vec2RightSide = gpGlobals->v_right.Make2D().Normalize(); - - if ( DotProduct ( vec2DirToPoint, vec2RightSide ) > 0 ) - { - // strafe right - m_movementActivity = ACT_STRAFE_RIGHT; - } - else - { - // strafe left - m_movementActivity = ACT_STRAFE_LEFT; - } - TaskComplete(); - break; - } - - - case TASK_WAIT_FOR_MOVEMENT: - { - if (FRouteClear()) - { - TaskComplete(); - } - break; - } - - case TASK_EAT: - { - Eat( pTask->flData ); - TaskComplete(); - break; - } - case TASK_SMALL_FLINCH: - { - m_IdealActivity = GetSmallFlinchActivity(); - break; - } - case TASK_DIE: - { - RouteClear(); - - m_IdealActivity = GetDeathActivity(); - - pev->deadflag = DEAD_DYING; - break; - } - case TASK_SOUND_WAKE: - { - AlertSound(); - TaskComplete(); - break; - } - case TASK_SOUND_DIE: - { - DeathSound(); - TaskComplete(); - break; - } - case TASK_SOUND_IDLE: - { - IdleSound(); - TaskComplete(); - break; - } - case TASK_SOUND_PAIN: - { - PainSound(); - TaskComplete(); - break; - } - case TASK_SOUND_DEATH: - { - DeathSound(); - TaskComplete(); - break; - } - case TASK_SOUND_ANGRY: - { - // sounds are complete as soon as we get here, cause we've already played them. - ALERT ( at_aiconsole, "SOUND\n" ); - TaskComplete(); - break; - } - - case TASK_SUGGEST_STATE: - { - m_IdealMonsterState = (MONSTERSTATE)(int)pTask->flData; - TaskComplete(); - break; - } - - case TASK_SET_FAIL_SCHEDULE: - m_failSchedule = (int)pTask->flData; - TaskComplete(); - break; - - case TASK_CLEAR_FAIL_SCHEDULE: - m_failSchedule = SCHED_NONE; - TaskComplete(); - break; - - default: - { - ALERT ( at_aiconsole, "No StartTask entry for %d\n", (SHARED_TASKS)pTask->iTask ); - break; - } - } -} - -//========================================================= -// GetTask - returns a pointer to the current -// scheduled task. NULL if there's a problem. -//========================================================= -Task_t *CMBaseMonster :: GetTask ( void ) -{ - if ( m_iScheduleIndex < 0 || m_iScheduleIndex >= m_pSchedule->cTasks ) - { - // m_iScheduleIndex is not within valid range for the monster's current schedule. - return NULL; - } - else - { - return &m_pSchedule->pTasklist[ m_iScheduleIndex ]; - } -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMBaseMonster :: GetSchedule ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_PRONE: - { - return GetScheduleOfType( SCHED_BARNACLE_VICTIM_GRAB ); - break; - } - case MONSTERSTATE_NONE: - { - ALERT ( at_aiconsole, "MONSTERSTATE IS NONE!\n" ); - break; - } - case MONSTERSTATE_IDLE: - { - if ( HasConditions ( bits_COND_HEAR_SOUND ) ) - { - return GetScheduleOfType( SCHED_ALERT_FACE ); - } - else if ( FRouteClear() ) - { - // no valid route! - return GetScheduleOfType( SCHED_IDLE_STAND ); - } - else - { - // valid route. Get moving - return GetScheduleOfType( SCHED_IDLE_WALK ); - } - break; - } - case MONSTERSTATE_ALERT: - { - if ( HasConditions( bits_COND_ENEMY_DEAD ) && LookupActivity( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE ) - { - return GetScheduleOfType ( SCHED_VICTORY_DANCE ); - } - - if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) - { - if ( fabs( FlYawDiff() ) < (1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction - { - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ORIGIN ); - } - else - { - return GetScheduleOfType( SCHED_ALERT_SMALL_FLINCH ); - } - } - - else if ( HasConditions ( bits_COND_HEAR_SOUND ) ) - { - return GetScheduleOfType( SCHED_ALERT_FACE ); - } - else - { - return GetScheduleOfType( SCHED_ALERT_STAND ); - } - break; - } - case MONSTERSTATE_COMBAT: - { - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // clear the current (dead) enemy and try to find another. - m_hEnemy = NULL; - - if ( GetEnemy() ) - { - ClearConditions( bits_COND_ENEMY_DEAD ); - return GetSchedule(); - } - else - { - SetState( MONSTERSTATE_ALERT ); - return GetSchedule(); - } - } - - if ( HasConditions(bits_COND_NEW_ENEMY) ) - { - return GetScheduleOfType ( SCHED_WAKE_ANGRY ); - } - else if (HasConditions(bits_COND_LIGHT_DAMAGE) && !HasMemory( bits_MEMORY_FLINCHED) ) - { - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - else if ( !HasConditions(bits_COND_SEE_ENEMY) ) - { - // we can't see the enemy - if ( !HasConditions(bits_COND_ENEMY_OCCLUDED) ) - { - // enemy is unseen, but not occluded! - // turn to face enemy - return GetScheduleOfType( SCHED_COMBAT_FACE ); - } - else - { - // chase! - return GetScheduleOfType( SCHED_CHASE_ENEMY ); - } - } - else - { - // we can see the enemy - if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) ) - { - return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); - } - if ( HasConditions(bits_COND_CAN_RANGE_ATTACK2) ) - { - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } - if ( HasConditions(bits_COND_CAN_MELEE_ATTACK1) ) - { - return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); - } - if ( HasConditions(bits_COND_CAN_MELEE_ATTACK2) ) - { - return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); - } - if ( !HasConditions(bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1) ) - { - // if we can see enemy but can't use either attack type, we must need to get closer to enemy - return GetScheduleOfType( SCHED_CHASE_ENEMY ); - } - else if ( !FacingIdeal() ) - { - //turn - return GetScheduleOfType( SCHED_COMBAT_FACE ); - } - else - { - ALERT ( at_aiconsole, "No suitable combat schedule!\n" ); - } - } - break; - } - case MONSTERSTATE_DEAD: - { - return GetScheduleOfType( SCHED_DIE ); - break; - } - default: - { - ALERT ( at_aiconsole, "Invalid State for GetSchedule!\n" ); - break; - } - } - - return &slError[ 0 ]; -} +/*** +* +* 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// schedule.cpp - functions and data pertaining to the +// monsters' AI scheduling system. +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "animation.h" +#include "nodes.h" +#include "defaultai.h" + +extern CGraph WorldGraph; + +//========================================================= +// FHaveSchedule - Returns TRUE if monster's m_pSchedule +// is anything other than NULL. +//========================================================= +BOOL CMBaseMonster :: FHaveSchedule( void ) +{ + if ( m_pSchedule == NULL ) + { + return FALSE; + } + + return TRUE; +} + +//========================================================= +// ClearSchedule - blanks out the caller's schedule pointer +// and index. +//========================================================= +void CMBaseMonster :: ClearSchedule( void ) +{ + m_iTaskStatus = TASKSTATUS_NEW; + m_pSchedule = NULL; + m_iScheduleIndex = 0; +} + +//========================================================= +// FScheduleDone - Returns TRUE if the caller is on the +// last task in the schedule +//========================================================= +BOOL CMBaseMonster :: FScheduleDone ( void ) +{ + ASSERT( m_pSchedule != NULL ); + + if ( m_iScheduleIndex == m_pSchedule->cTasks ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// ChangeSchedule - replaces the monster's schedule pointer +// with the passed pointer, and sets the ScheduleIndex back +// to 0 +//========================================================= +void CMBaseMonster :: ChangeSchedule ( Schedule_t *pNewSchedule ) +{ + ASSERT( pNewSchedule != NULL ); + + m_pSchedule = pNewSchedule; + m_iScheduleIndex = 0; + m_iTaskStatus = TASKSTATUS_NEW; + m_afConditions = 0;// clear all of the conditions + m_failSchedule = SCHED_NONE; + +#if _DEBUG + if ( !ScheduleFromName( pNewSchedule->pName ) ) + { + ALERT( at_console, "Schedule %s not in table!!!\n", pNewSchedule->pName ); + } +#endif + +// this is very useful code if you can isolate a test case in a level with a single monster. It will notify +// you of every schedule selection the monster makes. +#if 0 + if ( strcmp( STRING(pev->model), "models/hgrunt.mdl" ) == 0 ) + { + Task_t *pTask = GetTask(); + + if ( pTask ) + { + const char *pName = NULL; + + if ( m_pSchedule ) + { + pName = m_pSchedule->pName; + } + else + { + pName = "No Schedule"; + } + + if ( !pName ) + { + pName = "Unknown"; + } + + ALERT( at_aiconsole, "%s: picked schedule %s\n", STRING( pev->classname ), pName ); + } + } +#endif// 0 + +} + +//========================================================= +// NextScheduledTask - increments the ScheduleIndex +//========================================================= +void CMBaseMonster :: NextScheduledTask ( void ) +{ + ASSERT( m_pSchedule != NULL ); + + m_iTaskStatus = TASKSTATUS_NEW; + m_iScheduleIndex++; + + if ( FScheduleDone() ) + { + // just completed last task in schedule, so make it invalid by clearing it. + SetConditions( bits_COND_SCHEDULE_DONE ); + //ClearSchedule(); + } +} + +//========================================================= +// IScheduleFlags - returns an integer with all Conditions +// bits that are currently set and also set in the current +// schedule's Interrupt mask. +//========================================================= +int CMBaseMonster :: IScheduleFlags ( void ) +{ + if( !m_pSchedule ) + { + return 0; + } + + // strip off all bits excepts the ones capable of breaking this schedule. + return m_afConditions & m_pSchedule->iInterruptMask; +} + +//========================================================= +// FScheduleValid - returns TRUE as long as the current +// schedule is still the proper schedule to be executing, +// taking into account all conditions +//========================================================= +BOOL CMBaseMonster :: FScheduleValid ( void ) +{ + if ( m_pSchedule == NULL ) + { + // schedule is empty, and therefore not valid. + return FALSE; + } + + if ( HasConditions( m_pSchedule->iInterruptMask | bits_COND_SCHEDULE_DONE | bits_COND_TASK_FAILED ) ) + { +#ifdef DEBUG + if ( HasConditions ( bits_COND_TASK_FAILED ) && m_failSchedule == SCHED_NONE ) + { + // fail! Send a visual indicator. + ALERT ( at_aiconsole, "Schedule: %s Failed\n", m_pSchedule->pName ); + +/*jlb spark + Vector tmp = pev->origin; + tmp.z = pev->absmax.z + 16; + UTIL_Sparks( tmp ); +jlb */ + } +#endif // DEBUG + + // some condition has interrupted the schedule, or the schedule is done + return FALSE; + } + + return TRUE; +} + +//========================================================= +// MaintainSchedule - does all the per-think schedule maintenance. +// ensures that the monster leaves this function with a valid +// schedule! +//========================================================= +void CMBaseMonster :: MaintainSchedule ( void ) +{ + Schedule_t *pNewSchedule; + int i; + + // UNDONE: Tune/fix this 10... This is just here so infinite loops are impossible + for ( i = 0; i < 10; i++ ) + { + if ( m_pSchedule != NULL && TaskIsComplete() ) + { + NextScheduledTask(); + } + + // validate existing schedule + if ( !FScheduleValid() || m_MonsterState != m_IdealMonsterState ) + { + // if we come into this block of code, the schedule is going to have to be changed. + // if the previous schedule was interrupted by a condition, GetIdealState will be + // called. Else, a schedule finished normally. + + // Notify the monster that his schedule is changing + ScheduleChange(); + + // Call GetIdealState if we're not dead and one or more of the following... + // - in COMBAT state with no enemy (it died?) + // - conditions bits (excluding SCHEDULE_DONE) indicate interruption, + // - schedule is done but schedule indicates it wants GetIdealState called + // after successful completion (by setting bits_COND_SCHEDULE_DONE in iInterruptMask) + // DEAD & SCRIPT are not suggestions, they are commands! + if ( m_IdealMonsterState != MONSTERSTATE_DEAD && + (m_IdealMonsterState != MONSTERSTATE_SCRIPT || m_IdealMonsterState == m_MonsterState) ) + { + if ( (m_afConditions && !HasConditions(bits_COND_SCHEDULE_DONE)) || + (m_pSchedule && (m_pSchedule->iInterruptMask & bits_COND_SCHEDULE_DONE)) || + ((m_MonsterState == MONSTERSTATE_COMBAT) && (m_hEnemy == NULL)) ) + { + GetIdealState(); + } + } + if ( HasConditions( bits_COND_TASK_FAILED ) && m_MonsterState == m_IdealMonsterState ) + { + if ( m_failSchedule != SCHED_NONE ) + pNewSchedule = GetScheduleOfType( m_failSchedule ); + else + pNewSchedule = GetScheduleOfType( SCHED_FAIL ); + // schedule was invalid because the current task failed to start or complete + ALERT ( at_aiconsole, "Schedule Failed at %d!\n", m_iScheduleIndex ); + ChangeSchedule( pNewSchedule ); + } + else + { + SetState( m_IdealMonsterState ); + if ( m_MonsterState == MONSTERSTATE_SCRIPT || m_MonsterState == MONSTERSTATE_DEAD ) + pNewSchedule = CMBaseMonster::GetSchedule(); + else + pNewSchedule = GetSchedule(); + ChangeSchedule( pNewSchedule ); + } + } + + if ( m_iTaskStatus == TASKSTATUS_NEW ) + { + Task_t *pTask = GetTask(); + ASSERT( pTask != NULL ); + TaskBegin(); + StartTask( pTask ); + } + + // UNDONE: Twice?!!! + if ( m_Activity != m_IdealActivity ) + { + SetActivity ( m_IdealActivity ); + } + + if ( !TaskIsComplete() && m_iTaskStatus != TASKSTATUS_NEW ) + break; + } + + if ( TaskIsRunning() ) + { + Task_t *pTask = GetTask(); + ASSERT( pTask != NULL ); + RunTask( pTask ); + } + + // UNDONE: We have to do this so that we have an animation set to blend to if RunTask changes the animation + // RunTask() will always change animations at the end of a script! + // Don't do this twice + if ( m_Activity != m_IdealActivity ) + { + SetActivity ( m_IdealActivity ); + } +} + +//========================================================= +// RunTask +//========================================================= +void CMBaseMonster :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_TURN_RIGHT: + case TASK_TURN_LEFT: + { + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + + case TASK_PLAY_SEQUENCE_FACE_ENEMY: + case TASK_PLAY_SEQUENCE_FACE_TARGET: + { + edict_t *pTarget; + + if ( pTask->iTask == TASK_PLAY_SEQUENCE_FACE_TARGET ) + pTarget = m_hTargetEnt; + else + pTarget = m_hEnemy; + if ( pTarget ) + { + pev->ideal_yaw = UTIL_VecToYaw( pTarget->v.origin - pev->origin ); + ChangeYaw( pev->yaw_speed ); + } + if ( m_fSequenceFinished ) + TaskComplete(); + } + break; + + case TASK_PLAY_SEQUENCE: + case TASK_PLAY_ACTIVE_IDLE: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + break; + } + + + case TASK_FACE_ENEMY: + { + MakeIdealYaw( m_vecEnemyLKP ); + + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + case TASK_FACE_HINTNODE: + case TASK_FACE_LASTPOSITION: + case TASK_FACE_TARGET: + case TASK_FACE_IDEAL: + case TASK_FACE_ROUTE: + { + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_PVS: + { + if ( !FNullEnt(FIND_CLIENT_IN_PVS(edict())) ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_INDEFINITE: + { + // don't do anything. + break; + } + case TASK_WAIT: + case TASK_WAIT_RANDOM: + { + if ( gpGlobals->time >= m_flWaitFinished ) + { + TaskComplete(); + } + break; + } + case TASK_WAIT_FACE_ENEMY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if ( gpGlobals->time >= m_flWaitFinished ) + { + TaskComplete(); + } + break; + } + case TASK_MOVE_TO_TARGET_RANGE: + { + float distance; + + if ( m_hTargetEnt == NULL ) + TaskFail(); + else + { + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + // Re-evaluate when you think your finished, or the target has moved too far + if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->v.origin).Length() > pTask->flData * 0.5 ) + { + m_vecMoveGoal = m_hTargetEnt->v.origin; + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + FRefreshRoute(); + } + + // Set the appropriate activity based on an overlapping range + // overlap the range to prevent oscillation + if ( distance < pTask->flData ) + { + TaskComplete(); + RouteClear(); // Stop moving + } + else if ( distance < 190 && m_movementActivity != ACT_WALK ) + m_movementActivity = ACT_WALK; + else if ( distance >= 270 && m_movementActivity != ACT_RUN ) + m_movementActivity = ACT_RUN; + } + + break; + } + case TASK_WAIT_FOR_MOVEMENT: + { + if (MovementIsComplete()) + { + TaskComplete(); + RouteClear(); // Stop moving + } + break; + } + case TASK_DIE: + { + if ( m_fSequenceFinished && pev->frame >= 255 ) + { + pev->deadflag = DEAD_DEAD; + + SetThink ( NULL ); + StopAnimation(); + + if ( !BBoxFlat() ) + { + // a bit of a hack. If a corpses' bbox is positioned such that being left solid so that it can be attacked will + // block the player on a slope or stairs, the corpse is made nonsolid. +// pev->solid = SOLID_NOT; + UTIL_SetSize ( pev, Vector ( -4, -4, 0 ), Vector ( 4, 4, 1 ) ); + } + else // !!!HACKHACK - put monster in a thin, wide bounding box until we fix the solid type/bounding volume problem + UTIL_SetSize ( pev, Vector ( pev->mins.x, pev->mins.y, pev->mins.z ), Vector ( pev->maxs.x, pev->maxs.y, pev->mins.z + 1 ) ); + + if ( ShouldFadeOnDeath() ) + { + // this monster was created by a monstermaker... fade the corpse out. + SUB_StartFadeOut(); + } + } + break; + } + case TASK_RANGE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK2_NOTURN: + case TASK_RANGE_ATTACK2_NOTURN: + case TASK_RELOAD_NOTURN: + { + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + } + case TASK_RANGE_ATTACK1: + case TASK_MELEE_ATTACK1: + case TASK_MELEE_ATTACK2: + case TASK_RANGE_ATTACK2: + case TASK_SPECIAL_ATTACK1: + case TASK_SPECIAL_ATTACK2: + case TASK_RELOAD: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + } + case TASK_SMALL_FLINCH: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + } + break; + } +} + +//========================================================= +// SetTurnActivity - measures the difference between the way +// the monster is facing and determines whether or not to +// select one of the 180 turn animations. +//========================================================= +void CMBaseMonster :: SetTurnActivity ( void ) +{ + float flYD; + flYD = FlYawDiff(); + + if ( flYD <= -45 && LookupActivity ( ACT_TURN_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) + {// big right turn + m_IdealActivity = ACT_TURN_RIGHT; + } + else if ( flYD > 45 && LookupActivity ( ACT_TURN_LEFT ) != ACTIVITY_NOT_AVAILABLE ) + {// big left turn + m_IdealActivity = ACT_TURN_LEFT; + } +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. +//========================================================= +void CMBaseMonster :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_TURN_RIGHT: + { + float flCurrentYaw; + + flCurrentYaw = UTIL_AngleMod( pev->angles.y ); + pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw - pTask->flData ); + SetTurnActivity(); + break; + } + case TASK_TURN_LEFT: + { + float flCurrentYaw; + + flCurrentYaw = UTIL_AngleMod( pev->angles.y ); + pev->ideal_yaw = UTIL_AngleMod( flCurrentYaw + pTask->flData ); + SetTurnActivity(); + break; + } + case TASK_REMEMBER: + { + Remember ( (int)pTask->flData ); + TaskComplete(); + break; + } + case TASK_FORGET: + { + Forget ( (int)pTask->flData ); + TaskComplete(); + break; + } + case TASK_FIND_HINTNODE: + { + m_iHintNode = FindHintNode(); + + if ( m_iHintNode != NO_NODE ) + { + TaskComplete(); + } + else + { + TaskFail(); + } + break; + } + case TASK_STORE_LASTPOSITION: + { + m_vecLastPosition = pev->origin; + TaskComplete(); + break; + } + case TASK_CLEAR_LASTPOSITION: + { + m_vecLastPosition = g_vecZero; + TaskComplete(); + break; + } + case TASK_CLEAR_HINTNODE: + { + m_iHintNode = NO_NODE; + TaskComplete(); + break; + } + case TASK_STOP_MOVING: + { + if ( m_IdealActivity == m_movementActivity ) + { + m_IdealActivity = GetStoppedActivity(); + } + + RouteClear(); + TaskComplete(); + break; + } + case TASK_PLAY_SEQUENCE_FACE_ENEMY: + case TASK_PLAY_SEQUENCE_FACE_TARGET: + case TASK_PLAY_SEQUENCE: + { + m_IdealActivity = ( Activity )( int )pTask->flData; + break; + } + case TASK_PLAY_ACTIVE_IDLE: + { + // monsters verify that they have a sequence for the node's activity BEFORE + // moving towards the node, so it's ok to just set the activity without checking here. + m_IdealActivity = ( Activity )WorldGraph.m_pNodes[ m_iHintNode ].m_sHintActivity; + break; + } + case TASK_SET_SCHEDULE: + { + Schedule_t *pNewSchedule; + + pNewSchedule = GetScheduleOfType( (int)pTask->flData ); + + if ( pNewSchedule ) + { + ChangeSchedule( pNewSchedule ); + } + else + { + TaskFail(); + } + + break; + } + case TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, 0, pTask->flData ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_FAR_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, pTask->flData, CoverRadius() ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_NODE_COVER_FROM_ENEMY: + { + if ( m_hEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( FindCover( m_hEnemy->v.origin, m_hEnemy->v.view_ofs, 0, CoverRadius() ) ) + { + // try for cover farther than the FLData from the schedule. + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_COVER_FROM_ENEMY: + { + entvars_t *pevCover; + + if ( m_hEnemy == NULL ) + { + // Find cover from self if no enemy available + pevCover = pev; +// TaskFail(); +// return; + } + else + { + edict_t *pEdict = m_hEnemy; + pevCover = VARS(pEdict); + } + + if ( FindLateralCover( pevCover->origin, pevCover->view_ofs ) ) + { + // try lateral first + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else if ( FindCover( pevCover->origin, pevCover->view_ofs, 0, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else + { + // no coverwhatsoever. + TaskFail(); + } + break; + } + case TASK_FIND_COVER_FROM_ORIGIN: + { + if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + pTask->flData; + TaskComplete(); + } + else + { + // no cover! + TaskFail(); + } + } + break; + case TASK_FIND_COVER_FROM_BEST_SOUND: + { + { + // no coverwhatsoever. or no sound in list + TaskFail(); + } + break; + } + case TASK_FACE_HINTNODE: + { + pev->ideal_yaw = WorldGraph.m_pNodes[ m_iHintNode ].m_flHintYaw; + SetTurnActivity(); + break; + } + + case TASK_FACE_LASTPOSITION: + MakeIdealYaw ( m_vecLastPosition ); + SetTurnActivity(); + break; + + case TASK_FACE_TARGET: + if ( m_hTargetEnt != NULL ) + { + MakeIdealYaw ( m_hTargetEnt->v.origin ); + SetTurnActivity(); + } + else + TaskFail(); + break; + case TASK_FACE_ENEMY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + SetTurnActivity(); + break; + } + case TASK_FACE_IDEAL: + { + SetTurnActivity(); + break; + } + case TASK_FACE_ROUTE: + { + if (FRouteClear()) + { + ALERT(at_aiconsole, "No route to face!\n"); + TaskFail(); + } + else + { + MakeIdealYaw(m_Route[m_iRouteIndex].vecLocation); + SetTurnActivity(); + } + break; + } + case TASK_WAIT_PVS: + case TASK_WAIT_INDEFINITE: + { + // don't do anything. + break; + } + case TASK_WAIT: + case TASK_WAIT_FACE_ENEMY: + {// set a future time that tells us when the wait is over. + m_flWaitFinished = gpGlobals->time + pTask->flData; + break; + } + case TASK_WAIT_RANDOM: + {// set a future time that tells us when the wait is over. + m_flWaitFinished = gpGlobals->time + RANDOM_FLOAT( 0.1, pTask->flData ); + break; + } + case TASK_MOVE_TO_TARGET_RANGE: + { + if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) + TaskComplete(); + else + { + m_vecMoveGoal = m_hTargetEnt->v.origin; + if ( !MoveToTarget( ACT_WALK, 2 ) ) + TaskFail(); + } + break; + } + case TASK_RUN_TO_TARGET: + case TASK_WALK_TO_TARGET: + { + Activity newActivity; + + if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) + TaskComplete(); + else + { + if ( pTask->iTask == TASK_WALK_TO_TARGET ) + newActivity = ACT_WALK; + else + newActivity = ACT_RUN; + // This monster can't do this! + if ( LookupActivity( newActivity ) == ACTIVITY_NOT_AVAILABLE ) + TaskComplete(); + else + { + if ( m_hTargetEnt == NULL || !MoveToTarget( newActivity, 2 ) ) + { + TaskFail(); + ALERT( at_aiconsole, "%s Failed to reach target!!!\n", STRING(pev->classname) ); + RouteClear(); + } + } + } + TaskComplete(); + break; + } + case TASK_CLEAR_MOVE_WAIT: + { + m_flMoveWaitFinished = gpGlobals->time; + TaskComplete(); + break; + } + case TASK_MELEE_ATTACK1_NOTURN: + case TASK_MELEE_ATTACK1: + { + m_IdealActivity = ACT_MELEE_ATTACK1; + break; + } + case TASK_MELEE_ATTACK2_NOTURN: + case TASK_MELEE_ATTACK2: + { + m_IdealActivity = ACT_MELEE_ATTACK2; + break; + } + case TASK_RANGE_ATTACK1_NOTURN: + case TASK_RANGE_ATTACK1: + { + m_IdealActivity = ACT_RANGE_ATTACK1; + break; + } + case TASK_RANGE_ATTACK2_NOTURN: + case TASK_RANGE_ATTACK2: + { + m_IdealActivity = ACT_RANGE_ATTACK2; + break; + } + case TASK_RELOAD_NOTURN: + case TASK_RELOAD: + { + m_IdealActivity = ACT_RELOAD; + break; + } + case TASK_SPECIAL_ATTACK1: + { + m_IdealActivity = ACT_SPECIAL_ATTACK1; + break; + } + case TASK_SPECIAL_ATTACK2: + { + m_IdealActivity = ACT_SPECIAL_ATTACK2; + break; + } + case TASK_SET_ACTIVITY: + { + m_IdealActivity = (Activity)(int)pTask->flData; + TaskComplete(); + break; + } + case TASK_GET_PATH_TO_ENEMY_LKP: + { + if ( BuildRoute ( m_vecEnemyLKP, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, 0, (m_vecEnemyLKP - pev->origin).Length() )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_ENEMY: + { + edict_t *pEnemy = m_hEnemy; + + if ( pEnemy == NULL ) + { + TaskFail(); + return; + } + + if ( BuildRoute ( pEnemy->v.origin, bits_MF_TO_ENEMY, pEnemy ) ) + { + TaskComplete(); + } + else if (BuildNearestRoute( pEnemy->v.origin, pEnemy->v.view_ofs, 0, (pEnemy->v.origin - pev->origin).Length() )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 64, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT ( at_aiconsole, "GetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + case TASK_GET_PATH_TO_SPOT: + { + edict_t *pPlayer = FIND_ENTITY_BY_CLASSNAME( NULL, "player" ); + if ( BuildRoute ( m_vecMoveGoal, bits_MF_TO_LOCATION, pPlayer ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); + TaskFail(); + } + break; + } + + case TASK_GET_PATH_TO_TARGET: + { + RouteClear(); + if ( m_hTargetEnt != NULL && MoveToTarget( m_movementActivity, 1 ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToSpot failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_HINTNODE:// for active idles! + { + if ( MoveToLocation( m_movementActivity, 2, WorldGraph.m_pNodes[ m_iHintNode ].m_vecOrigin ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToHintNode failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_LASTPOSITION: + { + m_vecMoveGoal = m_vecLastPosition; + + if ( MoveToLocation( m_movementActivity, 2, m_vecMoveGoal ) ) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToLastPosition failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_BESTSOUND: + { + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToBestSound failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_BESTSCENT: + { + if ( m_hEnemy != NULL ) + { +/*jlb + m_hTargetEnt = m_hEnemy; + if ( MoveToTarget( m_movementActivity, 2 ) ) + { + TaskComplete(); + } + else +jlb*/ + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToBestScent failed!!\n" ); + TaskFail(); + } + } + break; + } + case TASK_RUN_PATH: + { + // UNDONE: This is in some default AI and some monsters can't run? -- walk instead? + if ( LookupActivity( ACT_RUN ) != ACTIVITY_NOT_AVAILABLE ) + { + m_movementActivity = ACT_RUN; + } + else + { + m_movementActivity = ACT_WALK; + } + TaskComplete(); + break; + } + case TASK_WALK_PATH: + { + if ( pev->movetype == MOVETYPE_FLY ) + { + m_movementActivity = ACT_FLY; + } + if ( LookupActivity( ACT_WALK ) != ACTIVITY_NOT_AVAILABLE ) + { + m_movementActivity = ACT_WALK; + } + else + { + m_movementActivity = ACT_RUN; + } + TaskComplete(); + break; + } + case TASK_STRAFE_PATH: + { + Vector2D vec2DirToPoint; + Vector2D vec2RightSide; + + // to start strafing, we have to first figure out if the target is on the left side or right side + UTIL_MakeVectors ( pev->angles ); + + vec2DirToPoint = ( m_Route[ 0 ].vecLocation - pev->origin ).Make2D().Normalize(); + vec2RightSide = gpGlobals->v_right.Make2D().Normalize(); + + if ( DotProduct ( vec2DirToPoint, vec2RightSide ) > 0 ) + { + // strafe right + m_movementActivity = ACT_STRAFE_RIGHT; + } + else + { + // strafe left + m_movementActivity = ACT_STRAFE_LEFT; + } + TaskComplete(); + break; + } + + + case TASK_WAIT_FOR_MOVEMENT: + { + if (FRouteClear()) + { + TaskComplete(); + } + break; + } + + case TASK_EAT: + { + Eat( pTask->flData ); + TaskComplete(); + break; + } + case TASK_SMALL_FLINCH: + { + m_IdealActivity = GetSmallFlinchActivity(); + break; + } + case TASK_DIE: + { + RouteClear(); + + m_IdealActivity = GetDeathActivity(); + + pev->deadflag = DEAD_DYING; + break; + } + case TASK_SOUND_WAKE: + { + AlertSound(); + TaskComplete(); + break; + } + case TASK_SOUND_DIE: + { + DeathSound(); + TaskComplete(); + break; + } + case TASK_SOUND_IDLE: + { + IdleSound(); + TaskComplete(); + break; + } + case TASK_SOUND_PAIN: + { + PainSound(); + TaskComplete(); + break; + } + case TASK_SOUND_DEATH: + { + DeathSound(); + TaskComplete(); + break; + } + case TASK_SOUND_ANGRY: + { + // sounds are complete as soon as we get here, cause we've already played them. + ALERT ( at_aiconsole, "SOUND\n" ); + TaskComplete(); + break; + } + + case TASK_SUGGEST_STATE: + { + m_IdealMonsterState = (MONSTERSTATE)(int)pTask->flData; + TaskComplete(); + break; + } + + case TASK_SET_FAIL_SCHEDULE: + m_failSchedule = (int)pTask->flData; + TaskComplete(); + break; + + case TASK_CLEAR_FAIL_SCHEDULE: + m_failSchedule = SCHED_NONE; + TaskComplete(); + break; + + default: + { + ALERT ( at_aiconsole, "No StartTask entry for %d\n", (SHARED_TASKS)pTask->iTask ); + break; + } + } +} + +//========================================================= +// GetTask - returns a pointer to the current +// scheduled task. NULL if there's a problem. +//========================================================= +Task_t *CMBaseMonster :: GetTask ( void ) +{ + if ( m_iScheduleIndex < 0 || m_iScheduleIndex >= m_pSchedule->cTasks ) + { + // m_iScheduleIndex is not within valid range for the monster's current schedule. + return NULL; + } + else + { + return &m_pSchedule->pTasklist[ m_iScheduleIndex ]; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMBaseMonster :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_PRONE: + { + return GetScheduleOfType( SCHED_BARNACLE_VICTIM_GRAB ); + break; + } + case MONSTERSTATE_NONE: + { + ALERT ( at_aiconsole, "MONSTERSTATE IS NONE!\n" ); + break; + } + case MONSTERSTATE_IDLE: + { + if ( HasConditions ( bits_COND_HEAR_SOUND ) ) + { + return GetScheduleOfType( SCHED_ALERT_FACE ); + } + else if ( FRouteClear() ) + { + // no valid route! + return GetScheduleOfType( SCHED_IDLE_STAND ); + } + else + { + // valid route. Get moving + return GetScheduleOfType( SCHED_IDLE_WALK ); + } + break; + } + case MONSTERSTATE_ALERT: + { + if ( HasConditions( bits_COND_ENEMY_DEAD ) && LookupActivity( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE ) + { + return GetScheduleOfType ( SCHED_VICTORY_DANCE ); + } + + if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) + { + if ( fabs( FlYawDiff() ) < (1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ORIGIN ); + } + else + { + return GetScheduleOfType( SCHED_ALERT_SMALL_FLINCH ); + } + } + + else if ( HasConditions ( bits_COND_HEAR_SOUND ) ) + { + return GetScheduleOfType( SCHED_ALERT_FACE ); + } + else + { + return GetScheduleOfType( SCHED_ALERT_STAND ); + } + break; + } + case MONSTERSTATE_COMBAT: + { + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // clear the current (dead) enemy and try to find another. + m_hEnemy = NULL; + + if ( GetEnemy() ) + { + ClearConditions( bits_COND_ENEMY_DEAD ); + return GetSchedule(); + } + else + { + SetState( MONSTERSTATE_ALERT ); + return GetSchedule(); + } + } + + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + return GetScheduleOfType ( SCHED_WAKE_ANGRY ); + } + else if (HasConditions(bits_COND_LIGHT_DAMAGE) && !HasMemory( bits_MEMORY_FLINCHED) ) + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + else if ( !HasConditions(bits_COND_SEE_ENEMY) ) + { + // we can't see the enemy + if ( !HasConditions(bits_COND_ENEMY_OCCLUDED) ) + { + // enemy is unseen, but not occluded! + // turn to face enemy + return GetScheduleOfType( SCHED_COMBAT_FACE ); + } + else + { + // chase! + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + } + } + else + { + // we can see the enemy + if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + if ( HasConditions(bits_COND_CAN_RANGE_ATTACK2) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + if ( HasConditions(bits_COND_CAN_MELEE_ATTACK1) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); + } + if ( HasConditions(bits_COND_CAN_MELEE_ATTACK2) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); + } + if ( !HasConditions(bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1) ) + { + // if we can see enemy but can't use either attack type, we must need to get closer to enemy + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + } + else if ( !FacingIdeal() ) + { + //turn + return GetScheduleOfType( SCHED_COMBAT_FACE ); + } + else + { + ALERT ( at_aiconsole, "No suitable combat schedule!\n" ); + } + } + break; + } + case MONSTERSTATE_DEAD: + { + return GetScheduleOfType( SCHED_DIE ); + break; + } + default: + { + ALERT ( at_aiconsole, "Invalid State for GetSchedule!\n" ); + break; + } + } + + return &slError[ 0 ]; +} diff --git a/src/dlls/activity.h b/src/dlls/activity.h index c091712..5c33ac8 100644 --- a/src/dlls/activity.h +++ b/src/dlls/activity.h @@ -1,109 +1,109 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - -#ifndef ACTIVITY_H -#define ACTIVITY_H - - -typedef enum { - ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity - ACT_IDLE = 1, - ACT_GUARD, - ACT_WALK, - ACT_RUN, - ACT_FLY, // Fly (and flap if appropriate) - ACT_SWIM, - ACT_HOP, // vertical jump - ACT_LEAP, // long forward jump - ACT_FALL, - ACT_LAND, - ACT_STRAFE_LEFT, - ACT_STRAFE_RIGHT, - ACT_ROLL_LEFT, // tuck and roll, left - ACT_ROLL_RIGHT, // tuck and roll, right - ACT_TURN_LEFT, // turn quickly left (stationary) - ACT_TURN_RIGHT, // turn quickly right (stationary) - ACT_CROUCH, // the act of crouching down from a standing position - ACT_CROUCHIDLE, // holding body in crouched position (loops) - ACT_STAND, // the act of standing from a crouched position - ACT_USE, - ACT_SIGNAL1, - ACT_SIGNAL2, - ACT_SIGNAL3, - ACT_TWITCH, - ACT_COWER, - ACT_SMALL_FLINCH, - ACT_BIG_FLINCH, - ACT_RANGE_ATTACK1, - ACT_RANGE_ATTACK2, - ACT_MELEE_ATTACK1, - ACT_MELEE_ATTACK2, - ACT_RELOAD, - ACT_ARM, // pull out gun, for instance - ACT_DISARM, // reholster gun - ACT_EAT, // monster chowing on a large food item (loop) - ACT_DIESIMPLE, - ACT_DIEBACKWARD, - ACT_DIEFORWARD, - ACT_DIEVIOLENT, - ACT_BARNACLE_HIT, // barnacle tongue hits a monster - ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop ) - ACT_BARNACLE_CHOMP, // barnacle latches on to the monster - ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop ) - ACT_SLEEP, - ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor - ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ( doesn't HAVE to be a wall or on a wall ) - ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop) - ACT_WALK_HURT, // limp (loop) - ACT_RUN_HURT, // limp (loop) - ACT_HOVER, // Idle while in flight - ACT_GLIDE, // Fly (don't flap) - ACT_FLY_LEFT, // Turn left in flight - ACT_FLY_RIGHT, // Turn right in flight - ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air - ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster - ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops. - ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc ) - ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of - ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever. - ACT_SPECIAL_ATTACK1, // very monster specific special attacks. - ACT_SPECIAL_ATTACK2, - ACT_COMBAT_IDLE, // agitated idle. - ACT_WALK_SCARED, - ACT_RUN_SCARED, - ACT_VICTORY_DANCE, // killed a player, do a victory dance. - ACT_DIE_HEADSHOT, // die, hit in head. - ACT_DIE_CHESTSHOT, // die, hit in chest - ACT_DIE_GUTSHOT, // die, hit in gut - ACT_DIE_BACKSHOT, // die, hit in back - ACT_FLINCH_HEAD, - ACT_FLINCH_CHEST, - ACT_FLINCH_STOMACH, - ACT_FLINCH_LEFTARM, - ACT_FLINCH_RIGHTARM, - ACT_FLINCH_LEFTLEG, - ACT_FLINCH_RIGHTLEG, -} Activity; - - -typedef struct { - int type; - char *name; -} activity_map_t; - -extern activity_map_t activity_map[]; - - -#endif //ACTIVITY_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + +#ifndef ACTIVITY_H +#define ACTIVITY_H + + +typedef enum { + ACT_RESET = 0, // Set m_Activity to this invalid value to force a reset to m_IdealActivity + ACT_IDLE = 1, + ACT_GUARD, + ACT_WALK, + ACT_RUN, + ACT_FLY, // Fly (and flap if appropriate) + ACT_SWIM, + ACT_HOP, // vertical jump + ACT_LEAP, // long forward jump + ACT_FALL, + ACT_LAND, + ACT_STRAFE_LEFT, + ACT_STRAFE_RIGHT, + ACT_ROLL_LEFT, // tuck and roll, left + ACT_ROLL_RIGHT, // tuck and roll, right + ACT_TURN_LEFT, // turn quickly left (stationary) + ACT_TURN_RIGHT, // turn quickly right (stationary) + ACT_CROUCH, // the act of crouching down from a standing position + ACT_CROUCHIDLE, // holding body in crouched position (loops) + ACT_STAND, // the act of standing from a crouched position + ACT_USE, + ACT_SIGNAL1, + ACT_SIGNAL2, + ACT_SIGNAL3, + ACT_TWITCH, + ACT_COWER, + ACT_SMALL_FLINCH, + ACT_BIG_FLINCH, + ACT_RANGE_ATTACK1, + ACT_RANGE_ATTACK2, + ACT_MELEE_ATTACK1, + ACT_MELEE_ATTACK2, + ACT_RELOAD, + ACT_ARM, // pull out gun, for instance + ACT_DISARM, // reholster gun + ACT_EAT, // monster chowing on a large food item (loop) + ACT_DIESIMPLE, + ACT_DIEBACKWARD, + ACT_DIEFORWARD, + ACT_DIEVIOLENT, + ACT_BARNACLE_HIT, // barnacle tongue hits a monster + ACT_BARNACLE_PULL, // barnacle is lifting the monster ( loop ) + ACT_BARNACLE_CHOMP, // barnacle latches on to the monster + ACT_BARNACLE_CHEW, // barnacle is holding the monster in its mouth ( loop ) + ACT_SLEEP, + ACT_INSPECT_FLOOR, // for active idles, look at something on or near the floor + ACT_INSPECT_WALL, // for active idles, look at something directly ahead of you ( doesn't HAVE to be a wall or on a wall ) + ACT_IDLE_ANGRY, // alternate idle animation in which the monster is clearly agitated. (loop) + ACT_WALK_HURT, // limp (loop) + ACT_RUN_HURT, // limp (loop) + ACT_HOVER, // Idle while in flight + ACT_GLIDE, // Fly (don't flap) + ACT_FLY_LEFT, // Turn left in flight + ACT_FLY_RIGHT, // Turn right in flight + ACT_DETECT_SCENT, // this means the monster smells a scent carried by the air + ACT_SNIFF, // this is the act of actually sniffing an item in front of the monster + ACT_BITE, // some large monsters can eat small things in one bite. This plays one time, EAT loops. + ACT_THREAT_DISPLAY, // without attacking, monster demonstrates that it is angry. (Yell, stick out chest, etc ) + ACT_FEAR_DISPLAY, // monster just saw something that it is afraid of + ACT_EXCITED, // for some reason, monster is excited. Sees something he really likes to eat, or whatever. + ACT_SPECIAL_ATTACK1, // very monster specific special attacks. + ACT_SPECIAL_ATTACK2, + ACT_COMBAT_IDLE, // agitated idle. + ACT_WALK_SCARED, + ACT_RUN_SCARED, + ACT_VICTORY_DANCE, // killed a player, do a victory dance. + ACT_DIE_HEADSHOT, // die, hit in head. + ACT_DIE_CHESTSHOT, // die, hit in chest + ACT_DIE_GUTSHOT, // die, hit in gut + ACT_DIE_BACKSHOT, // die, hit in back + ACT_FLINCH_HEAD, + ACT_FLINCH_CHEST, + ACT_FLINCH_STOMACH, + ACT_FLINCH_LEFTARM, + ACT_FLINCH_RIGHTARM, + ACT_FLINCH_LEFTLEG, + ACT_FLINCH_RIGHTLEG, +} Activity; + + +typedef struct { + int type; + char *name; +} activity_map_t; + +extern activity_map_t activity_map[]; + + +#endif //ACTIVITY_H diff --git a/src/dlls/activitymap.h b/src/dlls/activitymap.h index 0748551..6cfe2a4 100644 --- a/src/dlls/activitymap.h +++ b/src/dlls/activitymap.h @@ -1,97 +1,97 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - -#define _A( a ) { a, #a } - -activity_map_t activity_map[] = -{ -_A( ACT_IDLE ), -_A( ACT_GUARD ), -_A( ACT_WALK ), -_A( ACT_RUN ), -_A( ACT_FLY ), -_A( ACT_SWIM ), -_A( ACT_HOP ), -_A( ACT_LEAP ), -_A( ACT_FALL ), -_A( ACT_LAND ), -_A( ACT_STRAFE_LEFT ), -_A( ACT_STRAFE_RIGHT ), -_A( ACT_ROLL_LEFT ), -_A( ACT_ROLL_RIGHT ), -_A( ACT_TURN_LEFT ), -_A( ACT_TURN_RIGHT ), -_A( ACT_CROUCH ), -_A( ACT_CROUCHIDLE ), -_A( ACT_STAND ), -_A( ACT_USE ), -_A( ACT_SIGNAL1 ), -_A( ACT_SIGNAL2 ), -_A( ACT_SIGNAL3 ), -_A( ACT_TWITCH ), -_A( ACT_COWER ), -_A( ACT_SMALL_FLINCH ), -_A( ACT_BIG_FLINCH ), -_A( ACT_RANGE_ATTACK1 ), -_A( ACT_RANGE_ATTACK2 ), -_A( ACT_MELEE_ATTACK1 ), -_A( ACT_MELEE_ATTACK2 ), -_A( ACT_RELOAD ), -_A( ACT_ARM ), -_A( ACT_DISARM ), -_A( ACT_EAT ), -_A( ACT_DIESIMPLE ), -_A( ACT_DIEBACKWARD ), -_A( ACT_DIEFORWARD ), -_A( ACT_DIEVIOLENT ), -_A( ACT_BARNACLE_HIT ), -_A( ACT_BARNACLE_PULL ), -_A( ACT_BARNACLE_CHOMP ), -_A( ACT_BARNACLE_CHEW ), -_A( ACT_SLEEP ), -_A( ACT_INSPECT_FLOOR ), -_A( ACT_INSPECT_WALL ), -_A( ACT_IDLE_ANGRY ), -_A( ACT_WALK_HURT ), -_A( ACT_RUN_HURT ), -_A( ACT_HOVER ), -_A( ACT_GLIDE ), -_A( ACT_FLY_LEFT ), -_A( ACT_FLY_RIGHT ), -_A( ACT_DETECT_SCENT ), -_A( ACT_SNIFF ), -_A( ACT_BITE ), -_A( ACT_THREAT_DISPLAY ), -_A( ACT_FEAR_DISPLAY ), -_A( ACT_EXCITED ), -_A( ACT_SPECIAL_ATTACK1 ), -_A( ACT_SPECIAL_ATTACK2 ), -_A( ACT_COMBAT_IDLE ), -_A( ACT_WALK_SCARED ), -_A( ACT_RUN_SCARED ), -_A( ACT_VICTORY_DANCE ), -_A( ACT_DIE_HEADSHOT ), -_A( ACT_DIE_CHESTSHOT ), -_A( ACT_DIE_GUTSHOT ), -_A( ACT_DIE_BACKSHOT ), -_A( ACT_FLINCH_HEAD ), -_A( ACT_FLINCH_CHEST ), -_A( ACT_FLINCH_STOMACH ), -_A( ACT_FLINCH_LEFTARM ), -_A( ACT_FLINCH_RIGHTARM ), -_A( ACT_FLINCH_LEFTLEG ), -_A( ACT_FLINCH_RIGHTLEG ), -0, NULL -}; +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + +#define _A( a ) { a, #a } + +activity_map_t activity_map[] = +{ +_A( ACT_IDLE ), +_A( ACT_GUARD ), +_A( ACT_WALK ), +_A( ACT_RUN ), +_A( ACT_FLY ), +_A( ACT_SWIM ), +_A( ACT_HOP ), +_A( ACT_LEAP ), +_A( ACT_FALL ), +_A( ACT_LAND ), +_A( ACT_STRAFE_LEFT ), +_A( ACT_STRAFE_RIGHT ), +_A( ACT_ROLL_LEFT ), +_A( ACT_ROLL_RIGHT ), +_A( ACT_TURN_LEFT ), +_A( ACT_TURN_RIGHT ), +_A( ACT_CROUCH ), +_A( ACT_CROUCHIDLE ), +_A( ACT_STAND ), +_A( ACT_USE ), +_A( ACT_SIGNAL1 ), +_A( ACT_SIGNAL2 ), +_A( ACT_SIGNAL3 ), +_A( ACT_TWITCH ), +_A( ACT_COWER ), +_A( ACT_SMALL_FLINCH ), +_A( ACT_BIG_FLINCH ), +_A( ACT_RANGE_ATTACK1 ), +_A( ACT_RANGE_ATTACK2 ), +_A( ACT_MELEE_ATTACK1 ), +_A( ACT_MELEE_ATTACK2 ), +_A( ACT_RELOAD ), +_A( ACT_ARM ), +_A( ACT_DISARM ), +_A( ACT_EAT ), +_A( ACT_DIESIMPLE ), +_A( ACT_DIEBACKWARD ), +_A( ACT_DIEFORWARD ), +_A( ACT_DIEVIOLENT ), +_A( ACT_BARNACLE_HIT ), +_A( ACT_BARNACLE_PULL ), +_A( ACT_BARNACLE_CHOMP ), +_A( ACT_BARNACLE_CHEW ), +_A( ACT_SLEEP ), +_A( ACT_INSPECT_FLOOR ), +_A( ACT_INSPECT_WALL ), +_A( ACT_IDLE_ANGRY ), +_A( ACT_WALK_HURT ), +_A( ACT_RUN_HURT ), +_A( ACT_HOVER ), +_A( ACT_GLIDE ), +_A( ACT_FLY_LEFT ), +_A( ACT_FLY_RIGHT ), +_A( ACT_DETECT_SCENT ), +_A( ACT_SNIFF ), +_A( ACT_BITE ), +_A( ACT_THREAT_DISPLAY ), +_A( ACT_FEAR_DISPLAY ), +_A( ACT_EXCITED ), +_A( ACT_SPECIAL_ATTACK1 ), +_A( ACT_SPECIAL_ATTACK2 ), +_A( ACT_COMBAT_IDLE ), +_A( ACT_WALK_SCARED ), +_A( ACT_RUN_SCARED ), +_A( ACT_VICTORY_DANCE ), +_A( ACT_DIE_HEADSHOT ), +_A( ACT_DIE_CHESTSHOT ), +_A( ACT_DIE_GUTSHOT ), +_A( ACT_DIE_BACKSHOT ), +_A( ACT_FLINCH_HEAD ), +_A( ACT_FLINCH_CHEST ), +_A( ACT_FLINCH_STOMACH ), +_A( ACT_FLINCH_LEFTARM ), +_A( ACT_FLINCH_RIGHTARM ), +_A( ACT_FLINCH_LEFTLEG ), +_A( ACT_FLINCH_RIGHTLEG ), +0, NULL +}; diff --git a/src/dlls/agrunt.cpp b/src/dlls/agrunt.cpp index 2981c12..f41d5c2 100644 --- a/src/dlls/agrunt.cpp +++ b/src/dlls/agrunt.cpp @@ -1,1115 +1,1115 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Agrunt - Dominant, warlike alien grunt monster -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "weapons.h" -#include "hornet.h" -#include "skill.h" - - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_AGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, - SCHED_AGRUNT_THREAT_DISPLAY, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_COMMON_TASK + 1, - TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, -}; - -int iAgruntMuzzleFlash; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define AGRUNT_AE_HORNET1 ( 1 ) -#define AGRUNT_AE_HORNET2 ( 2 ) -#define AGRUNT_AE_HORNET3 ( 3 ) -#define AGRUNT_AE_HORNET4 ( 4 ) -#define AGRUNT_AE_HORNET5 ( 5 ) -// some events are set up in the QC file that aren't recognized by the code yet. -#define AGRUNT_AE_PUNCH ( 6 ) -#define AGRUNT_AE_BITE ( 7 ) - -#define AGRUNT_AE_LEFT_FOOT ( 10 ) -#define AGRUNT_AE_RIGHT_FOOT ( 11 ) - -#define AGRUNT_AE_LEFT_PUNCH ( 12 ) -#define AGRUNT_AE_RIGHT_PUNCH ( 13 ) - -#define AGRUNT_MELEE_DIST 100 - -const char *CMAGrunt::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CMAGrunt::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CMAGrunt::pAttackSounds[] = -{ - "agrunt/ag_attack1.wav", - "agrunt/ag_attack2.wav", - "agrunt/ag_attack3.wav", -}; - -const char *CMAGrunt::pDieSounds[] = -{ - "agrunt/ag_die1.wav", - "agrunt/ag_die4.wav", - "agrunt/ag_die5.wav", -}; - -const char *CMAGrunt::pPainSounds[] = -{ - "agrunt/ag_pain1.wav", - "agrunt/ag_pain2.wav", - "agrunt/ag_pain3.wav", - "agrunt/ag_pain4.wav", - "agrunt/ag_pain5.wav", -}; - -const char *CMAGrunt::pIdleSounds[] = -{ - "agrunt/ag_idle1.wav", - "agrunt/ag_idle2.wav", - "agrunt/ag_idle3.wav", - "agrunt/ag_idle4.wav", -}; - -const char *CMAGrunt::pAlertSounds[] = -{ - "agrunt/ag_alert1.wav", - "agrunt/ag_alert3.wav", - "agrunt/ag_alert4.wav", - "agrunt/ag_alert5.wav", -}; - -//========================================================= -// IRelationship - overridden because Human Grunts are -// Alien Grunt's nemesis. -//========================================================= -int CMAGrunt::IRelationship ( CMBaseEntity *pTarget ) -{ - // ditto hgrunt.cpp - /* - if ( strcmp(STRING(pTarget->pev->model), "models/hgrunt.mdl") == 0 ) - { - return R_NM; - } - */ - - return CMBaseMonster :: IRelationship( pTarget ); -} - -//========================================================= -// ISoundMask -//========================================================= -int CMAGrunt :: ISoundMask ( void ) -{ - return 0; -} - -//========================================================= -// TraceAttack -//========================================================= -void CMAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( ptr->iHitgroup == 10 && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))) - { - // hit armor - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); - pev->dmgtime = gpGlobals->time; - } - - if ( RANDOM_LONG( 0, 1 ) == 0 ) - { - Vector vecTracerDir = vecDir; - - vecTracerDir.x += RANDOM_FLOAT( -0.3, 0.3 ); - vecTracerDir.y += RANDOM_FLOAT( -0.3, 0.3 ); - vecTracerDir.z += RANDOM_FLOAT( -0.3, 0.3 ); - - vecTracerDir = vecTracerDir * -512; - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, ptr->vecEndPos ); - WRITE_BYTE( TE_TRACER ); - WRITE_COORD( ptr->vecEndPos.x ); - WRITE_COORD( ptr->vecEndPos.y ); - WRITE_COORD( ptr->vecEndPos.z ); - - WRITE_COORD( vecTracerDir.x ); - WRITE_COORD( vecTracerDir.y ); - WRITE_COORD( vecTracerDir.z ); - MESSAGE_END(); - } - - flDamage -= 20; - if (flDamage <= 0) - flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated - } - else - { - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - } - - AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); -} - -//========================================================= -// StopTalking - won't speak again for 10-20 seconds. -//========================================================= -void CMAGrunt::StopTalking( void ) -{ - m_flNextWordTime = m_flNextSpeakTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); -} - -//========================================================= -// ShouldSpeak - Should this agrunt be talking? -//========================================================= -BOOL CMAGrunt::ShouldSpeak( void ) -{ - if ( m_flNextSpeakTime > gpGlobals->time ) - { - // my time to talk is still in the future. - return FALSE; - } - - if ( pev->spawnflags & SF_MONSTER_GAG ) - { - if ( m_MonsterState != MONSTERSTATE_COMBAT ) - { - // if gagged, don't talk outside of combat. - // if not going to talk because of this, put the talk time - // into the future a bit, so we don't talk immediately after - // going into combat - m_flNextSpeakTime = gpGlobals->time + 3; - return FALSE; - } - } - - return TRUE; -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CMAGrunt :: PrescheduleThink ( void ) -{ - if ( ShouldSpeak() ) - { - if ( m_flNextWordTime < gpGlobals->time ) - { - int num = -1; - - do - { - num = RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1); - } while( num == m_iLastWord ); - - m_iLastWord = num; - - // play a new sound - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pIdleSounds[ num ], 1.0, ATTN_NORM ); - - // is this word our last? - if ( RANDOM_LONG( 1, 10 ) <= 1 ) - { - // stop talking. - StopTalking(); - } - else - { - m_flNextWordTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 1 ); - } - } - } -} - -//========================================================= -// DieSound -//========================================================= -void CMAGrunt :: DeathSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// AlertSound -//========================================================= -void CMAGrunt :: AlertSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// AttackSound -//========================================================= -void CMAGrunt :: AttackSound ( void ) -{ - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAttackSounds[RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// PainSound -//========================================================= -void CMAGrunt :: PainSound ( void ) -{ - if ( m_flNextPainTime > gpGlobals->time ) - { - return; - } - - m_flNextPainTime = gpGlobals->time + 0.6; - - StopTalking(); - - EMIT_SOUND ( ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1)], 1.0, ATTN_NORM ); -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMAGrunt :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MILITARY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMAGrunt :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 110; - break; - default: ys = 100; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case AGRUNT_AE_HORNET1: - case AGRUNT_AE_HORNET2: - case AGRUNT_AE_HORNET3: - case AGRUNT_AE_HORNET4: - case AGRUNT_AE_HORNET5: - { - // m_vecEnemyLKP should be center of enemy body - Vector vecArmPos, vecArmDir; - Vector vecDirToEnemy; - Vector angDir; - - if (HasConditions( bits_COND_SEE_ENEMY)) - { - vecDirToEnemy = ( ( m_vecEnemyLKP ) - pev->origin ); - angDir = UTIL_VecToAngles( vecDirToEnemy ); - vecDirToEnemy = vecDirToEnemy.Normalize(); - } - else - { - angDir = pev->angles; - UTIL_MakeAimVectors( angDir ); - vecDirToEnemy = gpGlobals->v_forward; - } - - pev->effects = EF_MUZZLEFLASH; - - // make angles +-180 - if (angDir.x > 180) - { - angDir.x = angDir.x - 360; - } - - SetBlending( 0, angDir.x ); - GetAttachment( 0, vecArmPos, vecArmDir ); - - vecArmPos = vecArmPos + vecDirToEnemy * 32; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecArmPos ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( vecArmPos.x ); // pos - WRITE_COORD( vecArmPos.y ); - WRITE_COORD( vecArmPos.z ); - WRITE_SHORT( iAgruntMuzzleFlash ); // model - WRITE_BYTE( 6 ); // size * 10 - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); - -//jlb CMBaseEntity *pHornet = CMBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict() ); - CMHornet *pHornet = CreateClassPtr((CMHornet *)NULL); - - if (pHornet != NULL) - { - pHornet->pev->origin = vecArmPos; - pHornet->pev->angles = UTIL_VecToAngles( vecDirToEnemy ); - pHornet->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pHornet->pev->absmin = pHornet->pev->origin - Vector(1,1,1); - pHornet->pev->absmax = pHornet->pev->origin + Vector(1,1,1); - - pHornet->Spawn(); - - UTIL_MakeVectors ( pHornet->pev->angles ); - pHornet->pev->velocity = gpGlobals->v_forward * 300; - - CMBaseMonster *pHornetMonster = pHornet->MyMonsterPointer(); - - if ( pHornetMonster ) - { - pHornetMonster->m_hEnemy = m_hEnemy; - } - } - } - break; - - case AGRUNT_AE_LEFT_FOOT: - switch (RANDOM_LONG(0,1)) - { - // left foot - case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder2.wav", 1, ATTN_NORM, 0, 70 ); break; - case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder4.wav", 1, ATTN_NORM, 0, 70 ); break; - } - break; - case AGRUNT_AE_RIGHT_FOOT: - // right foot - switch (RANDOM_LONG(0,1)) - { - case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder1.wav", 1, ATTN_NORM, 0, 70 ); break; - case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder3.wav", 1, ATTN_NORM, 0 ,70); break; - } - break; - - case AGRUNT_AE_LEFT_PUNCH: - { - edict_t *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); - - if ( pHurt ) - { - pHurt->v.punchangle.y = -25; - pHurt->v.punchangle.x = 8; - - // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. - if ( UTIL_IsPlayer(pHurt) ) - { - // this is a player. Knock him around. - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 250; - } - - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - - if (UTIL_IsPlayer(pHurt)) - SpawnBlood(vecArmPos, BLOOD_COLOR_RED, 25); - else if (pHurt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - SpawnBlood(vecArmPos, pMonster->BloodColor(), 25);// a little surface blood. - } - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - break; - - case AGRUNT_AE_RIGHT_PUNCH: - { - edict_t *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); - - if ( pHurt ) - { - pHurt->v.punchangle.y = 25; - pHurt->v.punchangle.x = 8; - - // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. - if ( UTIL_IsPlayer(pHurt) ) - { - // this is a player. Knock him around. - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -250; - } - - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - - if (UTIL_IsPlayer(pHurt)) - SpawnBlood(vecArmPos, BLOOD_COLOR_RED, 25); - else if (pHurt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - SpawnBlood(vecArmPos, pMonster->BloodColor(), 25);// a little surface blood. - } - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMAGrunt :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/agrunt.mdl"); - UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.agruntHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = 0; - - m_HackedGunPos = Vector( 24, 64, 48 ); - - m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_alien_grunt" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Alien Grunt" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMAGrunt :: Precache() -{ - int i; - - PRECACHE_MODEL("models/agrunt.mdl"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) - PRECACHE_SOUND((char *)pDieSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - - PRECACHE_SOUND( "hassault/hw_shoot1.wav" ); - - iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" ); - - CMHornet hornet; - hornet.Precache(); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// Fail Schedule -//========================================================= -Task_t tlAGruntFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slAGruntFail[] = -{ - { - tlAGruntFail, - ARRAYSIZE ( tlAGruntFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AGrunt Fail" - }, -}; - -//========================================================= -// Combat Fail Schedule -//========================================================= -Task_t tlAGruntCombatFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slAGruntCombatFail[] = -{ - { - tlAGruntCombatFail, - ARRAYSIZE ( tlAGruntCombatFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AGrunt Combat Fail" - }, -}; - -//========================================================= -// Standoff schedule. Used in combat when a monster is -// hiding in cover or the enemy has moved out of sight. -// Should we look around in this schedule? -//========================================================= -Task_t tlAGruntStandoff[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, -}; - -Schedule_t slAGruntStandoff[] = -{ - { - tlAGruntStandoff, - ARRAYSIZE ( tlAGruntStandoff ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND, - 0, - "Agrunt Standoff" - } -}; - -//========================================================= -// Suppress -//========================================================= -Task_t tlAGruntSuppressHornet[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slAGruntSuppress[] = -{ - { - tlAGruntSuppressHornet, - ARRAYSIZE ( tlAGruntSuppressHornet ), - 0, - 0, - "AGrunt Suppress Hornet", - }, -}; - -//========================================================= -// primary range attacks -//========================================================= -Task_t tlAGruntRangeAttack1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slAGruntRangeAttack1[] = -{ - { - tlAGruntRangeAttack1, - ARRAYSIZE ( tlAGruntRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE, - - 0, - "AGrunt Range Attack1" - }, -}; - - -Task_t tlAGruntHiddenRangeAttack1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_STANDOFF }, - { TASK_AGRUNT_SETUP_HIDE_ATTACK, 0 }, - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, 0 }, - { TASK_RANGE_ATTACK1_NOTURN, (float)0 }, -}; - -Schedule_t slAGruntHiddenRangeAttack[] = -{ - { - tlAGruntHiddenRangeAttack1, - ARRAYSIZE ( tlAGruntHiddenRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "AGrunt Hidden Range Attack1" - }, -}; - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAGruntTakeCoverFromEnemy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAGruntTakeCoverFromEnemy[] = -{ - { - tlAGruntTakeCoverFromEnemy, - ARRAYSIZE ( tlAGruntTakeCoverFromEnemy ), - bits_COND_NEW_ENEMY, - 0, - "AGruntTakeCoverFromEnemy" - }, -}; - -//========================================================= -// Victory dance! -//========================================================= -Task_t tlAGruntVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_AGRUNT_THREAT_DISPLAY }, - { TASK_WAIT, (float)0.2 }, - { TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, - { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, -}; - -Schedule_t slAGruntVictoryDance[] = -{ - { - tlAGruntVictoryDance, - ARRAYSIZE ( tlAGruntVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "AGruntVictoryDance" - }, -}; - -//========================================================= -//========================================================= -Task_t tlAGruntThreatDisplay[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, -}; - -Schedule_t slAGruntThreatDisplay[] = -{ - { - tlAGruntThreatDisplay, - ARRAYSIZE ( tlAGruntThreatDisplay ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "AGruntThreatDisplay" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMAGrunt ) -{ - slAGruntFail, - slAGruntCombatFail, - slAGruntStandoff, - slAGruntSuppress, - slAGruntRangeAttack1, - slAGruntHiddenRangeAttack, - slAGruntTakeCoverFromEnemy, - slAGruntVictoryDance, - slAGruntThreatDisplay, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMAGrunt, CMBaseMonster ); - -//========================================================= -// FCanCheckAttacks - this is overridden for alien grunts -// because they can use their smart weapons against unseen -// enemies. Base class doesn't attack anyone it can't see. -//========================================================= -BOOL CMAGrunt :: FCanCheckAttacks ( void ) -{ - if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -//========================================================= -// CheckMeleeAttack1 - alien grunts zap the crap out of -// any enemy that gets too close. -//========================================================= -BOOL CMAGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if ( HasConditions ( bits_COND_SEE_ENEMY ) && flDist <= AGRUNT_MELEE_DIST && flDot >= 0.6 && m_hEnemy != NULL ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 -// -// !!!LATER - we may want to load balance this. Several -// tracelines are done, so we may not want to do this every -// server frame. Definitely not while firing. -//========================================================= -BOOL CMAGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( gpGlobals->time < m_flNextHornetAttackCheck ) - { - return m_fCanHornetAttack; - } - - if ( HasConditions( bits_COND_SEE_ENEMY ) && flDist >= AGRUNT_MELEE_DIST && flDist <= 1024 && flDot >= 0.5 ) - { - TraceResult tr; - Vector vecArmPos, vecArmDir; - - // verify that a shot fired from the gun will hit the enemy before the world. - // !!!LATER - we may wish to do something different for projectile weapons as opposed to instant-hit - UTIL_MakeVectors( pev->angles ); - GetAttachment( 0, vecArmPos, vecArmDir ); -// UTIL_TraceLine( vecArmPos, vecArmPos + gpGlobals->v_forward * 256, ignore_monsters, ENT(pev), &tr); - UTIL_TraceLine( vecArmPos, UTIL_BodyTarget(m_hEnemy, vecArmPos), dont_ignore_monsters, ENT(pev), &tr); - - if ( tr.flFraction == 1.0 || (tr.pHit == m_hEnemy) ) - { - m_flNextHornetAttackCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 ); - m_fCanHornetAttack = TRUE; - return m_fCanHornetAttack; - } - } - - m_flNextHornetAttackCheck = gpGlobals->time + 0.2;// don't check for half second if this check wasn't successful - m_fCanHornetAttack = FALSE; - return m_fCanHornetAttack; -} - -//========================================================= -// StartTask -//========================================================= -void CMAGrunt :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE: - { - UTIL_MakeVectors( pev->angles ); - if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else - { - ALERT ( at_aiconsole, "AGruntGetPathToEnemyCorpse failed!!\n" ); - TaskFail(); - } - } - break; - - case TASK_AGRUNT_SETUP_HIDE_ATTACK: - // alien grunt shoots hornets back out into the open from a concealed location. - // try to find a spot to throw that gives the smart weapon a good chance of finding the enemy. - // ideally, this spot is along a line that is perpendicular to a line drawn from the agrunt to the enemy. - - { - Vector vecCenter; - TraceResult tr; - BOOL fSkip; - - fSkip = FALSE; - vecCenter = Center(); - - UTIL_VecToAngles( m_vecEnemyLKP - pev->origin ); - - UTIL_TraceLine( Center() + gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin + gpGlobals->v_right * 128 ); - fSkip = TRUE; - TaskComplete(); - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() - gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin - gpGlobals->v_right * 128 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() + gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin + gpGlobals->v_right * 256 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - UTIL_TraceLine( Center() - gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); - if ( tr.flFraction == 1.0 ) - { - MakeIdealYaw ( pev->origin - gpGlobals->v_right * 256 ); - fSkip = TRUE; - TaskComplete(); - } - } - - if ( !fSkip ) - { - TaskFail(); - } - } - break; - - default: - CMBaseMonster :: StartTask ( pTask ); - break; - } -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMAGrunt :: GetSchedule ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - if ( HasConditions(bits_COND_NEW_ENEMY) ) - { - return GetScheduleOfType( SCHED_WAKE_ANGRY ); - } - - // zap player! - if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - AttackSound();// this is a total hack. Should be parto f the schedule - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } - - if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) - { - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - - // can attack - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - return GetScheduleOfType ( SCHED_CHASE_ENEMY ); - } - } - - return CMBaseMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CMAGrunt :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - return &slAGruntTakeCoverFromEnemy[ 0 ]; - break; - - case SCHED_RANGE_ATTACK1: - if ( HasConditions( bits_COND_SEE_ENEMY ) ) - { - //normal attack - return &slAGruntRangeAttack1[ 0 ]; - } - else - { - // attack an unseen enemy - // return &slAGruntHiddenRangeAttack[ 0 ]; - return &slAGruntRangeAttack1[ 0 ]; - } - break; - - case SCHED_AGRUNT_THREAT_DISPLAY: - return &slAGruntThreatDisplay[ 0 ]; - break; - - case SCHED_AGRUNT_SUPPRESS: - return &slAGruntSuppress[ 0 ]; - break; - - case SCHED_STANDOFF: - return &slAGruntStandoff[ 0 ]; - break; - - case SCHED_VICTORY_DANCE: - return &slAGruntVictoryDance[ 0 ]; - break; - - case SCHED_FAIL: - // no fail schedule specified, so pick a good generic one. - { - if ( m_hEnemy != NULL ) - { - // I have an enemy - // !!!LATER - what if this enemy is really far away and i'm chasing him? - // this schedule will make me stop, face his last known position for 2 - // seconds, and then try to move again - return &slAGruntCombatFail[ 0 ]; - } - - return &slAGruntFail[ 0 ]; - } - break; - - } - - return CMBaseMonster :: GetScheduleOfType( Type ); -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Agrunt - Dominant, warlike alien grunt monster +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "weapons.h" +#include "hornet.h" +#include "skill.h" + + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_AGRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_AGRUNT_THREAT_DISPLAY, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_COMMON_TASK + 1, + TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, +}; + +int iAgruntMuzzleFlash; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define AGRUNT_AE_HORNET1 ( 1 ) +#define AGRUNT_AE_HORNET2 ( 2 ) +#define AGRUNT_AE_HORNET3 ( 3 ) +#define AGRUNT_AE_HORNET4 ( 4 ) +#define AGRUNT_AE_HORNET5 ( 5 ) +// some events are set up in the QC file that aren't recognized by the code yet. +#define AGRUNT_AE_PUNCH ( 6 ) +#define AGRUNT_AE_BITE ( 7 ) + +#define AGRUNT_AE_LEFT_FOOT ( 10 ) +#define AGRUNT_AE_RIGHT_FOOT ( 11 ) + +#define AGRUNT_AE_LEFT_PUNCH ( 12 ) +#define AGRUNT_AE_RIGHT_PUNCH ( 13 ) + +#define AGRUNT_MELEE_DIST 100 + +const char *CMAGrunt::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CMAGrunt::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CMAGrunt::pAttackSounds[] = +{ + "agrunt/ag_attack1.wav", + "agrunt/ag_attack2.wav", + "agrunt/ag_attack3.wav", +}; + +const char *CMAGrunt::pDieSounds[] = +{ + "agrunt/ag_die1.wav", + "agrunt/ag_die4.wav", + "agrunt/ag_die5.wav", +}; + +const char *CMAGrunt::pPainSounds[] = +{ + "agrunt/ag_pain1.wav", + "agrunt/ag_pain2.wav", + "agrunt/ag_pain3.wav", + "agrunt/ag_pain4.wav", + "agrunt/ag_pain5.wav", +}; + +const char *CMAGrunt::pIdleSounds[] = +{ + "agrunt/ag_idle1.wav", + "agrunt/ag_idle2.wav", + "agrunt/ag_idle3.wav", + "agrunt/ag_idle4.wav", +}; + +const char *CMAGrunt::pAlertSounds[] = +{ + "agrunt/ag_alert1.wav", + "agrunt/ag_alert3.wav", + "agrunt/ag_alert4.wav", + "agrunt/ag_alert5.wav", +}; + +//========================================================= +// IRelationship - overridden because Human Grunts are +// Alien Grunt's nemesis. +//========================================================= +int CMAGrunt::IRelationship ( CMBaseEntity *pTarget ) +{ + // ditto hgrunt.cpp + /* + if ( strcmp(STRING(pTarget->pev->model), "models/hgrunt.mdl") == 0 ) + { + return R_NM; + } + */ + + return CMBaseMonster :: IRelationship( pTarget ); +} + +//========================================================= +// ISoundMask +//========================================================= +int CMAGrunt :: ISoundMask ( void ) +{ + return 0; +} + +//========================================================= +// TraceAttack +//========================================================= +void CMAGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( ptr->iHitgroup == 10 && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB))) + { + // hit armor + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); + pev->dmgtime = gpGlobals->time; + } + + if ( RANDOM_LONG( 0, 1 ) == 0 ) + { + Vector vecTracerDir = vecDir; + + vecTracerDir.x += RANDOM_FLOAT( -0.3, 0.3 ); + vecTracerDir.y += RANDOM_FLOAT( -0.3, 0.3 ); + vecTracerDir.z += RANDOM_FLOAT( -0.3, 0.3 ); + + vecTracerDir = vecTracerDir * -512; + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, ptr->vecEndPos ); + WRITE_BYTE( TE_TRACER ); + WRITE_COORD( ptr->vecEndPos.x ); + WRITE_COORD( ptr->vecEndPos.y ); + WRITE_COORD( ptr->vecEndPos.z ); + + WRITE_COORD( vecTracerDir.x ); + WRITE_COORD( vecTracerDir.y ); + WRITE_COORD( vecTracerDir.z ); + MESSAGE_END(); + } + + flDamage -= 20; + if (flDamage <= 0) + flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + else + { + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + } + + AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); +} + +//========================================================= +// StopTalking - won't speak again for 10-20 seconds. +//========================================================= +void CMAGrunt::StopTalking( void ) +{ + m_flNextWordTime = m_flNextSpeakTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); +} + +//========================================================= +// ShouldSpeak - Should this agrunt be talking? +//========================================================= +BOOL CMAGrunt::ShouldSpeak( void ) +{ + if ( m_flNextSpeakTime > gpGlobals->time ) + { + // my time to talk is still in the future. + return FALSE; + } + + if ( pev->spawnflags & SF_MONSTER_GAG ) + { + if ( m_MonsterState != MONSTERSTATE_COMBAT ) + { + // if gagged, don't talk outside of combat. + // if not going to talk because of this, put the talk time + // into the future a bit, so we don't talk immediately after + // going into combat + m_flNextSpeakTime = gpGlobals->time + 3; + return FALSE; + } + } + + return TRUE; +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CMAGrunt :: PrescheduleThink ( void ) +{ + if ( ShouldSpeak() ) + { + if ( m_flNextWordTime < gpGlobals->time ) + { + int num = -1; + + do + { + num = RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1); + } while( num == m_iLastWord ); + + m_iLastWord = num; + + // play a new sound + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pIdleSounds[ num ], 1.0, ATTN_NORM ); + + // is this word our last? + if ( RANDOM_LONG( 1, 10 ) <= 1 ) + { + // stop talking. + StopTalking(); + } + else + { + m_flNextWordTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 1 ); + } + } + } +} + +//========================================================= +// DieSound +//========================================================= +void CMAGrunt :: DeathSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// AlertSound +//========================================================= +void CMAGrunt :: AlertSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// AttackSound +//========================================================= +void CMAGrunt :: AttackSound ( void ) +{ + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pAttackSounds[RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// PainSound +//========================================================= +void CMAGrunt :: PainSound ( void ) +{ + if ( m_flNextPainTime > gpGlobals->time ) + { + return; + } + + m_flNextPainTime = gpGlobals->time + 0.6; + + StopTalking(); + + EMIT_SOUND ( ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1)], 1.0, ATTN_NORM ); +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMAGrunt :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MILITARY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMAGrunt :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 110; + break; + default: ys = 100; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case AGRUNT_AE_HORNET1: + case AGRUNT_AE_HORNET2: + case AGRUNT_AE_HORNET3: + case AGRUNT_AE_HORNET4: + case AGRUNT_AE_HORNET5: + { + // m_vecEnemyLKP should be center of enemy body + Vector vecArmPos, vecArmDir; + Vector vecDirToEnemy; + Vector angDir; + + if (HasConditions( bits_COND_SEE_ENEMY)) + { + vecDirToEnemy = ( ( m_vecEnemyLKP ) - pev->origin ); + angDir = UTIL_VecToAngles( vecDirToEnemy ); + vecDirToEnemy = vecDirToEnemy.Normalize(); + } + else + { + angDir = pev->angles; + UTIL_MakeAimVectors( angDir ); + vecDirToEnemy = gpGlobals->v_forward; + } + + pev->effects = EF_MUZZLEFLASH; + + // make angles +-180 + if (angDir.x > 180) + { + angDir.x = angDir.x - 360; + } + + SetBlending( 0, angDir.x ); + GetAttachment( 0, vecArmPos, vecArmDir ); + + vecArmPos = vecArmPos + vecDirToEnemy * 32; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecArmPos ); + WRITE_BYTE( TE_SPRITE ); + WRITE_COORD( vecArmPos.x ); // pos + WRITE_COORD( vecArmPos.y ); + WRITE_COORD( vecArmPos.z ); + WRITE_SHORT( iAgruntMuzzleFlash ); // model + WRITE_BYTE( 6 ); // size * 10 + WRITE_BYTE( 128 ); // brightness + MESSAGE_END(); + +//jlb CMBaseEntity *pHornet = CMBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict() ); + CMHornet *pHornet = CreateClassPtr((CMHornet *)NULL); + + if (pHornet != NULL) + { + pHornet->pev->origin = vecArmPos; + pHornet->pev->angles = UTIL_VecToAngles( vecDirToEnemy ); + pHornet->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pHornet->pev->absmin = pHornet->pev->origin - Vector(1,1,1); + pHornet->pev->absmax = pHornet->pev->origin + Vector(1,1,1); + + pHornet->Spawn(); + + UTIL_MakeVectors ( pHornet->pev->angles ); + pHornet->pev->velocity = gpGlobals->v_forward * 300; + + CMBaseMonster *pHornetMonster = pHornet->MyMonsterPointer(); + + if ( pHornetMonster ) + { + pHornetMonster->m_hEnemy = m_hEnemy; + } + } + } + break; + + case AGRUNT_AE_LEFT_FOOT: + switch (RANDOM_LONG(0,1)) + { + // left foot + case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder2.wav", 1, ATTN_NORM, 0, 70 ); break; + case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder4.wav", 1, ATTN_NORM, 0, 70 ); break; + } + break; + case AGRUNT_AE_RIGHT_FOOT: + // right foot + switch (RANDOM_LONG(0,1)) + { + case 0: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder1.wav", 1, ATTN_NORM, 0, 70 ); break; + case 1: EMIT_SOUND_DYN ( ENT(pev), CHAN_BODY, "player/pl_ladder3.wav", 1, ATTN_NORM, 0 ,70); break; + } + break; + + case AGRUNT_AE_LEFT_PUNCH: + { + edict_t *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); + + if ( pHurt ) + { + pHurt->v.punchangle.y = -25; + pHurt->v.punchangle.x = 8; + + // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. + if ( UTIL_IsPlayer(pHurt) ) + { + // this is a player. Knock him around. + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 250; + } + + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + + if (UTIL_IsPlayer(pHurt)) + SpawnBlood(vecArmPos, BLOOD_COLOR_RED, 25); + else if (pHurt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + SpawnBlood(vecArmPos, pMonster->BloodColor(), 25);// a little surface blood. + } + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + break; + + case AGRUNT_AE_RIGHT_PUNCH: + { + edict_t *pHurt = CheckTraceHullAttack( AGRUNT_MELEE_DIST, gSkillData.agruntDmgPunch, DMG_CLUB ); + + if ( pHurt ) + { + pHurt->v.punchangle.y = 25; + pHurt->v.punchangle.x = 8; + + // OK to use gpGlobals without calling MakeVectors, cause CheckTraceHullAttack called it above. + if ( UTIL_IsPlayer(pHurt) ) + { + // this is a player. Knock him around. + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -250; + } + + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + + if (UTIL_IsPlayer(pHurt)) + SpawnBlood(vecArmPos, BLOOD_COLOR_RED, 25); + else if (pHurt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + SpawnBlood(vecArmPos, pMonster->BloodColor(), 25);// a little surface blood. + } + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMAGrunt :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/agrunt.mdl"); + UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.agruntHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = 0; + + m_HackedGunPos = Vector( 24, 64, 48 ); + + m_flNextSpeakTime = m_flNextWordTime = gpGlobals->time + 10 + RANDOM_LONG(0, 10); + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_alien_grunt" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Alien Grunt" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMAGrunt :: Precache() +{ + int i; + + PRECACHE_MODEL("models/agrunt.mdl"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) + PRECACHE_SOUND((char *)pDieSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + + PRECACHE_SOUND( "hassault/hw_shoot1.wav" ); + + iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" ); + + CMHornet hornet; + hornet.Precache(); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// Fail Schedule +//========================================================= +Task_t tlAGruntFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slAGruntFail[] = +{ + { + tlAGruntFail, + ARRAYSIZE ( tlAGruntFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AGrunt Fail" + }, +}; + +//========================================================= +// Combat Fail Schedule +//========================================================= +Task_t tlAGruntCombatFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slAGruntCombatFail[] = +{ + { + tlAGruntCombatFail, + ARRAYSIZE ( tlAGruntCombatFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AGrunt Combat Fail" + }, +}; + +//========================================================= +// Standoff schedule. Used in combat when a monster is +// hiding in cover or the enemy has moved out of sight. +// Should we look around in this schedule? +//========================================================= +Task_t tlAGruntStandoff[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, +}; + +Schedule_t slAGruntStandoff[] = +{ + { + tlAGruntStandoff, + ARRAYSIZE ( tlAGruntStandoff ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND, + 0, + "Agrunt Standoff" + } +}; + +//========================================================= +// Suppress +//========================================================= +Task_t tlAGruntSuppressHornet[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slAGruntSuppress[] = +{ + { + tlAGruntSuppressHornet, + ARRAYSIZE ( tlAGruntSuppressHornet ), + 0, + 0, + "AGrunt Suppress Hornet", + }, +}; + +//========================================================= +// primary range attacks +//========================================================= +Task_t tlAGruntRangeAttack1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slAGruntRangeAttack1[] = +{ + { + tlAGruntRangeAttack1, + ARRAYSIZE ( tlAGruntRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE, + + 0, + "AGrunt Range Attack1" + }, +}; + + +Task_t tlAGruntHiddenRangeAttack1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_STANDOFF }, + { TASK_AGRUNT_SETUP_HIDE_ATTACK, 0 }, + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, 0 }, + { TASK_RANGE_ATTACK1_NOTURN, (float)0 }, +}; + +Schedule_t slAGruntHiddenRangeAttack[] = +{ + { + tlAGruntHiddenRangeAttack1, + ARRAYSIZE ( tlAGruntHiddenRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "AGrunt Hidden Range Attack1" + }, +}; + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAGruntTakeCoverFromEnemy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAGruntTakeCoverFromEnemy[] = +{ + { + tlAGruntTakeCoverFromEnemy, + ARRAYSIZE ( tlAGruntTakeCoverFromEnemy ), + bits_COND_NEW_ENEMY, + 0, + "AGruntTakeCoverFromEnemy" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlAGruntVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_AGRUNT_THREAT_DISPLAY }, + { TASK_WAIT, (float)0.2 }, + { TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, + { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, +}; + +Schedule_t slAGruntVictoryDance[] = +{ + { + tlAGruntVictoryDance, + ARRAYSIZE ( tlAGruntVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "AGruntVictoryDance" + }, +}; + +//========================================================= +//========================================================= +Task_t tlAGruntThreatDisplay[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_THREAT_DISPLAY }, +}; + +Schedule_t slAGruntThreatDisplay[] = +{ + { + tlAGruntThreatDisplay, + ARRAYSIZE ( tlAGruntThreatDisplay ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "AGruntThreatDisplay" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMAGrunt ) +{ + slAGruntFail, + slAGruntCombatFail, + slAGruntStandoff, + slAGruntSuppress, + slAGruntRangeAttack1, + slAGruntHiddenRangeAttack, + slAGruntTakeCoverFromEnemy, + slAGruntVictoryDance, + slAGruntThreatDisplay, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMAGrunt, CMBaseMonster ); + +//========================================================= +// FCanCheckAttacks - this is overridden for alien grunts +// because they can use their smart weapons against unseen +// enemies. Base class doesn't attack anyone it can't see. +//========================================================= +BOOL CMAGrunt :: FCanCheckAttacks ( void ) +{ + if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +//========================================================= +// CheckMeleeAttack1 - alien grunts zap the crap out of +// any enemy that gets too close. +//========================================================= +BOOL CMAGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if ( HasConditions ( bits_COND_SEE_ENEMY ) && flDist <= AGRUNT_MELEE_DIST && flDot >= 0.6 && m_hEnemy != NULL ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 +// +// !!!LATER - we may want to load balance this. Several +// tracelines are done, so we may not want to do this every +// server frame. Definitely not while firing. +//========================================================= +BOOL CMAGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( gpGlobals->time < m_flNextHornetAttackCheck ) + { + return m_fCanHornetAttack; + } + + if ( HasConditions( bits_COND_SEE_ENEMY ) && flDist >= AGRUNT_MELEE_DIST && flDist <= 1024 && flDot >= 0.5 ) + { + TraceResult tr; + Vector vecArmPos, vecArmDir; + + // verify that a shot fired from the gun will hit the enemy before the world. + // !!!LATER - we may wish to do something different for projectile weapons as opposed to instant-hit + UTIL_MakeVectors( pev->angles ); + GetAttachment( 0, vecArmPos, vecArmDir ); +// UTIL_TraceLine( vecArmPos, vecArmPos + gpGlobals->v_forward * 256, ignore_monsters, ENT(pev), &tr); + UTIL_TraceLine( vecArmPos, UTIL_BodyTarget(m_hEnemy, vecArmPos), dont_ignore_monsters, ENT(pev), &tr); + + if ( tr.flFraction == 1.0 || (tr.pHit == m_hEnemy) ) + { + m_flNextHornetAttackCheck = gpGlobals->time + RANDOM_FLOAT( 2, 5 ); + m_fCanHornetAttack = TRUE; + return m_fCanHornetAttack; + } + } + + m_flNextHornetAttackCheck = gpGlobals->time + 0.2;// don't check for half second if this check wasn't successful + m_fCanHornetAttack = FALSE; + return m_fCanHornetAttack; +} + +//========================================================= +// StartTask +//========================================================= +void CMAGrunt :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_AGRUNT_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if ( BuildRoute ( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT ( at_aiconsole, "AGruntGetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + + case TASK_AGRUNT_SETUP_HIDE_ATTACK: + // alien grunt shoots hornets back out into the open from a concealed location. + // try to find a spot to throw that gives the smart weapon a good chance of finding the enemy. + // ideally, this spot is along a line that is perpendicular to a line drawn from the agrunt to the enemy. + + { + Vector vecCenter; + TraceResult tr; + BOOL fSkip; + + fSkip = FALSE; + vecCenter = Center(); + + UTIL_VecToAngles( m_vecEnemyLKP - pev->origin ); + + UTIL_TraceLine( Center() + gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin + gpGlobals->v_right * 128 ); + fSkip = TRUE; + TaskComplete(); + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() - gpGlobals->v_forward * 128, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin - gpGlobals->v_right * 128 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() + gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin + gpGlobals->v_right * 256 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + UTIL_TraceLine( Center() - gpGlobals->v_forward * 256, m_vecEnemyLKP, ignore_monsters, ENT(pev), &tr); + if ( tr.flFraction == 1.0 ) + { + MakeIdealYaw ( pev->origin - gpGlobals->v_right * 256 ); + fSkip = TRUE; + TaskComplete(); + } + } + + if ( !fSkip ) + { + TaskFail(); + } + } + break; + + default: + CMBaseMonster :: StartTask ( pTask ); + break; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMAGrunt :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + return GetScheduleOfType( SCHED_WAKE_ANGRY ); + } + + // zap player! + if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + AttackSound();// this is a total hack. Should be parto f the schedule + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } + + if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + + // can attack + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + return GetScheduleOfType ( SCHED_CHASE_ENEMY ); + } + } + + return CMBaseMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CMAGrunt :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + return &slAGruntTakeCoverFromEnemy[ 0 ]; + break; + + case SCHED_RANGE_ATTACK1: + if ( HasConditions( bits_COND_SEE_ENEMY ) ) + { + //normal attack + return &slAGruntRangeAttack1[ 0 ]; + } + else + { + // attack an unseen enemy + // return &slAGruntHiddenRangeAttack[ 0 ]; + return &slAGruntRangeAttack1[ 0 ]; + } + break; + + case SCHED_AGRUNT_THREAT_DISPLAY: + return &slAGruntThreatDisplay[ 0 ]; + break; + + case SCHED_AGRUNT_SUPPRESS: + return &slAGruntSuppress[ 0 ]; + break; + + case SCHED_STANDOFF: + return &slAGruntStandoff[ 0 ]; + break; + + case SCHED_VICTORY_DANCE: + return &slAGruntVictoryDance[ 0 ]; + break; + + case SCHED_FAIL: + // no fail schedule specified, so pick a good generic one. + { + if ( m_hEnemy != NULL ) + { + // I have an enemy + // !!!LATER - what if this enemy is really far away and i'm chasing him? + // this schedule will make me stop, face his last known position for 2 + // seconds, and then try to move again + return &slAGruntCombatFail[ 0 ]; + } + + return &slAGruntFail[ 0 ]; + } + break; + + } + + return CMBaseMonster :: GetScheduleOfType( Type ); +} + diff --git a/src/dlls/animating.cpp b/src/dlls/animating.cpp index dff438e..afcf943 100644 --- a/src/dlls/animating.cpp +++ b/src/dlls/animating.cpp @@ -1,306 +1,306 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== monsters.cpp ======================================================== - - Monster-related utility code - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "animation.h" - - -//========================================================= -// StudioFrameAdvance - advance the animation frame up to the current time -// if an flInterval is passed in, only advance animation that number of seconds -//========================================================= -float CMBaseAnimating :: StudioFrameAdvance ( float flInterval ) -{ - if (flInterval == 0.0) - { - flInterval = (gpGlobals->time - pev->animtime); - if (flInterval <= 0.001) - { - pev->animtime = gpGlobals->time; - return 0.0; - } - } - if (! pev->animtime) - flInterval = 0.0; - - pev->frame += flInterval * m_flFrameRate * pev->framerate; - pev->animtime = gpGlobals->time; - - if (pev->frame < 0.0 || pev->frame >= 256.0) - { - if (m_fSequenceLoops) - pev->frame -= (int)(pev->frame / 256.0) * 256.0; - else - pev->frame = (pev->frame < 0.0) ? 0 : 255; - m_fSequenceFinished = TRUE; // just in case it wasn't caught in GetEvents - } - - return flInterval; -} - -//========================================================= -// LookupActivity -//========================================================= -int CMBaseAnimating :: LookupActivity ( int activity ) -{ - ASSERT( activity != 0 ); - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return ::LookupActivity( pmodel, pev, activity ); -} - -//========================================================= -// LookupActivityHeaviest -// -// Get activity with highest 'weight' -// -//========================================================= -int CMBaseAnimating :: LookupActivityHeaviest ( int activity ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return ::LookupActivityHeaviest( pmodel, pev, activity ); -} - -//========================================================= -//========================================================= -int CMBaseAnimating :: LookupSequence ( const char *label ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return ::LookupSequence( pmodel, label ); -} - - -//========================================================= -//========================================================= -void CMBaseAnimating :: ResetSequenceInfo ( ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - GetSequenceInfo( pmodel, pev, &m_flFrameRate, &m_flGroundSpeed ); - m_fSequenceLoops = ((GetSequenceFlags() & STUDIO_LOOPING) != 0); - pev->animtime = gpGlobals->time; - pev->framerate = 1.0; - m_fSequenceFinished = FALSE; - m_flLastEventCheck = gpGlobals->time; -} - - - -//========================================================= -//========================================================= -BOOL CMBaseAnimating :: GetSequenceFlags( ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return ::GetSequenceFlags( pmodel, pev ); -} - -//========================================================= -// DispatchAnimEvents -//========================================================= -void CMBaseAnimating :: DispatchAnimEvents ( float flInterval ) -{ - MonsterEvent_t event; - - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - if ( !pmodel ) - { - ALERT( at_aiconsole, "Gibbed monster is thinking!\n" ); - return; - } - - // FIXME: I have to do this or some events get missed, and this is probably causing the problem below - flInterval = 0.1; - - // FIX: this still sometimes hits events twice - float flStart = pev->frame + (m_flLastEventCheck - pev->animtime) * m_flFrameRate * pev->framerate; - float flEnd = pev->frame + flInterval * m_flFrameRate * pev->framerate; - m_flLastEventCheck = pev->animtime + flInterval; - - m_fSequenceFinished = FALSE; - if (flEnd >= 256 || flEnd <= 0.0) - m_fSequenceFinished = TRUE; - - int index = 0; - - while ( (index = GetAnimationEvent( pmodel, pev, &event, flStart, flEnd, index ) ) != 0 ) - { - HandleAnimEvent( &event ); - } -} - - -//========================================================= -//========================================================= -float CMBaseAnimating :: SetBoneController ( int iController, float flValue ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return SetController( pmodel, pev, iController, flValue ); -} - -//========================================================= -//========================================================= -void CMBaseAnimating :: InitBoneControllers ( void ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - SetController( pmodel, pev, 0, 0.0 ); - SetController( pmodel, pev, 1, 0.0 ); - SetController( pmodel, pev, 2, 0.0 ); - SetController( pmodel, pev, 3, 0.0 ); -} - -//========================================================= -//========================================================= -float CMBaseAnimating :: SetBlending ( int iBlender, float flValue ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - return ::SetBlending( pmodel, pev, iBlender, flValue ); -} - -//========================================================= -//========================================================= -void CMBaseAnimating :: GetBonePosition ( int iBone, Vector &origin, Vector &angles ) -{ - GET_BONE_POSITION( ENT(pev), iBone, origin, angles ); -} - -//========================================================= -//========================================================= -void CMBaseAnimating :: GetAttachment ( int iAttachment, Vector &origin, Vector &angles ) -{ - GET_ATTACHMENT( ENT(pev), iAttachment, origin, angles ); -} - -//========================================================= -//========================================================= -int CMBaseAnimating :: FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ) -{ - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - if (piDir == NULL) - { - int iDir; - int sequence = ::FindTransition( pmodel, iEndingSequence, iGoalSequence, &iDir ); - if (iDir != 1) - return -1; - else - return sequence; - } - - return ::FindTransition( pmodel, iEndingSequence, iGoalSequence, piDir ); -} - -//========================================================= -//========================================================= -void CMBaseAnimating :: GetAutomovement( Vector &origin, Vector &angles, float flInterval ) -{ - -} - -void CMBaseAnimating :: SetBodygroup( int iGroup, int iValue ) -{ - ::SetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup, iValue ); -} - -int CMBaseAnimating :: GetBodygroup( int iGroup ) -{ - return ::GetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup ); -} - - -int CMBaseAnimating :: ExtractBbox( int sequence, float *mins, float *maxs ) -{ - return ::ExtractBbox( GET_MODEL_PTR( ENT(pev) ), sequence, mins, maxs ); -} - -//========================================================= -//========================================================= - -void CMBaseAnimating :: SetSequenceBox( void ) -{ - Vector mins, maxs; - - // Get sequence bbox - if ( ExtractBbox( pev->sequence, mins, maxs ) ) - { - // expand box for rotation - // find min / max for rotations - float yaw = pev->angles.y * (M_PI / 180.0); - - Vector xvector, yvector; - xvector.x = cos(yaw); - xvector.y = sin(yaw); - yvector.x = -sin(yaw); - yvector.y = cos(yaw); - Vector bounds[2]; - - bounds[0] = mins; - bounds[1] = maxs; - - Vector rmin( 9999, 9999, 9999 ); - Vector rmax( -9999, -9999, -9999 ); - Vector base, transformed; - - for (int i = 0; i <= 1; i++ ) - { - base.x = bounds[i].x; - for ( int j = 0; j <= 1; j++ ) - { - base.y = bounds[j].y; - for ( int k = 0; k <= 1; k++ ) - { - base.z = bounds[k].z; - - // transform the point - transformed.x = xvector.x*base.x + yvector.x*base.y; - transformed.y = xvector.y*base.x + yvector.y*base.y; - transformed.z = base.z; - - if (transformed.x < rmin.x) - rmin.x = transformed.x; - if (transformed.x > rmax.x) - rmax.x = transformed.x; - if (transformed.y < rmin.y) - rmin.y = transformed.y; - if (transformed.y > rmax.y) - rmax.y = transformed.y; - if (transformed.z < rmin.z) - rmin.z = transformed.z; - if (transformed.z > rmax.z) - rmax.z = transformed.z; - } - } - } - rmin.z = 0; - rmax.z = rmin.z + 1; - UTIL_SetSize( pev, rmin, rmax ); - } -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== monsters.cpp ======================================================== + + Monster-related utility code + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "animation.h" + + +//========================================================= +// StudioFrameAdvance - advance the animation frame up to the current time +// if an flInterval is passed in, only advance animation that number of seconds +//========================================================= +float CMBaseAnimating :: StudioFrameAdvance ( float flInterval ) +{ + if (flInterval == 0.0) + { + flInterval = (gpGlobals->time - pev->animtime); + if (flInterval <= 0.001) + { + pev->animtime = gpGlobals->time; + return 0.0; + } + } + if (! pev->animtime) + flInterval = 0.0; + + pev->frame += flInterval * m_flFrameRate * pev->framerate; + pev->animtime = gpGlobals->time; + + if (pev->frame < 0.0 || pev->frame >= 256.0) + { + if (m_fSequenceLoops) + pev->frame -= (int)(pev->frame / 256.0) * 256.0; + else + pev->frame = (pev->frame < 0.0) ? 0 : 255; + m_fSequenceFinished = TRUE; // just in case it wasn't caught in GetEvents + } + + return flInterval; +} + +//========================================================= +// LookupActivity +//========================================================= +int CMBaseAnimating :: LookupActivity ( int activity ) +{ + ASSERT( activity != 0 ); + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return ::LookupActivity( pmodel, pev, activity ); +} + +//========================================================= +// LookupActivityHeaviest +// +// Get activity with highest 'weight' +// +//========================================================= +int CMBaseAnimating :: LookupActivityHeaviest ( int activity ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return ::LookupActivityHeaviest( pmodel, pev, activity ); +} + +//========================================================= +//========================================================= +int CMBaseAnimating :: LookupSequence ( const char *label ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return ::LookupSequence( pmodel, label ); +} + + +//========================================================= +//========================================================= +void CMBaseAnimating :: ResetSequenceInfo ( ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + GetSequenceInfo( pmodel, pev, &m_flFrameRate, &m_flGroundSpeed ); + m_fSequenceLoops = ((GetSequenceFlags() & STUDIO_LOOPING) != 0); + pev->animtime = gpGlobals->time; + pev->framerate = 1.0; + m_fSequenceFinished = FALSE; + m_flLastEventCheck = gpGlobals->time; +} + + + +//========================================================= +//========================================================= +BOOL CMBaseAnimating :: GetSequenceFlags( ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return ::GetSequenceFlags( pmodel, pev ); +} + +//========================================================= +// DispatchAnimEvents +//========================================================= +void CMBaseAnimating :: DispatchAnimEvents ( float flInterval ) +{ + MonsterEvent_t event; + + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + if ( !pmodel ) + { + ALERT( at_aiconsole, "Gibbed monster is thinking!\n" ); + return; + } + + // FIXME: I have to do this or some events get missed, and this is probably causing the problem below + flInterval = 0.1; + + // FIX: this still sometimes hits events twice + float flStart = pev->frame + (m_flLastEventCheck - pev->animtime) * m_flFrameRate * pev->framerate; + float flEnd = pev->frame + flInterval * m_flFrameRate * pev->framerate; + m_flLastEventCheck = pev->animtime + flInterval; + + m_fSequenceFinished = FALSE; + if (flEnd >= 256 || flEnd <= 0.0) + m_fSequenceFinished = TRUE; + + int index = 0; + + while ( (index = GetAnimationEvent( pmodel, pev, &event, flStart, flEnd, index ) ) != 0 ) + { + HandleAnimEvent( &event ); + } +} + + +//========================================================= +//========================================================= +float CMBaseAnimating :: SetBoneController ( int iController, float flValue ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return SetController( pmodel, pev, iController, flValue ); +} + +//========================================================= +//========================================================= +void CMBaseAnimating :: InitBoneControllers ( void ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + SetController( pmodel, pev, 0, 0.0 ); + SetController( pmodel, pev, 1, 0.0 ); + SetController( pmodel, pev, 2, 0.0 ); + SetController( pmodel, pev, 3, 0.0 ); +} + +//========================================================= +//========================================================= +float CMBaseAnimating :: SetBlending ( int iBlender, float flValue ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + return ::SetBlending( pmodel, pev, iBlender, flValue ); +} + +//========================================================= +//========================================================= +void CMBaseAnimating :: GetBonePosition ( int iBone, Vector &origin, Vector &angles ) +{ + GET_BONE_POSITION( ENT(pev), iBone, origin, angles ); +} + +//========================================================= +//========================================================= +void CMBaseAnimating :: GetAttachment ( int iAttachment, Vector &origin, Vector &angles ) +{ + GET_ATTACHMENT( ENT(pev), iAttachment, origin, angles ); +} + +//========================================================= +//========================================================= +int CMBaseAnimating :: FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ) +{ + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + if (piDir == NULL) + { + int iDir; + int sequence = ::FindTransition( pmodel, iEndingSequence, iGoalSequence, &iDir ); + if (iDir != 1) + return -1; + else + return sequence; + } + + return ::FindTransition( pmodel, iEndingSequence, iGoalSequence, piDir ); +} + +//========================================================= +//========================================================= +void CMBaseAnimating :: GetAutomovement( Vector &origin, Vector &angles, float flInterval ) +{ + +} + +void CMBaseAnimating :: SetBodygroup( int iGroup, int iValue ) +{ + ::SetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup, iValue ); +} + +int CMBaseAnimating :: GetBodygroup( int iGroup ) +{ + return ::GetBodygroup( GET_MODEL_PTR( ENT(pev) ), pev, iGroup ); +} + + +int CMBaseAnimating :: ExtractBbox( int sequence, float *mins, float *maxs ) +{ + return ::ExtractBbox( GET_MODEL_PTR( ENT(pev) ), sequence, mins, maxs ); +} + +//========================================================= +//========================================================= + +void CMBaseAnimating :: SetSequenceBox( void ) +{ + Vector mins, maxs; + + // Get sequence bbox + if ( ExtractBbox( pev->sequence, mins, maxs ) ) + { + // expand box for rotation + // find min / max for rotations + float yaw = pev->angles.y * (M_PI / 180.0); + + Vector xvector, yvector; + xvector.x = cos(yaw); + xvector.y = sin(yaw); + yvector.x = -sin(yaw); + yvector.y = cos(yaw); + Vector bounds[2]; + + bounds[0] = mins; + bounds[1] = maxs; + + Vector rmin( 9999, 9999, 9999 ); + Vector rmax( -9999, -9999, -9999 ); + Vector base, transformed; + + for (int i = 0; i <= 1; i++ ) + { + base.x = bounds[i].x; + for ( int j = 0; j <= 1; j++ ) + { + base.y = bounds[j].y; + for ( int k = 0; k <= 1; k++ ) + { + base.z = bounds[k].z; + + // transform the point + transformed.x = xvector.x*base.x + yvector.x*base.y; + transformed.y = xvector.y*base.x + yvector.y*base.y; + transformed.z = base.z; + + if (transformed.x < rmin.x) + rmin.x = transformed.x; + if (transformed.x > rmax.x) + rmax.x = transformed.x; + if (transformed.y < rmin.y) + rmin.y = transformed.y; + if (transformed.y > rmax.y) + rmax.y = transformed.y; + if (transformed.z < rmin.z) + rmin.z = transformed.z; + if (transformed.z > rmax.z) + rmax.z = transformed.z; + } + } + } + rmin.z = 0; + rmax.z = rmin.z + 1; + UTIL_SetSize( pev, rmin, rmax ); + } +} + diff --git a/src/dlls/animation.cpp b/src/dlls/animation.cpp index 8bf3e0d..18ae15f 100644 --- a/src/dlls/animation.cpp +++ b/src/dlls/animation.cpp @@ -1,522 +1,522 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#include -#include -#include - -#include "../common/nowin.h" - -typedef int BOOL; -#define TRUE 1 -#define FALSE 0 - -// hack into header files that we can ship -typedef int qboolean; -typedef unsigned char byte; -#include "../utils/common/mathlib.h" -#include "const.h" -#include "progdefs.h" -#include "edict.h" -#include "eiface.h" - -#include "studio.h" - -#ifndef ACTIVITY_H -#include "activity.h" -#endif - -#include "activitymap.h" - -#ifndef ANIMATION_H -#include "animation.h" -#endif - -#ifndef ENGINECALLBACK_H -#include "enginecallback.h" -#endif - -extern globalvars_t *gpGlobals; - -#pragma warning( disable : 4244 ) - - - -int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return 0; - - mstudioseqdesc_t *pseqdesc; - - 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; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return 0; - - mstudioseqdesc_t *pseqdesc; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - int weighttotal = 0; - int seq = ACTIVITY_NOT_AVAILABLE; - for (int i = 0; i < pstudiohdr->numseq; i++) - { - if (pseqdesc[i].activity == activity) - { - weighttotal += pseqdesc[i].actweight; - if (!weighttotal || RANDOM_LONG(0,weighttotal-1) < pseqdesc[i].actweight) - seq = i; - } - } - - return seq; -} - - -int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if ( !pstudiohdr ) - return 0; - - mstudioseqdesc_t *pseqdesc; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - int weight = 0; - int seq = ACTIVITY_NOT_AVAILABLE; - 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; -} - -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; - } - - VectorCopy ( pstudiohdr->eyeposition, vecEyePosition ); -} - -int LookupSequence( void *pmodel, const char *label ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return 0; - - mstudioseqdesc_t *pseqdesc; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); - - for (int i = 0; i < pstudiohdr->numseq; i++) - { - if (stricmp( pseqdesc[i].label, label ) == 0) - return i; - } - - return -1; -} - - -int IsSoundEvent( int eventNumber ) -{ - return 0; -} - - -void SequencePrecache( void *pmodel, const char *pSequenceName ) -{ - int index = LookupSequence( pmodel, pSequenceName ); - if ( index >= 0 ) - { - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if ( !pstudiohdr || index >= pstudiohdr->numseq ) - return; - - mstudioseqdesc_t *pseqdesc; - mstudioevent_t *pevent; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index; - 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 ( !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; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return; - - mstudioseqdesc_t *pseqdesc; - - if (pev->sequence >= pstudiohdr->numseq) - { - *pflFrameRate = 0.0; - *pflGroundSpeed = 0.0; - return; - } - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; - - if (pseqdesc->numframes > 1) - { - *pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1); - *pflGroundSpeed = 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); - } - else - { - *pflFrameRate = 256.0; - *pflGroundSpeed = 0.0; - } -} - - -int GetSequenceFlags( void *pmodel, entvars_t *pev ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq ) - return 0; - - mstudioseqdesc_t *pseqdesc; - 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; - - pstudiohdr = (studiohdr_t *)pmodel; - if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent ) - return 0; - - int events = 0; - - mstudioseqdesc_t *pseqdesc; - mstudioevent_t *pevent; - - pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; - 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; - int i; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return flValue; - - mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); - - // find first controller that matches the index - for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) - { - if (pbonecontroller->index == iController) - break; - } - if (i >= pstudiohdr->numbonecontrollers) - return flValue; - - // wrap 0..360 if it's a rotational controller - - if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) - { - // ugly hack, invert value if end < start - if (pbonecontroller->end < pbonecontroller->start) - flValue = -flValue; - - // does the controller not wrap? - if (pbonecontroller->start + 359.0 >= pbonecontroller->end) - { - if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) - flValue = flValue - 360; - if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) - flValue = flValue + 360; - } - else - { - if (flValue > 360) - flValue = flValue - (int)(flValue / 360.0) * 360.0; - else if (flValue < 0) - flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; - } - } - - int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); - - if (setting < 0) setting = 0; - if (setting > 255) setting = 255; - pev->controller[iController] = setting; - - return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; -} - - -float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return flValue; - - mstudioseqdesc_t *pseqdesc; - - 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 = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]); - - if (setting < 0) setting = 0; - if (setting > 255) setting = 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; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return iGoalAnim; - - mstudioseqdesc_t *pseqdesc; - 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; - - // ALERT( at_console, "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode ); - - 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; - - int i; - - // look for someone going - for (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; - - 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 = (pev->body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base)); -} - - -int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ) -{ - studiohdr_t *pstudiohdr; - - pstudiohdr = (studiohdr_t *)pmodel; - if (! pstudiohdr) - return 0; - - if (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; +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#include +#include +#include + +#include "../common/nowin.h" + +typedef int BOOL; +#define TRUE 1 +#define FALSE 0 + +// hack into header files that we can ship +typedef int qboolean; +typedef unsigned char byte; +#include "../utils/common/mathlib.h" +#include "const.h" +#include "progdefs.h" +#include "edict.h" +#include "eiface.h" + +#include "studio.h" + +#ifndef ACTIVITY_H +#include "activity.h" +#endif + +#include "activitymap.h" + +#ifndef ANIMATION_H +#include "animation.h" +#endif + +#ifndef ENGINECALLBACK_H +#include "enginecallback.h" +#endif + +extern globalvars_t *gpGlobals; + +#pragma warning( disable : 4244 ) + + + +int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return 0; + + mstudioseqdesc_t *pseqdesc; + + 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; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return 0; + + mstudioseqdesc_t *pseqdesc; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + int weighttotal = 0; + int seq = ACTIVITY_NOT_AVAILABLE; + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (pseqdesc[i].activity == activity) + { + weighttotal += pseqdesc[i].actweight; + if (!weighttotal || RANDOM_LONG(0,weighttotal-1) < pseqdesc[i].actweight) + seq = i; + } + } + + return seq; +} + + +int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if ( !pstudiohdr ) + return 0; + + mstudioseqdesc_t *pseqdesc; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + int weight = 0; + int seq = ACTIVITY_NOT_AVAILABLE; + 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; +} + +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; + } + + VectorCopy ( pstudiohdr->eyeposition, vecEyePosition ); +} + +int LookupSequence( void *pmodel, const char *label ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return 0; + + mstudioseqdesc_t *pseqdesc; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex); + + for (int i = 0; i < pstudiohdr->numseq; i++) + { + if (stricmp( pseqdesc[i].label, label ) == 0) + return i; + } + + return -1; +} + + +int IsSoundEvent( int eventNumber ) +{ + return 0; +} + + +void SequencePrecache( void *pmodel, const char *pSequenceName ) +{ + int index = LookupSequence( pmodel, pSequenceName ); + if ( index >= 0 ) + { + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if ( !pstudiohdr || index >= pstudiohdr->numseq ) + return; + + mstudioseqdesc_t *pseqdesc; + mstudioevent_t *pevent; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + index; + 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 ( !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; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return; + + mstudioseqdesc_t *pseqdesc; + + if (pev->sequence >= pstudiohdr->numseq) + { + *pflFrameRate = 0.0; + *pflGroundSpeed = 0.0; + return; + } + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; + + if (pseqdesc->numframes > 1) + { + *pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1); + *pflGroundSpeed = 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); + } + else + { + *pflFrameRate = 256.0; + *pflGroundSpeed = 0.0; + } +} + + +int GetSequenceFlags( void *pmodel, entvars_t *pev ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq ) + return 0; + + mstudioseqdesc_t *pseqdesc; + 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; + + pstudiohdr = (studiohdr_t *)pmodel; + if ( !pstudiohdr || pev->sequence >= pstudiohdr->numseq || !pMonsterEvent ) + return 0; + + int events = 0; + + mstudioseqdesc_t *pseqdesc; + mstudioevent_t *pevent; + + pseqdesc = (mstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex) + (int)pev->sequence; + 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; + int i; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return flValue; + + mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)pstudiohdr + pstudiohdr->bonecontrollerindex); + + // find first controller that matches the index + for (i = 0; i < pstudiohdr->numbonecontrollers; i++, pbonecontroller++) + { + if (pbonecontroller->index == iController) + break; + } + if (i >= pstudiohdr->numbonecontrollers) + return flValue; + + // wrap 0..360 if it's a rotational controller + + if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) + { + // ugly hack, invert value if end < start + if (pbonecontroller->end < pbonecontroller->start) + flValue = -flValue; + + // does the controller not wrap? + if (pbonecontroller->start + 359.0 >= pbonecontroller->end) + { + if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) + flValue = flValue - 360; + if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) + flValue = flValue + 360; + } + else + { + if (flValue > 360) + flValue = flValue - (int)(flValue / 360.0) * 360.0; + else if (flValue < 0) + flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; + } + } + + int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); + + if (setting < 0) setting = 0; + if (setting > 255) setting = 255; + pev->controller[iController] = setting; + + return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; +} + + +float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return flValue; + + mstudioseqdesc_t *pseqdesc; + + 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 = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]); + + if (setting < 0) setting = 0; + if (setting > 255) setting = 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; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return iGoalAnim; + + mstudioseqdesc_t *pseqdesc; + 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; + + // ALERT( at_console, "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode ); + + 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; + + int i; + + // look for someone going + for (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; + + 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 = (pev->body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base)); +} + + +int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ) +{ + studiohdr_t *pstudiohdr; + + pstudiohdr = (studiohdr_t *)pmodel; + if (! pstudiohdr) + return 0; + + if (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; } \ No newline at end of file diff --git a/src/dlls/animation.h b/src/dlls/animation.h index ec00ca4..d9810d7 100644 --- a/src/dlls/animation.h +++ b/src/dlls/animation.h @@ -1,47 +1,47 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef ANIMATION_H -#define ANIMATION_H - -#define ACTIVITY_NOT_AVAILABLE -1 - -#ifndef MONSTEREVENT_H -#include "monsterevent.h" -#endif - -extern int IsSoundEvent( int eventNumber ); - -int LookupActivity( void *pmodel, entvars_t *pev, int activity ); -int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ); -int LookupSequence( void *pmodel, const char *label ); -void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed ); -int GetSequenceFlags( void *pmodel, entvars_t *pev ); -int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd ); -float SetController( void *pmodel, entvars_t *pev, int iController, float flValue ); -float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ); -void GetEyePosition( void *pmodel, float *vecEyePosition ); -void SequencePrecache( void *pmodel, const char *pSequenceName ); -int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ); -void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ); -int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ); - -int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ); -int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ); - -// From /engine/studio.h -#define STUDIO_LOOPING 0x0001 - - -#endif //ANIMATION_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef ANIMATION_H +#define ANIMATION_H + +#define ACTIVITY_NOT_AVAILABLE -1 + +#ifndef MONSTEREVENT_H +#include "monsterevent.h" +#endif + +extern int IsSoundEvent( int eventNumber ); + +int LookupActivity( void *pmodel, entvars_t *pev, int activity ); +int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ); +int LookupSequence( void *pmodel, const char *label ); +void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed ); +int GetSequenceFlags( void *pmodel, entvars_t *pev ); +int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd ); +float SetController( void *pmodel, entvars_t *pev, int iController, float flValue ); +float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ); +void GetEyePosition( void *pmodel, float *vecEyePosition ); +void SequencePrecache( void *pmodel, const char *pSequenceName ); +int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ); +void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ); +int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ); + +int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ); +int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ); + +// From /engine/studio.h +#define STUDIO_LOOPING 0x0001 + + +#endif //ANIMATION_H diff --git a/src/dlls/apache.cpp b/src/dlls/apache.cpp index 7af6c0f..b1906b2 100644 --- a/src/dlls/apache.cpp +++ b/src/dlls/apache.cpp @@ -1,992 +1,992 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef OEM_BUILD - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "effects.h" -#include "explode.h" - - -#define SF_WAITFORTRIGGER (0x04 | 0x40) // UNDONE: Fix! -#define SF_NOWRECKAGE 0x08 - -void CMApache :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "models/apache.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) ); - UTIL_SetOrigin( pev, pev->origin ); - - pev->flags |= FL_MONSTER; - pev->takedamage = DAMAGE_AIM; - pev->health = gSkillData.apacheHealth; - - m_flFieldOfView = -0.707; // 270 degrees - - pev->sequence = 0; - ResetSequenceInfo( ); - pev->frame = RANDOM_LONG(0, 0xFF); - - InitBoneControllers(); - - if (pev->spawnflags & SF_WAITFORTRIGGER) - { - SetUse( &CMApache::StartupUse ); - } - else - { - SetThink( &CMApache::HuntThink ); - SetTouch( &CMApache::FlyTouch ); - pev->nextthink = gpGlobals->time + 1.0; - } - - m_iRockets = 10; - m_pBeam = NULL; - - m_pGoalEnt = NULL; - m_flGoalSpeed = 0.0f; - - m_flForce = 0.0f; - m_flNextRocket = 0.0f; - - Vector m_vecTarget = g_vecZero; - Vector m_posTarget = g_vecZero; - - Vector m_vecDesired = g_vecZero; - Vector m_posDesired = g_vecZero; - - Vector m_vecGoal = g_vecZero; - Vector m_angGun = g_vecZero; - - m_flLastSeen = 0.0f; - m_flPrevSeen = 0.0f; - - m_iSoundState = 0; - - pev->classname = MAKE_STRING( "monster_apache" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Apache" ); - } -} - - -void CMApache::Precache( void ) -{ - PRECACHE_MODEL("models/apache.mdl"); - - PRECACHE_SOUND("apache/ap_rotor1.wav"); - PRECACHE_SOUND("apache/ap_rotor2.wav"); - PRECACHE_SOUND("apache/ap_rotor3.wav"); - PRECACHE_SOUND("apache/ap_whine1.wav"); - - PRECACHE_SOUND("weapons/mortarhit.wav"); - - m_iSpriteTexture = PRECACHE_MODEL( "sprites/white.spr" ); - - PRECACHE_SOUND("turret/tu_fire1.wav"); - - PRECACHE_MODEL("sprites/lgtning.spr"); - - m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" ); - m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" ); - - CMApacheHVR apache_rocket; - apache_rocket.Precache(); -} - -int CMApache :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_HUMAN_MILITARY; -} - -void CMApache::NullThink( void ) -{ - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.5; -} - - -void CMApache::StartupUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - SetThink( &CMApache::HuntThink ); - SetTouch( &CMApache::FlyTouch ); - pev->nextthink = gpGlobals->time + 0.1; - SetUse( NULL ); -} - -void CMApache :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->movetype = MOVETYPE_TOSS; - pev->gravity = 0.3; - - STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav" ); - - UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) ); - SetThink( &CMApache::DyingThink ); - SetTouch( &CMApache::CrashTouch ); - pev->nextthink = gpGlobals->time + 0.1; - pev->health = 0; - pev->takedamage = DAMAGE_NO; - - if (pev->spawnflags & SF_NOWRECKAGE) - { - m_flNextRocket = gpGlobals->time + 4.0; - } - else - { - m_flNextRocket = gpGlobals->time + 15.0; - } -} - -void CMApache :: DyingThink( void ) -{ - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - pev->avelocity = pev->avelocity * 1.02; - - // still falling? - if (m_flNextRocket > gpGlobals->time ) - { - if (g_sModelIndexFireball == 0) - g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr"); // fireball - - // random explosions - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now - WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); - WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); - WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 )); - WRITE_SHORT( g_sModelIndexFireball ); - WRITE_BYTE( RANDOM_LONG(0,29) + 30 ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - WRITE_BYTE( TE_EXPLFLAG_NONE ); - MESSAGE_END(); - - if (g_sModelIndexSmoke == 0) - g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr"); // smoke - - // lots of smoke - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); - WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); - WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 )); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( 100 ); // scale * 10 - WRITE_BYTE( 10 ); // framerate - MESSAGE_END(); - - Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); - WRITE_BYTE( TE_BREAKMODEL); - - // position - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z ); - - // size - WRITE_COORD( 400 ); - WRITE_COORD( 400 ); - WRITE_COORD( 132 ); - - // velocity - WRITE_COORD( pev->velocity.x ); - WRITE_COORD( pev->velocity.y ); - WRITE_COORD( pev->velocity.z ); - - // randomization - WRITE_BYTE( 50 ); - - // Model - WRITE_SHORT( m_iBodyGibs ); //model id# - - // # of shards - WRITE_BYTE( 4 ); // let client decide - - // duration - WRITE_BYTE( 30 );// 3.0 seconds - - // flags - - WRITE_BYTE( BREAK_METAL ); - MESSAGE_END(); - - // don't stop it we touch a entity - pev->flags &= ~FL_ONGROUND; - pev->nextthink = gpGlobals->time + 0.2; - return; - } - else - { - Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - - /* - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z + 300 ); - WRITE_SHORT( g_sModelIndexFireball ); - WRITE_BYTE( 250 ); // scale * 10 - WRITE_BYTE( 8 ); // framerate - MESSAGE_END(); - */ - - // fireball - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z + 256 ); - WRITE_SHORT( m_iExplode ); - WRITE_BYTE( 120 ); // scale * 10 - WRITE_BYTE( 255 ); // brightness - MESSAGE_END(); - - // big smoke - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z + 512 ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( 250 ); // scale * 10 - WRITE_BYTE( 5 ); // framerate - MESSAGE_END(); - - // blast circle - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 4 ); // life - WRITE_BYTE( 32 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 192 ); // r, g, b - WRITE_BYTE( 128 ); // brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3); - - RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST ); - - if (/*!(pev->spawnflags & SF_NOWRECKAGE) && */(pev->flags & FL_ONGROUND)) - { -/*jlb - CMBaseEntity *pWreckage = Create( "cycler_wreckage", pev->origin, pev->angles ); - // SET_MODEL( ENT(pWreckage->pev), STRING(pev->model) ); - UTIL_SetSize( pWreckage->pev, Vector( -200, -200, -128 ), Vector( 200, 200, -32 ) ); - pWreckage->pev->frame = pev->frame; - pWreckage->pev->sequence = pev->sequence; - pWreckage->pev->framerate = 0; - pWreckage->pev->dmgtime = gpGlobals->time + 5; -jlb*/ - } - - // gibs - vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); - WRITE_BYTE( TE_BREAKMODEL); - - // position - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z + 64); - - // size - WRITE_COORD( 400 ); - WRITE_COORD( 400 ); - WRITE_COORD( 128 ); - - // velocity - WRITE_COORD( 0 ); - WRITE_COORD( 0 ); - WRITE_COORD( 200 ); - - // randomization - WRITE_BYTE( 30 ); - - // Model - WRITE_SHORT( m_iBodyGibs ); //model id# - - // # of shards - WRITE_BYTE( 200 ); - - // duration - WRITE_BYTE( 200 );// 10.0 seconds - - // flags - - WRITE_BYTE( BREAK_METAL ); - MESSAGE_END(); - - SetThink( &CMApache::SUB_Remove ); - pev->nextthink = gpGlobals->time + 0.1; - } -} - - -void CMApache::FlyTouch( edict_t *pOther ) -{ - // bounce if we hit something solid - if ( pOther->v.solid == SOLID_BSP) - { - TraceResult tr = UTIL_GetGlobalTrace( ); - - // UNDONE, do a real bounce - pev->velocity = pev->velocity + tr.vecPlaneNormal * (pev->velocity.Length() + 200); - } -} - - -void CMApache::CrashTouch( edict_t *pOther ) -{ - // only crash if we hit something solid - if ( pOther->v.solid == SOLID_BSP) - { - SetTouch( NULL ); - m_flNextRocket = gpGlobals->time; - pev->nextthink = gpGlobals->time; - } -} - - - -void CMApache :: GibMonster( void ) -{ - // EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200); -} - - -void CMApache :: HuntThink( void ) -{ - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - ShowDamage( ); - - if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target - { - m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); - if (m_pGoalEnt) - { - m_posDesired = m_pGoalEnt->v.origin; - UTIL_MakeAimVectors( m_pGoalEnt->v.angles ); - m_vecGoal = gpGlobals->v_forward; - } - } - - // if (m_hEnemy == NULL) - { - Look( 4092 ); - m_hEnemy = BestVisibleEnemy( ); - } - - // generic speed up - if (m_flGoalSpeed < 800) - m_flGoalSpeed += 5; - - if (m_hEnemy != NULL) - { - // ALERT( at_console, "%s\n", STRING( m_hEnemy->pev->classname ) ); - if (UTIL_FVisible( m_hEnemy, this->edict() )) - { - if (m_flLastSeen < gpGlobals->time - 5) - m_flPrevSeen = gpGlobals->time; - m_flLastSeen = gpGlobals->time; - m_posTarget = UTIL_Center( m_hEnemy ); - } - else - { - m_hEnemy = NULL; - } - } - - m_vecTarget = (m_posTarget - pev->origin).Normalize(); - - float flLength = (pev->origin - m_posDesired).Length(); - - if (m_pGoalEnt) - { - // ALERT( at_console, "%.0f\n", flLength ); - - if (flLength < 128) - { - m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->v.target ) ); - if (m_pGoalEnt) - { - m_posDesired = m_pGoalEnt->v.origin; - UTIL_MakeAimVectors( m_pGoalEnt->v.angles ); - m_vecGoal = gpGlobals->v_forward; - flLength = (pev->origin - m_posDesired).Length(); - } - } - } - else - { - m_posDesired = pev->origin; - } - - if (flLength > 250) // 500 - { - // float flLength2 = (m_posTarget - pev->origin).Length() * (1.5 - DotProduct((m_posTarget - pev->origin).Normalize(), pev->velocity.Normalize() )); - // if (flLength2 < flLength) - if (m_flLastSeen + 90 > gpGlobals->time && DotProduct( (m_posTarget - pev->origin).Normalize(), (m_posDesired - pev->origin).Normalize( )) > 0.25) - { - m_vecDesired = (m_posTarget - pev->origin).Normalize( ); - } - else - { - m_vecDesired = (m_posDesired - pev->origin).Normalize( ); - } - } - else - { - m_vecDesired = m_vecGoal; - } - - Flight( ); - - // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->time, m_flLastSeen, m_flPrevSeen ); - if ((m_flLastSeen + 1 > gpGlobals->time) && (m_flPrevSeen + 2 < gpGlobals->time)) - { - if (FireGun( )) - { - // slow down if we're fireing - if (m_flGoalSpeed > 400) - m_flGoalSpeed = 400; - } - } - - UTIL_MakeAimVectors( pev->angles ); - Vector vecEst = (gpGlobals->v_forward * 800 + pev->velocity).Normalize( ); - // ALERT( at_console, "%d %d %d %4.2f\n", pev->angles.x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->time, DotProduct( m_vecTarget, vecEst ) ); - - if ((m_iRockets % 2) == 1) - { - FireRocket( ); - m_flNextRocket = gpGlobals->time + 0.5; - if (m_iRockets <= 0) - { - m_flNextRocket = gpGlobals->time + 10; - m_iRockets = 10; - } - } - else if (pev->angles.x < 0 && DotProduct( pev->velocity, gpGlobals->v_forward ) > -100 && m_flNextRocket < gpGlobals->time) - { - if (m_flLastSeen + 60 > gpGlobals->time) - { - if (m_hEnemy != NULL) - { - // make sure it's a good shot - if (DotProduct( m_vecTarget, vecEst) > .965) - { - TraceResult tr; - - UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, ignore_monsters, edict(), &tr ); - if ((tr.vecEndPos - m_posTarget).Length() < 512) - FireRocket( ); - } - } - else - { - TraceResult tr; - - UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, dont_ignore_monsters, edict(), &tr ); - // just fire when close - if ((tr.vecEndPos - m_posTarget).Length() < 512) - FireRocket( ); - } - } - } -} - - -void CMApache :: Flight( void ) -{ - // tilt model 5 degrees - Vector vecAdj = Vector( 5.0, 0, 0 ); - - // estimate where I'll be facing in one seconds - UTIL_MakeAimVectors( pev->angles + pev->avelocity * 2 + vecAdj); - // Vector vecEst1 = pev->origin + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 ); - // float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right ); - - float flSide = DotProduct( m_vecDesired, gpGlobals->v_right ); - - if (flSide < 0) - { - if (pev->avelocity.y < 60) - { - pev->avelocity.y += 8; // 9 * (3.0/2.0); - } - } - else - { - if (pev->avelocity.y > -60) - { - pev->avelocity.y -= 8; // 9 * (3.0/2.0); - } - } - pev->avelocity.y *= 0.98; - - // estimate where I'll be in two seconds - UTIL_MakeAimVectors( pev->angles + pev->avelocity * 1 + vecAdj); - Vector vecEst = pev->origin + pev->velocity * 2.0 + gpGlobals->v_up * m_flForce * 20 - Vector( 0, 0, 384 * 2 ); - - // add immediate force - UTIL_MakeAimVectors( pev->angles + vecAdj); - pev->velocity.x += gpGlobals->v_up.x * m_flForce; - pev->velocity.y += gpGlobals->v_up.y * m_flForce; - pev->velocity.z += gpGlobals->v_up.z * m_flForce; - // add gravity - pev->velocity.z -= 38.4; // 32ft/sec - - - float flSpeed = pev->velocity.Length(); - float flDir = DotProduct( Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, 0 ), Vector( pev->velocity.x, pev->velocity.y, 0 ) ); - if (flDir < 0) - flSpeed = -flSpeed; - - float flDist = DotProduct( m_posDesired - vecEst, gpGlobals->v_forward ); - - // float flSlip = DotProduct( pev->velocity, gpGlobals->v_right ); - float flSlip = -DotProduct( m_posDesired - vecEst, gpGlobals->v_right ); - - // fly sideways - if (flSlip > 0) - { - if (pev->angles.z > -30 && pev->avelocity.z > -15) - pev->avelocity.z -= 4; - else - pev->avelocity.z += 2; - } - else - { - - if (pev->angles.z < 30 && pev->avelocity.z < 15) - pev->avelocity.z += 4; - else - pev->avelocity.z -= 2; - } - - // sideways drag - pev->velocity.x = pev->velocity.x * (1.0 - fabs( gpGlobals->v_right.x ) * 0.05); - pev->velocity.y = pev->velocity.y * (1.0 - fabs( gpGlobals->v_right.y ) * 0.05); - pev->velocity.z = pev->velocity.z * (1.0 - fabs( gpGlobals->v_right.z ) * 0.05); - - // general drag - pev->velocity = pev->velocity * 0.995; - - // apply power to stay correct height - if (m_flForce < 80 && vecEst.z < m_posDesired.z) - { - m_flForce += 12; - } - else if (m_flForce > 30) - { - if (vecEst.z > m_posDesired.z) - m_flForce -= 8; - } - - // pitch forward or back to get to target - if (flDist > 0 && flSpeed < m_flGoalSpeed /* && flSpeed < flDist */ && pev->angles.x + pev->avelocity.x > -40) - { - // ALERT( at_console, "F " ); - // lean forward - pev->avelocity.x -= 12.0; - } - else if (flDist < 0 && flSpeed > -50 && pev->angles.x + pev->avelocity.x < 20) - { - // ALERT( at_console, "B " ); - // lean backward - pev->avelocity.x += 12.0; - } - else if (pev->angles.x + pev->avelocity.x > 0) - { - // ALERT( at_console, "f " ); - pev->avelocity.x -= 4.0; - } - else if (pev->angles.x + pev->avelocity.x < 0) - { - // ALERT( at_console, "b " ); - pev->avelocity.x += 4.0; - } - - // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", pev->origin.x, pev->velocity.x, flDist, flSpeed, pev->angles.x, pev->avelocity.x, m_flForce ); - // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", pev->origin.z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce ); - - // make rotor, engine sounds - if (m_iSoundState == 0) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, 0, 110 ); - // EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 ); - - m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions - } - else - { - edict_t *pPlayer = NULL; - - pPlayer = UTIL_FindEntityByClassname( NULL, "player" ); - // UNDONE: this needs to send different sounds to every player for multiplayer. - if (pPlayer) - { - - float pitch = DotProduct( pev->velocity - pPlayer->v.velocity, (pPlayer->v.origin - pev->origin).Normalize() ); - - pitch = (int)(100 + pitch / 50.0); - - if (pitch > 250) - pitch = 250; - if (pitch < 50) - pitch = 50; - if (pitch == 100) - pitch = 101; - - float flVol = (m_flForce / 100.0) + .1; - if (flVol > 1.0) - flVol = 1.0; - - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch); - } - // EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch); - - // ALERT( at_console, "%.0f %.2f\n", pitch, flVol ); - } -} - - -void CMApache :: FireRocket( void ) -{ - static float side = 1.0; - static int count; - - if (m_iRockets <= 0) - return; - - UTIL_MakeAimVectors( pev->angles ); - Vector vecSrc = pev->origin + 1.5 * (gpGlobals->v_forward * 21 + gpGlobals->v_right * 70 * side + gpGlobals->v_up * -79); - - switch( m_iRockets % 5) - { - case 0: vecSrc = vecSrc + gpGlobals->v_right * 10; break; - case 1: vecSrc = vecSrc - gpGlobals->v_right * 10; break; - case 2: vecSrc = vecSrc + gpGlobals->v_up * 10; break; - case 3: vecSrc = vecSrc - gpGlobals->v_up * 10; break; - case 4: break; - } - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( vecSrc.x ); - WRITE_COORD( vecSrc.y ); - WRITE_COORD( vecSrc.z ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( 20 ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - MESSAGE_END(); - -//jlb CMBaseEntity *pRocket = CMBaseEntity::Create( "hvr_rocket", vecSrc, pev->angles, edict() ); - CMApacheHVR *pRocket = CreateClassPtr((CMApacheHVR *)NULL); - - if (pRocket) - { - pRocket->pev->origin = vecSrc; - pRocket->pev->angles = pev->angles; - pRocket->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pRocket->pev->absmin = pRocket->pev->origin - Vector(1,1,1); - pRocket->pev->absmax = pRocket->pev->origin + Vector(1,1,1); - - pRocket->Spawn(); - - pRocket->pev->velocity = pev->velocity + gpGlobals->v_forward * 100; - } - - m_iRockets--; - - side = - side; -} - - - -BOOL CMApache :: FireGun( ) -{ - UTIL_MakeAimVectors( pev->angles ); - - Vector posGun, angGun; - GetAttachment( 1, posGun, angGun ); - - Vector vecTarget = (m_posTarget - posGun).Normalize( ); - - Vector vecOut; - - vecOut.x = DotProduct( gpGlobals->v_forward, vecTarget ); - vecOut.y = -DotProduct( gpGlobals->v_right, vecTarget ); - vecOut.z = DotProduct( gpGlobals->v_up, vecTarget ); - - Vector angles = UTIL_VecToAngles (vecOut); - - angles.x = -angles.x; - if (angles.y > 180) - angles.y = angles.y - 360; - if (angles.y < -180) - angles.y = angles.y + 360; - if (angles.x > 180) - angles.x = angles.x - 360; - if (angles.x < -180) - angles.x = angles.x + 360; - - if (angles.x > m_angGun.x) - m_angGun.x = min( angles.x, m_angGun.x + 12 ); - if (angles.x < m_angGun.x) - m_angGun.x = max( angles.x, m_angGun.x - 12 ); - if (angles.y > m_angGun.y) - m_angGun.y = min( angles.y, m_angGun.y + 12 ); - if (angles.y < m_angGun.y) - m_angGun.y = max( angles.y, m_angGun.y - 12 ); - - m_angGun.y = SetBoneController( 0, m_angGun.y ); - m_angGun.x = SetBoneController( 1, m_angGun.x ); - - Vector posBarrel, angBarrel; - GetAttachment( 0, posBarrel, angBarrel ); - Vector vecGun = (posBarrel - posGun).Normalize( ); - - if (DotProduct( vecGun, vecTarget ) > 0.98) - { -#if 1 - FireBullets( 1, posGun, vecGun, VECTOR_CONE_4DEGREES, 8192, BULLET_MONSTER_12MM, 1 ); - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.3); -#else - static float flNext; - TraceResult tr; - UTIL_TraceLine( posGun, posGun + vecGun * 8192, dont_ignore_monsters, ENT( pev ), &tr ); - - if (!m_pBeam) - { - m_pBeam = CBeam::BeamCreate( "sprites/lgtning.spr", 80 ); - m_pBeam->PointEntInit( pev->origin, entindex( ) ); - m_pBeam->SetEndAttachment( 1 ); - m_pBeam->SetColor( 255, 180, 96 ); - m_pBeam->SetBrightness( 192 ); - } - - if (flNext < gpGlobals->time) - { - flNext = gpGlobals->time + 0.5; - m_pBeam->SetStartPos( tr.vecEndPos ); - } -#endif - return TRUE; - } - else - { - if (m_pBeam) - { - UTIL_Remove( m_pBeam->edict() ); - m_pBeam = NULL; - } - } - return FALSE; -} - - - -void CMApache :: ShowDamage( void ) -{ - if (m_iDoSmokePuff > 0 || RANDOM_LONG(0,99) > pev->health) - { - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z - 32 ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - MESSAGE_END(); - } - if (m_iDoSmokePuff > 0) - m_iDoSmokePuff--; -} - - -int CMApache :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) -{ - if (pevInflictor->owner == edict()) - return 0; - - if (bitsDamageType & DMG_BLAST) - { - flDamage *= 2; - } - - /* - if ( (bitsDamageType & DMG_BULLET) && flDamage > 50) - { - // clip bullet damage at 50 - flDamage = 50; - } - */ - - // ALERT( at_console, "%.0f\n", flDamage ); - return CMBaseEntity::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - - - -void CMApache::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - // ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage ); - - // ignore blades - if (ptr->iHitgroup == 6 && (bitsDamageType & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB))) - return; - - // hit hard, hits cockpit, hits engines - if (flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2) - { - // ALERT( at_console, "%.0f\n", flDamage ); - AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); - m_iDoSmokePuff = 3 + (flDamage / 5.0); - } - else - { - // do half damage in the body - // AddMultiDamage( pevAttacker, this, flDamage / 2.0, bitsDamageType ); - UTIL_Ricochet( ptr->vecEndPos, 2.0 ); - } -} - - -void CMApacheHVR :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "models/HVR.mdl"); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( pev, pev->origin ); - - SetThink( &CMApacheHVR::IgniteThink ); - SetTouch( &CMApacheHVR::ExplodeTouch ); - - UTIL_MakeAimVectors( pev->angles ); - m_vecForward = gpGlobals->v_forward; - pev->gravity = 0.5; - - pev->nextthink = gpGlobals->time + 0.1; - - pev->dmg = 150; -} - - -void CMApacheHVR :: Precache( void ) -{ - PRECACHE_MODEL("models/HVR.mdl"); - m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); - PRECACHE_SOUND ("weapons/rocket1.wav"); -} - - -void CMApacheHVR :: IgniteThink( void ) -{ - // pev->movetype = MOVETYPE_TOSS; - - // pev->movetype = MOVETYPE_FLY; - pev->effects |= EF_LIGHT; - - // make rocket sound - EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 ); - - // rocket trail - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT(entindex()); // entity - WRITE_SHORT(m_iTrail ); // model - WRITE_BYTE( 15 ); // life - WRITE_BYTE( 5 ); // width - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - - MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) - - // set to accelerate - SetThink( &CMApacheHVR::AccelerateThink ); - pev->nextthink = gpGlobals->time + 0.1; -} - - -void CMApacheHVR :: AccelerateThink( void ) -{ - // check world boundaries - if (pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) - { - UTIL_Remove( this->edict() ); - return; - } - - // accelerate - float flSpeed = pev->velocity.Length(); - if (flSpeed < 1800) - { - pev->velocity = pev->velocity + m_vecForward * 200; - } - - // re-aim - pev->angles = UTIL_VecToAngles( pev->velocity ); - - pev->nextthink = gpGlobals->time + 0.1; -} - - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef OEM_BUILD + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "effects.h" +#include "explode.h" + + +#define SF_WAITFORTRIGGER (0x04 | 0x40) // UNDONE: Fix! +#define SF_NOWRECKAGE 0x08 + +void CMApache :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/apache.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) ); + UTIL_SetOrigin( pev, pev->origin ); + + pev->flags |= FL_MONSTER; + pev->takedamage = DAMAGE_AIM; + pev->health = gSkillData.apacheHealth; + + m_flFieldOfView = -0.707; // 270 degrees + + pev->sequence = 0; + ResetSequenceInfo( ); + pev->frame = RANDOM_LONG(0, 0xFF); + + InitBoneControllers(); + + if (pev->spawnflags & SF_WAITFORTRIGGER) + { + SetUse( &CMApache::StartupUse ); + } + else + { + SetThink( &CMApache::HuntThink ); + SetTouch( &CMApache::FlyTouch ); + pev->nextthink = gpGlobals->time + 1.0; + } + + m_iRockets = 10; + m_pBeam = NULL; + + m_pGoalEnt = NULL; + m_flGoalSpeed = 0.0f; + + m_flForce = 0.0f; + m_flNextRocket = 0.0f; + + Vector m_vecTarget = g_vecZero; + Vector m_posTarget = g_vecZero; + + Vector m_vecDesired = g_vecZero; + Vector m_posDesired = g_vecZero; + + Vector m_vecGoal = g_vecZero; + Vector m_angGun = g_vecZero; + + m_flLastSeen = 0.0f; + m_flPrevSeen = 0.0f; + + m_iSoundState = 0; + + pev->classname = MAKE_STRING( "monster_apache" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Apache" ); + } +} + + +void CMApache::Precache( void ) +{ + PRECACHE_MODEL("models/apache.mdl"); + + PRECACHE_SOUND("apache/ap_rotor1.wav"); + PRECACHE_SOUND("apache/ap_rotor2.wav"); + PRECACHE_SOUND("apache/ap_rotor3.wav"); + PRECACHE_SOUND("apache/ap_whine1.wav"); + + PRECACHE_SOUND("weapons/mortarhit.wav"); + + m_iSpriteTexture = PRECACHE_MODEL( "sprites/white.spr" ); + + PRECACHE_SOUND("turret/tu_fire1.wav"); + + PRECACHE_MODEL("sprites/lgtning.spr"); + + m_iExplode = PRECACHE_MODEL( "sprites/fexplo.spr" ); + m_iBodyGibs = PRECACHE_MODEL( "models/metalplategibs_green.mdl" ); + + CMApacheHVR apache_rocket; + apache_rocket.Precache(); +} + +int CMApache :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_HUMAN_MILITARY; +} + +void CMApache::NullThink( void ) +{ + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.5; +} + + +void CMApache::StartupUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + SetThink( &CMApache::HuntThink ); + SetTouch( &CMApache::FlyTouch ); + pev->nextthink = gpGlobals->time + 0.1; + SetUse( NULL ); +} + +void CMApache :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->movetype = MOVETYPE_TOSS; + pev->gravity = 0.3; + + STOP_SOUND( ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav" ); + + UTIL_SetSize( pev, Vector( -32, -32, -64), Vector( 32, 32, 0) ); + SetThink( &CMApache::DyingThink ); + SetTouch( &CMApache::CrashTouch ); + pev->nextthink = gpGlobals->time + 0.1; + pev->health = 0; + pev->takedamage = DAMAGE_NO; + + if (pev->spawnflags & SF_NOWRECKAGE) + { + m_flNextRocket = gpGlobals->time + 4.0; + } + else + { + m_flNextRocket = gpGlobals->time + 15.0; + } +} + +void CMApache :: DyingThink( void ) +{ + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + pev->avelocity = pev->avelocity * 1.02; + + // still falling? + if (m_flNextRocket > gpGlobals->time ) + { + if (g_sModelIndexFireball == 0) + g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr"); // fireball + + // random explosions + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now + WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); + WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); + WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 )); + WRITE_SHORT( g_sModelIndexFireball ); + WRITE_BYTE( RANDOM_LONG(0,29) + 30 ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + WRITE_BYTE( TE_EXPLFLAG_NONE ); + MESSAGE_END(); + + if (g_sModelIndexSmoke == 0) + g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr"); // smoke + + // lots of smoke + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -150, 150 )); + WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -150, 150 )); + WRITE_COORD( pev->origin.z + RANDOM_FLOAT( -150, -50 )); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( 100 ); // scale * 10 + WRITE_BYTE( 10 ); // framerate + MESSAGE_END(); + + Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); + WRITE_BYTE( TE_BREAKMODEL); + + // position + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z ); + + // size + WRITE_COORD( 400 ); + WRITE_COORD( 400 ); + WRITE_COORD( 132 ); + + // velocity + WRITE_COORD( pev->velocity.x ); + WRITE_COORD( pev->velocity.y ); + WRITE_COORD( pev->velocity.z ); + + // randomization + WRITE_BYTE( 50 ); + + // Model + WRITE_SHORT( m_iBodyGibs ); //model id# + + // # of shards + WRITE_BYTE( 4 ); // let client decide + + // duration + WRITE_BYTE( 30 );// 3.0 seconds + + // flags + + WRITE_BYTE( BREAK_METAL ); + MESSAGE_END(); + + // don't stop it we touch a entity + pev->flags &= ~FL_ONGROUND; + pev->nextthink = gpGlobals->time + 0.2; + return; + } + else + { + Vector vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; + + /* + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_EXPLOSION); // This just makes a dynamic light now + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z + 300 ); + WRITE_SHORT( g_sModelIndexFireball ); + WRITE_BYTE( 250 ); // scale * 10 + WRITE_BYTE( 8 ); // framerate + MESSAGE_END(); + */ + + // fireball + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); + WRITE_BYTE( TE_SPRITE ); + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z + 256 ); + WRITE_SHORT( m_iExplode ); + WRITE_BYTE( 120 ); // scale * 10 + WRITE_BYTE( 255 ); // brightness + MESSAGE_END(); + + // big smoke + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z + 512 ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( 250 ); // scale * 10 + WRITE_BYTE( 5 ); // framerate + MESSAGE_END(); + + // blast circle + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 2000 ); // reach damage radius over .2 seconds + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 4 ); // life + WRITE_BYTE( 32 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 192 ); // r, g, b + WRITE_BYTE( 128 ); // brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + EMIT_SOUND(ENT(pev), CHAN_STATIC, "weapons/mortarhit.wav", 1.0, 0.3); + + RadiusDamage( pev->origin, pev, pev, 300, CLASS_NONE, DMG_BLAST ); + + if (/*!(pev->spawnflags & SF_NOWRECKAGE) && */(pev->flags & FL_ONGROUND)) + { +/*jlb + CMBaseEntity *pWreckage = Create( "cycler_wreckage", pev->origin, pev->angles ); + // SET_MODEL( ENT(pWreckage->pev), STRING(pev->model) ); + UTIL_SetSize( pWreckage->pev, Vector( -200, -200, -128 ), Vector( 200, 200, -32 ) ); + pWreckage->pev->frame = pev->frame; + pWreckage->pev->sequence = pev->sequence; + pWreckage->pev->framerate = 0; + pWreckage->pev->dmgtime = gpGlobals->time + 5; +jlb*/ + } + + // gibs + vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpot ); + WRITE_BYTE( TE_BREAKMODEL); + + // position + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z + 64); + + // size + WRITE_COORD( 400 ); + WRITE_COORD( 400 ); + WRITE_COORD( 128 ); + + // velocity + WRITE_COORD( 0 ); + WRITE_COORD( 0 ); + WRITE_COORD( 200 ); + + // randomization + WRITE_BYTE( 30 ); + + // Model + WRITE_SHORT( m_iBodyGibs ); //model id# + + // # of shards + WRITE_BYTE( 200 ); + + // duration + WRITE_BYTE( 200 );// 10.0 seconds + + // flags + + WRITE_BYTE( BREAK_METAL ); + MESSAGE_END(); + + SetThink( &CMApache::SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; + } +} + + +void CMApache::FlyTouch( edict_t *pOther ) +{ + // bounce if we hit something solid + if ( pOther->v.solid == SOLID_BSP) + { + TraceResult tr = UTIL_GetGlobalTrace( ); + + // UNDONE, do a real bounce + pev->velocity = pev->velocity + tr.vecPlaneNormal * (pev->velocity.Length() + 200); + } +} + + +void CMApache::CrashTouch( edict_t *pOther ) +{ + // only crash if we hit something solid + if ( pOther->v.solid == SOLID_BSP) + { + SetTouch( NULL ); + m_flNextRocket = gpGlobals->time; + pev->nextthink = gpGlobals->time; + } +} + + + +void CMApache :: GibMonster( void ) +{ + // EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200); +} + + +void CMApache :: HuntThink( void ) +{ + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + ShowDamage( ); + + if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target + { + m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); + if (m_pGoalEnt) + { + m_posDesired = m_pGoalEnt->v.origin; + UTIL_MakeAimVectors( m_pGoalEnt->v.angles ); + m_vecGoal = gpGlobals->v_forward; + } + } + + // if (m_hEnemy == NULL) + { + Look( 4092 ); + m_hEnemy = BestVisibleEnemy( ); + } + + // generic speed up + if (m_flGoalSpeed < 800) + m_flGoalSpeed += 5; + + if (m_hEnemy != NULL) + { + // ALERT( at_console, "%s\n", STRING( m_hEnemy->pev->classname ) ); + if (UTIL_FVisible( m_hEnemy, this->edict() )) + { + if (m_flLastSeen < gpGlobals->time - 5) + m_flPrevSeen = gpGlobals->time; + m_flLastSeen = gpGlobals->time; + m_posTarget = UTIL_Center( m_hEnemy ); + } + else + { + m_hEnemy = NULL; + } + } + + m_vecTarget = (m_posTarget - pev->origin).Normalize(); + + float flLength = (pev->origin - m_posDesired).Length(); + + if (m_pGoalEnt) + { + // ALERT( at_console, "%.0f\n", flLength ); + + if (flLength < 128) + { + m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->v.target ) ); + if (m_pGoalEnt) + { + m_posDesired = m_pGoalEnt->v.origin; + UTIL_MakeAimVectors( m_pGoalEnt->v.angles ); + m_vecGoal = gpGlobals->v_forward; + flLength = (pev->origin - m_posDesired).Length(); + } + } + } + else + { + m_posDesired = pev->origin; + } + + if (flLength > 250) // 500 + { + // float flLength2 = (m_posTarget - pev->origin).Length() * (1.5 - DotProduct((m_posTarget - pev->origin).Normalize(), pev->velocity.Normalize() )); + // if (flLength2 < flLength) + if (m_flLastSeen + 90 > gpGlobals->time && DotProduct( (m_posTarget - pev->origin).Normalize(), (m_posDesired - pev->origin).Normalize( )) > 0.25) + { + m_vecDesired = (m_posTarget - pev->origin).Normalize( ); + } + else + { + m_vecDesired = (m_posDesired - pev->origin).Normalize( ); + } + } + else + { + m_vecDesired = m_vecGoal; + } + + Flight( ); + + // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->time, m_flLastSeen, m_flPrevSeen ); + if ((m_flLastSeen + 1 > gpGlobals->time) && (m_flPrevSeen + 2 < gpGlobals->time)) + { + if (FireGun( )) + { + // slow down if we're fireing + if (m_flGoalSpeed > 400) + m_flGoalSpeed = 400; + } + } + + UTIL_MakeAimVectors( pev->angles ); + Vector vecEst = (gpGlobals->v_forward * 800 + pev->velocity).Normalize( ); + // ALERT( at_console, "%d %d %d %4.2f\n", pev->angles.x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->time, DotProduct( m_vecTarget, vecEst ) ); + + if ((m_iRockets % 2) == 1) + { + FireRocket( ); + m_flNextRocket = gpGlobals->time + 0.5; + if (m_iRockets <= 0) + { + m_flNextRocket = gpGlobals->time + 10; + m_iRockets = 10; + } + } + else if (pev->angles.x < 0 && DotProduct( pev->velocity, gpGlobals->v_forward ) > -100 && m_flNextRocket < gpGlobals->time) + { + if (m_flLastSeen + 60 > gpGlobals->time) + { + if (m_hEnemy != NULL) + { + // make sure it's a good shot + if (DotProduct( m_vecTarget, vecEst) > .965) + { + TraceResult tr; + + UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, ignore_monsters, edict(), &tr ); + if ((tr.vecEndPos - m_posTarget).Length() < 512) + FireRocket( ); + } + } + else + { + TraceResult tr; + + UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, dont_ignore_monsters, edict(), &tr ); + // just fire when close + if ((tr.vecEndPos - m_posTarget).Length() < 512) + FireRocket( ); + } + } + } +} + + +void CMApache :: Flight( void ) +{ + // tilt model 5 degrees + Vector vecAdj = Vector( 5.0, 0, 0 ); + + // estimate where I'll be facing in one seconds + UTIL_MakeAimVectors( pev->angles + pev->avelocity * 2 + vecAdj); + // Vector vecEst1 = pev->origin + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 ); + // float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right ); + + float flSide = DotProduct( m_vecDesired, gpGlobals->v_right ); + + if (flSide < 0) + { + if (pev->avelocity.y < 60) + { + pev->avelocity.y += 8; // 9 * (3.0/2.0); + } + } + else + { + if (pev->avelocity.y > -60) + { + pev->avelocity.y -= 8; // 9 * (3.0/2.0); + } + } + pev->avelocity.y *= 0.98; + + // estimate where I'll be in two seconds + UTIL_MakeAimVectors( pev->angles + pev->avelocity * 1 + vecAdj); + Vector vecEst = pev->origin + pev->velocity * 2.0 + gpGlobals->v_up * m_flForce * 20 - Vector( 0, 0, 384 * 2 ); + + // add immediate force + UTIL_MakeAimVectors( pev->angles + vecAdj); + pev->velocity.x += gpGlobals->v_up.x * m_flForce; + pev->velocity.y += gpGlobals->v_up.y * m_flForce; + pev->velocity.z += gpGlobals->v_up.z * m_flForce; + // add gravity + pev->velocity.z -= 38.4; // 32ft/sec + + + float flSpeed = pev->velocity.Length(); + float flDir = DotProduct( Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, 0 ), Vector( pev->velocity.x, pev->velocity.y, 0 ) ); + if (flDir < 0) + flSpeed = -flSpeed; + + float flDist = DotProduct( m_posDesired - vecEst, gpGlobals->v_forward ); + + // float flSlip = DotProduct( pev->velocity, gpGlobals->v_right ); + float flSlip = -DotProduct( m_posDesired - vecEst, gpGlobals->v_right ); + + // fly sideways + if (flSlip > 0) + { + if (pev->angles.z > -30 && pev->avelocity.z > -15) + pev->avelocity.z -= 4; + else + pev->avelocity.z += 2; + } + else + { + + if (pev->angles.z < 30 && pev->avelocity.z < 15) + pev->avelocity.z += 4; + else + pev->avelocity.z -= 2; + } + + // sideways drag + pev->velocity.x = pev->velocity.x * (1.0 - fabs( gpGlobals->v_right.x ) * 0.05); + pev->velocity.y = pev->velocity.y * (1.0 - fabs( gpGlobals->v_right.y ) * 0.05); + pev->velocity.z = pev->velocity.z * (1.0 - fabs( gpGlobals->v_right.z ) * 0.05); + + // general drag + pev->velocity = pev->velocity * 0.995; + + // apply power to stay correct height + if (m_flForce < 80 && vecEst.z < m_posDesired.z) + { + m_flForce += 12; + } + else if (m_flForce > 30) + { + if (vecEst.z > m_posDesired.z) + m_flForce -= 8; + } + + // pitch forward or back to get to target + if (flDist > 0 && flSpeed < m_flGoalSpeed /* && flSpeed < flDist */ && pev->angles.x + pev->avelocity.x > -40) + { + // ALERT( at_console, "F " ); + // lean forward + pev->avelocity.x -= 12.0; + } + else if (flDist < 0 && flSpeed > -50 && pev->angles.x + pev->avelocity.x < 20) + { + // ALERT( at_console, "B " ); + // lean backward + pev->avelocity.x += 12.0; + } + else if (pev->angles.x + pev->avelocity.x > 0) + { + // ALERT( at_console, "f " ); + pev->avelocity.x -= 4.0; + } + else if (pev->angles.x + pev->avelocity.x < 0) + { + // ALERT( at_console, "b " ); + pev->avelocity.x += 4.0; + } + + // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", pev->origin.x, pev->velocity.x, flDist, flSpeed, pev->angles.x, pev->avelocity.x, m_flForce ); + // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", pev->origin.z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce ); + + // make rotor, engine sounds + if (m_iSoundState == 0) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, 0, 110 ); + // EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", 0.5, 0.2, 0, 110 ); + + m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions + } + else + { + edict_t *pPlayer = NULL; + + pPlayer = UTIL_FindEntityByClassname( NULL, "player" ); + // UNDONE: this needs to send different sounds to every player for multiplayer. + if (pPlayer) + { + + float pitch = DotProduct( pev->velocity - pPlayer->v.velocity, (pPlayer->v.origin - pev->origin).Normalize() ); + + pitch = (int)(100 + pitch / 50.0); + + if (pitch > 250) + pitch = 250; + if (pitch < 50) + pitch = 50; + if (pitch == 100) + pitch = 101; + + float flVol = (m_flForce / 100.0) + .1; + if (flVol > 1.0) + flVol = 1.0; + + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_rotor2.wav", 1.0, 0.3, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch); + } + // EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "apache/ap_whine1.wav", flVol, 0.2, SND_CHANGE_PITCH | SND_CHANGE_VOL, pitch); + + // ALERT( at_console, "%.0f %.2f\n", pitch, flVol ); + } +} + + +void CMApache :: FireRocket( void ) +{ + static float side = 1.0; + static int count; + + if (m_iRockets <= 0) + return; + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = pev->origin + 1.5 * (gpGlobals->v_forward * 21 + gpGlobals->v_right * 70 * side + gpGlobals->v_up * -79); + + switch( m_iRockets % 5) + { + case 0: vecSrc = vecSrc + gpGlobals->v_right * 10; break; + case 1: vecSrc = vecSrc - gpGlobals->v_right * 10; break; + case 2: vecSrc = vecSrc + gpGlobals->v_up * 10; break; + case 3: vecSrc = vecSrc - gpGlobals->v_up * 10; break; + case 4: break; + } + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( vecSrc.x ); + WRITE_COORD( vecSrc.y ); + WRITE_COORD( vecSrc.z ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( 20 ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + MESSAGE_END(); + +//jlb CMBaseEntity *pRocket = CMBaseEntity::Create( "hvr_rocket", vecSrc, pev->angles, edict() ); + CMApacheHVR *pRocket = CreateClassPtr((CMApacheHVR *)NULL); + + if (pRocket) + { + pRocket->pev->origin = vecSrc; + pRocket->pev->angles = pev->angles; + pRocket->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pRocket->pev->absmin = pRocket->pev->origin - Vector(1,1,1); + pRocket->pev->absmax = pRocket->pev->origin + Vector(1,1,1); + + pRocket->Spawn(); + + pRocket->pev->velocity = pev->velocity + gpGlobals->v_forward * 100; + } + + m_iRockets--; + + side = - side; +} + + + +BOOL CMApache :: FireGun( ) +{ + UTIL_MakeAimVectors( pev->angles ); + + Vector posGun, angGun; + GetAttachment( 1, posGun, angGun ); + + Vector vecTarget = (m_posTarget - posGun).Normalize( ); + + Vector vecOut; + + vecOut.x = DotProduct( gpGlobals->v_forward, vecTarget ); + vecOut.y = -DotProduct( gpGlobals->v_right, vecTarget ); + vecOut.z = DotProduct( gpGlobals->v_up, vecTarget ); + + Vector angles = UTIL_VecToAngles (vecOut); + + angles.x = -angles.x; + if (angles.y > 180) + angles.y = angles.y - 360; + if (angles.y < -180) + angles.y = angles.y + 360; + if (angles.x > 180) + angles.x = angles.x - 360; + if (angles.x < -180) + angles.x = angles.x + 360; + + if (angles.x > m_angGun.x) + m_angGun.x = min( angles.x, m_angGun.x + 12 ); + if (angles.x < m_angGun.x) + m_angGun.x = max( angles.x, m_angGun.x - 12 ); + if (angles.y > m_angGun.y) + m_angGun.y = min( angles.y, m_angGun.y + 12 ); + if (angles.y < m_angGun.y) + m_angGun.y = max( angles.y, m_angGun.y - 12 ); + + m_angGun.y = SetBoneController( 0, m_angGun.y ); + m_angGun.x = SetBoneController( 1, m_angGun.x ); + + Vector posBarrel, angBarrel; + GetAttachment( 0, posBarrel, angBarrel ); + Vector vecGun = (posBarrel - posGun).Normalize( ); + + if (DotProduct( vecGun, vecTarget ) > 0.98) + { +#if 1 + FireBullets( 1, posGun, vecGun, VECTOR_CONE_4DEGREES, 8192, BULLET_MONSTER_12MM, 1 ); + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.3); +#else + static float flNext; + TraceResult tr; + UTIL_TraceLine( posGun, posGun + vecGun * 8192, dont_ignore_monsters, ENT( pev ), &tr ); + + if (!m_pBeam) + { + m_pBeam = CBeam::BeamCreate( "sprites/lgtning.spr", 80 ); + m_pBeam->PointEntInit( pev->origin, entindex( ) ); + m_pBeam->SetEndAttachment( 1 ); + m_pBeam->SetColor( 255, 180, 96 ); + m_pBeam->SetBrightness( 192 ); + } + + if (flNext < gpGlobals->time) + { + flNext = gpGlobals->time + 0.5; + m_pBeam->SetStartPos( tr.vecEndPos ); + } +#endif + return TRUE; + } + else + { + if (m_pBeam) + { + UTIL_Remove( m_pBeam->edict() ); + m_pBeam = NULL; + } + } + return FALSE; +} + + + +void CMApache :: ShowDamage( void ) +{ + if (m_iDoSmokePuff > 0 || RANDOM_LONG(0,99) > pev->health) + { + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z - 32 ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( RANDOM_LONG(0,9) + 20 ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + MESSAGE_END(); + } + if (m_iDoSmokePuff > 0) + m_iDoSmokePuff--; +} + + +int CMApache :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) +{ + if (pevInflictor->owner == edict()) + return 0; + + if (bitsDamageType & DMG_BLAST) + { + flDamage *= 2; + } + + /* + if ( (bitsDamageType & DMG_BULLET) && flDamage > 50) + { + // clip bullet damage at 50 + flDamage = 50; + } + */ + + // ALERT( at_console, "%.0f\n", flDamage ); + return CMBaseEntity::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + + + +void CMApache::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + // ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage ); + + // ignore blades + if (ptr->iHitgroup == 6 && (bitsDamageType & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB))) + return; + + // hit hard, hits cockpit, hits engines + if (flDamage > 50 || ptr->iHitgroup == 1 || ptr->iHitgroup == 2) + { + // ALERT( at_console, "%.0f\n", flDamage ); + AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); + m_iDoSmokePuff = 3 + (flDamage / 5.0); + } + else + { + // do half damage in the body + // AddMultiDamage( pevAttacker, this, flDamage / 2.0, bitsDamageType ); + UTIL_Ricochet( ptr->vecEndPos, 2.0 ); + } +} + + +void CMApacheHVR :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/HVR.mdl"); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( &CMApacheHVR::IgniteThink ); + SetTouch( &CMApacheHVR::ExplodeTouch ); + + UTIL_MakeAimVectors( pev->angles ); + m_vecForward = gpGlobals->v_forward; + pev->gravity = 0.5; + + pev->nextthink = gpGlobals->time + 0.1; + + pev->dmg = 150; +} + + +void CMApacheHVR :: Precache( void ) +{ + PRECACHE_MODEL("models/HVR.mdl"); + m_iTrail = PRECACHE_MODEL("sprites/smoke.spr"); + PRECACHE_SOUND ("weapons/rocket1.wav"); +} + + +void CMApacheHVR :: IgniteThink( void ) +{ + // pev->movetype = MOVETYPE_TOSS; + + // pev->movetype = MOVETYPE_FLY; + pev->effects |= EF_LIGHT; + + // make rocket sound + EMIT_SOUND( ENT(pev), CHAN_VOICE, "weapons/rocket1.wav", 1, 0.5 ); + + // rocket trail + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT(entindex()); // entity + WRITE_SHORT(m_iTrail ); // model + WRITE_BYTE( 15 ); // life + WRITE_BYTE( 5 ); // width + WRITE_BYTE( 224 ); // r, g, b + WRITE_BYTE( 224 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // brightness + + MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + + // set to accelerate + SetThink( &CMApacheHVR::AccelerateThink ); + pev->nextthink = gpGlobals->time + 0.1; +} + + +void CMApacheHVR :: AccelerateThink( void ) +{ + // check world boundaries + if (pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) + { + UTIL_Remove( this->edict() ); + return; + } + + // accelerate + float flSpeed = pev->velocity.Length(); + if (flSpeed < 1800) + { + pev->velocity = pev->velocity + m_vecForward * 200; + } + + // re-aim + pev->angles = UTIL_VecToAngles( pev->velocity ); + + pev->nextthink = gpGlobals->time + 0.1; +} + + +#endif diff --git a/src/dlls/barney.cpp b/src/dlls/barney.cpp index 188a488..848f2d1 100644 --- a/src/dlls/barney.cpp +++ b/src/dlls/barney.cpp @@ -1,678 +1,678 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// monster template -//========================================================= -// UNDONE: Holster weapon? - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "defaultai.h" -#include "weapons.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -// first flag is barney dying for scripted sequences? -#define BARNEY_AE_DRAW ( 2 ) -#define BARNEY_AE_SHOOT ( 3 ) -#define BARNEY_AE_HOLSTER ( 4 ) - -#define BARNEY_BODY_GUNHOLSTERED 0 -#define BARNEY_BODY_GUNDRAWN 1 -#define BARNEY_BODY_GUNGONE 2 - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -Task_t tlBaFollow[] = -{ - { TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client) - { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, -}; - -Schedule_t slBaFollow[] = -{ - { - tlBaFollow, - ARRAYSIZE ( tlBaFollow ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_PROVOKED, - 0, - "Follow" - }, -}; - -//========================================================= -// BarneyDraw- much better looking draw schedule for when -// barney knows who he's gonna attack. -//========================================================= -Task_t tlBarneyEnemyDraw[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, 0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float) ACT_ARM }, -}; - -Schedule_t slBarneyEnemyDraw[] = -{ - { - tlBarneyEnemyDraw, - ARRAYSIZE ( tlBarneyEnemyDraw ), - 0, - 0, - "Barney Enemy Draw" - } -}; - -Task_t tlBaFaceTarget[] = -{ - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_TARGET, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE }, -}; - -Schedule_t slBaFaceTarget[] = -{ - { - tlBaFaceTarget, - ARRAYSIZE ( tlBaFaceTarget ), - bits_COND_CLIENT_PUSH | - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_PROVOKED, - 0, - "FaceTarget" - }, -}; - - -Task_t tlIdleBaStand[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds. - { TASK_TLK_HEADRESET, (float)0 }, // reset head position -}; - -Schedule_t slIdleBaStand[] = -{ - { - tlIdleBaStand, - ARRAYSIZE ( tlIdleBaStand ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL | - bits_COND_PROVOKED, - - 0, - "IdleStand" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMBarney ) -{ - slBaFollow, - slBarneyEnemyDraw, - slBaFaceTarget, - slIdleBaStand, -}; - - -IMPLEMENT_CUSTOM_SCHEDULES( CMBarney, CMTalkMonster ); - -void CMBarney :: StartTask( Task_t *pTask ) -{ - CMTalkMonster::StartTask( pTask ); -} - -void CMBarney :: RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - if (m_hEnemy != NULL && (UTIL_IsPlayer(m_hEnemy))) - { - pev->framerate = 1.5; - } - CMTalkMonster::RunTask( pTask ); - break; - default: - CMTalkMonster::RunTask( pTask ); - break; - } -} - - - - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. -//========================================================= -int CMBarney :: ISoundMask ( void) -{ - return 0; -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMBarney :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_PLAYER_ALLY; -} - -//========================================================= -// ALertSound - barney says "Freeze!" -//========================================================= -void CMBarney :: AlertSound( void ) -{ - if ( m_hEnemy != NULL ) - { - if ( FOkToSpeak() ) - { - PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - } - } - -} -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMBarney :: SetYawSpeed ( void ) -{ - int ys; - - ys = 0; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 70; - break; - case ACT_WALK: - ys = 70; - break; - case ACT_RUN: - ys = 90; - break; - default: - ys = 70; - break; - } - - pev->yaw_speed = ys; -} - - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMBarney :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( flDist <= 1024 && flDot >= 0.5 ) - { - if ( gpGlobals->time > m_checkAttackTime ) - { - TraceResult tr; - - Vector shootOrigin = pev->origin + Vector( 0, 0, 55 ); - - edict_t *pEnemy = m_hEnemy; - - Vector shootTarget = ( (UTIL_BodyTarget( pEnemy, shootOrigin ) - pEnemy->v.origin) + m_vecEnemyLKP ); - - UTIL_TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, ENT(pev), &tr ); - - m_checkAttackTime = gpGlobals->time + 1; - - if ( tr.flFraction == 1.0 || (tr.pHit != NULL) && (tr.pHit == pEnemy) ) - m_lastAttackCheck = TRUE; - else - m_lastAttackCheck = FALSE; - - m_checkAttackTime = gpGlobals->time + 1.5; - } - return m_lastAttackCheck; - } - return FALSE; -} - - -//========================================================= -// BarneyFirePistol - shoots one round from the pistol at -// the enemy barney is facing. -//========================================================= -void CMBarney :: BarneyFirePistol ( void ) -{ - Vector vecShootOrigin; - - UTIL_MakeVectors(pev->angles); - vecShootOrigin = pev->origin + Vector( 0, 0, 55 ); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); - pev->effects = EF_MUZZLEFLASH; - - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM ); - - int pitchShift = RANDOM_LONG( 0, 20 ); - - // Only shift about half the time - if ( pitchShift > 10 ) - pitchShift = 0; - else - pitchShift -= 5; - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift ); - - // UNDONE: Reload? - m_cAmmoLoaded--;// take away a bullet! -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMBarney :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case BARNEY_AE_SHOOT: - BarneyFirePistol(); - break; - - case BARNEY_AE_DRAW: - // barney's bodygroup switches here so he can pull gun from holster - pev->body = BARNEY_BODY_GUNDRAWN; - m_fGunDrawn = TRUE; - break; - - case BARNEY_AE_HOLSTER: - // change bodygroup to replace gun in holster - pev->body = BARNEY_BODY_GUNHOLSTERED; - m_fGunDrawn = FALSE; - break; - - default: - CMTalkMonster::HandleAnimEvent( pEvent ); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMBarney :: Spawn() -{ - Precache( ); - - // every new barney must call this, otherwise - // when a level is loaded, nobody will talk (time is reset to 0) - TalkInit(); - - SET_MODEL(ENT(pev), "models/barney.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->health = gSkillData.barneyHealth; - pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. - m_flFieldOfView = VIEW_FIELD_FULL; - m_MonsterState = MONSTERSTATE_NONE; - - pev->body = 0; // gun in holster - m_fGunDrawn = FALSE; - - m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_barney" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Barney" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMBarney :: Precache() -{ - PRECACHE_MODEL("models/barney.mdl"); - - PRECACHE_SOUND("barney/ba_attack1.wav" ); - PRECACHE_SOUND("barney/ba_attack2.wav" ); - - PRECACHE_SOUND("barney/ba_pain1.wav"); - PRECACHE_SOUND("barney/ba_pain2.wav"); - PRECACHE_SOUND("barney/ba_pain3.wav"); - - PRECACHE_SOUND("barney/ba_die1.wav"); - PRECACHE_SOUND("barney/ba_die2.wav"); - PRECACHE_SOUND("barney/ba_die3.wav"); - - CMTalkMonster::Precache(); -} - -// Init talk data -void CMBarney :: TalkInit() -{ - CMTalkMonster::TalkInit(); - - // scientists speach group names (group names are in sentences.txt) - - m_szGrp[TLK_ANSWER] = "BA_ANSWER"; - m_szGrp[TLK_QUESTION] = "BA_QUESTION"; - m_szGrp[TLK_IDLE] = "BA_IDLE"; - m_szGrp[TLK_STARE] = "BA_STARE"; - m_szGrp[TLK_USE] = "BA_OK"; - m_szGrp[TLK_UNUSE] = "BA_WAIT"; - m_szGrp[TLK_STOP] = "BA_STOP"; - - m_szGrp[TLK_NOSHOOT] = "BA_SCARED"; - m_szGrp[TLK_HELLO] = "BA_HELLO"; - - m_szGrp[TLK_PLHURT1] = "!BA_CUREA"; - m_szGrp[TLK_PLHURT2] = "!BA_CUREB"; - m_szGrp[TLK_PLHURT3] = "!BA_CUREC"; - - m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE - m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE - m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE - - m_szGrp[TLK_SMELL] = "BA_SMELL"; - - m_szGrp[TLK_WOUND] = "BA_WOUND"; - m_szGrp[TLK_MORTAL] = "BA_MORTAL"; - - // get voice for head - just one barney voice for now - m_voicePitch = 100; -} - - -static BOOL IsFacing( entvars_t *pevTest, const Vector &reference ) -{ - Vector vecDir = (reference - pevTest->origin); - vecDir.z = 0; - vecDir = vecDir.Normalize(); - Vector forward, angle; - angle = pevTest->v_angle; - angle.x = 0; - UTIL_MakeVectorsPrivate( angle, forward, NULL, NULL ); - // He's facing me, he meant it - if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so - { - return TRUE; - } - return FALSE; -} - - -int CMBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - // make sure friends talk about it if player hurts talkmonsters... - int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - if ( !IsAlive() || pev->deadflag == DEAD_DYING ) - return ret; - - if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) ) - { - // This is a heurstic to determine if the player intended to harm me - // If I have an enemy, we can't establish intent (may just be crossfire) - if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) ) - { - Remember( bits_MEMORY_PROVOKED ); - } - } - - return ret; -} - - -//========================================================= -// PainSound -//========================================================= -void CMBarney :: PainSound ( void ) -{ - if (gpGlobals->time < m_painTime) - return; - - m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); - - switch (RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CMBarney :: DeathSound ( void ) -{ - switch (RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - } -} - - -void CMBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - switch( ptr->iHitgroup) - { - case HITGROUP_CHEST: - case HITGROUP_STOMACH: - if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) - { - flDamage = flDamage / 2; - } - break; - case 10: - if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB)) - { - flDamage -= 20; - if (flDamage <= 0) - { - UTIL_Ricochet( ptr->vecEndPos, 1.0 ); - flDamage = 0.01; - } - } - // always a head shot - ptr->iHitgroup = HITGROUP_HEAD; - break; - } - - CMTalkMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -void CMBarney::Killed( entvars_t *pevAttacker, int iGib ) -{ - if ( pev->body < BARNEY_BODY_GUNGONE ) - {// drop the gun! - Vector vecGunPos; - Vector vecGunAngles; - - pev->body = BARNEY_BODY_GUNGONE; - - GetAttachment( 0, vecGunPos, vecGunAngles ); - } - - SetUse( NULL ); - CMTalkMonster::Killed( pevAttacker, iGib ); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -Schedule_t* CMBarney :: GetScheduleOfType ( int Type ) -{ - Schedule_t *psched; - - switch( Type ) - { - case SCHED_ARM_WEAPON: - if ( m_hEnemy != NULL ) - { - // face enemy, then draw. - return slBarneyEnemyDraw; - } - break; - - // Hook these to make a looping schedule - case SCHED_TARGET_FACE: - // call base class default so that barney will talk - // when 'used' - psched = CMTalkMonster::GetScheduleOfType(Type); - - if (psched == slIdleStand) - return slBaFaceTarget; // override this for different target face behavior - else - return psched; - - case SCHED_TARGET_CHASE: - return slBaFollow; - - case SCHED_IDLE_STAND: - // call base class default so that scientist will talk - // when standing during idle - psched = CMTalkMonster::GetScheduleOfType(Type); - - if (psched == slIdleStand) - { - // just look straight ahead. - return slIdleBaStand; - } - else - return psched; - } - - return CMTalkMonster::GetScheduleOfType( Type ); -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMBarney :: GetSchedule ( void ) -{ - if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() ) - { - PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM ); - } - - switch( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - // always act surprized with a new enemy - if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) ) - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - - // wait for one schedule to draw gun - if (!m_fGunDrawn ) - return GetScheduleOfType( SCHED_ARM_WEAPON ); - - if ( HasConditions( bits_COND_HEAVY_DAMAGE ) ) - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - break; - - case MONSTERSTATE_ALERT: - case MONSTERSTATE_IDLE: - if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) - { - // flinch if hurt - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - - if ( m_hEnemy == NULL && IsFollowing() ) - { - if ( !UTIL_IsAlive(m_hTargetEnt) ) - { - // UNDONE: Comment about the recently dead player here? - StopFollowing( FALSE ); - break; - } - else - { - if ( HasConditions( bits_COND_CLIENT_PUSH ) ) - { - return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); - } - return GetScheduleOfType( SCHED_TARGET_FACE ); - } - } - - if ( HasConditions( bits_COND_CLIENT_PUSH ) ) - { - return GetScheduleOfType( SCHED_MOVE_AWAY ); - } - - // try to say something about smells - TrySmellTalk(); - break; - } - - return CMTalkMonster::GetSchedule(); -} - -MONSTERSTATE CMBarney :: GetIdealState ( void ) -{ - return CMTalkMonster::GetIdealState(); -} - - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +// UNDONE: Holster weapon? + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "defaultai.h" +#include "weapons.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +// first flag is barney dying for scripted sequences? +#define BARNEY_AE_DRAW ( 2 ) +#define BARNEY_AE_SHOOT ( 3 ) +#define BARNEY_AE_HOLSTER ( 4 ) + +#define BARNEY_BODY_GUNHOLSTERED 0 +#define BARNEY_BODY_GUNDRAWN 1 +#define BARNEY_BODY_GUNGONE 2 + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +Task_t tlBaFollow[] = +{ + { TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client) + { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, +}; + +Schedule_t slBaFollow[] = +{ + { + tlBaFollow, + ARRAYSIZE ( tlBaFollow ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_PROVOKED, + 0, + "Follow" + }, +}; + +//========================================================= +// BarneyDraw- much better looking draw schedule for when +// barney knows who he's gonna attack. +//========================================================= +Task_t tlBarneyEnemyDraw[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, 0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float) ACT_ARM }, +}; + +Schedule_t slBarneyEnemyDraw[] = +{ + { + tlBarneyEnemyDraw, + ARRAYSIZE ( tlBarneyEnemyDraw ), + 0, + 0, + "Barney Enemy Draw" + } +}; + +Task_t tlBaFaceTarget[] = +{ + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_TARGET, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE }, +}; + +Schedule_t slBaFaceTarget[] = +{ + { + tlBaFaceTarget, + ARRAYSIZE ( tlBaFaceTarget ), + bits_COND_CLIENT_PUSH | + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_PROVOKED, + 0, + "FaceTarget" + }, +}; + + +Task_t tlIdleBaStand[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds. + { TASK_TLK_HEADRESET, (float)0 }, // reset head position +}; + +Schedule_t slIdleBaStand[] = +{ + { + tlIdleBaStand, + ARRAYSIZE ( tlIdleBaStand ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL | + bits_COND_PROVOKED, + + 0, + "IdleStand" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMBarney ) +{ + slBaFollow, + slBarneyEnemyDraw, + slBaFaceTarget, + slIdleBaStand, +}; + + +IMPLEMENT_CUSTOM_SCHEDULES( CMBarney, CMTalkMonster ); + +void CMBarney :: StartTask( Task_t *pTask ) +{ + CMTalkMonster::StartTask( pTask ); +} + +void CMBarney :: RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + if (m_hEnemy != NULL && (UTIL_IsPlayer(m_hEnemy))) + { + pev->framerate = 1.5; + } + CMTalkMonster::RunTask( pTask ); + break; + default: + CMTalkMonster::RunTask( pTask ); + break; + } +} + + + + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. +//========================================================= +int CMBarney :: ISoundMask ( void) +{ + return 0; +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMBarney :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_PLAYER_ALLY; +} + +//========================================================= +// ALertSound - barney says "Freeze!" +//========================================================= +void CMBarney :: AlertSound( void ) +{ + if ( m_hEnemy != NULL ) + { + if ( FOkToSpeak() ) + { + PlaySentence( "BA_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + } + } + +} +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMBarney :: SetYawSpeed ( void ) +{ + int ys; + + ys = 0; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 70; + break; + case ACT_WALK: + ys = 70; + break; + case ACT_RUN: + ys = 90; + break; + default: + ys = 70; + break; + } + + pev->yaw_speed = ys; +} + + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMBarney :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( flDist <= 1024 && flDot >= 0.5 ) + { + if ( gpGlobals->time > m_checkAttackTime ) + { + TraceResult tr; + + Vector shootOrigin = pev->origin + Vector( 0, 0, 55 ); + + edict_t *pEnemy = m_hEnemy; + + Vector shootTarget = ( (UTIL_BodyTarget( pEnemy, shootOrigin ) - pEnemy->v.origin) + m_vecEnemyLKP ); + + UTIL_TraceLine( shootOrigin, shootTarget, dont_ignore_monsters, ENT(pev), &tr ); + + m_checkAttackTime = gpGlobals->time + 1; + + if ( tr.flFraction == 1.0 || (tr.pHit != NULL) && (tr.pHit == pEnemy) ) + m_lastAttackCheck = TRUE; + else + m_lastAttackCheck = FALSE; + + m_checkAttackTime = gpGlobals->time + 1.5; + } + return m_lastAttackCheck; + } + return FALSE; +} + + +//========================================================= +// BarneyFirePistol - shoots one round from the pistol at +// the enemy barney is facing. +//========================================================= +void CMBarney :: BarneyFirePistol ( void ) +{ + Vector vecShootOrigin; + + UTIL_MakeVectors(pev->angles); + vecShootOrigin = pev->origin + Vector( 0, 0, 55 ); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); + pev->effects = EF_MUZZLEFLASH; + + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_9MM ); + + int pitchShift = RANDOM_LONG( 0, 20 ); + + // Only shift about half the time + if ( pitchShift > 10 ) + pitchShift = 0; + else + pitchShift -= 5; + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift ); + + // UNDONE: Reload? + m_cAmmoLoaded--;// take away a bullet! +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMBarney :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case BARNEY_AE_SHOOT: + BarneyFirePistol(); + break; + + case BARNEY_AE_DRAW: + // barney's bodygroup switches here so he can pull gun from holster + pev->body = BARNEY_BODY_GUNDRAWN; + m_fGunDrawn = TRUE; + break; + + case BARNEY_AE_HOLSTER: + // change bodygroup to replace gun in holster + pev->body = BARNEY_BODY_GUNHOLSTERED; + m_fGunDrawn = FALSE; + break; + + default: + CMTalkMonster::HandleAnimEvent( pEvent ); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMBarney :: Spawn() +{ + Precache( ); + + // every new barney must call this, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + TalkInit(); + + SET_MODEL(ENT(pev), "models/barney.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.barneyHealth; + pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_FULL; + m_MonsterState = MONSTERSTATE_NONE; + + pev->body = 0; // gun in holster + m_fGunDrawn = FALSE; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_barney" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Barney" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMBarney :: Precache() +{ + PRECACHE_MODEL("models/barney.mdl"); + + PRECACHE_SOUND("barney/ba_attack1.wav" ); + PRECACHE_SOUND("barney/ba_attack2.wav" ); + + PRECACHE_SOUND("barney/ba_pain1.wav"); + PRECACHE_SOUND("barney/ba_pain2.wav"); + PRECACHE_SOUND("barney/ba_pain3.wav"); + + PRECACHE_SOUND("barney/ba_die1.wav"); + PRECACHE_SOUND("barney/ba_die2.wav"); + PRECACHE_SOUND("barney/ba_die3.wav"); + + CMTalkMonster::Precache(); +} + +// Init talk data +void CMBarney :: TalkInit() +{ + CMTalkMonster::TalkInit(); + + // scientists speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = "BA_ANSWER"; + m_szGrp[TLK_QUESTION] = "BA_QUESTION"; + m_szGrp[TLK_IDLE] = "BA_IDLE"; + m_szGrp[TLK_STARE] = "BA_STARE"; + m_szGrp[TLK_USE] = "BA_OK"; + m_szGrp[TLK_UNUSE] = "BA_WAIT"; + m_szGrp[TLK_STOP] = "BA_STOP"; + + m_szGrp[TLK_NOSHOOT] = "BA_SCARED"; + m_szGrp[TLK_HELLO] = "BA_HELLO"; + + m_szGrp[TLK_PLHURT1] = "!BA_CUREA"; + m_szGrp[TLK_PLHURT2] = "!BA_CUREB"; + m_szGrp[TLK_PLHURT3] = "!BA_CUREC"; + + m_szGrp[TLK_PHELLO] = NULL; //"BA_PHELLO"; // UNDONE + m_szGrp[TLK_PIDLE] = NULL; //"BA_PIDLE"; // UNDONE + m_szGrp[TLK_PQUESTION] = "BA_PQUEST"; // UNDONE + + m_szGrp[TLK_SMELL] = "BA_SMELL"; + + m_szGrp[TLK_WOUND] = "BA_WOUND"; + m_szGrp[TLK_MORTAL] = "BA_MORTAL"; + + // get voice for head - just one barney voice for now + m_voicePitch = 100; +} + + +static BOOL IsFacing( entvars_t *pevTest, const Vector &reference ) +{ + Vector vecDir = (reference - pevTest->origin); + vecDir.z = 0; + vecDir = vecDir.Normalize(); + Vector forward, angle; + angle = pevTest->v_angle; + angle.x = 0; + UTIL_MakeVectorsPrivate( angle, forward, NULL, NULL ); + // He's facing me, he meant it + if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so + { + return TRUE; + } + return FALSE; +} + + +int CMBarney :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // make sure friends talk about it if player hurts talkmonsters... + int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + if ( !IsAlive() || pev->deadflag == DEAD_DYING ) + return ret; + + if ( m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT) ) + { + // This is a heurstic to determine if the player intended to harm me + // If I have an enemy, we can't establish intent (may just be crossfire) + if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) ) + { + Remember( bits_MEMORY_PROVOKED ); + } + } + + return ret; +} + + +//========================================================= +// PainSound +//========================================================= +void CMBarney :: PainSound ( void ) +{ + if (gpGlobals->time < m_painTime) + return; + + m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); + + switch (RANDOM_LONG(0,2)) + { + case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CMBarney :: DeathSound ( void ) +{ + switch (RANDOM_LONG(0,2)) + { + case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + } +} + + +void CMBarney::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + switch( ptr->iHitgroup) + { + case HITGROUP_CHEST: + case HITGROUP_STOMACH: + if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) + { + flDamage = flDamage / 2; + } + break; + case 10: + if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_CLUB)) + { + flDamage -= 20; + if (flDamage <= 0) + { + UTIL_Ricochet( ptr->vecEndPos, 1.0 ); + flDamage = 0.01; + } + } + // always a head shot + ptr->iHitgroup = HITGROUP_HEAD; + break; + } + + CMTalkMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +void CMBarney::Killed( entvars_t *pevAttacker, int iGib ) +{ + if ( pev->body < BARNEY_BODY_GUNGONE ) + {// drop the gun! + Vector vecGunPos; + Vector vecGunAngles; + + pev->body = BARNEY_BODY_GUNGONE; + + GetAttachment( 0, vecGunPos, vecGunAngles ); + } + + SetUse( NULL ); + CMTalkMonster::Killed( pevAttacker, iGib ); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +Schedule_t* CMBarney :: GetScheduleOfType ( int Type ) +{ + Schedule_t *psched; + + switch( Type ) + { + case SCHED_ARM_WEAPON: + if ( m_hEnemy != NULL ) + { + // face enemy, then draw. + return slBarneyEnemyDraw; + } + break; + + // Hook these to make a looping schedule + case SCHED_TARGET_FACE: + // call base class default so that barney will talk + // when 'used' + psched = CMTalkMonster::GetScheduleOfType(Type); + + if (psched == slIdleStand) + return slBaFaceTarget; // override this for different target face behavior + else + return psched; + + case SCHED_TARGET_CHASE: + return slBaFollow; + + case SCHED_IDLE_STAND: + // call base class default so that scientist will talk + // when standing during idle + psched = CMTalkMonster::GetScheduleOfType(Type); + + if (psched == slIdleStand) + { + // just look straight ahead. + return slIdleBaStand; + } + else + return psched; + } + + return CMTalkMonster::GetScheduleOfType( Type ); +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMBarney :: GetSchedule ( void ) +{ + if ( HasConditions( bits_COND_ENEMY_DEAD ) && FOkToSpeak() ) + { + PlaySentence( "BA_KILL", 4, VOL_NORM, ATTN_NORM ); + } + + switch( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + // always act surprized with a new enemy + if ( HasConditions( bits_COND_NEW_ENEMY ) && HasConditions( bits_COND_LIGHT_DAMAGE) ) + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + + // wait for one schedule to draw gun + if (!m_fGunDrawn ) + return GetScheduleOfType( SCHED_ARM_WEAPON ); + + if ( HasConditions( bits_COND_HEAVY_DAMAGE ) ) + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + break; + + case MONSTERSTATE_ALERT: + case MONSTERSTATE_IDLE: + if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) + { + // flinch if hurt + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + + if ( m_hEnemy == NULL && IsFollowing() ) + { + if ( !UTIL_IsAlive(m_hTargetEnt) ) + { + // UNDONE: Comment about the recently dead player here? + StopFollowing( FALSE ); + break; + } + else + { + if ( HasConditions( bits_COND_CLIENT_PUSH ) ) + { + return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); + } + return GetScheduleOfType( SCHED_TARGET_FACE ); + } + } + + if ( HasConditions( bits_COND_CLIENT_PUSH ) ) + { + return GetScheduleOfType( SCHED_MOVE_AWAY ); + } + + // try to say something about smells + TrySmellTalk(); + break; + } + + return CMTalkMonster::GetSchedule(); +} + +MONSTERSTATE CMBarney :: GetIdealState ( void ) +{ + return CMTalkMonster::GetIdealState(); +} + + diff --git a/src/dlls/bigmomma.cpp b/src/dlls/bigmomma.cpp index ec89a27..48fbd08 100644 --- a/src/dlls/bigmomma.cpp +++ b/src/dlls/bigmomma.cpp @@ -1,1207 +1,1207 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -//========================================================= -// monster template -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "decals.h" - -#define SF_INFOBM_RUN 0x0001 -#define SF_INFOBM_WAIT 0x0002 - -// AI Nodes for Big Momma -class CMInfoBM : public CMPointEntity -{ -public: - void Spawn( void ); - void KeyValue( KeyValueData* pkvd ); - - // name in pev->targetname - // next in pev->target - // radius in pev->scale - // health in pev->health - // Reach target in pev->message - // Reach delay in pev->speed - // Reach sequence in pev->netname - - int m_preSequence; -}; - -void CMInfoBM::Spawn( void ) -{ - pev->classname = MAKE_STRING( "info_bigmomma" ); -} - -void CMInfoBM::KeyValue( KeyValueData* pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "radius")) - { - pev->scale = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachdelay")) - { - pev->speed = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachtarget")) - { - pev->message = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "reachsequence")) - { - pev->netname = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "presequence")) - { - m_preSequence = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMPointEntity::KeyValue( pkvd ); -} - -//========================================================= -// Mortar shot entity -//========================================================= -class CMBMortar : public CMBaseEntity -{ -public: - void Spawn( void ); - - static CMBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ); - void Touch( edict_t *pOther ); - void EXPORT Animate( void ); - - int m_maxFrame; -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define BIG_AE_STEP1 1 // Footstep left -#define BIG_AE_STEP2 2 // Footstep right -#define BIG_AE_STEP3 3 // Footstep back left -#define BIG_AE_STEP4 4 // Footstep back right -#define BIG_AE_SACK 5 // Sack slosh -#define BIG_AE_DEATHSOUND 6 // Death sound - -#define BIG_AE_MELEE_ATTACKBR 8 // Leg attack -#define BIG_AE_MELEE_ATTACKBL 9 // Leg attack -#define BIG_AE_MELEE_ATTACK1 10 // Leg attack -#define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar -#define BIG_AE_LAY_CRAB 12 // Lay a headcrab -#define BIG_AE_JUMP_FORWARD 13 // Jump up and forward -#define BIG_AE_SCREAM 14 // alert sound -#define BIG_AE_PAIN_SOUND 15 // pain sound -#define BIG_AE_ATTACK_SOUND 16 // attack sound -#define BIG_AE_BIRTH_SOUND 17 // birth sound -#define BIG_AE_EARLY_TARGET 50 // Fire target early - - - -// User defined conditions -#define bits_COND_NODE_SEQUENCE ( bits_COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play - -// Attack distance constants -#define BIG_ATTACKDIST 170 -#define BIG_MORTARDIST 800 -#define BIG_MAXCHILDREN 20 // Max # of live headcrab children - - -#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1) -#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2) -#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3) -#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4) - -int gSpitSprite, gSpitDebrisSprite; -Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); - - -// UNDONE: -// -//#define BIG_CHILDCLASS "monster_babycrab" - -const char *CMBigMomma::pChildDieSounds[] = -{ - "gonarch/gon_childdie1.wav", - "gonarch/gon_childdie2.wav", - "gonarch/gon_childdie3.wav", -}; - -const char *CMBigMomma::pSackSounds[] = -{ - "gonarch/gon_sack1.wav", - "gonarch/gon_sack2.wav", - "gonarch/gon_sack3.wav", -}; - -const char *CMBigMomma::pDeathSounds[] = -{ - "gonarch/gon_die1.wav", -}; - -const char *CMBigMomma::pAttackSounds[] = -{ - "gonarch/gon_attack1.wav", - "gonarch/gon_attack2.wav", - "gonarch/gon_attack3.wav", -}; -const char *CMBigMomma::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CMBigMomma::pBirthSounds[] = -{ - "gonarch/gon_birth1.wav", - "gonarch/gon_birth2.wav", - "gonarch/gon_birth3.wav", -}; - -const char *CMBigMomma::pAlertSounds[] = -{ - "gonarch/gon_alert1.wav", - "gonarch/gon_alert2.wav", - "gonarch/gon_alert3.wav", -}; - -const char *CMBigMomma::pPainSounds[] = -{ - "gonarch/gon_pain2.wav", - "gonarch/gon_pain4.wav", - "gonarch/gon_pain5.wav", -}; - -const char *CMBigMomma::pFootSounds[] = -{ - "gonarch/gon_step1.wav", - "gonarch/gon_step2.wav", - "gonarch/gon_step3.wav", -}; - - -int CMBigMomma :: GetNodeSequence( void ) -{ - edict_t *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->v.netname; // netname holds node sequence - } - return 0; -} - -int CMBigMomma :: GetNodePresequence( void ) -{ - if (!m_hTargetEnt || (m_hTargetEnt->v.euser4 == NULL)) - return 0; - - CMInfoBM *pTarget = (CMInfoBM *)CMBaseEntity::Instance(m_hTargetEnt); - if ( pTarget ) - { - return pTarget->m_preSequence; - } - return 0; -} - -float CMBigMomma :: GetNodeDelay( void ) -{ - edict_t *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->v.speed; // Speed holds node delay - } - return 0; -} - -float CMBigMomma :: GetNodeRange( void ) -{ - edict_t *pTarget = m_hTargetEnt; - if ( pTarget ) - { - return pTarget->v.scale; // Scale holds node delay - } - return 1e6; -} - -float CMBigMomma :: GetNodeYaw( void ) -{ - edict_t *pTarget = m_hTargetEnt; - if ( pTarget ) - { - if ( pTarget->v.angles.y != 0 ) - return pTarget->v.angles.y; - } - return pev->angles.y; -} - -BOOL CMBigMomma :: CanLayCrab( void ) -{ - if ( m_crabTime < gpGlobals->time && m_crabCount < BIG_MAXCHILDREN ) - { - // Don't spawn crabs inside each other - Vector mins = pev->origin - Vector( 32, 32, 0 ); - Vector maxs = pev->origin + Vector( 32, 32, 0 ); - - edict_t *pList[2]; - int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_MONSTER ); - for ( int i = 0; i < count; i++ ) - { - if ( pList[i] != edict() ) // Don't hurt yourself! - return FALSE; - } - return TRUE; - } - - return FALSE; -} - -void CMBigMomma :: KeyValue( KeyValueData *pkvd ) -{ -#if 0 - if (FStrEq(pkvd->szKeyName, "volume")) - { - m_volume = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else -#endif - CMBaseMonster::KeyValue( pkvd ); -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMBigMomma :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMBigMomma :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 100; - break; - default: - ys = 90; - } - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMBigMomma :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case BIG_AE_MELEE_ATTACKBR: - case BIG_AE_MELEE_ATTACKBL: - case BIG_AE_MELEE_ATTACK1: - { - Vector forward, right; - - UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); - - Vector center = pev->origin + forward * 128; - Vector mins = center - Vector( 64, 64, 0 ); - Vector maxs = center + Vector( 64, 64, 64 ); - - edict_t *pList[8]; - int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_MONSTER|FL_CLIENT ); - edict_t *pHurt = NULL; - - for ( int i = 0; i < count && !pHurt; i++ ) - { - if ( pList[i] != this->edict() ) - { - if ( pList[i]->v.owner != edict() ) - pHurt = pList[i]; - } - } - - if ( pHurt ) - { - if (UTIL_IsPlayer(pHurt)) - { - UTIL_TakeDamage( pHurt, pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); - pHurt->v.punchangle.x = 15; - switch( pEvent->event ) - { - case BIG_AE_MELEE_ATTACKBR: - pHurt->v.velocity = pHurt->v.velocity + (forward * 150) + Vector(0,0,250) - (right * 200); - break; - - case BIG_AE_MELEE_ATTACKBL: - pHurt->v.velocity = pHurt->v.velocity + (forward * 150) + Vector(0,0,250) + (right * 200); - break; - - case BIG_AE_MELEE_ATTACK1: - pHurt->v.velocity = pHurt->v.velocity + (forward * 220) + Vector(0,0,200); - break; - } - - pHurt->v.flags &= ~FL_ONGROUND; - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else if (pHurt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - - pMonster->TakeDamage( pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); - pMonster->pev->punchangle.x = 15; - switch( pEvent->event ) - { - case BIG_AE_MELEE_ATTACKBR: - pMonster->pev->velocity = pMonster->pev->velocity + (forward * 150) + Vector(0,0,250) - (right * 200); - break; - - case BIG_AE_MELEE_ATTACKBL: - pMonster->pev->velocity = pMonster->pev->velocity + (forward * 150) + Vector(0,0,250) + (right * 200); - break; - - case BIG_AE_MELEE_ATTACK1: - pMonster->pev->velocity = pMonster->pev->velocity + (forward * 220) + Vector(0,0,200); - break; - } - - pMonster->pev->flags &= ~FL_ONGROUND; - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - } - } - break; - - case BIG_AE_SCREAM: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); - break; - - case BIG_AE_PAIN_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); - break; - - case BIG_AE_ATTACK_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackSounds ); - break; - - case BIG_AE_BIRTH_SOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pBirthSounds ); - break; - - case BIG_AE_SACK: - if ( RANDOM_LONG(0,100) < 30 ) - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pSackSounds ); - break; - - case BIG_AE_DEATHSOUND: - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); - break; - - case BIG_AE_STEP1: // Footstep left - case BIG_AE_STEP3: // Footstep back left - EMIT_SOUND_ARRAY_DYN( CHAN_ITEM, pFootSounds ); - break; - - case BIG_AE_STEP4: // Footstep back right - case BIG_AE_STEP2: // Footstep right - EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pFootSounds ); - break; - - case BIG_AE_MORTAR_ATTACK1: - LaunchMortar(); - break; - - case BIG_AE_LAY_CRAB: - LayHeadcrab(); - break; - - case BIG_AE_JUMP_FORWARD: - ClearBits( pev->flags, FL_ONGROUND ); - - UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground - UTIL_MakeVectors ( pev->angles ); - - pev->velocity = (gpGlobals->v_forward * 200) + gpGlobals->v_up * 500; - break; - - case BIG_AE_EARLY_TARGET: - { - edict_t *pTarget = m_hTargetEnt; - if ( pTarget && pTarget->v.message ) - FireTargets( STRING(pTarget->v.message), this->edict(), this->edict(), USE_TOGGLE, 0 ); - Remember( bits_MEMORY_FIRED_NODE ); - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -void CMBigMomma :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ -/* - if ( ptr->iHitgroup != 1 ) - { - // didn't hit the sack? - - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); - pev->dmgtime = gpGlobals->time; - } - - flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated - } - else */ - if ( gpGlobals->time > m_painSoundTime ) - { - m_painSoundTime = gpGlobals->time + RANDOM_LONG(1, 3); - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); - } - - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -int CMBigMomma :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Don't take any acid damage -- BigMomma's mortar is acid - if ( bitsDamageType & DMG_ACID ) - flDamage = 0; - - if ( !HasMemory(bits_MEMORY_PATH_FINISHED) ) - { - if ( pev->health <= flDamage ) - { - pev->health = flDamage + 1; - Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE ); - ALERT( at_aiconsole, "BM: Finished node health!!!\n" ); - } - } - - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CMBigMomma :: LayHeadcrab( void ) -{ -// CMBaseEntity *pChild = CMBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, edict() ); - - CMBabyCrab *pChild = CreateClassPtr((CMBabyCrab *)NULL); - - if (pChild != NULL) - { - pChild->pev->origin = pev->origin; - pChild->pev->angles = pev->angles; - pChild->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pChild->pev->absmin = pChild->pev->origin - Vector(1,1,1); - pChild->pev->absmax = pChild->pev->origin + Vector(1,1,1); - - pChild->Spawn(); - - pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND; - - // Is this the second crab in a pair? - if ( HasMemory( bits_MEMORY_CHILDPAIR ) ) - { - m_crabTime = gpGlobals->time + RANDOM_FLOAT( 5, 10 ); - Forget( bits_MEMORY_CHILDPAIR ); - } - else - { - m_crabTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 2.5 ); - Remember( bits_MEMORY_CHILDPAIR ); - } - - TraceResult tr; - UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,100), ignore_monsters, edict(), &tr); - UTIL_DecalTrace( &tr, DECAL_MOMMABIRTH ); - - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBirthSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - m_crabCount++; - } -} - - - -void CMBigMomma::DeathNotice( entvars_t *pevChild ) -{ - if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then - m_crabCount--; - if ( IsAlive() ) - { - // Make the "my baby's dead" noise! - EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pChildDieSounds ); - } -} - - -void CMBigMomma::LaunchMortar( void ) -{ - m_mortarTime = gpGlobals->time + RANDOM_FLOAT( 2, 15 ); - - Vector startPos = pev->origin; - startPos.z += 180; - - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pSackSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - CMBMortar *pBomb = CMBMortar::Shoot( edict(), startPos, pev->movedir ); - if (pBomb) - { - pBomb->pev->gravity = 1.0; - MortarSpray( startPos, Vector(0,0,1), gSpitSprite, 24 ); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMBigMomma :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/big_mom.mdl"); -// UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); - UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = 150 * gSkillData.bigmommaHealthFactor; - pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_bigmomma" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Big Momma" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMBigMomma :: Precache() -{ - PRECACHE_MODEL("models/big_mom.mdl"); - - PRECACHE_SOUND_ARRAY( pChildDieSounds ); - PRECACHE_SOUND_ARRAY( pSackSounds ); - PRECACHE_SOUND_ARRAY( pDeathSounds ); - PRECACHE_SOUND_ARRAY( pAttackSounds ); - PRECACHE_SOUND_ARRAY( pAttackHitSounds ); - PRECACHE_SOUND_ARRAY( pBirthSounds ); - PRECACHE_SOUND_ARRAY( pAlertSounds ); - PRECACHE_SOUND_ARRAY( pPainSounds ); - PRECACHE_SOUND_ARRAY( pFootSounds ); - -// UTIL_PrecacheOther( BIG_CHILDCLASS ); - CMBabyCrab babycrab; - babycrab.Precache(); - - // TEMP: Squid - PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile. - gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle. - gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" ); - - PRECACHE_SOUND( "bullchicken/bc_acid1.wav" ); - PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" ); - PRECACHE_SOUND( "bullchicken/bc_spithit2.wav" ); -} - - -void CMBigMomma::Activate( void ) -{ - if ( m_hTargetEnt == NULL ) - Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up -} - - -void CMBigMomma::NodeStart( int iszNextNode ) -{ - pev->netname = iszNextNode; - - edict_t *pTarget = NULL; - - if ( pev->netname ) - { - edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->netname) ); - - if ( !FNullEnt(pentTarget) ) - pTarget = pentTarget; - } - - - if ( !pTarget ) - { - ALERT( at_aiconsole, "BM: Finished the path!!\n" ); - Remember( bits_MEMORY_PATH_FINISHED ); - return; - } - Remember( bits_MEMORY_ON_PATH ); - m_hTargetEnt = pTarget; -} - - -void CMBigMomma::NodeReach( void ) -{ - edict_t *pTarget = m_hTargetEnt; - - Forget( bits_MEMORY_ADVANCE_NODE ); - - if ( !pTarget ) - return; - - if ( pTarget->v.health ) - pev->max_health = pev->health = pTarget->v.health * gSkillData.bigmommaHealthFactor; - - if ( !HasMemory( bits_MEMORY_FIRED_NODE ) ) - { - if ( pTarget->v.message ) - FireTargets( STRING(pTarget->v.message), this->edict(), this->edict(), USE_TOGGLE, 0 ); - } - Forget( bits_MEMORY_FIRED_NODE ); - - pev->netname = pTarget->v.target; - if ( pTarget->v.health == 0 ) - Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node -} - - - // Slash -BOOL CMBigMomma::CheckMeleeAttack1( float flDot, float flDist ) -{ - if (flDot >= 0.7) - { - if ( flDist <= BIG_ATTACKDIST ) - return TRUE; - } - return FALSE; -} - - -// Lay a crab -BOOL CMBigMomma::CheckMeleeAttack2( float flDot, float flDist ) -{ - return CanLayCrab(); -} - - -// Mortar launch -BOOL CMBigMomma::CheckRangeAttack1( float flDot, float flDist ) -{ - if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->time ) - { - edict_t *pEnemy = m_hEnemy; - - if ( pEnemy ) - { - Vector startPos = pev->origin; - startPos.z += 180; - pev->movedir = VecCheckSplatToss( pev, startPos, UTIL_BodyTarget( pEnemy, pev->origin ), RANDOM_FLOAT( 150, 500 ) ); - if ( pev->movedir != g_vecZero ) - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -enum -{ - SCHED_BIG_NODE = LAST_COMMON_SCHEDULE + 1, - SCHED_NODE_FAIL, -}; - -enum -{ - TASK_MOVE_TO_NODE_RANGE = LAST_COMMON_TASK + 1, // Move within node range - TASK_FIND_NODE, // Find my next node - TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script - TASK_PLAY_NODE_SEQUENCE, // Play node script - TASK_PROCESS_NODE, // Fire targets, etc. - TASK_WAIT_NODE, // Wait at the node - TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there - TASK_NODE_YAW, // Get the best facing direction for this node -}; - - -Task_t tlBigNode[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_NODE_FAIL }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_NODE, (float)0 }, // Find my next node - { TASK_PLAY_NODE_PRESEQUENCE,(float)0 }, // Play the pre-approach sequence if any - { TASK_MOVE_TO_NODE_RANGE, (float)0 }, // Move within node range - { TASK_STOP_MOVING, (float)0 }, - { TASK_NODE_YAW, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_WAIT_NODE, (float)0 }, // Wait for node delay - { TASK_PLAY_NODE_SEQUENCE, (float)0 }, // Play the sequence if one exists - { TASK_PROCESS_NODE, (float)0 }, // Fire targets, etc. - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slBigNode[] = -{ - { - tlBigNode, - ARRAYSIZE ( tlBigNode ), - 0, - 0, - "Big Node" - }, -}; - - -Task_t tlNodeFail[] = -{ - { TASK_NODE_DELAY, (float)10 }, // Try to do something else for 10 seconds - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slNodeFail[] = -{ - { - tlNodeFail, - ARRAYSIZE ( tlNodeFail ), - 0, - 0, - "NodeFail" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMBigMomma ) -{ - slBigNode, - slNodeFail, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMBigMomma, CMBaseMonster ); - - - - -Schedule_t *CMBigMomma::GetScheduleOfType( int Type ) -{ - switch( Type ) - { - case SCHED_BIG_NODE: - return slBigNode; - break; - - case SCHED_NODE_FAIL: - return slNodeFail; - break; - } - - return CMBaseMonster::GetScheduleOfType( Type ); -} - - -BOOL CMBigMomma::ShouldGoToNode( void ) -{ - if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) ) - { - if ( m_nodeTime < gpGlobals->time ) - return TRUE; - } - return FALSE; -} - - - -Schedule_t *CMBigMomma::GetSchedule( void ) -{ - if ( ShouldGoToNode() ) - { - return GetScheduleOfType( SCHED_BIG_NODE ); - } - - return CMBaseMonster::GetSchedule(); -} - - -void CMBigMomma::StartTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_FIND_NODE: - { - edict_t *pTarget = m_hTargetEnt; - if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) ) - { - if ( pTarget ) - pev->netname = m_hTargetEnt->v.target; - } - NodeStart( pev->netname ); - TaskComplete(); - ALERT( at_aiconsole, "BM: Found node %s\n", STRING(pev->netname) ); - } - break; - - case TASK_NODE_DELAY: - m_nodeTime = gpGlobals->time + pTask->flData; - TaskComplete(); - ALERT( at_aiconsole, "BM: FAIL! Delay %.2f\n", pTask->flData ); - break; - - case TASK_PROCESS_NODE: - ALERT( at_aiconsole, "BM: Reached node %s\n", STRING(pev->netname) ); - NodeReach(); - TaskComplete(); - break; - - case TASK_PLAY_NODE_PRESEQUENCE: - case TASK_PLAY_NODE_SEQUENCE: - { - int sequence; - if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE ) - sequence = GetNodeSequence(); - else - sequence = GetNodePresequence(); - - ALERT( at_aiconsole, "BM: Playing node sequence %s\n", STRING(sequence) ); - if ( sequence ) - { - sequence = LookupSequence( STRING( sequence ) ); - if ( sequence != -1 ) - { - pev->sequence = sequence; - pev->frame = 0; - ResetSequenceInfo( ); - ALERT( at_aiconsole, "BM: Sequence %s\n", STRING(GetNodeSequence()) ); - return; - } - } - TaskComplete(); - } - break; - - case TASK_NODE_YAW: - pev->ideal_yaw = GetNodeYaw(); - TaskComplete(); - break; - - case TASK_WAIT_NODE: - m_flWait = gpGlobals->time + GetNodeDelay(); - if ( m_hTargetEnt->v.spawnflags & SF_INFOBM_WAIT ) - ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING(pev->netname) ); - else - ALERT( at_aiconsole, "BM: Wait at node %s for %.2f\n", STRING(pev->netname), GetNodeDelay() ); - break; - - - case TASK_MOVE_TO_NODE_RANGE: - { - edict_t *pTarget = m_hTargetEnt; - if ( !pTarget ) - TaskFail(); - else - { - if ( (pTarget->v.origin - pev->origin).Length() < GetNodeRange() ) - TaskComplete(); - else - { - Activity act = ACT_WALK; - if ( pTarget->v.spawnflags & SF_INFOBM_RUN ) - act = ACT_RUN; - - m_vecMoveGoal = pTarget->v.origin; - if ( !MoveToTarget( act, 2 ) ) - { - TaskFail(); - } - } - } - } - ALERT( at_aiconsole, "BM: Moving to node %s\n", STRING(pev->netname) ); - - break; - - case TASK_MELEE_ATTACK1: - // Play an attack sound here - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), 1.0, ATTN_NORM, 0, PITCH_NORM ); - CMBaseMonster::StartTask( pTask ); - break; - - default: - CMBaseMonster::StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CMBigMomma::RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_MOVE_TO_NODE_RANGE: - { - float distance; - - if ( m_hTargetEnt == NULL ) - TaskFail(); - else - { - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - // Set the appropriate activity based on an overlapping range - // overlap the range to prevent oscillation - if ( (distance < GetNodeRange()) || MovementIsComplete() ) - { - ALERT( at_aiconsole, "BM: Reached node!\n" ); - TaskComplete(); - RouteClear(); // Stop moving - } - } - } - - break; - - case TASK_WAIT_NODE: - if ( m_hTargetEnt != NULL && (m_hTargetEnt->v.spawnflags & SF_INFOBM_WAIT) ) - return; - - if ( gpGlobals->time > m_flWaitFinished ) - TaskComplete(); - ALERT( at_aiconsole, "BM: The WAIT is over!\n" ); - break; - - case TASK_PLAY_NODE_PRESEQUENCE: - case TASK_PLAY_NODE_SEQUENCE: - if ( m_fSequenceFinished ) - { - m_Activity = ACT_RESET; - TaskComplete(); - } - break; - - default: - CMBaseMonster::RunTask( pTask ); - break; - } -} - - - -Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ) -{ - TraceResult tr; - Vector vecMidPoint;// halfway point between Spot1 and Spot2 - Vector vecApex;// highest point - Vector vecScale; - Vector vecGrenadeVel; - Vector vecTemp; - float flGravity = g_psv_gravity->value; - - // calculate the midpoint and apex of the 'triangle' - vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), ignore_monsters, ENT(pev), &tr); - vecApex = tr.vecEndPos; - - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - // Don't worry about actually hitting the target, this won't hurt us! - - // How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)? - float height = (vecApex.z - vecSpot1.z) - 15; - // How fast does the grenade need to travel to reach that height given gravity? - float speed = sqrt( 2 * flGravity * height ); - - // How much time does it take to get there? - float time = speed / flGravity; - vecGrenadeVel = (vecSpot2 - vecSpot1); - vecGrenadeVel.z = 0; - float distance = vecGrenadeVel.Length(); - - // Travel half the distance to the target in that time (apex is at the midpoint) - vecGrenadeVel = vecGrenadeVel * ( 0.5 / time ); - // Speed to offset gravity at the desired height - vecGrenadeVel.z = speed; - - return vecGrenadeVel; -} - - - - -// --------------------------------- -// -// Mortar -// -// --------------------------------- -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) -{ - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( position.x); // pos - WRITE_COORD( position.y); - WRITE_COORD( position.z); - WRITE_COORD( direction.x); // dir - WRITE_COORD( direction.y); - WRITE_COORD( direction.z); - WRITE_SHORT( spriteModel ); // model - WRITE_BYTE ( count ); // count - WRITE_BYTE ( 130 ); // speed - WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) - MESSAGE_END(); -} - - -// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage -void CMBMortar:: Spawn( void ) -{ - pev->movetype = MOVETYPE_TOSS; - pev->classname = MAKE_STRING( "bmortar" ); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), "sprites/mommaspit.spr"); - pev->frame = 0; - pev->scale = 0.5; - - UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; - pev->dmgtime = gpGlobals->time + 0.4; -} - -void CMBMortar::Animate( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - if ( gpGlobals->time > pev->dmgtime ) - { - pev->dmgtime = gpGlobals->time + 0.2; - MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 ); - } - if ( pev->frame++ ) - { - if ( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } -} - -CMBMortar *CMBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ) -{ - CMBMortar *pSpit = CreateClassPtr( (CMBMortar *)NULL ); - if (pSpit) - { - pSpit->Spawn(); - - UTIL_SetOrigin( pSpit->pev, vecStart ); - pSpit->pev->velocity = vecVelocity; - pSpit->pev->owner = pOwner; - pSpit->pev->scale = 2.5; - pSpit->SetThink ( &CMBMortar::Animate ); - pSpit->pev->nextthink = gpGlobals->time + 0.1; - } - return pSpit; -} - - -void CMBMortar::Touch( edict_t *pOther ) -{ - TraceResult tr; - int iPitch; - - // splat sound - iPitch = RANDOM_FLOAT( 90, 110 ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); - - switch ( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } - - if ( UTIL_IsBSPModel(pOther) ) - { - - // make a splat on the wall - UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); - UTIL_DecalTrace(&tr, DECAL_MOMMASPLAT); - } - else - { - tr.vecEndPos = pev->origin; - tr.vecPlaneNormal = -1 * pev->velocity.Normalize(); - } - // make some flecks - MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 ); - - entvars_t *pevOwner = NULL; - if ( pev->owner ) - pevOwner = VARS(pev->owner); - - RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID ); - UTIL_Remove( this->edict() ); -} - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +//========================================================= +// monster template +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "decals.h" + +#define SF_INFOBM_RUN 0x0001 +#define SF_INFOBM_WAIT 0x0002 + +// AI Nodes for Big Momma +class CMInfoBM : public CMPointEntity +{ +public: + void Spawn( void ); + void KeyValue( KeyValueData* pkvd ); + + // name in pev->targetname + // next in pev->target + // radius in pev->scale + // health in pev->health + // Reach target in pev->message + // Reach delay in pev->speed + // Reach sequence in pev->netname + + int m_preSequence; +}; + +void CMInfoBM::Spawn( void ) +{ + pev->classname = MAKE_STRING( "info_bigmomma" ); +} + +void CMInfoBM::KeyValue( KeyValueData* pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "radius")) + { + pev->scale = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachdelay")) + { + pev->speed = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachtarget")) + { + pev->message = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "reachsequence")) + { + pev->netname = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "presequence")) + { + m_preSequence = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMPointEntity::KeyValue( pkvd ); +} + +//========================================================= +// Mortar shot entity +//========================================================= +class CMBMortar : public CMBaseEntity +{ +public: + void Spawn( void ); + + static CMBMortar *Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ); + void Touch( edict_t *pOther ); + void EXPORT Animate( void ); + + int m_maxFrame; +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define BIG_AE_STEP1 1 // Footstep left +#define BIG_AE_STEP2 2 // Footstep right +#define BIG_AE_STEP3 3 // Footstep back left +#define BIG_AE_STEP4 4 // Footstep back right +#define BIG_AE_SACK 5 // Sack slosh +#define BIG_AE_DEATHSOUND 6 // Death sound + +#define BIG_AE_MELEE_ATTACKBR 8 // Leg attack +#define BIG_AE_MELEE_ATTACKBL 9 // Leg attack +#define BIG_AE_MELEE_ATTACK1 10 // Leg attack +#define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar +#define BIG_AE_LAY_CRAB 12 // Lay a headcrab +#define BIG_AE_JUMP_FORWARD 13 // Jump up and forward +#define BIG_AE_SCREAM 14 // alert sound +#define BIG_AE_PAIN_SOUND 15 // pain sound +#define BIG_AE_ATTACK_SOUND 16 // attack sound +#define BIG_AE_BIRTH_SOUND 17 // birth sound +#define BIG_AE_EARLY_TARGET 50 // Fire target early + + + +// User defined conditions +#define bits_COND_NODE_SEQUENCE ( bits_COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play + +// Attack distance constants +#define BIG_ATTACKDIST 170 +#define BIG_MORTARDIST 800 +#define BIG_MAXCHILDREN 20 // Max # of live headcrab children + + +#define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1) +#define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2) +#define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3) +#define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4) + +int gSpitSprite, gSpitDebrisSprite; +Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); +void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); + + +// UNDONE: +// +//#define BIG_CHILDCLASS "monster_babycrab" + +const char *CMBigMomma::pChildDieSounds[] = +{ + "gonarch/gon_childdie1.wav", + "gonarch/gon_childdie2.wav", + "gonarch/gon_childdie3.wav", +}; + +const char *CMBigMomma::pSackSounds[] = +{ + "gonarch/gon_sack1.wav", + "gonarch/gon_sack2.wav", + "gonarch/gon_sack3.wav", +}; + +const char *CMBigMomma::pDeathSounds[] = +{ + "gonarch/gon_die1.wav", +}; + +const char *CMBigMomma::pAttackSounds[] = +{ + "gonarch/gon_attack1.wav", + "gonarch/gon_attack2.wav", + "gonarch/gon_attack3.wav", +}; +const char *CMBigMomma::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CMBigMomma::pBirthSounds[] = +{ + "gonarch/gon_birth1.wav", + "gonarch/gon_birth2.wav", + "gonarch/gon_birth3.wav", +}; + +const char *CMBigMomma::pAlertSounds[] = +{ + "gonarch/gon_alert1.wav", + "gonarch/gon_alert2.wav", + "gonarch/gon_alert3.wav", +}; + +const char *CMBigMomma::pPainSounds[] = +{ + "gonarch/gon_pain2.wav", + "gonarch/gon_pain4.wav", + "gonarch/gon_pain5.wav", +}; + +const char *CMBigMomma::pFootSounds[] = +{ + "gonarch/gon_step1.wav", + "gonarch/gon_step2.wav", + "gonarch/gon_step3.wav", +}; + + +int CMBigMomma :: GetNodeSequence( void ) +{ + edict_t *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->v.netname; // netname holds node sequence + } + return 0; +} + +int CMBigMomma :: GetNodePresequence( void ) +{ + if (!m_hTargetEnt || (m_hTargetEnt->v.euser4 == NULL)) + return 0; + + CMInfoBM *pTarget = (CMInfoBM *)CMBaseEntity::Instance(m_hTargetEnt); + if ( pTarget ) + { + return pTarget->m_preSequence; + } + return 0; +} + +float CMBigMomma :: GetNodeDelay( void ) +{ + edict_t *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->v.speed; // Speed holds node delay + } + return 0; +} + +float CMBigMomma :: GetNodeRange( void ) +{ + edict_t *pTarget = m_hTargetEnt; + if ( pTarget ) + { + return pTarget->v.scale; // Scale holds node delay + } + return 1e6; +} + +float CMBigMomma :: GetNodeYaw( void ) +{ + edict_t *pTarget = m_hTargetEnt; + if ( pTarget ) + { + if ( pTarget->v.angles.y != 0 ) + return pTarget->v.angles.y; + } + return pev->angles.y; +} + +BOOL CMBigMomma :: CanLayCrab( void ) +{ + if ( m_crabTime < gpGlobals->time && m_crabCount < BIG_MAXCHILDREN ) + { + // Don't spawn crabs inside each other + Vector mins = pev->origin - Vector( 32, 32, 0 ); + Vector maxs = pev->origin + Vector( 32, 32, 0 ); + + edict_t *pList[2]; + int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_MONSTER ); + for ( int i = 0; i < count; i++ ) + { + if ( pList[i] != edict() ) // Don't hurt yourself! + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +void CMBigMomma :: KeyValue( KeyValueData *pkvd ) +{ +#if 0 + if (FStrEq(pkvd->szKeyName, "volume")) + { + m_volume = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else +#endif + CMBaseMonster::KeyValue( pkvd ); +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMBigMomma :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMBigMomma :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 100; + break; + default: + ys = 90; + } + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMBigMomma :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case BIG_AE_MELEE_ATTACKBR: + case BIG_AE_MELEE_ATTACKBL: + case BIG_AE_MELEE_ATTACK1: + { + Vector forward, right; + + UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL ); + + Vector center = pev->origin + forward * 128; + Vector mins = center - Vector( 64, 64, 0 ); + Vector maxs = center + Vector( 64, 64, 64 ); + + edict_t *pList[8]; + int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_MONSTER|FL_CLIENT ); + edict_t *pHurt = NULL; + + for ( int i = 0; i < count && !pHurt; i++ ) + { + if ( pList[i] != this->edict() ) + { + if ( pList[i]->v.owner != edict() ) + pHurt = pList[i]; + } + } + + if ( pHurt ) + { + if (UTIL_IsPlayer(pHurt)) + { + UTIL_TakeDamage( pHurt, pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); + pHurt->v.punchangle.x = 15; + switch( pEvent->event ) + { + case BIG_AE_MELEE_ATTACKBR: + pHurt->v.velocity = pHurt->v.velocity + (forward * 150) + Vector(0,0,250) - (right * 200); + break; + + case BIG_AE_MELEE_ATTACKBL: + pHurt->v.velocity = pHurt->v.velocity + (forward * 150) + Vector(0,0,250) + (right * 200); + break; + + case BIG_AE_MELEE_ATTACK1: + pHurt->v.velocity = pHurt->v.velocity + (forward * 220) + Vector(0,0,200); + break; + } + + pHurt->v.flags &= ~FL_ONGROUND; + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else if (pHurt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + + pMonster->TakeDamage( pev, pev, gSkillData.bigmommaDmgSlash, DMG_CRUSH | DMG_SLASH ); + pMonster->pev->punchangle.x = 15; + switch( pEvent->event ) + { + case BIG_AE_MELEE_ATTACKBR: + pMonster->pev->velocity = pMonster->pev->velocity + (forward * 150) + Vector(0,0,250) - (right * 200); + break; + + case BIG_AE_MELEE_ATTACKBL: + pMonster->pev->velocity = pMonster->pev->velocity + (forward * 150) + Vector(0,0,250) + (right * 200); + break; + + case BIG_AE_MELEE_ATTACK1: + pMonster->pev->velocity = pMonster->pev->velocity + (forward * 220) + Vector(0,0,200); + break; + } + + pMonster->pev->flags &= ~FL_ONGROUND; + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + } + } + break; + + case BIG_AE_SCREAM: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); + break; + + case BIG_AE_PAIN_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); + break; + + case BIG_AE_ATTACK_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackSounds ); + break; + + case BIG_AE_BIRTH_SOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pBirthSounds ); + break; + + case BIG_AE_SACK: + if ( RANDOM_LONG(0,100) < 30 ) + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pSackSounds ); + break; + + case BIG_AE_DEATHSOUND: + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); + break; + + case BIG_AE_STEP1: // Footstep left + case BIG_AE_STEP3: // Footstep back left + EMIT_SOUND_ARRAY_DYN( CHAN_ITEM, pFootSounds ); + break; + + case BIG_AE_STEP4: // Footstep back right + case BIG_AE_STEP2: // Footstep right + EMIT_SOUND_ARRAY_DYN( CHAN_BODY, pFootSounds ); + break; + + case BIG_AE_MORTAR_ATTACK1: + LaunchMortar(); + break; + + case BIG_AE_LAY_CRAB: + LayHeadcrab(); + break; + + case BIG_AE_JUMP_FORWARD: + ClearBits( pev->flags, FL_ONGROUND ); + + UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground + UTIL_MakeVectors ( pev->angles ); + + pev->velocity = (gpGlobals->v_forward * 200) + gpGlobals->v_up * 500; + break; + + case BIG_AE_EARLY_TARGET: + { + edict_t *pTarget = m_hTargetEnt; + if ( pTarget && pTarget->v.message ) + FireTargets( STRING(pTarget->v.message), this->edict(), this->edict(), USE_TOGGLE, 0 ); + Remember( bits_MEMORY_FIRED_NODE ); + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +void CMBigMomma :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ +/* + if ( ptr->iHitgroup != 1 ) + { + // didn't hit the sack? + + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); + pev->dmgtime = gpGlobals->time; + } + + flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + else */ + if ( gpGlobals->time > m_painSoundTime ) + { + m_painSoundTime = gpGlobals->time + RANDOM_LONG(1, 3); + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); + } + + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +int CMBigMomma :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Don't take any acid damage -- BigMomma's mortar is acid + if ( bitsDamageType & DMG_ACID ) + flDamage = 0; + + if ( !HasMemory(bits_MEMORY_PATH_FINISHED) ) + { + if ( pev->health <= flDamage ) + { + pev->health = flDamage + 1; + Remember( bits_MEMORY_ADVANCE_NODE | bits_MEMORY_COMPLETED_NODE ); + ALERT( at_aiconsole, "BM: Finished node health!!!\n" ); + } + } + + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CMBigMomma :: LayHeadcrab( void ) +{ +// CMBaseEntity *pChild = CMBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, edict() ); + + CMBabyCrab *pChild = CreateClassPtr((CMBabyCrab *)NULL); + + if (pChild != NULL) + { + pChild->pev->origin = pev->origin; + pChild->pev->angles = pev->angles; + pChild->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pChild->pev->absmin = pChild->pev->origin - Vector(1,1,1); + pChild->pev->absmax = pChild->pev->origin + Vector(1,1,1); + + pChild->Spawn(); + + pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND; + + // Is this the second crab in a pair? + if ( HasMemory( bits_MEMORY_CHILDPAIR ) ) + { + m_crabTime = gpGlobals->time + RANDOM_FLOAT( 5, 10 ); + Forget( bits_MEMORY_CHILDPAIR ); + } + else + { + m_crabTime = gpGlobals->time + RANDOM_FLOAT( 0.5, 2.5 ); + Remember( bits_MEMORY_CHILDPAIR ); + } + + TraceResult tr; + UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,100), ignore_monsters, edict(), &tr); + UTIL_DecalTrace( &tr, DECAL_MOMMABIRTH ); + + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBirthSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + m_crabCount++; + } +} + + + +void CMBigMomma::DeathNotice( entvars_t *pevChild ) +{ + if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then + m_crabCount--; + if ( IsAlive() ) + { + // Make the "my baby's dead" noise! + EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pChildDieSounds ); + } +} + + +void CMBigMomma::LaunchMortar( void ) +{ + m_mortarTime = gpGlobals->time + RANDOM_FLOAT( 2, 15 ); + + Vector startPos = pev->origin; + startPos.z += 180; + + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pSackSounds), 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + CMBMortar *pBomb = CMBMortar::Shoot( edict(), startPos, pev->movedir ); + if (pBomb) + { + pBomb->pev->gravity = 1.0; + MortarSpray( startPos, Vector(0,0,1), gSpitSprite, 24 ); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMBigMomma :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/big_mom.mdl"); +// UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); + UTIL_SetSize( pev, Vector( -64, -64, 0 ), Vector( 64, 64, 128 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = 150 * gSkillData.bigmommaHealthFactor; + pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_bigmomma" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Big Momma" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMBigMomma :: Precache() +{ + PRECACHE_MODEL("models/big_mom.mdl"); + + PRECACHE_SOUND_ARRAY( pChildDieSounds ); + PRECACHE_SOUND_ARRAY( pSackSounds ); + PRECACHE_SOUND_ARRAY( pDeathSounds ); + PRECACHE_SOUND_ARRAY( pAttackSounds ); + PRECACHE_SOUND_ARRAY( pAttackHitSounds ); + PRECACHE_SOUND_ARRAY( pBirthSounds ); + PRECACHE_SOUND_ARRAY( pAlertSounds ); + PRECACHE_SOUND_ARRAY( pPainSounds ); + PRECACHE_SOUND_ARRAY( pFootSounds ); + +// UTIL_PrecacheOther( BIG_CHILDCLASS ); + CMBabyCrab babycrab; + babycrab.Precache(); + + // TEMP: Squid + PRECACHE_MODEL("sprites/mommaspit.spr");// spit projectile. + gSpitSprite = PRECACHE_MODEL("sprites/mommaspout.spr");// client side spittle. + gSpitDebrisSprite = PRECACHE_MODEL("sprites/mommablob.spr" ); + + PRECACHE_SOUND( "bullchicken/bc_acid1.wav" ); + PRECACHE_SOUND( "bullchicken/bc_spithit1.wav" ); + PRECACHE_SOUND( "bullchicken/bc_spithit2.wav" ); +} + + +void CMBigMomma::Activate( void ) +{ + if ( m_hTargetEnt == NULL ) + Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up +} + + +void CMBigMomma::NodeStart( int iszNextNode ) +{ + pev->netname = iszNextNode; + + edict_t *pTarget = NULL; + + if ( pev->netname ) + { + edict_t *pentTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pev->netname) ); + + if ( !FNullEnt(pentTarget) ) + pTarget = pentTarget; + } + + + if ( !pTarget ) + { + ALERT( at_aiconsole, "BM: Finished the path!!\n" ); + Remember( bits_MEMORY_PATH_FINISHED ); + return; + } + Remember( bits_MEMORY_ON_PATH ); + m_hTargetEnt = pTarget; +} + + +void CMBigMomma::NodeReach( void ) +{ + edict_t *pTarget = m_hTargetEnt; + + Forget( bits_MEMORY_ADVANCE_NODE ); + + if ( !pTarget ) + return; + + if ( pTarget->v.health ) + pev->max_health = pev->health = pTarget->v.health * gSkillData.bigmommaHealthFactor; + + if ( !HasMemory( bits_MEMORY_FIRED_NODE ) ) + { + if ( pTarget->v.message ) + FireTargets( STRING(pTarget->v.message), this->edict(), this->edict(), USE_TOGGLE, 0 ); + } + Forget( bits_MEMORY_FIRED_NODE ); + + pev->netname = pTarget->v.target; + if ( pTarget->v.health == 0 ) + Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node +} + + + // Slash +BOOL CMBigMomma::CheckMeleeAttack1( float flDot, float flDist ) +{ + if (flDot >= 0.7) + { + if ( flDist <= BIG_ATTACKDIST ) + return TRUE; + } + return FALSE; +} + + +// Lay a crab +BOOL CMBigMomma::CheckMeleeAttack2( float flDot, float flDist ) +{ + return CanLayCrab(); +} + + +// Mortar launch +BOOL CMBigMomma::CheckRangeAttack1( float flDot, float flDist ) +{ + if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->time ) + { + edict_t *pEnemy = m_hEnemy; + + if ( pEnemy ) + { + Vector startPos = pev->origin; + startPos.z += 180; + pev->movedir = VecCheckSplatToss( pev, startPos, UTIL_BodyTarget( pEnemy, pev->origin ), RANDOM_FLOAT( 150, 500 ) ); + if ( pev->movedir != g_vecZero ) + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +enum +{ + SCHED_BIG_NODE = LAST_COMMON_SCHEDULE + 1, + SCHED_NODE_FAIL, +}; + +enum +{ + TASK_MOVE_TO_NODE_RANGE = LAST_COMMON_TASK + 1, // Move within node range + TASK_FIND_NODE, // Find my next node + TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script + TASK_PLAY_NODE_SEQUENCE, // Play node script + TASK_PROCESS_NODE, // Fire targets, etc. + TASK_WAIT_NODE, // Wait at the node + TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there + TASK_NODE_YAW, // Get the best facing direction for this node +}; + + +Task_t tlBigNode[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_NODE_FAIL }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_NODE, (float)0 }, // Find my next node + { TASK_PLAY_NODE_PRESEQUENCE,(float)0 }, // Play the pre-approach sequence if any + { TASK_MOVE_TO_NODE_RANGE, (float)0 }, // Move within node range + { TASK_STOP_MOVING, (float)0 }, + { TASK_NODE_YAW, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_WAIT_NODE, (float)0 }, // Wait for node delay + { TASK_PLAY_NODE_SEQUENCE, (float)0 }, // Play the sequence if one exists + { TASK_PROCESS_NODE, (float)0 }, // Fire targets, etc. + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slBigNode[] = +{ + { + tlBigNode, + ARRAYSIZE ( tlBigNode ), + 0, + 0, + "Big Node" + }, +}; + + +Task_t tlNodeFail[] = +{ + { TASK_NODE_DELAY, (float)10 }, // Try to do something else for 10 seconds + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slNodeFail[] = +{ + { + tlNodeFail, + ARRAYSIZE ( tlNodeFail ), + 0, + 0, + "NodeFail" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMBigMomma ) +{ + slBigNode, + slNodeFail, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMBigMomma, CMBaseMonster ); + + + + +Schedule_t *CMBigMomma::GetScheduleOfType( int Type ) +{ + switch( Type ) + { + case SCHED_BIG_NODE: + return slBigNode; + break; + + case SCHED_NODE_FAIL: + return slNodeFail; + break; + } + + return CMBaseMonster::GetScheduleOfType( Type ); +} + + +BOOL CMBigMomma::ShouldGoToNode( void ) +{ + if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) ) + { + if ( m_nodeTime < gpGlobals->time ) + return TRUE; + } + return FALSE; +} + + + +Schedule_t *CMBigMomma::GetSchedule( void ) +{ + if ( ShouldGoToNode() ) + { + return GetScheduleOfType( SCHED_BIG_NODE ); + } + + return CMBaseMonster::GetSchedule(); +} + + +void CMBigMomma::StartTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_FIND_NODE: + { + edict_t *pTarget = m_hTargetEnt; + if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) ) + { + if ( pTarget ) + pev->netname = m_hTargetEnt->v.target; + } + NodeStart( pev->netname ); + TaskComplete(); + ALERT( at_aiconsole, "BM: Found node %s\n", STRING(pev->netname) ); + } + break; + + case TASK_NODE_DELAY: + m_nodeTime = gpGlobals->time + pTask->flData; + TaskComplete(); + ALERT( at_aiconsole, "BM: FAIL! Delay %.2f\n", pTask->flData ); + break; + + case TASK_PROCESS_NODE: + ALERT( at_aiconsole, "BM: Reached node %s\n", STRING(pev->netname) ); + NodeReach(); + TaskComplete(); + break; + + case TASK_PLAY_NODE_PRESEQUENCE: + case TASK_PLAY_NODE_SEQUENCE: + { + int sequence; + if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE ) + sequence = GetNodeSequence(); + else + sequence = GetNodePresequence(); + + ALERT( at_aiconsole, "BM: Playing node sequence %s\n", STRING(sequence) ); + if ( sequence ) + { + sequence = LookupSequence( STRING( sequence ) ); + if ( sequence != -1 ) + { + pev->sequence = sequence; + pev->frame = 0; + ResetSequenceInfo( ); + ALERT( at_aiconsole, "BM: Sequence %s\n", STRING(GetNodeSequence()) ); + return; + } + } + TaskComplete(); + } + break; + + case TASK_NODE_YAW: + pev->ideal_yaw = GetNodeYaw(); + TaskComplete(); + break; + + case TASK_WAIT_NODE: + m_flWait = gpGlobals->time + GetNodeDelay(); + if ( m_hTargetEnt->v.spawnflags & SF_INFOBM_WAIT ) + ALERT( at_aiconsole, "BM: Wait at node %s forever\n", STRING(pev->netname) ); + else + ALERT( at_aiconsole, "BM: Wait at node %s for %.2f\n", STRING(pev->netname), GetNodeDelay() ); + break; + + + case TASK_MOVE_TO_NODE_RANGE: + { + edict_t *pTarget = m_hTargetEnt; + if ( !pTarget ) + TaskFail(); + else + { + if ( (pTarget->v.origin - pev->origin).Length() < GetNodeRange() ) + TaskComplete(); + else + { + Activity act = ACT_WALK; + if ( pTarget->v.spawnflags & SF_INFOBM_RUN ) + act = ACT_RUN; + + m_vecMoveGoal = pTarget->v.origin; + if ( !MoveToTarget( act, 2 ) ) + { + TaskFail(); + } + } + } + } + ALERT( at_aiconsole, "BM: Moving to node %s\n", STRING(pev->netname) ); + + break; + + case TASK_MELEE_ATTACK1: + // Play an attack sound here + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackSounds), 1.0, ATTN_NORM, 0, PITCH_NORM ); + CMBaseMonster::StartTask( pTask ); + break; + + default: + CMBaseMonster::StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CMBigMomma::RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_MOVE_TO_NODE_RANGE: + { + float distance; + + if ( m_hTargetEnt == NULL ) + TaskFail(); + else + { + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + // Set the appropriate activity based on an overlapping range + // overlap the range to prevent oscillation + if ( (distance < GetNodeRange()) || MovementIsComplete() ) + { + ALERT( at_aiconsole, "BM: Reached node!\n" ); + TaskComplete(); + RouteClear(); // Stop moving + } + } + } + + break; + + case TASK_WAIT_NODE: + if ( m_hTargetEnt != NULL && (m_hTargetEnt->v.spawnflags & SF_INFOBM_WAIT) ) + return; + + if ( gpGlobals->time > m_flWaitFinished ) + TaskComplete(); + ALERT( at_aiconsole, "BM: The WAIT is over!\n" ); + break; + + case TASK_PLAY_NODE_PRESEQUENCE: + case TASK_PLAY_NODE_SEQUENCE: + if ( m_fSequenceFinished ) + { + m_Activity = ACT_RESET; + TaskComplete(); + } + break; + + default: + CMBaseMonster::RunTask( pTask ); + break; + } +} + + + +Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ) +{ + TraceResult tr; + Vector vecMidPoint;// halfway point between Spot1 and Spot2 + Vector vecApex;// highest point + Vector vecScale; + Vector vecGrenadeVel; + Vector vecTemp; + float flGravity = g_psv_gravity->value; + + // calculate the midpoint and apex of the 'triangle' + vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), ignore_monsters, ENT(pev), &tr); + vecApex = tr.vecEndPos; + + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + // Don't worry about actually hitting the target, this won't hurt us! + + // How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)? + float height = (vecApex.z - vecSpot1.z) - 15; + // How fast does the grenade need to travel to reach that height given gravity? + float speed = sqrt( 2 * flGravity * height ); + + // How much time does it take to get there? + float time = speed / flGravity; + vecGrenadeVel = (vecSpot2 - vecSpot1); + vecGrenadeVel.z = 0; + float distance = vecGrenadeVel.Length(); + + // Travel half the distance to the target in that time (apex is at the midpoint) + vecGrenadeVel = vecGrenadeVel * ( 0.5 / time ); + // Speed to offset gravity at the desired height + vecGrenadeVel.z = speed; + + return vecGrenadeVel; +} + + + + +// --------------------------------- +// +// Mortar +// +// --------------------------------- +void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); + WRITE_BYTE( TE_SPRITE_SPRAY ); + WRITE_COORD( position.x); // pos + WRITE_COORD( position.y); + WRITE_COORD( position.z); + WRITE_COORD( direction.x); // dir + WRITE_COORD( direction.y); + WRITE_COORD( direction.z); + WRITE_SHORT( spriteModel ); // model + WRITE_BYTE ( count ); // count + WRITE_BYTE ( 130 ); // speed + WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) + MESSAGE_END(); +} + + +// UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage +void CMBMortar:: Spawn( void ) +{ + pev->movetype = MOVETYPE_TOSS; + pev->classname = MAKE_STRING( "bmortar" ); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), "sprites/mommaspit.spr"); + pev->frame = 0; + pev->scale = 0.5; + + UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; + pev->dmgtime = gpGlobals->time + 0.4; +} + +void CMBMortar::Animate( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + if ( gpGlobals->time > pev->dmgtime ) + { + pev->dmgtime = gpGlobals->time + 0.2; + MortarSpray( pev->origin, -pev->velocity.Normalize(), gSpitSprite, 3 ); + } + if ( pev->frame++ ) + { + if ( pev->frame > m_maxFrame ) + { + pev->frame = 0; + } + } +} + +CMBMortar *CMBMortar::Shoot( edict_t *pOwner, Vector vecStart, Vector vecVelocity ) +{ + CMBMortar *pSpit = CreateClassPtr( (CMBMortar *)NULL ); + if (pSpit) + { + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->owner = pOwner; + pSpit->pev->scale = 2.5; + pSpit->SetThink ( &CMBMortar::Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; + } + return pSpit; +} + + +void CMBMortar::Touch( edict_t *pOther ) +{ + TraceResult tr; + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT( 90, 110 ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); + + switch ( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + if ( UTIL_IsBSPModel(pOther) ) + { + + // make a splat on the wall + UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); + UTIL_DecalTrace(&tr, DECAL_MOMMASPLAT); + } + else + { + tr.vecEndPos = pev->origin; + tr.vecPlaneNormal = -1 * pev->velocity.Normalize(); + } + // make some flecks + MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, gSpitSprite, 24 ); + + entvars_t *pevOwner = NULL; + if ( pev->owner ) + pevOwner = VARS(pev->owner); + + RadiusDamage( pev->origin, pev, pevOwner, gSkillData.bigmommaDmgBlast, gSkillData.bigmommaRadiusBlast, CLASS_NONE, DMG_ACID ); + UTIL_Remove( this->edict() ); +} + +#endif diff --git a/src/dlls/bullsquid.cpp b/src/dlls/bullsquid.cpp index 7af2538..783a58e 100644 --- a/src/dlls/bullsquid.cpp +++ b/src/dlls/bullsquid.cpp @@ -1,1186 +1,1186 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// bullsquid - big, spotty tentacle-mouthed meanie. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "nodes.h" -#include "effects.h" -#include "decals.h" - -#define SQUID_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve - -int iSquidSpitSprite; - - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_SQUID_HURTHOP = LAST_COMMON_SCHEDULE + 1, - SCHED_SQUID_SMELLFOOD, - SCHED_SQUID_SEECRAB, - SCHED_SQUID_EAT, - SCHED_SQUID_SNIFF_AND_EAT, - SCHED_SQUID_WALLOW, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_SQUID_HOPTURN = LAST_COMMON_TASK + 1, -}; - -//========================================================= -// Bullsquid's spit projectile -//========================================================= -class CSquidSpit : public CMBaseEntity -{ -public: - void Spawn( void ); - - static void Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - void SpitTouch( edict_t *pOther ); - void EXPORT Animate( void ); - - int m_maxFrame; -}; - -void CSquidSpit:: Spawn( void ) -{ - pev->movetype = MOVETYPE_FLY; - pev->classname = MAKE_STRING( "squidspit" ); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), "sprites/bigspit.spr"); - pev->frame = 0; - pev->scale = 0.5; - - UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; -} - -void CSquidSpit::Animate( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - if ( pev->frame++ ) - { - if ( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } -} - -void CSquidSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) -{ - CSquidSpit *pSpit = CreateClassPtr( (CSquidSpit *)NULL ); - - if (pSpit == NULL) - return; - - pSpit->Spawn(); - - UTIL_SetOrigin( pSpit->pev, vecStart ); - pSpit->pev->velocity = vecVelocity; - pSpit->pev->owner = ENT(pevOwner); - - pSpit->SetThink ( &CSquidSpit::Animate ); - pSpit->pev->nextthink = gpGlobals->time + 0.1; - pSpit->SetTouch ( &CSquidSpit::SpitTouch ); -} - -void CSquidSpit :: SpitTouch ( edict_t *pOther ) -{ - TraceResult tr; - int iPitch; - - // splat sound - iPitch = RANDOM_FLOAT( 90, 110 ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); - - switch ( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } - - if ( !pOther->v.takedamage ) - { - // make a splat on the wall - UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); - UTIL_DecalTrace(&tr, DECAL_SPIT1 + RANDOM_LONG(0,1)); - - // make some flecks - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, tr.vecEndPos ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( tr.vecEndPos.x); // pos - WRITE_COORD( tr.vecEndPos.y); - WRITE_COORD( tr.vecEndPos.z); - WRITE_COORD( tr.vecPlaneNormal.x); // dir - WRITE_COORD( tr.vecPlaneNormal.y); - WRITE_COORD( tr.vecPlaneNormal.z); - WRITE_SHORT( iSquidSpitSprite ); // model - WRITE_BYTE ( 5 ); // count - WRITE_BYTE ( 30 ); // speed - WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) - MESSAGE_END(); - } - else - { - if (UTIL_IsPlayer(pOther)) - UTIL_TakeDamage( pOther, pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); - } - } - - SetThink ( &CSquidSpit::SUB_Remove ); - pev->nextthink = gpGlobals->time; -} - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define BSQUID_AE_SPIT ( 1 ) -#define BSQUID_AE_BITE ( 2 ) -#define BSQUID_AE_BLINK ( 3 ) -#define BSQUID_AE_TAILWHIP ( 4 ) -#define BSQUID_AE_HOP ( 5 ) -#define BSQUID_AE_THROW ( 6 ) - - -//========================================================= -// IgnoreConditions -//========================================================= -int CMBullsquid::IgnoreConditions ( void ) -{ - int iIgnore = CMBaseMonster::IgnoreConditions(); - - if ( gpGlobals->time - m_flLastHurtTime <= 20 ) - { - // haven't been hurt in 20 seconds, so let the squid care about stink. - iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; - } - - if ( m_hEnemy != NULL ) - { - if ( strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0 ) - { - // (Unless after a tasty headcrab) - iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; - } - } - - - return iIgnore; -} - -//========================================================= -// IRelationship - overridden for bullsquid so that it can -// be made to ignore its love of headcrabs for a while. -//========================================================= -int CMBullsquid::IRelationship ( CMBaseEntity *pTarget ) -{ - if ( gpGlobals->time - m_flLastHurtTime < 5 && (strcmp(STRING(pTarget->pev->model), "models/headcrab.mdl") == 0) ) - { - // if squid has been hurt in the last 5 seconds, and is getting relationship for a headcrab, - // tell squid to disregard crab. - return R_NO; - } - - return CMBaseMonster :: IRelationship ( pTarget ); -} - -//========================================================= -// TakeDamage - overridden for bullsquid so we can keep track -// of how much time has passed since it was last injured -//========================================================= -int CMBullsquid :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - float flDist; - Vector vecApex; - - // if the squid is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy, - // it will swerve. (whew). - if ( m_hEnemy != NULL && IsMoving() && pevAttacker == VARS((edict_t *)m_hEnemy) && gpGlobals->time - m_flLastHurtTime > 3 ) - { - flDist = ( pev->origin - m_hEnemy->v.origin ).Length2D(); - - if ( flDist > SQUID_SPRINT_DIST ) - { - flDist = ( pev->origin - m_Route[ m_iRouteIndex ].vecLocation ).Length2D();// reusing flDist. - - if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist * 0.5, m_hEnemy, &vecApex ) ) - { - InsertWaypoint( vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY ); - } - } - } - - if ( !strcmp(STRING(pev->model), "models/headcrab.mdl") == 0 ) - { - // don't forget about headcrabs if it was a headcrab that hurt the squid. - m_flLastHurtTime = gpGlobals->time; - } - - return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMBullsquid :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( IsMoving() && flDist >= 512 ) - { - // squid will far too far behind if he stops running to spit at this distance from the enemy. - return FALSE; - } - - if ( flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextSpitTime ) - { - if ( m_hEnemy != NULL ) - { - if ( fabs( pev->origin.z - m_hEnemy->v.origin.z ) > 256 ) - { - // don't try to spit at someone up really high or down really low. - return FALSE; - } - } - - if ( IsMoving() ) - { - // don't spit again for a long time, resume chasing enemy. - m_flNextSpitTime = gpGlobals->time + 5; - } - else - { - // not moving, so spit again pretty soon. - m_flNextSpitTime = gpGlobals->time + 0.5; - } - - return TRUE; - } - - return FALSE; -} - -//========================================================= -// CheckMeleeAttack1 - bullsquid is a big guy, so has a longer -// melee range than most monsters. This is the tailwhip attack -//========================================================= -BOOL CMBullsquid :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if ( m_hEnemy->v.health <= gSkillData.bullsquidDmgWhip && flDist <= 85 && flDot >= 0.7 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckMeleeAttack2 - bullsquid is a big guy, so has a longer -// melee range than most monsters. This is the bite attack. -// this attack will not be performed if the tailwhip attack -// is valid. -//========================================================= -BOOL CMBullsquid :: CheckMeleeAttack2 ( float flDot, float flDist ) -{ - if ( flDist <= 85 && flDot >= 0.7 && !HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) // The player & bullsquid can be as much as their bboxes - { // apart (48 * sqrt(3)) and he can still attack (85 is a little more than 48*sqrt(3)) - return TRUE; - } - return FALSE; -} - -//========================================================= -// FValidateHintType -//========================================================= -BOOL CMBullsquid :: FValidateHintType ( short sHint ) -{ - int i; - - static short sSquidHints[] = - { - HINT_WORLD_HUMAN_BLOOD, - }; - - for ( i = 0 ; i < ARRAYSIZE ( sSquidHints ) ; i++ ) - { - if ( sSquidHints[ i ] == sHint ) - { - return TRUE; - } - } - - ALERT ( at_aiconsole, "Couldn't validate hint type" ); - return FALSE; -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMBullsquid :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// IdleSound -//========================================================= -#define SQUID_ATTN_IDLE (float)1.5 -void CMBullsquid :: IdleSound ( void ) -{ - switch ( RANDOM_LONG(0,4) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle1.wav", 1, SQUID_ATTN_IDLE ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle2.wav", 1, SQUID_ATTN_IDLE ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle3.wav", 1, SQUID_ATTN_IDLE ); - break; - case 3: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle4.wav", 1, SQUID_ATTN_IDLE ); - break; - case 4: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle5.wav", 1, SQUID_ATTN_IDLE ); - break; - } -} - -//========================================================= -// PainSound -//========================================================= -void CMBullsquid :: PainSound ( void ) -{ - int iPitch = RANDOM_LONG( 85, 120 ); - - switch ( RANDOM_LONG(0,3) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 2: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain3.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 3: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain4.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } -} - -//========================================================= -// AlertSound -//========================================================= -void CMBullsquid :: AlertSound ( void ) -{ - int iPitch = RANDOM_LONG( 140, 160 ); - - switch ( RANDOM_LONG ( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMBullsquid :: SetYawSpeed ( void ) -{ - int ys; - - ys = 0; - - switch ( m_Activity ) - { - case ACT_WALK: ys = 90; break; - case ACT_RUN: ys = 90; break; - case ACT_IDLE: ys = 90; break; - case ACT_RANGE_ATTACK1: ys = 90; break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMBullsquid :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case BSQUID_AE_SPIT: - { - if (m_hEnemy) - { - Vector vecSpitOffset; - Vector vecSpitDir; - - UTIL_MakeVectors ( pev->angles ); - - // !!!HACKHACK - the spot at which the spit originates (in front of the mouth) was measured in 3ds and hardcoded here. - // we should be able to read the position of bones at runtime for this info. - vecSpitOffset = ( gpGlobals->v_right * 8 + gpGlobals->v_forward * 37 + gpGlobals->v_up * 23 ); - vecSpitOffset = ( pev->origin + vecSpitOffset ); - vecSpitDir = ( ( m_hEnemy->v.origin + m_hEnemy->v.view_ofs ) - vecSpitOffset ).Normalize(); - - vecSpitDir.x += RANDOM_FLOAT( -0.05, 0.05 ); - vecSpitDir.y += RANDOM_FLOAT( -0.05, 0.05 ); - vecSpitDir.z += RANDOM_FLOAT( -0.05, 0 ); - - // do stuff for this event. - AttackSound(); - - // spew the spittle temporary ents. - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOffset ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( vecSpitOffset.x); // pos - WRITE_COORD( vecSpitOffset.y); - WRITE_COORD( vecSpitOffset.z); - WRITE_COORD( vecSpitDir.x); // dir - WRITE_COORD( vecSpitDir.y); - WRITE_COORD( vecSpitDir.z); - WRITE_SHORT( iSquidSpitSprite ); // model - WRITE_BYTE ( 15 ); // count - WRITE_BYTE ( 210 ); // speed - WRITE_BYTE ( 25 ); // noise ( client will divide by 100 ) - MESSAGE_END(); - - CSquidSpit::Shoot( pev, vecSpitOffset, vecSpitDir * 900 ); - } - } - break; - - case BSQUID_AE_BITE: - { - // SOUND HERE! - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.bullsquidDmgBite, DMG_SLASH ); - - if ( pHurt ) - { - //pHurt->pev->punchangle.z = -15; - //pHurt->pev->punchangle.x = -45; - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 100; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; - } - } - break; - - case BSQUID_AE_TAILWHIP: - { - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.bullsquidDmgWhip, DMG_CLUB | DMG_ALWAYSGIB ); - if ( pHurt ) - { - pHurt->v.punchangle.z = -20; - pHurt->v.punchangle.x = 20; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 200; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; - } - } - break; - - case BSQUID_AE_BLINK: - { - // close eye. - pev->skin = 1; - } - break; - - case BSQUID_AE_HOP: - { - float flGravity = g_psv_gravity->value; - - // throw the squid up into the air on this frame. - if ( FBitSet ( pev->flags, FL_ONGROUND ) ) - { - pev->flags -= FL_ONGROUND; - } - - // jump into air for 0.8 (24/30) seconds -// pev->velocity.z += (0.875 * flGravity) * 0.5; - pev->velocity.z += (0.625 * flGravity) * 0.5; - } - break; - - case BSQUID_AE_THROW: - { - int iPitch; - - // squid throws its prey IF the prey is a client. - edict_t *pHurt = CheckTraceHullAttack( 70, 0, 0 ); - - if ( pHurt ) - { - // croonchy bite sound - iPitch = RANDOM_FLOAT( 90, 110 ); - switch ( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } - - - //pHurt->pev->punchangle.x = RANDOM_LONG(0,34) - 5; - //pHurt->pev->punchangle.z = RANDOM_LONG(0,49) - 25; - //pHurt->pev->punchangle.y = RANDOM_LONG(0,89) - 45; - - // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. - UTIL_ScreenShake( pHurt->v.origin, 25.0, 1.5, 0.7, 2 ); - - if ( UTIL_IsPlayer(pHurt) ) - { - UTIL_MakeVectors( pev->angles ); - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 300 + gpGlobals->v_up * 300; - } - } - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMBullsquid :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/bullsquid.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.bullsquidHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_flNextSpitTime = gpGlobals->time; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_bullchicken" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Bullsquid" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMBullsquid :: Precache() -{ - PRECACHE_MODEL("models/bullsquid.mdl"); - - PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. - - iSquidSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle. - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - PRECACHE_SOUND("bullchicken/bc_attack2.wav"); - PRECACHE_SOUND("bullchicken/bc_attack3.wav"); - - PRECACHE_SOUND("bullchicken/bc_die1.wav"); - PRECACHE_SOUND("bullchicken/bc_die2.wav"); - PRECACHE_SOUND("bullchicken/bc_die3.wav"); - - PRECACHE_SOUND("bullchicken/bc_idle1.wav"); - PRECACHE_SOUND("bullchicken/bc_idle2.wav"); - PRECACHE_SOUND("bullchicken/bc_idle3.wav"); - PRECACHE_SOUND("bullchicken/bc_idle4.wav"); - PRECACHE_SOUND("bullchicken/bc_idle5.wav"); - - PRECACHE_SOUND("bullchicken/bc_pain1.wav"); - PRECACHE_SOUND("bullchicken/bc_pain2.wav"); - PRECACHE_SOUND("bullchicken/bc_pain3.wav"); - PRECACHE_SOUND("bullchicken/bc_pain4.wav"); - - PRECACHE_SOUND("bullchicken/bc_attackgrowl.wav"); - PRECACHE_SOUND("bullchicken/bc_attackgrowl2.wav"); - PRECACHE_SOUND("bullchicken/bc_attackgrowl3.wav"); - - PRECACHE_SOUND("bullchicken/bc_acid1.wav"); - - PRECACHE_SOUND("bullchicken/bc_bite2.wav"); - PRECACHE_SOUND("bullchicken/bc_bite3.wav"); - - PRECACHE_SOUND("bullchicken/bc_spithit1.wav"); - PRECACHE_SOUND("bullchicken/bc_spithit2.wav"); - -} - -//========================================================= -// DeathSound -//========================================================= -void CMBullsquid :: DeathSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// AttackSound -//========================================================= -void CMBullsquid :: AttackSound ( void ) -{ - switch ( RANDOM_LONG(0,1) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "bullchicken/bc_attack2.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "bullchicken/bc_attack3.wav", 1, ATTN_NORM ); - break; - } -} - - -//======================================================== -// RunAI - overridden for bullsquid because there are things -// that need to be checked every think. -//======================================================== -void CMBullsquid :: RunAI ( void ) -{ - // first, do base class stuff - CMBaseMonster :: RunAI(); - - if ( pev->skin != 0 ) - { - // close eye if it was open. - pev->skin = 0; - } - - if ( RANDOM_LONG(0,39) == 0 ) - { - pev->skin = 1; - } - - if ( m_hEnemy != NULL && m_Activity == ACT_RUN ) - { - // chasing enemy. Sprint for last bit - if ( (pev->origin - m_hEnemy->v.origin).Length2D() < SQUID_SPRINT_DIST ) - { - pev->framerate = 1.25; - } - } - -} - -//======================================================== -// AI Schedules Specific to this monster -//========================================================= - -// primary range attack -Task_t tlSquidRangeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slSquidRangeAttack1[] = -{ - { - tlSquidRangeAttack1, - ARRAYSIZE ( tlSquidRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "Squid Range Attack1" - }, -}; - -// Chase enemy schedule -Task_t tlSquidChaseEnemy1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation. - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slSquidChaseEnemy[] = -{ - { - tlSquidChaseEnemy1, - ARRAYSIZE ( tlSquidChaseEnemy1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_SMELL_FOOD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_TASK_FAILED | - bits_COND_HEAR_SOUND, - 0, - "Squid Chase Enemy" - }, -}; - -Task_t tlSquidHurtHop[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SOUND_WAKE, (float)0 }, - { TASK_SQUID_HOPTURN, (float)0 }, - { TASK_FACE_ENEMY, (float)0 },// in case squid didn't turn all the way in the air. -}; - -Schedule_t slSquidHurtHop[] = -{ - { - tlSquidHurtHop, - ARRAYSIZE ( tlSquidHurtHop ), - 0, - 0, - "SquidHurtHop" - } -}; - -Task_t tlSquidSeeCrab[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SOUND_WAKE, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_EXCITED }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slSquidSeeCrab[] = -{ - { - tlSquidSeeCrab, - ARRAYSIZE ( tlSquidSeeCrab ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "SquidSeeCrab" - } -}; - -// squid walks to something tasty and eats it. -Task_t tlSquidEat[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_EAT, (float)10 },// this is in case the squid can't get to the food - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_EAT, (float)50 }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slSquidEat[] = -{ - { - tlSquidEat, - ARRAYSIZE( tlSquidEat ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY , - 0, - "SquidEat" - } -}; - -// this is a bit different than just Eat. We use this schedule when the food is far away, occluded, or behind -// the squid. This schedule plays a sniff animation before going to the source of food. -Task_t tlSquidSniffAndEat[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_EAT, (float)10 },// this is in case the squid can't get to the food - { TASK_PLAY_SEQUENCE, (float)ACT_DETECT_SCENT }, - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_EAT, (float)50 }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slSquidSniffAndEat[] = -{ - { - tlSquidSniffAndEat, - ARRAYSIZE( tlSquidSniffAndEat ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY , - 0, - "SquidSniffAndEat" - } -}; - -// squid does this to stinky things. -Task_t tlSquidWallow[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_EAT, (float)10 },// this is in case the squid can't get to the stinkiness - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_INSPECT_FLOOR}, - { TASK_EAT, (float)50 },// keeps squid from eating or sniffing anything else for a while. - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slSquidWallow[] = -{ - { - tlSquidWallow, - ARRAYSIZE( tlSquidWallow ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY , - 0, - "SquidWallow" - } -}; - -DEFINE_CUSTOM_SCHEDULES( CMBullsquid ) -{ - slSquidRangeAttack1, - slSquidChaseEnemy, - slSquidHurtHop, - slSquidSeeCrab, - slSquidEat, - slSquidSniffAndEat, - slSquidWallow -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMBullsquid, CMBaseMonster ); - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMBullsquid :: GetSchedule( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_ALERT: - { - if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) - { - return GetScheduleOfType ( SCHED_SQUID_HURTHOP ); - } - - break; - } - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - if ( HasConditions(bits_COND_NEW_ENEMY) && (m_hEnemy != NULL) ) - { - if (UTIL_IsPlayer(m_hEnemy)) - { - return GetScheduleOfType ( SCHED_WAKE_ANGRY ); - } - else if (m_hEnemy->v.euser4 != NULL) - { - edict_t *pEdict = m_hEnemy; - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); - - if ( IRelationship( pMonster ) == R_HT ) - { - // this means squid sees a headcrab! - return GetScheduleOfType ( SCHED_SQUID_SEECRAB ); - } - else - { - return GetScheduleOfType ( SCHED_WAKE_ANGRY ); - } - } - } - -/*jlb - if ( strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0 ) - { - if ( !UTIL_FInViewCone ( m_hEnemy, ENT(pev), m_flFieldOfView ) || !UTIL_FVisible ( m_hEnemy, ENT(pev) ) ) - { - // scent is behind or occluded - return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); - } - - // food is right out in the open. Just go get it. - return GetScheduleOfType( SCHED_SQUID_EAT ); - } -jlb*/ - - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } - - if ( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) - { - return GetScheduleOfType ( SCHED_MELEE_ATTACK2 ); - } - - return GetScheduleOfType ( SCHED_CHASE_ENEMY ); - - break; - } - } - - return CMBaseMonster :: GetSchedule(); -} - -//========================================================= -// GetScheduleOfType -//========================================================= -Schedule_t* CMBullsquid :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_RANGE_ATTACK1: - return &slSquidRangeAttack1[ 0 ]; - break; - case SCHED_SQUID_HURTHOP: - return &slSquidHurtHop[ 0 ]; - break; - case SCHED_SQUID_SEECRAB: - return &slSquidSeeCrab[ 0 ]; - break; - case SCHED_SQUID_EAT: - return &slSquidEat[ 0 ]; - break; - case SCHED_SQUID_SNIFF_AND_EAT: - return &slSquidSniffAndEat[ 0 ]; - break; - case SCHED_SQUID_WALLOW: - return &slSquidWallow[ 0 ]; - break; - case SCHED_CHASE_ENEMY: - return &slSquidChaseEnemy[ 0 ]; - break; - } - - return CMBaseMonster :: GetScheduleOfType ( Type ); -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. OVERRIDDEN for bullsquid because it needs to -// know explicitly when the last attempt to chase the enemy -// failed, since that impacts its attack choices. -//========================================================= -void CMBullsquid :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_MELEE_ATTACK2: - { - switch ( RANDOM_LONG ( 0, 2 ) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl3.wav", 1, ATTN_NORM ); - break; - } - - CMBaseMonster :: StartTask ( pTask ); - break; - } - case TASK_SQUID_HOPTURN: - { - SetActivity ( ACT_HOP ); - MakeIdealYaw ( m_vecEnemyLKP ); - break; - } - case TASK_GET_PATH_TO_ENEMY: - { - if ( BuildRoute ( m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy ) ) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - else - { - ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); - TaskFail(); - } - break; - } - default: - { - CMBaseMonster :: StartTask ( pTask ); - break; - } - } -} - -//========================================================= -// RunTask -//========================================================= -void CMBullsquid :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_SQUID_HOPTURN: - { - MakeIdealYaw( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if ( m_fSequenceFinished ) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - break; - } - default: - { - CMBaseMonster :: RunTask( pTask ); - break; - } - } -} - - -//========================================================= -// GetIdealState - Overridden for Bullsquid to deal with -// the feature that makes it lose interest in headcrabs for -// a while if something injures it. -//========================================================= -MONSTERSTATE CMBullsquid :: GetIdealState ( void ) -{ - int iConditions; - - iConditions = IScheduleFlags(); - - // If no schedule conditions, the new ideal state is probably the reason we're in here. - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - /* - COMBAT goes to ALERT upon death of enemy - */ - { - if ( m_hEnemy != NULL && ( iConditions & bits_COND_LIGHT_DAMAGE || iConditions & bits_COND_HEAVY_DAMAGE ) && (strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0) ) - { - // if the squid has a headcrab enemy and something hurts it, it's going to forget about the crab for a while. - m_hEnemy = NULL; - m_IdealMonsterState = MONSTERSTATE_ALERT; - } - break; - } - } - - m_IdealMonsterState = CMBaseMonster :: GetIdealState(); - - return m_IdealMonsterState; -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// bullsquid - big, spotty tentacle-mouthed meanie. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "nodes.h" +#include "effects.h" +#include "decals.h" + +#define SQUID_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve + +int iSquidSpitSprite; + + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_SQUID_HURTHOP = LAST_COMMON_SCHEDULE + 1, + SCHED_SQUID_SMELLFOOD, + SCHED_SQUID_SEECRAB, + SCHED_SQUID_EAT, + SCHED_SQUID_SNIFF_AND_EAT, + SCHED_SQUID_WALLOW, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_SQUID_HOPTURN = LAST_COMMON_TASK + 1, +}; + +//========================================================= +// Bullsquid's spit projectile +//========================================================= +class CSquidSpit : public CMBaseEntity +{ +public: + void Spawn( void ); + + static void Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); + void SpitTouch( edict_t *pOther ); + void EXPORT Animate( void ); + + int m_maxFrame; +}; + +void CSquidSpit:: Spawn( void ) +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING( "squidspit" ); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), "sprites/bigspit.spr"); + pev->frame = 0; + pev->scale = 0.5; + + UTIL_SetSize( pev, Vector( 0, 0, 0), Vector(0, 0, 0) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; +} + +void CSquidSpit::Animate( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + if ( pev->frame++ ) + { + if ( pev->frame > m_maxFrame ) + { + pev->frame = 0; + } + } +} + +void CSquidSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) +{ + CSquidSpit *pSpit = CreateClassPtr( (CSquidSpit *)NULL ); + + if (pSpit == NULL) + return; + + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->owner = ENT(pevOwner); + + pSpit->SetThink ( &CSquidSpit::Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; + pSpit->SetTouch ( &CSquidSpit::SpitTouch ); +} + +void CSquidSpit :: SpitTouch ( edict_t *pOther ) +{ + TraceResult tr; + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT( 90, 110 ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); + + switch ( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + if ( !pOther->v.takedamage ) + { + // make a splat on the wall + UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); + UTIL_DecalTrace(&tr, DECAL_SPIT1 + RANDOM_LONG(0,1)); + + // make some flecks + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, tr.vecEndPos ); + WRITE_BYTE( TE_SPRITE_SPRAY ); + WRITE_COORD( tr.vecEndPos.x); // pos + WRITE_COORD( tr.vecEndPos.y); + WRITE_COORD( tr.vecEndPos.z); + WRITE_COORD( tr.vecPlaneNormal.x); // dir + WRITE_COORD( tr.vecPlaneNormal.y); + WRITE_COORD( tr.vecPlaneNormal.z); + WRITE_SHORT( iSquidSpitSprite ); // model + WRITE_BYTE ( 5 ); // count + WRITE_BYTE ( 30 ); // speed + WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) + MESSAGE_END(); + } + else + { + if (UTIL_IsPlayer(pOther)) + UTIL_TakeDamage( pOther, pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage ( pev, pev, gSkillData.bullsquidDmgSpit, DMG_GENERIC ); + } + } + + SetThink ( &CSquidSpit::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define BSQUID_AE_SPIT ( 1 ) +#define BSQUID_AE_BITE ( 2 ) +#define BSQUID_AE_BLINK ( 3 ) +#define BSQUID_AE_TAILWHIP ( 4 ) +#define BSQUID_AE_HOP ( 5 ) +#define BSQUID_AE_THROW ( 6 ) + + +//========================================================= +// IgnoreConditions +//========================================================= +int CMBullsquid::IgnoreConditions ( void ) +{ + int iIgnore = CMBaseMonster::IgnoreConditions(); + + if ( gpGlobals->time - m_flLastHurtTime <= 20 ) + { + // haven't been hurt in 20 seconds, so let the squid care about stink. + iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; + } + + if ( m_hEnemy != NULL ) + { + if ( strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0 ) + { + // (Unless after a tasty headcrab) + iIgnore = bits_COND_SMELL | bits_COND_SMELL_FOOD; + } + } + + + return iIgnore; +} + +//========================================================= +// IRelationship - overridden for bullsquid so that it can +// be made to ignore its love of headcrabs for a while. +//========================================================= +int CMBullsquid::IRelationship ( CMBaseEntity *pTarget ) +{ + if ( gpGlobals->time - m_flLastHurtTime < 5 && (strcmp(STRING(pTarget->pev->model), "models/headcrab.mdl") == 0) ) + { + // if squid has been hurt in the last 5 seconds, and is getting relationship for a headcrab, + // tell squid to disregard crab. + return R_NO; + } + + return CMBaseMonster :: IRelationship ( pTarget ); +} + +//========================================================= +// TakeDamage - overridden for bullsquid so we can keep track +// of how much time has passed since it was last injured +//========================================================= +int CMBullsquid :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + float flDist; + Vector vecApex; + + // if the squid is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy, + // it will swerve. (whew). + if ( m_hEnemy != NULL && IsMoving() && pevAttacker == VARS((edict_t *)m_hEnemy) && gpGlobals->time - m_flLastHurtTime > 3 ) + { + flDist = ( pev->origin - m_hEnemy->v.origin ).Length2D(); + + if ( flDist > SQUID_SPRINT_DIST ) + { + flDist = ( pev->origin - m_Route[ m_iRouteIndex ].vecLocation ).Length2D();// reusing flDist. + + if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist * 0.5, m_hEnemy, &vecApex ) ) + { + InsertWaypoint( vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY ); + } + } + } + + if ( !strcmp(STRING(pev->model), "models/headcrab.mdl") == 0 ) + { + // don't forget about headcrabs if it was a headcrab that hurt the squid. + m_flLastHurtTime = gpGlobals->time; + } + + return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMBullsquid :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( IsMoving() && flDist >= 512 ) + { + // squid will far too far behind if he stops running to spit at this distance from the enemy. + return FALSE; + } + + if ( flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextSpitTime ) + { + if ( m_hEnemy != NULL ) + { + if ( fabs( pev->origin.z - m_hEnemy->v.origin.z ) > 256 ) + { + // don't try to spit at someone up really high or down really low. + return FALSE; + } + } + + if ( IsMoving() ) + { + // don't spit again for a long time, resume chasing enemy. + m_flNextSpitTime = gpGlobals->time + 5; + } + else + { + // not moving, so spit again pretty soon. + m_flNextSpitTime = gpGlobals->time + 0.5; + } + + return TRUE; + } + + return FALSE; +} + +//========================================================= +// CheckMeleeAttack1 - bullsquid is a big guy, so has a longer +// melee range than most monsters. This is the tailwhip attack +//========================================================= +BOOL CMBullsquid :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if ( m_hEnemy->v.health <= gSkillData.bullsquidDmgWhip && flDist <= 85 && flDot >= 0.7 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckMeleeAttack2 - bullsquid is a big guy, so has a longer +// melee range than most monsters. This is the bite attack. +// this attack will not be performed if the tailwhip attack +// is valid. +//========================================================= +BOOL CMBullsquid :: CheckMeleeAttack2 ( float flDot, float flDist ) +{ + if ( flDist <= 85 && flDot >= 0.7 && !HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) // The player & bullsquid can be as much as their bboxes + { // apart (48 * sqrt(3)) and he can still attack (85 is a little more than 48*sqrt(3)) + return TRUE; + } + return FALSE; +} + +//========================================================= +// FValidateHintType +//========================================================= +BOOL CMBullsquid :: FValidateHintType ( short sHint ) +{ + int i; + + static short sSquidHints[] = + { + HINT_WORLD_HUMAN_BLOOD, + }; + + for ( i = 0 ; i < ARRAYSIZE ( sSquidHints ) ; i++ ) + { + if ( sSquidHints[ i ] == sHint ) + { + return TRUE; + } + } + + ALERT ( at_aiconsole, "Couldn't validate hint type" ); + return FALSE; +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMBullsquid :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// IdleSound +//========================================================= +#define SQUID_ATTN_IDLE (float)1.5 +void CMBullsquid :: IdleSound ( void ) +{ + switch ( RANDOM_LONG(0,4) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle1.wav", 1, SQUID_ATTN_IDLE ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle2.wav", 1, SQUID_ATTN_IDLE ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle3.wav", 1, SQUID_ATTN_IDLE ); + break; + case 3: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle4.wav", 1, SQUID_ATTN_IDLE ); + break; + case 4: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle5.wav", 1, SQUID_ATTN_IDLE ); + break; + } +} + +//========================================================= +// PainSound +//========================================================= +void CMBullsquid :: PainSound ( void ) +{ + int iPitch = RANDOM_LONG( 85, 120 ); + + switch ( RANDOM_LONG(0,3) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 2: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain3.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 3: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_pain4.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } +} + +//========================================================= +// AlertSound +//========================================================= +void CMBullsquid :: AlertSound ( void ) +{ + int iPitch = RANDOM_LONG( 140, 160 ); + + switch ( RANDOM_LONG ( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "bullchicken/bc_idle2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMBullsquid :: SetYawSpeed ( void ) +{ + int ys; + + ys = 0; + + switch ( m_Activity ) + { + case ACT_WALK: ys = 90; break; + case ACT_RUN: ys = 90; break; + case ACT_IDLE: ys = 90; break; + case ACT_RANGE_ATTACK1: ys = 90; break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMBullsquid :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case BSQUID_AE_SPIT: + { + if (m_hEnemy) + { + Vector vecSpitOffset; + Vector vecSpitDir; + + UTIL_MakeVectors ( pev->angles ); + + // !!!HACKHACK - the spot at which the spit originates (in front of the mouth) was measured in 3ds and hardcoded here. + // we should be able to read the position of bones at runtime for this info. + vecSpitOffset = ( gpGlobals->v_right * 8 + gpGlobals->v_forward * 37 + gpGlobals->v_up * 23 ); + vecSpitOffset = ( pev->origin + vecSpitOffset ); + vecSpitDir = ( ( m_hEnemy->v.origin + m_hEnemy->v.view_ofs ) - vecSpitOffset ).Normalize(); + + vecSpitDir.x += RANDOM_FLOAT( -0.05, 0.05 ); + vecSpitDir.y += RANDOM_FLOAT( -0.05, 0.05 ); + vecSpitDir.z += RANDOM_FLOAT( -0.05, 0 ); + + // do stuff for this event. + AttackSound(); + + // spew the spittle temporary ents. + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOffset ); + WRITE_BYTE( TE_SPRITE_SPRAY ); + WRITE_COORD( vecSpitOffset.x); // pos + WRITE_COORD( vecSpitOffset.y); + WRITE_COORD( vecSpitOffset.z); + WRITE_COORD( vecSpitDir.x); // dir + WRITE_COORD( vecSpitDir.y); + WRITE_COORD( vecSpitDir.z); + WRITE_SHORT( iSquidSpitSprite ); // model + WRITE_BYTE ( 15 ); // count + WRITE_BYTE ( 210 ); // speed + WRITE_BYTE ( 25 ); // noise ( client will divide by 100 ) + MESSAGE_END(); + + CSquidSpit::Shoot( pev, vecSpitOffset, vecSpitDir * 900 ); + } + } + break; + + case BSQUID_AE_BITE: + { + // SOUND HERE! + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.bullsquidDmgBite, DMG_SLASH ); + + if ( pHurt ) + { + //pHurt->pev->punchangle.z = -15; + //pHurt->pev->punchangle.x = -45; + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 100; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; + } + } + break; + + case BSQUID_AE_TAILWHIP: + { + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.bullsquidDmgWhip, DMG_CLUB | DMG_ALWAYSGIB ); + if ( pHurt ) + { + pHurt->v.punchangle.z = -20; + pHurt->v.punchangle.x = 20; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 200; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; + } + } + break; + + case BSQUID_AE_BLINK: + { + // close eye. + pev->skin = 1; + } + break; + + case BSQUID_AE_HOP: + { + float flGravity = g_psv_gravity->value; + + // throw the squid up into the air on this frame. + if ( FBitSet ( pev->flags, FL_ONGROUND ) ) + { + pev->flags -= FL_ONGROUND; + } + + // jump into air for 0.8 (24/30) seconds +// pev->velocity.z += (0.875 * flGravity) * 0.5; + pev->velocity.z += (0.625 * flGravity) * 0.5; + } + break; + + case BSQUID_AE_THROW: + { + int iPitch; + + // squid throws its prey IF the prey is a client. + edict_t *pHurt = CheckTraceHullAttack( 70, 0, 0 ); + + if ( pHurt ) + { + // croonchy bite sound + iPitch = RANDOM_FLOAT( 90, 110 ); + switch ( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + + //pHurt->pev->punchangle.x = RANDOM_LONG(0,34) - 5; + //pHurt->pev->punchangle.z = RANDOM_LONG(0,49) - 25; + //pHurt->pev->punchangle.y = RANDOM_LONG(0,89) - 45; + + // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. + UTIL_ScreenShake( pHurt->v.origin, 25.0, 1.5, 0.7, 2 ); + + if ( UTIL_IsPlayer(pHurt) ) + { + UTIL_MakeVectors( pev->angles ); + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 300 + gpGlobals->v_up * 300; + } + } + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMBullsquid :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/bullsquid.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.bullsquidHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextSpitTime = gpGlobals->time; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_bullchicken" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Bullsquid" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMBullsquid :: Precache() +{ + PRECACHE_MODEL("models/bullsquid.mdl"); + + PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. + + iSquidSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle. + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + PRECACHE_SOUND("bullchicken/bc_attack2.wav"); + PRECACHE_SOUND("bullchicken/bc_attack3.wav"); + + PRECACHE_SOUND("bullchicken/bc_die1.wav"); + PRECACHE_SOUND("bullchicken/bc_die2.wav"); + PRECACHE_SOUND("bullchicken/bc_die3.wav"); + + PRECACHE_SOUND("bullchicken/bc_idle1.wav"); + PRECACHE_SOUND("bullchicken/bc_idle2.wav"); + PRECACHE_SOUND("bullchicken/bc_idle3.wav"); + PRECACHE_SOUND("bullchicken/bc_idle4.wav"); + PRECACHE_SOUND("bullchicken/bc_idle5.wav"); + + PRECACHE_SOUND("bullchicken/bc_pain1.wav"); + PRECACHE_SOUND("bullchicken/bc_pain2.wav"); + PRECACHE_SOUND("bullchicken/bc_pain3.wav"); + PRECACHE_SOUND("bullchicken/bc_pain4.wav"); + + PRECACHE_SOUND("bullchicken/bc_attackgrowl.wav"); + PRECACHE_SOUND("bullchicken/bc_attackgrowl2.wav"); + PRECACHE_SOUND("bullchicken/bc_attackgrowl3.wav"); + + PRECACHE_SOUND("bullchicken/bc_acid1.wav"); + + PRECACHE_SOUND("bullchicken/bc_bite2.wav"); + PRECACHE_SOUND("bullchicken/bc_bite3.wav"); + + PRECACHE_SOUND("bullchicken/bc_spithit1.wav"); + PRECACHE_SOUND("bullchicken/bc_spithit2.wav"); + +} + +//========================================================= +// DeathSound +//========================================================= +void CMBullsquid :: DeathSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_die3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// AttackSound +//========================================================= +void CMBullsquid :: AttackSound ( void ) +{ + switch ( RANDOM_LONG(0,1) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "bullchicken/bc_attack2.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "bullchicken/bc_attack3.wav", 1, ATTN_NORM ); + break; + } +} + + +//======================================================== +// RunAI - overridden for bullsquid because there are things +// that need to be checked every think. +//======================================================== +void CMBullsquid :: RunAI ( void ) +{ + // first, do base class stuff + CMBaseMonster :: RunAI(); + + if ( pev->skin != 0 ) + { + // close eye if it was open. + pev->skin = 0; + } + + if ( RANDOM_LONG(0,39) == 0 ) + { + pev->skin = 1; + } + + if ( m_hEnemy != NULL && m_Activity == ACT_RUN ) + { + // chasing enemy. Sprint for last bit + if ( (pev->origin - m_hEnemy->v.origin).Length2D() < SQUID_SPRINT_DIST ) + { + pev->framerate = 1.25; + } + } + +} + +//======================================================== +// AI Schedules Specific to this monster +//========================================================= + +// primary range attack +Task_t tlSquidRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slSquidRangeAttack1[] = +{ + { + tlSquidRangeAttack1, + ARRAYSIZE ( tlSquidRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "Squid Range Attack1" + }, +}; + +// Chase enemy schedule +Task_t tlSquidChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation. + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slSquidChaseEnemy[] = +{ + { + tlSquidChaseEnemy1, + ARRAYSIZE ( tlSquidChaseEnemy1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_SMELL_FOOD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED | + bits_COND_HEAR_SOUND, + 0, + "Squid Chase Enemy" + }, +}; + +Task_t tlSquidHurtHop[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SOUND_WAKE, (float)0 }, + { TASK_SQUID_HOPTURN, (float)0 }, + { TASK_FACE_ENEMY, (float)0 },// in case squid didn't turn all the way in the air. +}; + +Schedule_t slSquidHurtHop[] = +{ + { + tlSquidHurtHop, + ARRAYSIZE ( tlSquidHurtHop ), + 0, + 0, + "SquidHurtHop" + } +}; + +Task_t tlSquidSeeCrab[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SOUND_WAKE, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_EXCITED }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slSquidSeeCrab[] = +{ + { + tlSquidSeeCrab, + ARRAYSIZE ( tlSquidSeeCrab ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "SquidSeeCrab" + } +}; + +// squid walks to something tasty and eats it. +Task_t tlSquidEat[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_EAT, (float)10 },// this is in case the squid can't get to the food + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_EAT, (float)50 }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slSquidEat[] = +{ + { + tlSquidEat, + ARRAYSIZE( tlSquidEat ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY , + 0, + "SquidEat" + } +}; + +// this is a bit different than just Eat. We use this schedule when the food is far away, occluded, or behind +// the squid. This schedule plays a sniff animation before going to the source of food. +Task_t tlSquidSniffAndEat[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_EAT, (float)10 },// this is in case the squid can't get to the food + { TASK_PLAY_SEQUENCE, (float)ACT_DETECT_SCENT }, + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_EAT, (float)50 }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slSquidSniffAndEat[] = +{ + { + tlSquidSniffAndEat, + ARRAYSIZE( tlSquidSniffAndEat ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY , + 0, + "SquidSniffAndEat" + } +}; + +// squid does this to stinky things. +Task_t tlSquidWallow[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_EAT, (float)10 },// this is in case the squid can't get to the stinkiness + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_INSPECT_FLOOR}, + { TASK_EAT, (float)50 },// keeps squid from eating or sniffing anything else for a while. + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slSquidWallow[] = +{ + { + tlSquidWallow, + ARRAYSIZE( tlSquidWallow ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY , + 0, + "SquidWallow" + } +}; + +DEFINE_CUSTOM_SCHEDULES( CMBullsquid ) +{ + slSquidRangeAttack1, + slSquidChaseEnemy, + slSquidHurtHop, + slSquidSeeCrab, + slSquidEat, + slSquidSniffAndEat, + slSquidWallow +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMBullsquid, CMBaseMonster ); + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMBullsquid :: GetSchedule( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_ALERT: + { + if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) + { + return GetScheduleOfType ( SCHED_SQUID_HURTHOP ); + } + + break; + } + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + if ( HasConditions(bits_COND_NEW_ENEMY) && (m_hEnemy != NULL) ) + { + if (UTIL_IsPlayer(m_hEnemy)) + { + return GetScheduleOfType ( SCHED_WAKE_ANGRY ); + } + else if (m_hEnemy->v.euser4 != NULL) + { + edict_t *pEdict = m_hEnemy; + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); + + if ( IRelationship( pMonster ) == R_HT ) + { + // this means squid sees a headcrab! + return GetScheduleOfType ( SCHED_SQUID_SEECRAB ); + } + else + { + return GetScheduleOfType ( SCHED_WAKE_ANGRY ); + } + } + } + +/*jlb + if ( strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0 ) + { + if ( !UTIL_FInViewCone ( m_hEnemy, ENT(pev), m_flFieldOfView ) || !UTIL_FVisible ( m_hEnemy, ENT(pev) ) ) + { + // scent is behind or occluded + return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); + } + + // food is right out in the open. Just go get it. + return GetScheduleOfType( SCHED_SQUID_EAT ); + } +jlb*/ + + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } + + if ( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) + { + return GetScheduleOfType ( SCHED_MELEE_ATTACK2 ); + } + + return GetScheduleOfType ( SCHED_CHASE_ENEMY ); + + break; + } + } + + return CMBaseMonster :: GetSchedule(); +} + +//========================================================= +// GetScheduleOfType +//========================================================= +Schedule_t* CMBullsquid :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_RANGE_ATTACK1: + return &slSquidRangeAttack1[ 0 ]; + break; + case SCHED_SQUID_HURTHOP: + return &slSquidHurtHop[ 0 ]; + break; + case SCHED_SQUID_SEECRAB: + return &slSquidSeeCrab[ 0 ]; + break; + case SCHED_SQUID_EAT: + return &slSquidEat[ 0 ]; + break; + case SCHED_SQUID_SNIFF_AND_EAT: + return &slSquidSniffAndEat[ 0 ]; + break; + case SCHED_SQUID_WALLOW: + return &slSquidWallow[ 0 ]; + break; + case SCHED_CHASE_ENEMY: + return &slSquidChaseEnemy[ 0 ]; + break; + } + + return CMBaseMonster :: GetScheduleOfType ( Type ); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. OVERRIDDEN for bullsquid because it needs to +// know explicitly when the last attempt to chase the enemy +// failed, since that impacts its attack choices. +//========================================================= +void CMBullsquid :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_MELEE_ATTACK2: + { + switch ( RANDOM_LONG ( 0, 2 ) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "bullchicken/bc_attackgrowl3.wav", 1, ATTN_NORM ); + break; + } + + CMBaseMonster :: StartTask ( pTask ); + break; + } + case TASK_SQUID_HOPTURN: + { + SetActivity ( ACT_HOP ); + MakeIdealYaw ( m_vecEnemyLKP ); + break; + } + case TASK_GET_PATH_TO_ENEMY: + { + if ( BuildRoute ( m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy ) ) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + else + { + ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); + TaskFail(); + } + break; + } + default: + { + CMBaseMonster :: StartTask ( pTask ); + break; + } + } +} + +//========================================================= +// RunTask +//========================================================= +void CMBullsquid :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_SQUID_HOPTURN: + { + MakeIdealYaw( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if ( m_fSequenceFinished ) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + break; + } + default: + { + CMBaseMonster :: RunTask( pTask ); + break; + } + } +} + + +//========================================================= +// GetIdealState - Overridden for Bullsquid to deal with +// the feature that makes it lose interest in headcrabs for +// a while if something injures it. +//========================================================= +MONSTERSTATE CMBullsquid :: GetIdealState ( void ) +{ + int iConditions; + + iConditions = IScheduleFlags(); + + // If no schedule conditions, the new ideal state is probably the reason we're in here. + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + /* + COMBAT goes to ALERT upon death of enemy + */ + { + if ( m_hEnemy != NULL && ( iConditions & bits_COND_LIGHT_DAMAGE || iConditions & bits_COND_HEAVY_DAMAGE ) && (strcmp(STRING(m_hEnemy->v.model), "models/headcrab.mdl") == 0) ) + { + // if the squid has a headcrab enemy and something hurts it, it's going to forget about the crab for a while. + m_hEnemy = NULL; + m_IdealMonsterState = MONSTERSTATE_ALERT; + } + break; + } + } + + m_IdealMonsterState = CMBaseMonster :: GetIdealState(); + + return m_IdealMonsterState; +} + diff --git a/src/dlls/cdll_dll.h b/src/dlls/cdll_dll.h index 0acf496..f6ed329 100644 --- a/src/dlls/cdll_dll.h +++ b/src/dlls/cdll_dll.h @@ -1,46 +1,46 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -// -// cdll_dll.h - -// this file is included by both the game-dll and the client-dll, - -#ifndef CDLL_DLL_H -#define CDLL_DLL_H - -#define MAX_WEAPONS 32 // ??? - -#define MAX_WEAPON_SLOTS 5 // hud item selection slots -#define MAX_ITEM_TYPES 6 // hud item selection slots - -#define MAX_ITEMS 5 // hard coded item types - -#define HIDEHUD_WEAPONS ( 1<<0 ) -#define HIDEHUD_FLASHLIGHT ( 1<<1 ) -#define HIDEHUD_ALL ( 1<<2 ) -#define HIDEHUD_HEALTH ( 1<<3 ) - -#define MAX_AMMO_TYPES 32 // ??? -#define MAX_AMMO_SLOTS 32 // not really slots - -#define HUD_PRINTNOTIFY 1 -#define HUD_PRINTCONSOLE 2 -#define HUD_PRINTTALK 3 -#define HUD_PRINTCENTER 4 - - -#define WEAPON_SUIT 31 - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +// +// cdll_dll.h + +// this file is included by both the game-dll and the client-dll, + +#ifndef CDLL_DLL_H +#define CDLL_DLL_H + +#define MAX_WEAPONS 32 // ??? + +#define MAX_WEAPON_SLOTS 5 // hud item selection slots +#define MAX_ITEM_TYPES 6 // hud item selection slots + +#define MAX_ITEMS 5 // hard coded item types + +#define HIDEHUD_WEAPONS ( 1<<0 ) +#define HIDEHUD_FLASHLIGHT ( 1<<1 ) +#define HIDEHUD_ALL ( 1<<2 ) +#define HIDEHUD_HEALTH ( 1<<3 ) + +#define MAX_AMMO_TYPES 32 // ??? +#define MAX_AMMO_SLOTS 32 // not really slots + +#define HUD_PRINTNOTIFY 1 +#define HUD_PRINTCONSOLE 2 +#define HUD_PRINTTALK 3 +#define HUD_PRINTCENTER 4 + + +#define WEAPON_SUIT 31 + #endif \ No newline at end of file diff --git a/src/dlls/cmbase.cpp b/src/dlls/cmbase.cpp index 2d64075..644a062 100644 --- a/src/dlls/cmbase.cpp +++ b/src/dlls/cmbase.cpp @@ -1,306 +1,306 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "decals.h" - -extern Vector VecBModelOrigin( entvars_t* pevBModel ); -extern DLL_GLOBAL Vector g_vecAttackDir; - -edict_t * EHANDLE::Get( void ) -{ - if (m_pent) - { - if (m_pent->serialnumber == m_serialnumber) - return m_pent; - else - return NULL; - } - return NULL; -}; - -edict_t * EHANDLE::Set( edict_t *pent ) -{ - m_pent = pent; - if (pent) - m_serialnumber = m_pent->serialnumber; - return pent; -}; - - -EHANDLE :: operator edict_t *() -{ - return Get( ); -}; - - -edict_t * EHANDLE :: operator = (edict_t *pEntity) -{ - if (pEntity) - { - m_pent = pEntity; - if (m_pent) - m_serialnumber = m_pent->serialnumber; - } - else - { - m_pent = NULL; - m_serialnumber = 0; - } - return pEntity; -} - - -edict_t * EHANDLE :: operator -> () -{ - return Get( ); -} - - -void *CMBaseEntity::operator new( size_t stAllocateBlock ) -{ - void *mem = ::operator new( stAllocateBlock ); - memset( mem, 0, stAllocateBlock ); - return mem; -} - - -edict_t *CMBaseEntity::CreateEntity(char *classname) -{ - int istr = MAKE_STRING(classname); - - edict_t *pent = CREATE_NAMED_ENTITY(istr); - - if ( FNullEnt( pent ) ) - return NULL; - - pev = VARS(pent); - - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_NOT; - pev->flags = 0; - - m_pfnThink = NULL; - m_pfnTouch = NULL; - m_pfnUse = NULL; - m_pfnBlocked = NULL; - - pev->euser4 = (edict_t *)this; - - return pent; -} - -// give health -int CMBaseEntity :: TakeHealth( float flHealth, int bitsDamageType ) -{ - if (!pev->takedamage) - return 0; - -// heal - if ( pev->health >= pev->max_health ) - return 0; - - pev->health += flHealth; - - if (pev->health > pev->max_health) - pev->health = pev->max_health; - - return 1; -} - -// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH - -int CMBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) -{ - Vector vecTemp; - - if (!pev->takedamage) - return 0; - - // UNDONE: some entity types may be immune or resistant to some bitsDamageType - - // 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 - ( VecBModelOrigin(pev) ); - } - else - // an actual missile was involved. - { - vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); - } - -// this global is still used for glass and other non-monster killables, along with decals. - g_vecAttackDir = vecTemp.Normalize(); - -// save damage based on the target's armor level - -// figure momentum add (don't let hurt brushes or other triggers move player) - if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) ) - { - Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; - vecDir = vecDir.Normalize(); - - float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; - - if (flForce > 1000.0) - flForce = 1000.0; - pev->velocity = pev->velocity + vecDir * flForce; - } - -// do the damage - pev->health -= flDamage; - if (pev->health <= 0) - { - Killed( pevAttacker, GIB_NORMAL ); - return 0; - } - - return 1; -} - - -void CMBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->takedamage = DAMAGE_NO; - pev->deadflag = DEAD_DEAD; - UTIL_Remove( this->edict() ); -} - - -// Initialize absmin & absmax to the appropriate box -void SetObjectCollisionBox( entvars_t *pev ) -{ - if ( (pev->solid == SOLID_BSP) && - (pev->angles.x || pev->angles.y|| pev->angles.z) ) - { // expand for rotation - float max, v; - int i; - - max = 0; - for (i=0 ; i<3 ; i++) - { - v = fabs( ((float *)pev->mins)[i]); - if (v > max) - max = v; - v = fabs( ((float *)pev->maxs)[i]); - if (v > max) - max = v; - } - for (i=0 ; i<3 ; i++) - { - ((float *)pev->absmin)[i] = ((float *)pev->origin)[i] - max; - ((float *)pev->absmax)[i] = ((float *)pev->origin)[i] + max; - } - } - else - { - pev->absmin = pev->origin + pev->mins; - pev->absmax = pev->origin + pev->maxs; - } - - pev->absmin.x -= 1; - pev->absmin.y -= 1; - pev->absmin.z -= 1; - pev->absmax.x += 1; - pev->absmax.y += 1; - pev->absmax.z += 1; -} - - -void CMBaseEntity::SetObjectCollisionBox( void ) -{ - ::SetObjectCollisionBox( pev ); -} - - -int CMBaseEntity :: Intersects( CMBaseEntity *pOther ) -{ - if ( pOther->pev->absmin.x > pev->absmax.x || - pOther->pev->absmin.y > pev->absmax.y || - pOther->pev->absmin.z > pev->absmax.z || - pOther->pev->absmax.x < pev->absmin.x || - pOther->pev->absmax.y < pev->absmin.y || - pOther->pev->absmax.z < pev->absmin.z ) - return 0; - return 1; -} - -void CMBaseEntity :: MakeDormant( void ) -{ - SetBits( pev->flags, FL_DORMANT ); - - // Don't touch - pev->solid = SOLID_NOT; - // Don't move - pev->movetype = MOVETYPE_NONE; - // Don't draw - SetBits( pev->effects, EF_NODRAW ); - // Don't think - pev->nextthink = 0; - // Relink - UTIL_SetOrigin( pev, pev->origin ); -} - -int CMBaseEntity :: IsDormant( void ) -{ - return FBitSet( pev->flags, FL_DORMANT ); -} - -BOOL CMBaseEntity :: IsInWorld( void ) -{ - // position - if (pev->origin.x >= 4096) return FALSE; - if (pev->origin.y >= 4096) return FALSE; - if (pev->origin.z >= 4096) return FALSE; - if (pev->origin.x <= -4096) return FALSE; - if (pev->origin.y <= -4096) return FALSE; - if (pev->origin.z <= -4096) return FALSE; - // speed - if (pev->velocity.x >= 2000) return FALSE; - if (pev->velocity.y >= 2000) return FALSE; - if (pev->velocity.z >= 2000) return FALSE; - if (pev->velocity.x <= -2000) return FALSE; - if (pev->velocity.y <= -2000) return FALSE; - if (pev->velocity.z <= -2000) return FALSE; - - return TRUE; -} - -int CMBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState ) -{ - if ( useType != USE_TOGGLE && useType != USE_SET ) - { - if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) ) - return 0; - } - return 1; -} - - -int CMBaseEntity :: DamageDecal( int bitsDamageType ) -{ - if ( pev->rendermode == kRenderTransAlpha ) - return -1; - - if ( pev->rendermode != kRenderNormal ) - return DECAL_BPROOF1; - - return DECAL_GUNSHOT1 + RANDOM_LONG(0,4); -} - - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "decals.h" + +extern Vector VecBModelOrigin( entvars_t* pevBModel ); +extern DLL_GLOBAL Vector g_vecAttackDir; + +edict_t * EHANDLE::Get( void ) +{ + if (m_pent) + { + if (m_pent->serialnumber == m_serialnumber) + return m_pent; + else + return NULL; + } + return NULL; +}; + +edict_t * EHANDLE::Set( edict_t *pent ) +{ + m_pent = pent; + if (pent) + m_serialnumber = m_pent->serialnumber; + return pent; +}; + + +EHANDLE :: operator edict_t *() +{ + return Get( ); +}; + + +edict_t * EHANDLE :: operator = (edict_t *pEntity) +{ + if (pEntity) + { + m_pent = pEntity; + if (m_pent) + m_serialnumber = m_pent->serialnumber; + } + else + { + m_pent = NULL; + m_serialnumber = 0; + } + return pEntity; +} + + +edict_t * EHANDLE :: operator -> () +{ + return Get( ); +} + + +void *CMBaseEntity::operator new( size_t stAllocateBlock ) +{ + void *mem = ::operator new( stAllocateBlock ); + memset( mem, 0, stAllocateBlock ); + return mem; +} + + +edict_t *CMBaseEntity::CreateEntity(char *classname) +{ + int istr = MAKE_STRING(classname); + + edict_t *pent = CREATE_NAMED_ENTITY(istr); + + if ( FNullEnt( pent ) ) + return NULL; + + pev = VARS(pent); + + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_NOT; + pev->flags = 0; + + m_pfnThink = NULL; + m_pfnTouch = NULL; + m_pfnUse = NULL; + m_pfnBlocked = NULL; + + pev->euser4 = (edict_t *)this; + + return pent; +} + +// give health +int CMBaseEntity :: TakeHealth( float flHealth, int bitsDamageType ) +{ + if (!pev->takedamage) + return 0; + +// heal + if ( pev->health >= pev->max_health ) + return 0; + + pev->health += flHealth; + + if (pev->health > pev->max_health) + pev->health = pev->max_health; + + return 1; +} + +// inflict damage on this entity. bitsDamageType indicates type of damage inflicted, ie: DMG_CRUSH + +int CMBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ) +{ + Vector vecTemp; + + if (!pev->takedamage) + return 0; + + // UNDONE: some entity types may be immune or resistant to some bitsDamageType + + // 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 - ( VecBModelOrigin(pev) ); + } + else + // an actual missile was involved. + { + vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) ); + } + +// this global is still used for glass and other non-monster killables, along with decals. + g_vecAttackDir = vecTemp.Normalize(); + +// save damage based on the target's armor level + +// figure momentum add (don't let hurt brushes or other triggers move player) + if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) ) + { + Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; + vecDir = vecDir.Normalize(); + + float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; + + if (flForce > 1000.0) + flForce = 1000.0; + pev->velocity = pev->velocity + vecDir * flForce; + } + +// do the damage + pev->health -= flDamage; + if (pev->health <= 0) + { + Killed( pevAttacker, GIB_NORMAL ); + return 0; + } + + return 1; +} + + +void CMBaseEntity :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->takedamage = DAMAGE_NO; + pev->deadflag = DEAD_DEAD; + UTIL_Remove( this->edict() ); +} + + +// Initialize absmin & absmax to the appropriate box +void SetObjectCollisionBox( entvars_t *pev ) +{ + if ( (pev->solid == SOLID_BSP) && + (pev->angles.x || pev->angles.y|| pev->angles.z) ) + { // expand for rotation + float max, v; + int i; + + max = 0; + for (i=0 ; i<3 ; i++) + { + v = fabs( ((float *)pev->mins)[i]); + if (v > max) + max = v; + v = fabs( ((float *)pev->maxs)[i]); + if (v > max) + max = v; + } + for (i=0 ; i<3 ; i++) + { + ((float *)pev->absmin)[i] = ((float *)pev->origin)[i] - max; + ((float *)pev->absmax)[i] = ((float *)pev->origin)[i] + max; + } + } + else + { + pev->absmin = pev->origin + pev->mins; + pev->absmax = pev->origin + pev->maxs; + } + + pev->absmin.x -= 1; + pev->absmin.y -= 1; + pev->absmin.z -= 1; + pev->absmax.x += 1; + pev->absmax.y += 1; + pev->absmax.z += 1; +} + + +void CMBaseEntity::SetObjectCollisionBox( void ) +{ + ::SetObjectCollisionBox( pev ); +} + + +int CMBaseEntity :: Intersects( CMBaseEntity *pOther ) +{ + if ( pOther->pev->absmin.x > pev->absmax.x || + pOther->pev->absmin.y > pev->absmax.y || + pOther->pev->absmin.z > pev->absmax.z || + pOther->pev->absmax.x < pev->absmin.x || + pOther->pev->absmax.y < pev->absmin.y || + pOther->pev->absmax.z < pev->absmin.z ) + return 0; + return 1; +} + +void CMBaseEntity :: MakeDormant( void ) +{ + SetBits( pev->flags, FL_DORMANT ); + + // Don't touch + pev->solid = SOLID_NOT; + // Don't move + pev->movetype = MOVETYPE_NONE; + // Don't draw + SetBits( pev->effects, EF_NODRAW ); + // Don't think + pev->nextthink = 0; + // Relink + UTIL_SetOrigin( pev, pev->origin ); +} + +int CMBaseEntity :: IsDormant( void ) +{ + return FBitSet( pev->flags, FL_DORMANT ); +} + +BOOL CMBaseEntity :: IsInWorld( void ) +{ + // position + if (pev->origin.x >= 4096) return FALSE; + if (pev->origin.y >= 4096) return FALSE; + if (pev->origin.z >= 4096) return FALSE; + if (pev->origin.x <= -4096) return FALSE; + if (pev->origin.y <= -4096) return FALSE; + if (pev->origin.z <= -4096) return FALSE; + // speed + if (pev->velocity.x >= 2000) return FALSE; + if (pev->velocity.y >= 2000) return FALSE; + if (pev->velocity.z >= 2000) return FALSE; + if (pev->velocity.x <= -2000) return FALSE; + if (pev->velocity.y <= -2000) return FALSE; + if (pev->velocity.z <= -2000) return FALSE; + + return TRUE; +} + +int CMBaseEntity::ShouldToggle( USE_TYPE useType, BOOL currentState ) +{ + if ( useType != USE_TOGGLE && useType != USE_SET ) + { + if ( (currentState && useType == USE_ON) || (!currentState && useType == USE_OFF) ) + return 0; + } + return 1; +} + + +int CMBaseEntity :: DamageDecal( int bitsDamageType ) +{ + if ( pev->rendermode == kRenderTransAlpha ) + return -1; + + if ( pev->rendermode != kRenderNormal ) + return DECAL_BPROOF1; + + return DECAL_GUNSHOT1 + RANDOM_LONG(0,4); +} + + diff --git a/src/dlls/cmbase.h b/src/dlls/cmbase.h index 962bf29..cf750bc 100644 --- a/src/dlls/cmbase.h +++ b/src/dlls/cmbase.h @@ -1,611 +1,611 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -Class Hierachy - -CMBaseEntity - CMBaseDelay - CMBaseAnimating - CMBaseToggle - CMBaseMonster -*/ - -#include "monster_plugin.h" - -#define MAX_PATH_SIZE 10 // max number of nodes available for a path. - -// These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) -#define FCAP_CUSTOMSAVE 0x00000001 -#define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions -#define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore -#define FCAP_DONT_SAVE 0x80000000 // Don't save this -#define FCAP_IMPULSE_USE 0x00000008 // can be used by the player -#define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player -#define FCAP_ONOFF_USE 0x00000020 // can be used by the player -#define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains) -#define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource) - -// UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! -#define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions - -#include "schedule.h" - -#ifndef MONSTEREVENT_H -#include "monsterevent.h" -#endif - -// C functions for external declarations that call the appropriate C++ methods - -#ifdef _WIN32 -#define EXPORT __declspec( dllexport ) -#else -#define EXPORT /* */ -#endif - -typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; - -class CMBaseEntity; - -extern void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - -typedef void (CMBaseEntity::*BASEPTR)(void); -typedef void (CMBaseEntity::*ENTITYFUNCPTR)(CMBaseEntity *pOther ); -typedef void (CMBaseEntity::*USEPTR)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - -// For CLASSIFY -#define CLASS_NONE 0 -#define CLASS_MACHINE 1 -#define CLASS_PLAYER 2 -#define CLASS_HUMAN_PASSIVE 3 -#define CLASS_HUMAN_MILITARY 4 -#define CLASS_ALIEN_MILITARY 5 -#define CLASS_ALIEN_PASSIVE 6 -#define CLASS_ALIEN_MONSTER 7 -#define CLASS_ALIEN_PREY 8 -#define CLASS_ALIEN_PREDATOR 9 -#define CLASS_INSECT 10 -#define CLASS_PLAYER_ALLY 11 -#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players -#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace -#define CLASS_RACEX_PITDRONE 14 -#define CLASS_RACEX_SHOCK 15 -#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. - -class CMBaseEntity; -class CMBaseMonster; - - -#define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. - -// -// EHANDLE. Safe way to point to edict_t who may die between frames -// -class EHANDLE -{ -private: - edict_t *m_pent; - int m_serialnumber; -public: - edict_t *Get( void ); - edict_t *Set( edict_t *pent ); - - operator edict_t *(); - - edict_t * operator = (edict_t *pEntity); - edict_t * operator ->(); -}; - - -template T * GetClassPtr( T *a ); - -// -// Base Entity. All entity types derive from this -// -class CMBaseEntity -{ -public: - // Constructor. Set engine to use C/C++ callback functions - // pointers to engine data - entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it - - // path corners - edict_t *m_pGoalEnt;// path corner we are heading towards - - edict_t *m_edictList[100]; - int m_edictList_count; - - void *operator new( size_t stAllocateBlock ); - - virtual edict_t *CreateEntity(char *classname); - - // initialization functions - virtual void Spawn( void ) { return; } - virtual void Precache( void ) { return; } - virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } - virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } - virtual void Activate( void ) {} - - // Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box) - virtual void SetObjectCollisionBox( void ); - -// Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames -// still realize that they are teammates. (overridden for monsters that form groups) - virtual int Classify ( void ) { return CLASS_NONE; }; - virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died. - - virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - virtual int TakeHealth( float flHealth, int bitsDamageType ); - virtual void Killed( entvars_t *pevAttacker, int iGib ); - virtual int BloodColor( void ) { return DONT_BLEED; } - virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - virtual BOOL IsTriggered( CMBaseEntity *pActivator ) {return TRUE;} - virtual CMBaseMonster *MyMonsterPointer( void ) { return NULL;} - virtual int GetToggleState( void ) { return TS_AT_TOP; } - virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} - virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} - virtual float GetDelay( void ) { return 0; } - virtual int IsMoving( void ) { return pev->velocity != g_vecZero; } - virtual void OverrideReset( void ) {} - virtual int DamageDecal( int bitsDamageType ); - // This is ONLY used by the node graph to test movement through a door - virtual void SetToggleState( int state ) {} - virtual void StartSneaking( void ) {} - virtual void StopSneaking( void ) {} - virtual BOOL OnControls( entvars_t *pev ) { return FALSE; } - virtual BOOL IsSneaking( void ) { return FALSE; } - virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; } - virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; } - virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); } - virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); } - virtual BOOL IsInWorld( void ); - virtual BOOL IsPlayer( void ) { return FALSE; } - virtual BOOL IsNetClient( void ) { return FALSE; } - virtual const char *TeamID( void ) { return ""; } - - -// virtual void SetActivator( CMBaseEntity *pActivator ) {} -//jlb virtual CMBaseEntity *GetNextTarget( void ); - - // fundamental callbacks - void (CMBaseEntity ::*m_pfnThink)(void); - void (CMBaseEntity ::*m_pfnTouch)( edict_t *pOther ); - void (CMBaseEntity ::*m_pfnUse)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - void (CMBaseEntity ::*m_pfnBlocked)( edict_t *pOther ); - - virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; - virtual void Touch( edict_t *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; - virtual void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) - { - if (m_pfnUse) - (this->*m_pfnUse)( pActivator, pCaller, useType, value ); - } - virtual void Blocked( edict_t *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; - - void UpdateOnRemove( void ); - - // common member functions - void EXPORT SUB_Remove( void ); - void EXPORT SUB_DoNothing( void ); - void EXPORT SUB_StartFadeOut ( void ); - void EXPORT SUB_FadeOut ( void ); - void EXPORT SUB_CallUseToggle( void ) { this->Use( this->edict(), this->edict(), USE_TOGGLE, 0 ); } - int ShouldToggle( USE_TYPE useType, BOOL currentState ); - void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL ); - Vector FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int shared_rand = 0 ); - - virtual CMBaseEntity *Respawn( void ) { return NULL; } - - void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); - // Do the bounding boxes of these two intersect? - int Intersects( CMBaseEntity *pOther ); - void MakeDormant( void ); - int IsDormant( void ); - BOOL IsLockedByMaster( void ) { return FALSE; } - - static CMBaseEntity *Instance( edict_t *pent ) - { - if ( !pent ) - pent = ENT(0); - if ( pent->v.euser4 == NULL ) - return (CMBaseEntity *)NULL; - CMBaseEntity *pEnt = GetClassPtr((CMBaseEntity *)VARS(pent)); - return pEnt; - } - - static CMBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } - static CMBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } - -/*jlb - CMBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) - { - CMBaseEntity *pEntity = Instance( pevMonster ); - if ( pEntity ) - return pEntity->MyMonsterPointer(); - return NULL; - } - CMBaseMonster *GetMonsterPointer( edict_t *pentMonster ) - { - CMBaseEntity *pEntity = Instance( pentMonster ); - if ( pEntity ) - return pEntity->MyMonsterPointer(); - return NULL; - } -jlb*/ - - // virtual functions used by a few classes - - // used by monsters that are created by the MonsterMaker - virtual void UpdateOwner( void ) { return; }; - - - // -//jlb static CMBaseEntity *Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL ); - - virtual BOOL FBecomeProne( void ) {return FALSE;}; - edict_t *edict() { return ENT( pev ); }; - EOFFSET eoffset( ) { return OFFSET( pev ); }; - int entindex( ) { return ENTINDEX( edict() ); }; - - virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity - virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes - virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears - virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at - - virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); }; - -// virtual BOOL FVisible ( edict_t *pEntity ); -// virtual BOOL FVisible ( const Vector &vecOrigin ); - - //We use this variables to store each ammo count. - int ammo_9mm; - int ammo_357; - int ammo_bolts; - int ammo_buckshot; - int ammo_rockets; - int ammo_uranium; - int ammo_hornets; - int ammo_argrens; - //Special stuff for grenades and satchels. - float m_flStartThrow; - float m_flReleaseThrow; - int m_chargeReady; - int m_fInAttack; - - enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE }; - int m_fireState; -}; - - - -// Ugly technique to override base member functions -// Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a -// member function of a base class. static_cast is a sleezy way around that problem. - -#define SetThink( a ) m_pfnThink = static_cast (a) -#define SetTouch( a ) m_pfnTouch = static_cast (a) -#define SetUse( a ) m_pfnUse = static_cast (a) -#define SetBlocked( a ) m_pfnBlocked = static_cast (a) - - -class CMPointEntity : public CMBaseEntity -{ -public: - void Spawn( void ); - virtual int ObjectCaps( void ) { return CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } -private: -}; - - -typedef struct locksounds // sounds that doors and buttons make when locked/unlocked -{ - string_t sLockedSound; // sound a door makes when it's locked - string_t sLockedSentence; // sentence group played when door is locked - string_t sUnlockedSound; // sound a door makes when it's unlocked - string_t sUnlockedSentence; // sentence group played when door is unlocked - - int iLockedSentence; // which sentence in sentence group to play next - int iUnlockedSentence; // which sentence in sentence group to play next - - float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds - float flwaitSentence; // time delay between playing consecutive sentences - BYTE bEOFLocked; // true if hit end of list of locked sentences - BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences -} locksound_t; - -void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton); - - -// -// generic Delay entity. -// -class CMBaseDelay : public CMBaseEntity -{ -public: - float m_flDelay; - int m_iszKillTarget; - - virtual void KeyValue( KeyValueData* pkvd); - - // common member functions - void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); - void EXPORT DelayThink( void ); -}; - - -class CMBaseAnimating : public CMBaseDelay -{ -public: - - // Basic Monster Animation functions - float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now - int GetSequenceFlags( void ); - int LookupActivity ( int activity ); - int LookupActivityHeaviest ( int activity ); - int LookupSequence ( const char *label ); - void ResetSequenceInfo ( ); - void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future - virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; }; - float SetBoneController ( int iController, float flValue ); - void InitBoneControllers ( void ); - float SetBlending ( int iBlender, float flValue ); - void GetBonePosition ( int iBone, Vector &origin, Vector &angles ); - void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 ); - int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ); - void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); - void SetBodygroup( int iGroup, int iValue ); - int GetBodygroup( int iGroup ); - int ExtractBbox( int sequence, float *mins, float *maxs ); - void SetSequenceBox( void ); - - // animation needs - float m_flFrameRate; // computed FPS for current sequence - float m_flGroundSpeed; // computed linear movement rate for current sequence - float m_flLastEventCheck; // last time the event list was checked - BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry - BOOL m_fSequenceLoops; // true if the sequence loops -}; - - -// -// generic Toggle entity. -// -#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! - -class CMBaseToggle : public CMBaseAnimating -{ -public: - void KeyValue( KeyValueData *pkvd ); - - TOGGLE_STATE m_toggle_state; - float m_flActivateFinished;//like attack_finished, but for doors - float m_flMoveDistance;// how far a door should slide or rotate - float m_flWait; - float m_flLip; - float m_flTWidth;// for plats - float m_flTLength;// for plats - - Vector m_vecPosition1; - Vector m_vecPosition2; - Vector m_vecAngle1; - Vector m_vecAngle2; - - int m_cTriggersLeft; // trigger_counter only, # of activations remaining - float m_flHeight; -//jlb EHANDLE m_hActivator; - void (CMBaseToggle::*m_pfnCallWhenMoveDone)(void); - Vector m_vecFinalDest; - Vector m_vecFinalAngle; - - int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does - - virtual int GetToggleState( void ) { return m_toggle_state; } - virtual float GetDelay( void ) { return m_flWait; } - - // common member functions - void LinearMove( Vector vecDest, float flSpeed ); - void EXPORT LinearMoveDone( void ); - void AngularMove( Vector vecDestAngle, float flSpeed ); - void EXPORT AngularMoveDone( void ); - BOOL IsLockedByMaster( void ); - - static float AxisValue( int flags, const Vector &angles ); - static void AxisDir( entvars_t *pev ); - static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ); - - string_t m_sMaster; // If this button has a master switch, this is the targetname. - // A master switch must be of the multisource type. If all - // of the switches in the multisource have been triggered, then - // the button will be allowed to operate. Otherwise, it will be - // deactivated. -}; -#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast (a) - - -// people gib if their health is <= this at the time of death -#define GIB_HEALTH_VALUE -30 - -#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time -#define MAX_OLD_ENEMIES 4 // how many old enemies to remember - -#define bits_CAP_DUCK ( 1 << 0 )// crouch -#define bits_CAP_JUMP ( 1 << 1 )// jump/leap -#define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways) -//??? #define bits_CAP_SQUAD ( 1 << 3 )// can form squads -#define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water -#define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes -#define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers -#define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds -#define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors -#define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors -#define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0 - -#define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1 -#define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2 -#define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1 -#define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2 - -#define bits_CAP_FLY ( 1 << 15)// can fly, move all around - -#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) - -// used by suit voice to indicate damage sustained and repaired type to player - -// instant damage - -#define DMG_GENERIC 0 // generic damage was done -#define DMG_CRUSH (1 << 0) // crushed by falling or moving object -#define DMG_BULLET (1 << 1) // shot -#define DMG_SLASH (1 << 2) // cut, clawed, stabbed -#define DMG_BURN (1 << 3) // heat burned -#define DMG_FREEZE (1 << 4) // frozen -#define DMG_FALL (1 << 5) // fell too far -#define DMG_BLAST (1 << 6) // explosive blast damage -#define DMG_CLUB (1 << 7) // crowbar, punch, headbutt -#define DMG_SHOCK (1 << 8) // electric shock -#define DMG_SONIC (1 << 9) // sound pulse shockwave -#define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam -#define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death -#define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death. -#define DMG_DROWN (1 << 14) // Drowning -// time-based damage -#define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage - -#define DMG_PARALYZE (1 << 15) // slows affected creature down -#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad -#define DMG_POISON (1 << 17) // blood poisioning -#define DMG_RADIATION (1 << 18) // radiation exposure -#define DMG_DROWNRECOVER (1 << 19) // drowning recovery -#define DMG_ACID (1 << 20) // toxic chemicals or acid burns -#define DMG_SLOWBURN (1 << 21) // in an oven -#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer -#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar) - -// these are the damage types that are allowed to gib corpses -#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) - -// these are the damage types that have client hud art -#define DMG_SHOWNHUD (DMG_POISON | DMG_ACID | DMG_FREEZE | DMG_SLOWFREEZE | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK) - -// NOTE: tweak these values based on gameplay feedback: - -#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage -#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval - -#define NERVEGAS_DURATION 2 -#define NERVEGAS_DAMAGE 5.0 - -#define POISON_DURATION 5 -#define POISON_DAMAGE 2.0 - -#define RADIATION_DURATION 2 -#define RADIATION_DAMAGE 1.0 - -#define ACID_DURATION 2 -#define ACID_DAMAGE 5.0 - -#define SLOWBURN_DURATION 2 -#define SLOWBURN_DAMAGE 1.0 - -#define SLOWFREEZE_DURATION 2 -#define SLOWFREEZE_DAMAGE 1.0 - - -#define itbd_Paralyze 0 -#define itbd_NerveGas 1 -#define itbd_Poison 2 -#define itbd_Radiation 3 -#define itbd_DrownRecover 4 -#define itbd_Acid 5 -#define itbd_SlowBurn 6 -#define itbd_SlowFreeze 7 -#define CDMG_TIMEBASED 8 - -// when calling KILLED(), a value that governs gib behavior is expected to be -// one of these three values -#define GIB_NORMAL 0// gib if entity was overkilled -#define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc ) -#define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite ) - -class CMBaseMonster; - -extern int GetMonsterIndex(void); - -// -// Converts a entvars_t * to a class pointer -// -template T * GetClassPtr( T *a ) -{ - entvars_t *pev = (entvars_t *)a; - - if (pev == NULL) - return NULL; - - // get the private data - a = (T *)pev->euser4; - - return a; -} - -// -// Converts a entvars_t * to a class pointer -// It will allocate the class and entity -// -template T * CreateClassPtr( T *a ) -{ - entvars_t *pev = (entvars_t *)a; - - if (pev != NULL) - return NULL; // don't allocate if pointer already provided - - // allocate entity... - edict_t *temp_edict; - int edict_index; - int monster_index; - - if ((monster_index = GetMonsterIndex()) == -1) - { - (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: No FREE Monster edicts in CreateClassPtr!\n" ); - return NULL; - } - - // allocate private data - a = new T; - - if ((temp_edict = a->CreateEntity("func_wall")) == NULL) - { - (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: NULL Ent in CreateClassPtr!\n" ); - delete a; - return NULL; - } - - edict_index = (*g_engfuncs.pfnIndexOfEdict)(temp_edict); - - // store the class pointer to the edict pev... - pev = VARS(temp_edict); - a->pev = pev; - - // store the class pointer in the array here!!! - monsters[monster_index].monster_index = edict_index; - monsters[monster_index].monster_pent = temp_edict; - monsters[monster_index].respawn_index = -1; - monsters[monster_index].pMonster = (CMBaseMonster *)a; - - // get the private data - a = (T *)pev->euser4; - - return a; -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +Class Hierachy + +CMBaseEntity + CMBaseDelay + CMBaseAnimating + CMBaseToggle + CMBaseMonster +*/ + +#include "monster_plugin.h" + +#define MAX_PATH_SIZE 10 // max number of nodes available for a path. + +// These are caps bits to indicate what an object's capabilities (currently used for save/restore and level transitions) +#define FCAP_CUSTOMSAVE 0x00000001 +#define FCAP_ACROSS_TRANSITION 0x00000002 // should transfer between transitions +#define FCAP_MUST_SPAWN 0x00000004 // Spawn after restore +#define FCAP_DONT_SAVE 0x80000000 // Don't save this +#define FCAP_IMPULSE_USE 0x00000008 // can be used by the player +#define FCAP_CONTINUOUS_USE 0x00000010 // can be used by the player +#define FCAP_ONOFF_USE 0x00000020 // can be used by the player +#define FCAP_DIRECTIONAL_USE 0x00000040 // Player sends +/- 1 when using (currently only tracktrains) +#define FCAP_MASTER 0x00000080 // Can be used to "master" other entities (like multisource) + +// UNDONE: This will ignore transition volumes (trigger_transition), but not the PVS!!! +#define FCAP_FORCE_TRANSITION 0x00000080 // ALWAYS goes across transitions + +#include "schedule.h" + +#ifndef MONSTEREVENT_H +#include "monsterevent.h" +#endif + +// C functions for external declarations that call the appropriate C++ methods + +#ifdef _WIN32 +#define EXPORT __declspec( dllexport ) +#else +#define EXPORT /* */ +#endif + +typedef enum { USE_OFF = 0, USE_ON = 1, USE_SET = 2, USE_TOGGLE = 3 } USE_TYPE; + +class CMBaseEntity; + +extern void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + +typedef void (CMBaseEntity::*BASEPTR)(void); +typedef void (CMBaseEntity::*ENTITYFUNCPTR)(CMBaseEntity *pOther ); +typedef void (CMBaseEntity::*USEPTR)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + +// For CLASSIFY +#define CLASS_NONE 0 +#define CLASS_MACHINE 1 +#define CLASS_PLAYER 2 +#define CLASS_HUMAN_PASSIVE 3 +#define CLASS_HUMAN_MILITARY 4 +#define CLASS_ALIEN_MILITARY 5 +#define CLASS_ALIEN_PASSIVE 6 +#define CLASS_ALIEN_MONSTER 7 +#define CLASS_ALIEN_PREY 8 +#define CLASS_ALIEN_PREDATOR 9 +#define CLASS_INSECT 10 +#define CLASS_PLAYER_ALLY 11 +#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players +#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace +#define CLASS_RACEX_PITDRONE 14 +#define CLASS_RACEX_SHOCK 15 +#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures. + +class CMBaseEntity; +class CMBaseMonster; + + +#define SF_NORESPAWN ( 1 << 30 )// !!!set this bit on guns and stuff that should never respawn. + +// +// EHANDLE. Safe way to point to edict_t who may die between frames +// +class EHANDLE +{ +private: + edict_t *m_pent; + int m_serialnumber; +public: + edict_t *Get( void ); + edict_t *Set( edict_t *pent ); + + operator edict_t *(); + + edict_t * operator = (edict_t *pEntity); + edict_t * operator ->(); +}; + + +template T * GetClassPtr( T *a ); + +// +// Base Entity. All entity types derive from this +// +class CMBaseEntity +{ +public: + // Constructor. Set engine to use C/C++ callback functions + // pointers to engine data + entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it + + // path corners + edict_t *m_pGoalEnt;// path corner we are heading towards + + edict_t *m_edictList[100]; + int m_edictList_count; + + void *operator new( size_t stAllocateBlock ); + + virtual edict_t *CreateEntity(char *classname); + + // initialization functions + virtual void Spawn( void ) { return; } + virtual void Precache( void ) { return; } + virtual void KeyValue( KeyValueData* pkvd) { pkvd->fHandled = FALSE; } + virtual int ObjectCaps( void ) { return FCAP_ACROSS_TRANSITION; } + virtual void Activate( void ) {} + + // Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box) + virtual void SetObjectCollisionBox( void ); + +// Classify - returns the type of group (i.e, "houndeye", or "human military" so that monsters with different classnames +// still realize that they are teammates. (overridden for monsters that form groups) + virtual int Classify ( void ) { return CLASS_NONE; }; + virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died. + + virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + virtual int TakeHealth( float flHealth, int bitsDamageType ); + virtual void Killed( entvars_t *pevAttacker, int iGib ); + virtual int BloodColor( void ) { return DONT_BLEED; } + virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + virtual BOOL IsTriggered( CMBaseEntity *pActivator ) {return TRUE;} + virtual CMBaseMonster *MyMonsterPointer( void ) { return NULL;} + virtual int GetToggleState( void ) { return TS_AT_TOP; } + virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} + virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} + virtual float GetDelay( void ) { return 0; } + virtual int IsMoving( void ) { return pev->velocity != g_vecZero; } + virtual void OverrideReset( void ) {} + virtual int DamageDecal( int bitsDamageType ); + // This is ONLY used by the node graph to test movement through a door + virtual void SetToggleState( int state ) {} + virtual void StartSneaking( void ) {} + virtual void StopSneaking( void ) {} + virtual BOOL OnControls( entvars_t *pev ) { return FALSE; } + virtual BOOL IsSneaking( void ) { return FALSE; } + virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; } + virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; } + virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); } + virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); } + virtual BOOL IsInWorld( void ); + virtual BOOL IsPlayer( void ) { return FALSE; } + virtual BOOL IsNetClient( void ) { return FALSE; } + virtual const char *TeamID( void ) { return ""; } + + +// virtual void SetActivator( CMBaseEntity *pActivator ) {} +//jlb virtual CMBaseEntity *GetNextTarget( void ); + + // fundamental callbacks + void (CMBaseEntity ::*m_pfnThink)(void); + void (CMBaseEntity ::*m_pfnTouch)( edict_t *pOther ); + void (CMBaseEntity ::*m_pfnUse)( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + void (CMBaseEntity ::*m_pfnBlocked)( edict_t *pOther ); + + virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); }; + virtual void Touch( edict_t *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); }; + virtual void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) + { + if (m_pfnUse) + (this->*m_pfnUse)( pActivator, pCaller, useType, value ); + } + virtual void Blocked( edict_t *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); }; + + void UpdateOnRemove( void ); + + // common member functions + void EXPORT SUB_Remove( void ); + void EXPORT SUB_DoNothing( void ); + void EXPORT SUB_StartFadeOut ( void ); + void EXPORT SUB_FadeOut ( void ); + void EXPORT SUB_CallUseToggle( void ) { this->Use( this->edict(), this->edict(), USE_TOGGLE, 0 ); } + int ShouldToggle( USE_TYPE useType, BOOL currentState ); + void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL ); + Vector FireBulletsPlayer( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL, int shared_rand = 0 ); + + virtual CMBaseEntity *Respawn( void ) { return NULL; } + + void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); + // Do the bounding boxes of these two intersect? + int Intersects( CMBaseEntity *pOther ); + void MakeDormant( void ); + int IsDormant( void ); + BOOL IsLockedByMaster( void ) { return FALSE; } + + static CMBaseEntity *Instance( edict_t *pent ) + { + if ( !pent ) + pent = ENT(0); + if ( pent->v.euser4 == NULL ) + return (CMBaseEntity *)NULL; + CMBaseEntity *pEnt = GetClassPtr((CMBaseEntity *)VARS(pent)); + return pEnt; + } + + static CMBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); } + static CMBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); } + +/*jlb + CMBaseMonster *GetMonsterPointer( entvars_t *pevMonster ) + { + CMBaseEntity *pEntity = Instance( pevMonster ); + if ( pEntity ) + return pEntity->MyMonsterPointer(); + return NULL; + } + CMBaseMonster *GetMonsterPointer( edict_t *pentMonster ) + { + CMBaseEntity *pEntity = Instance( pentMonster ); + if ( pEntity ) + return pEntity->MyMonsterPointer(); + return NULL; + } +jlb*/ + + // virtual functions used by a few classes + + // used by monsters that are created by the MonsterMaker + virtual void UpdateOwner( void ) { return; }; + + + // +//jlb static CMBaseEntity *Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL ); + + virtual BOOL FBecomeProne( void ) {return FALSE;}; + edict_t *edict() { return ENT( pev ); }; + EOFFSET eoffset( ) { return OFFSET( pev ); }; + int entindex( ) { return ENTINDEX( edict() ); }; + + virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity + virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes + virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears + virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at + + virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); }; + +// virtual BOOL FVisible ( edict_t *pEntity ); +// virtual BOOL FVisible ( const Vector &vecOrigin ); + + //We use this variables to store each ammo count. + int ammo_9mm; + int ammo_357; + int ammo_bolts; + int ammo_buckshot; + int ammo_rockets; + int ammo_uranium; + int ammo_hornets; + int ammo_argrens; + //Special stuff for grenades and satchels. + float m_flStartThrow; + float m_flReleaseThrow; + int m_chargeReady; + int m_fInAttack; + + enum EGON_FIRESTATE { FIRE_OFF, FIRE_CHARGE }; + int m_fireState; +}; + + + +// Ugly technique to override base member functions +// Normally it's illegal to cast a pointer to a member function of a derived class to a pointer to a +// member function of a base class. static_cast is a sleezy way around that problem. + +#define SetThink( a ) m_pfnThink = static_cast (a) +#define SetTouch( a ) m_pfnTouch = static_cast (a) +#define SetUse( a ) m_pfnUse = static_cast (a) +#define SetBlocked( a ) m_pfnBlocked = static_cast (a) + + +class CMPointEntity : public CMBaseEntity +{ +public: + void Spawn( void ); + virtual int ObjectCaps( void ) { return CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } +private: +}; + + +typedef struct locksounds // sounds that doors and buttons make when locked/unlocked +{ + string_t sLockedSound; // sound a door makes when it's locked + string_t sLockedSentence; // sentence group played when door is locked + string_t sUnlockedSound; // sound a door makes when it's unlocked + string_t sUnlockedSentence; // sentence group played when door is unlocked + + int iLockedSentence; // which sentence in sentence group to play next + int iUnlockedSentence; // which sentence in sentence group to play next + + float flwaitSound; // time delay between playing consecutive 'locked/unlocked' sounds + float flwaitSentence; // time delay between playing consecutive sentences + BYTE bEOFLocked; // true if hit end of list of locked sentences + BYTE bEOFUnlocked; // true if hit end of list of unlocked sentences +} locksound_t; + +void PlayLockSounds(entvars_t *pev, locksound_t *pls, int flocked, int fbutton); + + +// +// generic Delay entity. +// +class CMBaseDelay : public CMBaseEntity +{ +public: + float m_flDelay; + int m_iszKillTarget; + + virtual void KeyValue( KeyValueData* pkvd); + + // common member functions + void SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ); + void EXPORT DelayThink( void ); +}; + + +class CMBaseAnimating : public CMBaseDelay +{ +public: + + // Basic Monster Animation functions + float StudioFrameAdvance( float flInterval = 0.0 ); // accumulate animation frame time from last time called until now + int GetSequenceFlags( void ); + int LookupActivity ( int activity ); + int LookupActivityHeaviest ( int activity ); + int LookupSequence ( const char *label ); + void ResetSequenceInfo ( ); + void DispatchAnimEvents ( float flFutureInterval = 0.1 ); // Handle events that have happend since last time called up until X seconds into the future + virtual void HandleAnimEvent( MonsterEvent_t *pEvent ) { return; }; + float SetBoneController ( int iController, float flValue ); + void InitBoneControllers ( void ); + float SetBlending ( int iBlender, float flValue ); + void GetBonePosition ( int iBone, Vector &origin, Vector &angles ); + void GetAutomovement( Vector &origin, Vector &angles, float flInterval = 0.1 ); + int FindTransition( int iEndingSequence, int iGoalSequence, int *piDir ); + void GetAttachment ( int iAttachment, Vector &origin, Vector &angles ); + void SetBodygroup( int iGroup, int iValue ); + int GetBodygroup( int iGroup ); + int ExtractBbox( int sequence, float *mins, float *maxs ); + void SetSequenceBox( void ); + + // animation needs + float m_flFrameRate; // computed FPS for current sequence + float m_flGroundSpeed; // computed linear movement rate for current sequence + float m_flLastEventCheck; // last time the event list was checked + BOOL m_fSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry + BOOL m_fSequenceLoops; // true if the sequence loops +}; + + +// +// generic Toggle entity. +// +#define SF_ITEM_USE_ONLY 256 // ITEM_USE_ONLY = BUTTON_USE_ONLY = DOOR_USE_ONLY!!! + +class CMBaseToggle : public CMBaseAnimating +{ +public: + void KeyValue( KeyValueData *pkvd ); + + TOGGLE_STATE m_toggle_state; + float m_flActivateFinished;//like attack_finished, but for doors + float m_flMoveDistance;// how far a door should slide or rotate + float m_flWait; + float m_flLip; + float m_flTWidth;// for plats + float m_flTLength;// for plats + + Vector m_vecPosition1; + Vector m_vecPosition2; + Vector m_vecAngle1; + Vector m_vecAngle2; + + int m_cTriggersLeft; // trigger_counter only, # of activations remaining + float m_flHeight; +//jlb EHANDLE m_hActivator; + void (CMBaseToggle::*m_pfnCallWhenMoveDone)(void); + Vector m_vecFinalDest; + Vector m_vecFinalAngle; + + int m_bitsDamageInflict; // DMG_ damage type that the door or tigger does + + virtual int GetToggleState( void ) { return m_toggle_state; } + virtual float GetDelay( void ) { return m_flWait; } + + // common member functions + void LinearMove( Vector vecDest, float flSpeed ); + void EXPORT LinearMoveDone( void ); + void AngularMove( Vector vecDestAngle, float flSpeed ); + void EXPORT AngularMoveDone( void ); + BOOL IsLockedByMaster( void ); + + static float AxisValue( int flags, const Vector &angles ); + static void AxisDir( entvars_t *pev ); + static float AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ); + + string_t m_sMaster; // If this button has a master switch, this is the targetname. + // A master switch must be of the multisource type. If all + // of the switches in the multisource have been triggered, then + // the button will be allowed to operate. Otherwise, it will be + // deactivated. +}; +#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast (a) + + +// people gib if their health is <= this at the time of death +#define GIB_HEALTH_VALUE -30 + +#define ROUTE_SIZE 8 // how many waypoints a monster can store at one time +#define MAX_OLD_ENEMIES 4 // how many old enemies to remember + +#define bits_CAP_DUCK ( 1 << 0 )// crouch +#define bits_CAP_JUMP ( 1 << 1 )// jump/leap +#define bits_CAP_STRAFE ( 1 << 2 )// strafe ( walk/run sideways) +//??? #define bits_CAP_SQUAD ( 1 << 3 )// can form squads +#define bits_CAP_SWIM ( 1 << 4 )// proficiently navigate in water +#define bits_CAP_CLIMB ( 1 << 5 )// climb ladders/ropes +#define bits_CAP_USE ( 1 << 6 )// open doors/push buttons/pull levers +#define bits_CAP_HEAR ( 1 << 7 )// can hear forced sounds +#define bits_CAP_AUTO_DOORS ( 1 << 8 )// can trigger auto doors +#define bits_CAP_OPEN_DOORS ( 1 << 9 )// can open manual doors +#define bits_CAP_TURN_HEAD ( 1 << 10)// can turn head, always bone controller 0 + +#define bits_CAP_RANGE_ATTACK1 ( 1 << 11)// can do a range attack 1 +#define bits_CAP_RANGE_ATTACK2 ( 1 << 12)// can do a range attack 2 +#define bits_CAP_MELEE_ATTACK1 ( 1 << 13)// can do a melee attack 1 +#define bits_CAP_MELEE_ATTACK2 ( 1 << 14)// can do a melee attack 2 + +#define bits_CAP_FLY ( 1 << 15)// can fly, move all around + +#define bits_CAP_DOORS_GROUP (bits_CAP_USE | bits_CAP_AUTO_DOORS | bits_CAP_OPEN_DOORS) + +// used by suit voice to indicate damage sustained and repaired type to player + +// instant damage + +#define DMG_GENERIC 0 // generic damage was done +#define DMG_CRUSH (1 << 0) // crushed by falling or moving object +#define DMG_BULLET (1 << 1) // shot +#define DMG_SLASH (1 << 2) // cut, clawed, stabbed +#define DMG_BURN (1 << 3) // heat burned +#define DMG_FREEZE (1 << 4) // frozen +#define DMG_FALL (1 << 5) // fell too far +#define DMG_BLAST (1 << 6) // explosive blast damage +#define DMG_CLUB (1 << 7) // crowbar, punch, headbutt +#define DMG_SHOCK (1 << 8) // electric shock +#define DMG_SONIC (1 << 9) // sound pulse shockwave +#define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam +#define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death +#define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death. +#define DMG_DROWN (1 << 14) // Drowning +// time-based damage +#define DMG_TIMEBASED (~(0x3fff)) // mask for time-based damage + +#define DMG_PARALYZE (1 << 15) // slows affected creature down +#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad +#define DMG_POISON (1 << 17) // blood poisioning +#define DMG_RADIATION (1 << 18) // radiation exposure +#define DMG_DROWNRECOVER (1 << 19) // drowning recovery +#define DMG_ACID (1 << 20) // toxic chemicals or acid burns +#define DMG_SLOWBURN (1 << 21) // in an oven +#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer +#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar) + +// these are the damage types that are allowed to gib corpses +#define DMG_GIB_CORPSE ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) + +// these are the damage types that have client hud art +#define DMG_SHOWNHUD (DMG_POISON | DMG_ACID | DMG_FREEZE | DMG_SLOWFREEZE | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK) + +// NOTE: tweak these values based on gameplay feedback: + +#define PARALYZE_DURATION 2 // number of 2 second intervals to take damage +#define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval + +#define NERVEGAS_DURATION 2 +#define NERVEGAS_DAMAGE 5.0 + +#define POISON_DURATION 5 +#define POISON_DAMAGE 2.0 + +#define RADIATION_DURATION 2 +#define RADIATION_DAMAGE 1.0 + +#define ACID_DURATION 2 +#define ACID_DAMAGE 5.0 + +#define SLOWBURN_DURATION 2 +#define SLOWBURN_DAMAGE 1.0 + +#define SLOWFREEZE_DURATION 2 +#define SLOWFREEZE_DAMAGE 1.0 + + +#define itbd_Paralyze 0 +#define itbd_NerveGas 1 +#define itbd_Poison 2 +#define itbd_Radiation 3 +#define itbd_DrownRecover 4 +#define itbd_Acid 5 +#define itbd_SlowBurn 6 +#define itbd_SlowFreeze 7 +#define CDMG_TIMEBASED 8 + +// when calling KILLED(), a value that governs gib behavior is expected to be +// one of these three values +#define GIB_NORMAL 0// gib if entity was overkilled +#define GIB_NEVER 1// never gib, no matter how much death damage is done ( freezing, etc ) +#define GIB_ALWAYS 2// always gib ( Houndeye Shock, Barnacle Bite ) + +class CMBaseMonster; + +extern int GetMonsterIndex(void); + +// +// Converts a entvars_t * to a class pointer +// +template T * GetClassPtr( T *a ) +{ + entvars_t *pev = (entvars_t *)a; + + if (pev == NULL) + return NULL; + + // get the private data + a = (T *)pev->euser4; + + return a; +} + +// +// Converts a entvars_t * to a class pointer +// It will allocate the class and entity +// +template T * CreateClassPtr( T *a ) +{ + entvars_t *pev = (entvars_t *)a; + + if (pev != NULL) + return NULL; // don't allocate if pointer already provided + + // allocate entity... + edict_t *temp_edict; + int edict_index; + int monster_index; + + if ((monster_index = GetMonsterIndex()) == -1) + { + (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: No FREE Monster edicts in CreateClassPtr!\n" ); + return NULL; + } + + // allocate private data + a = new T; + + if ((temp_edict = a->CreateEntity("func_wall")) == NULL) + { + (*g_engfuncs.pfnServerPrint)("[MONSTER] ERROR: NULL Ent in CreateClassPtr!\n" ); + delete a; + return NULL; + } + + edict_index = (*g_engfuncs.pfnIndexOfEdict)(temp_edict); + + // store the class pointer to the edict pev... + pev = VARS(temp_edict); + a->pev = pev; + + // store the class pointer in the array here!!! + monsters[monster_index].monster_index = edict_index; + monsters[monster_index].monster_pent = temp_edict; + monsters[monster_index].respawn_index = -1; + monsters[monster_index].pMonster = (CMBaseMonster *)a; + + // get the private data + a = (T *)pev->euser4; + + return a; +} diff --git a/src/dlls/cmbasemonster.h b/src/dlls/cmbasemonster.h index 898745e..66baebb 100644 --- a/src/dlls/cmbasemonster.h +++ b/src/dlls/cmbasemonster.h @@ -1,1642 +1,1642 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ - -#ifndef BASEMONSTER_H -#define BASEMONSTER_H - -#ifndef SCHEDULE_H -#include "schedule.h" -#endif - -#ifndef SKILL_H -#include "skill.h" -#endif - -// -// generic Monster -// -class CMBaseMonster : public CMBaseToggle -{ -private: - int m_afConditions; - -public: - - // these fields have been added in the process of reworking the state machine. (sjb) - EHANDLE m_hEnemy; // the entity that the monster is fighting. - EHANDLE m_hTargetEnt; // the entity that the monster is trying to reach - EHANDLE m_hOldEnemy[ MAX_OLD_ENEMIES ]; - Vector m_vecOldEnemy[ MAX_OLD_ENEMIES ]; - - float m_flFieldOfView;// width of monster's field of view ( dot product ) - float m_flWaitFinished;// if we're told to wait, this is the time that the wait will be over. - float m_flMoveWaitFinished; - - Activity m_Activity;// what the monster is doing (animation) - Activity m_IdealActivity;// monster should switch to this activity - - int m_LastHitGroup; // the last body region that took damage - - MONSTERSTATE m_MonsterState;// monster's current state - MONSTERSTATE m_IdealMonsterState;// monster should change to this state - - int m_iTaskStatus; - Schedule_t *m_pSchedule; - int m_iScheduleIndex; - - WayPoint_t m_Route[ ROUTE_SIZE ]; // Positions of movement - int m_movementGoal; // Goal that defines route - int m_iRouteIndex; // index into m_Route[] - float m_moveWaitTime; // How long I should wait for something to move - - Vector m_vecMoveGoal; // kept around for node graph moves, so we know our ultimate goal - Activity m_movementActivity; // When moving, set this activity - - int m_iAudibleList; // first index of a linked list of sounds that the monster can hear. - int m_afSoundTypes; - - Vector m_vecLastPosition;// monster sometimes wants to return to where it started after an operation. - - int m_iHintNode; // this is the hint node that the monster is moving towards or performing active idle on. - - int m_afMemory; - - int m_iMaxHealth;// keeps track of monster's maximum health value (for re-healing, etc) - - Vector m_vecEnemyLKP;// last known position of enemy. (enemy's origin) - - int m_cAmmoLoaded; // how much ammo is in the weapon (used to trigger reload anim sequences) - - int m_afCapability;// tells us what a monster can/can't do. - - float m_flNextAttack; // cannot attack again until this time - - int m_bitsDamageType; // what types of damage has monster (player) taken - BYTE m_rgbTimeBasedDamage[CDMG_TIMEBASED]; - - int m_lastDamageAmount;// how much damage did monster (player) last take - // time based damage counters, decr. 1 per 2 seconds - int m_bloodColor; // color of blood particless - - int m_failSchedule; // Schedule type to choose if current schedule fails - - float m_flHungryTime;// set this is a future time to stop the monster from eating for a while. - - float m_flDistTooFar; // if enemy farther away than this, bits_COND_ENEMY_TOOFAR set in CheckEnemy - float m_flDistLook; // distance monster sees (Default 2048) - - int m_iTriggerCondition;// for scripted AI, this is the condition that will cause the activation of the monster's TriggerTarget - string_t m_iszTriggerTarget;// name of target that should be fired. - - Vector m_HackedGunPos; // HACK until we can query end of gun - - string_t m_szMonsterName; // Monster name to display on HUD - int m_iClassifyOverride; // Overriden classification for this monster - - void KeyValue( KeyValueData *pkvd ); - -// monster use function - void EXPORT MonsterUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - void EXPORT CorpseUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - -// overrideable Monster member functions - - virtual int BloodColor( void ) { return m_bloodColor; } - - virtual CMBaseMonster *MyMonsterPointer( void ) { return this; } - virtual void Look ( int iDistance );// basic sight function for monsters - virtual void RunAI ( void );// core ai function! - void Listen ( void ); - - virtual BOOL IsAlive( void ) { return (pev->deadflag != DEAD_DEAD); } - virtual BOOL ShouldFadeOnDeath( void ); - -// Basic Monster AI functions - virtual float ChangeYaw ( int speed ); - float VecToYaw( Vector vecDir ); - float FlYawDiff ( void ); - - float DamageForce( float damage ); - -// stuff written for new state machine - virtual void MonsterThink( void ); - void EXPORT CallMonsterThink( void ) { this->MonsterThink(); } - virtual int IRelationship ( CMBaseEntity *pTarget ); - virtual void MonsterInit ( void ); - virtual void MonsterInitDead( void ); // Call after animation/pose is set up - virtual void BecomeDead( void ); - void EXPORT CorpseFallThink( void ); - - void EXPORT MonsterInitThink ( void ); - virtual void StartMonster ( void ); - virtual edict_t* BestVisibleEnemy ( void );// finds best visible enemy for attack - virtual void HandleAnimEvent( MonsterEvent_t *pEvent ); - - virtual int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist );// check validity of a straight move through space - virtual void Move( float flInterval = 0.1 ); - virtual void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); - virtual BOOL ShouldAdvanceRoute( float flWaypointDist ); - - virtual Activity GetStoppedActivity( void ) { return ACT_IDLE; } - virtual void Stop( void ) { m_IdealActivity = GetStoppedActivity(); } - - // This will stop animation until you call ResetSequenceInfo() at some point in the future - inline void StopAnimation( void ) { pev->framerate = 0; } - - // these functions will survey conditions and set appropriate conditions bits for attack types. - virtual BOOL CheckRangeAttack1( float flDot, float flDist ); - virtual BOOL CheckRangeAttack2( float flDot, float flDist ); - virtual BOOL CheckMeleeAttack1( float flDot, float flDist ); - virtual BOOL CheckMeleeAttack2( float flDot, float flDist ); - - BOOL FHaveSchedule( void ); - BOOL FScheduleValid ( void ); - void ClearSchedule( void ); - BOOL FScheduleDone ( void ); - void ChangeSchedule ( Schedule_t *pNewSchedule ); - void NextScheduledTask ( void ); - Schedule_t *ScheduleInList( const char *pName, Schedule_t **pList, int listCount ); - - virtual Schedule_t *ScheduleFromName( const char *pName ); - static Schedule_t *m_scheduleList[]; - - void MaintainSchedule ( void ); - virtual void StartTask ( Task_t *pTask ); - virtual void RunTask ( Task_t *pTask ); - virtual Schedule_t *GetScheduleOfType( int Type ); - virtual Schedule_t *GetSchedule( void ); - virtual void ScheduleChange( void ) {} - virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); } - virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); - virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CMBaseEntity *pListener ); - - virtual void SentenceStop( void ); - - Task_t *GetTask ( void ); - virtual MONSTERSTATE GetIdealState ( void ); - virtual void SetActivity ( Activity NewActivity ); - void SetSequenceByName ( char *szSequence ); - void SetState ( MONSTERSTATE State ); - virtual void ReportAIState( void ); - - void CheckAttacks ( edict_t *pTarget, float flDist ); - virtual int CheckEnemy ( edict_t *pEnemy ); - void PushEnemy( edict_t *pEnemy, Vector &vecLastKnownPos ); - BOOL PopEnemy( void ); - - BOOL FGetNodeRoute ( Vector vecDest ); - - inline void TaskComplete( void ) { if ( !HasConditions(bits_COND_TASK_FAILED) ) m_iTaskStatus = TASKSTATUS_COMPLETE; } - void MovementComplete( void ); - void TaskFail( void ); - inline void TaskBegin( void ) { m_iTaskStatus = TASKSTATUS_RUNNING; } - int TaskIsRunning( void ); - inline int TaskIsComplete( void ) { return (m_iTaskStatus == TASKSTATUS_COMPLETE); } - inline int MovementIsComplete( void ) { return (m_movementGoal == MOVEGOAL_NONE); } - - int IScheduleFlags ( void ); - BOOL FRefreshRoute( void ); - BOOL FRouteClear ( void ); - void RouteSimplify( edict_t *pTargetEnt ); - void AdvanceRoute ( float distance ); - virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ); - void MakeIdealYaw( Vector vecTarget ); - virtual void SetYawSpeed ( void ) { return; }; // allows different yaw_speeds for each activity - BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget ); - virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ); - int RouteClassify( int iMoveFlag ); - void InsertWaypoint ( Vector vecLocation, int afMoveFlags ); - - BOOL FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset ); - virtual BOOL FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ); - virtual BOOL FValidateCover ( const Vector &vecCoverLocation ) { return TRUE; }; - virtual float CoverRadius( void ) { return 784; } // Default cover radius - - virtual BOOL FCanCheckAttacks ( void ); - virtual void CheckAmmo( void ) { return; }; - virtual int IgnoreConditions ( void ); - - inline void SetConditions( int iConditions ) { m_afConditions |= iConditions; } - inline void ClearConditions( int iConditions ) { m_afConditions &= ~iConditions; } - inline BOOL HasConditions( int iConditions ) { if ( m_afConditions & iConditions ) return TRUE; return FALSE; } - inline BOOL HasAllConditions( int iConditions ) { if ( (m_afConditions & iConditions) == iConditions ) return TRUE; return FALSE; } - - virtual BOOL FValidateHintType( short sHint ); - int FindHintNode ( void ); - virtual BOOL FCanActiveIdle ( void ); - void SetTurnActivity ( void ); - - BOOL MoveToNode( Activity movementAct, float waitTime, const Vector &goal ); - BOOL MoveToTarget( Activity movementAct, float waitTime ); - BOOL MoveToLocation( Activity movementAct, float waitTime, const Vector &goal ); - BOOL MoveToEnemy( Activity movementAct, float waitTime ); - - BOOL FBecomeProne ( void ); - virtual void BarnacleVictimBitten( entvars_t *pevBarnacle ); - virtual void BarnacleVictimReleased( void ); - - void SetEyePosition ( void ); - - BOOL FShouldEat( void );// see if a monster is 'hungry' - void Eat ( float flFullDuration );// make the monster 'full' for a while. - - edict_t *CheckTraceHullAttack( float flDist, int iDamage, int iDmgType ); - BOOL FacingIdeal( void ); - - BOOL FCheckAITrigger( void );// checks and, if necessary, fires the monster's trigger target. - - BOOL BBoxFlat( void ); - - // PrescheduleThink - virtual void PrescheduleThink( void ) { return; }; - - BOOL GetEnemy ( void ); - void MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - - // combat functions - float UpdateTarget ( entvars_t *pevTarget ); - virtual Activity GetDeathActivity ( void ); - Activity GetSmallFlinchActivity( void ); - virtual void Killed( entvars_t *pevAttacker, int iGib ); - virtual void GibMonster( void ); - BOOL ShouldGibMonster( int iGib ); - void CallGibMonster( void ); - virtual BOOL HasHumanGibs( void ); - virtual BOOL HasAlienGibs( void ); - virtual void FadeMonster( void ); // Called instead of GibMonster() when gibs are disabled - - Vector ShootAtEnemy( const Vector &shootOrigin ); - virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) * 0.75 + EyePosition() * 0.25; }; // position to shoot at - - virtual Vector GetGunPosition( void ); - - virtual int TakeHealth( float flHealth, int bitsDamageType ); - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - int DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - void RadiusDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); - void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); - virtual int IsMoving( void ) { return m_movementGoal != MOVEGOAL_NONE; } - - void RouteClear( void ); - void RouteNew( void ); - - virtual void DeathSound ( void ) { return; }; - virtual void AlertSound ( void ) { return; }; - virtual void IdleSound ( void ) { return; }; - virtual void PainSound ( void ) { return; }; - - virtual void StopFollowing( BOOL clearSchedule ) {} - - inline void Remember( int iMemory ) { m_afMemory |= iMemory; } - inline void Forget( int iMemory ) { m_afMemory &= ~iMemory; } - inline BOOL HasMemory( int iMemory ) { if ( m_afMemory & iMemory ) return TRUE; return FALSE; } - inline BOOL HasAllMemories( int iMemory ) { if ( (m_afMemory & iMemory) == iMemory ) return TRUE; return FALSE; } -}; - -// -// monster_plugin monsters -// - -#ifndef TALKMONSTER_H -#include "cmtalkmonster.h" -#endif - -#ifndef FLYINGMONSTER_H -#include "cmflyingmonster.h" -#endif - -#ifndef WEAPONS_H -#include "weapons.h" -#endif - -class CMHeadCrab : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void RunTask ( Task_t *pTask ); - void StartTask ( Task_t *pTask ); - void SetYawSpeed ( void ); - void EXPORT LeapTouch ( edict_t *pOther ); - Vector Center( void ); - Vector BodyTarget( const Vector &posSrc ); - void PainSound( void ); - void DeathSound( void ); - void IdleSound( void ); - void AlertSound( void ); - void PrescheduleThink( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; } - virtual int GetVoicePitch( void ) { return 100; } - virtual float GetSoundVolume( void ) { return 1.0; } - Schedule_t* GetScheduleOfType ( int Type ); - - CUSTOM_SCHEDULES; - - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackSounds[]; - static const char *pDeathSounds[]; - static const char *pBiteSounds[]; -}; - -class CMBabyCrab : public CMHeadCrab -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite * 0.3; } - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - Schedule_t* GetScheduleOfType ( int Type ); - virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); } - virtual float GetSoundVolume( void ) { return 0.8; } -}; - - -class CMZombie : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - int IgnoreConditions ( void ); - - float m_flNextFlinch; - - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - void AttackSound( void ); - - static const char *pAttackSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - - // No range attacks - BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } - BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); -}; - - -class CMSqueakGrenade : public CMGrenade -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify( void ); - void EXPORT SuperBounceTouch( edict_t *pOther ); - void EXPORT HuntThink( void ); - int BloodColor( void ) { return BLOOD_COLOR_YELLOW; } - void Killed( entvars_t *pevAttacker, int iGib ); - void GibMonster( void ); - - static float m_flNextBounceSoundTime; - - // CMBaseEntity *m_pTarget; - float m_flDie; - Vector m_vecTarget; - float m_flNextHunt; - float m_flNextHit; - Vector m_posPrev; -}; - - -class CMBullsquid : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void IdleSound( void ); - void PainSound( void ); - void DeathSound( void ); - void AlertSound ( void ); - void AttackSound( void ); - void StartTask ( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); - BOOL CheckMeleeAttack2 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - void RunAI( void ); - BOOL FValidateHintType ( short sHint ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType ( int Type ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - int IRelationship ( CMBaseEntity *pTarget ); - int IgnoreConditions ( void ); - MONSTERSTATE GetIdealState ( void ); - - CUSTOM_SCHEDULES; - - float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while. - float m_flNextSpitTime;// last time the bullsquid used the spit attack. -}; - - -class CMHoundeye : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void SetYawSpeed ( void ); - void WarmUpSound ( void ); - void AlertSound( void ); - void DeathSound( void ); - void WarnSound( void ); - void PainSound( void ); - void IdleSound( void ); - void StartTask( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - void SonicAttack( void ); - void PrescheduleThink( void ); - void SetActivity ( Activity NewActivity ); - void WriteBeamColor ( void ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL FValidateHintType ( short sHint ); - BOOL FCanActiveIdle ( void ); - Schedule_t *GetScheduleOfType ( int Type ); - Schedule_t *GetSchedule( void ); - - CUSTOM_SCHEDULES; - - int m_iSpriteTexture; - BOOL m_fAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down - BOOL m_fDontBlink;// don't try to open/close eye if this bit is set! - Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members. -}; - - -class CMHGrunt : public CMTalkMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL FCanCheckAttacks ( void ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - void CheckAmmo ( void ); - void SetActivity ( Activity NewActivity ); - void StartTask ( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - void DeathSound( void ); - void PainSound( void ); - void IdleSound ( void ); - Vector GetGunPosition( void ); - void Shoot ( void ); - void Shotgun ( void ); - void PrescheduleThink ( void ); - void GibMonster( void ); - void SpeakSentence( void ); - - edict_t *Kick( void ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType ( int Type ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - int IRelationship ( CMBaseEntity *pTarget ); - - BOOL FOkToSpeak( void ); - void JustSpoke( void ); - - CUSTOM_SCHEDULES; - - // checking the feasibility of a grenade toss is kind of costly, so we do it every couple of seconds, - // not every server frame. - float m_flNextGrenadeCheck; - float m_flNextPainTime; - float m_flLastEnemySightTime; - - Vector m_vecTossVelocity; - - BOOL m_fThrowGrenade; - BOOL m_fStanding; - BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter. - int m_cClipSize; - - int m_voicePitch; - - int m_iBrassShell; - int m_iShotgunShell; - - int m_iSentence; - - static const char *pGruntSentences[]; -}; - - -class CMHAssassin : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void); - void Shoot( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - Schedule_t* GetSchedule ( void ); - Schedule_t* GetScheduleOfType ( int Type ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // jump - // BOOL CheckMeleeAttack2 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); // shoot - BOOL CheckRangeAttack2 ( float flDot, float flDist ); // throw grenade - void StartTask ( Task_t *pTask ); - void RunAI( void ); - void RunTask ( Task_t *pTask ); - void DeathSound ( void ); - void IdleSound ( void ); - CUSTOM_SCHEDULES; - - float m_flLastShot; - float m_flDiviation; - - float m_flNextJump; - Vector m_vecJumpVelocity; - - float m_flNextGrenadeCheck; - Vector m_vecTossVelocity; - BOOL m_fThrowGrenade; - - int m_iTargetRanderamt; - - int m_iFrustration; - - int m_iShell; -}; - - -#define ISLAVE_MAX_BEAMS 8 - -class CMISlave : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int ISoundMask( void ); - int Classify ( void ); - int IRelationship( CMBaseEntity *pTarget ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack2 ( float flDot, float flDist ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - - void DeathSound( void ); - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - - void Killed( entvars_t *pevAttacker, int iGib ); - - void StartTask ( Task_t *pTask ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType ( int Type ); - CUSTOM_SCHEDULES; - - void ClearBeams( ); - void ArmBeam( int side ); - void WackBeam( int side, edict_t *pEntity ); - void ZapBeam( int side ); - void BeamGlow( void ); - - int m_iBravery; - - CMBeam *m_pBeam[ISLAVE_MAX_BEAMS]; - - int m_iBeams; - float m_flNextAttack; - - int m_voicePitch; - - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - static const char *pPainSounds[]; - static const char *pDeathSounds[]; -}; - - -class CMBarney : public CMTalkMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int ISoundMask( void ); - void BarneyFirePistol( void ); - void AlertSound( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - void RunTask( Task_t *pTask ); - void StartTask( Task_t *pTask ); - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - - // Override these to set behavior - Schedule_t *GetScheduleOfType ( int Type ); - Schedule_t *GetSchedule ( void ); - MONSTERSTATE GetIdealState ( void ); - - void DeathSound( void ); - void PainSound( void ); - - void TalkInit( void ); - - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - void Killed( entvars_t *pevAttacker, int iGib ); - - BOOL m_fGunDrawn; - float m_painTime; - float m_checkAttackTime; - BOOL m_lastAttackCheck; - - CUSTOM_SCHEDULES; -}; - - -class CMAGrunt : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed ( void ); - int Classify ( void ); - int ISoundMask ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -32, -32, 0 ); - pev->absmax = pev->origin + Vector( 32, 32, 85 ); - } - - Schedule_t* GetSchedule ( void ); - Schedule_t* GetScheduleOfType ( int Type ); - BOOL FCanCheckAttacks ( void ); - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); - void StartTask ( Task_t *pTask ); - void AlertSound( void ); - void DeathSound ( void ); - void PainSound ( void ); - void AttackSound ( void ); - void PrescheduleThink ( void ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - int IRelationship( CMBaseEntity *pTarget ); - void StopTalking ( void ); - BOOL ShouldSpeak( void ); - CUSTOM_SCHEDULES; - - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - static const char *pAttackSounds[]; - static const char *pDieSounds[]; - static const char *pPainSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - - BOOL m_fCanHornetAttack; - float m_flNextHornetAttackCheck; - - float m_flNextPainTime; - - // three hacky fields for speech stuff. These don't really need to be saved. - float m_flNextSpeakTime; - float m_flNextWordTime; - int m_iLastWord; -}; - - -class CMScientist : public CMTalkMonster -{ -public: - void Spawn( void ); - void Precache( void ); - - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void RunTask( Task_t *pTask ); - void StartTask( Task_t *pTask ); - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - virtual int FriendNumber( int arrayNumber ); - void SetActivity ( Activity newActivity ); - Activity GetStoppedActivity( void ); - int ISoundMask( void ); - void DeclineFollowing( void ); - - float CoverRadius( void ) { return 1200; } // Need more room for cover because scientists want to get far away! - BOOL DisregardEnemy( edict_t *pEnemy ); - - BOOL CanHeal( void ); - void Heal( void ); - void Scream( void ); - - // Override these to set behavior - Schedule_t *GetScheduleOfType ( int Type ); - Schedule_t *GetSchedule ( void ); - MONSTERSTATE GetIdealState ( void ); - - void DeathSound( void ); - void PainSound( void ); - - void TalkInit( void ); - - void Killed( entvars_t *pevAttacker, int iGib ); - - CUSTOM_SCHEDULES; - -private: - float m_painTime; - float m_healTime; - float m_fearTime; -}; - - -class CMApache : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify( void ); - int BloodColor( void ) { return DONT_BLEED; } - void Killed( entvars_t *pevAttacker, int iGib ); - void GibMonster( void ); - - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -300, -300, -172); - pev->absmax = pev->origin + Vector(300, 300, 8); - } - - void EXPORT HuntThink( void ); - void EXPORT FlyTouch( edict_t *pOther ); - void EXPORT CrashTouch( edict_t *pOther ); - void EXPORT DyingThink( void ); - void EXPORT StartupUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - void EXPORT NullThink( void ); - - void ShowDamage( void ); - void Flight( void ); - void FireRocket( void ); - BOOL FireGun( void ); - - int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - - int m_iRockets; - float m_flForce; - float m_flNextRocket; - - Vector m_vecTarget; - Vector m_posTarget; - - Vector m_vecDesired; - Vector m_posDesired; - - Vector m_vecGoal; - - Vector m_angGun; - float m_flLastSeen; - float m_flPrevSeen; - - int m_iSoundState; // don't save this - - int m_iSpriteTexture; - int m_iExplode; - int m_iBodyGibs; - - float m_flGoalSpeed; - - int m_iDoSmokePuff; - CMBeam *m_pBeam; -}; - - -class CMApacheHVR : public CMGrenade -{ -public: - void Spawn( void ); - void Precache( void ); - void EXPORT IgniteThink( void ); - void EXPORT AccelerateThink( void ); - - int m_iTrail; - Vector m_vecForward; -}; - - -class CMController : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - void RunAI( void ); - BOOL CheckRangeAttack1 ( float flDot, float flDist ); // balls - BOOL CheckRangeAttack2 ( float flDot, float flDist ); // head - BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // block, throw - Schedule_t* GetSchedule ( void ); - Schedule_t* GetScheduleOfType ( int Type ); - void StartTask ( Task_t *pTask ); - void RunTask ( Task_t *pTask ); - CUSTOM_SCHEDULES; - - void Stop( void ); - void Move ( float flInterval ); - int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ); - void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); - void SetActivity ( Activity NewActivity ); - BOOL ShouldAdvanceRoute( float flWaypointDist ); - int LookupFloat( ); - - float m_flNextFlinch; - - float m_flShootTime; - float m_flShootEnd; - - void PainSound( void ); - void AlertSound( void ); - void IdleSound( void ); - void AttackSound( void ); - void DeathSound( void ); - - static const char *pAttackSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pDeathSounds[]; - - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - void Killed( entvars_t *pevAttacker, int iGib ); - void GibMonster( void ); - - CMSprite *m_pBall[2]; // hand balls - int m_iBall[2]; // how bright it should be - float m_iBallTime[2]; // when it should be that color - int m_iBallCurrent[2]; // current brightness - - Vector m_vecEstVelocity; - - Vector m_velocity; - int m_fInCombat; -}; - -//========================================================= -// Controller bouncy ball attack -//========================================================= -class CMControllerHeadBall : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void EXPORT HuntThink( void ); - void EXPORT DieThink( void ); - void EXPORT BounceTouch( edict_t *pOther ); - void MovetoTarget( Vector vecTarget ); - void Crawl( void ); - int m_iTrail; - int m_flNextAttack; - Vector m_vecIdeal; - EHANDLE m_hOwner; -}; - -class CMControllerZapBall : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void EXPORT AnimateThink( void ); - void EXPORT ExplodeTouch( edict_t *pOther ); - - EHANDLE m_hOwner; -}; - - -class CInfoBM; - -class CMBigMomma : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - void Activate( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - void RunTask( Task_t *pTask ); - void StartTask( Task_t *pTask ); - Schedule_t *GetSchedule( void ); - Schedule_t *GetScheduleOfType( int Type ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - - void NodeStart( int iszNextNode ); - void NodeReach( void ); - BOOL ShouldGoToNode( void ); - - void SetYawSpeed( void ); - int Classify ( void ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void LayHeadcrab( void ); - - int GetNodeSequence( void ); - int GetNodePresequence( void ); - float GetNodeDelay( void ); - float GetNodeRange( void ); - float GetNodeYaw( void ); - - // Restart the crab count on each new level - void OverrideReset( void ) - { - m_crabCount = 0; - } - - void DeathNotice( entvars_t *pevChild ); - - BOOL CanLayCrab( void ); - - void LaunchMortar( void ); - - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -95, -95, 0 ); - pev->absmax = pev->origin + Vector( 95, 95, 190 ); - } - - BOOL CheckMeleeAttack1( float flDot, float flDist ); // Slash - BOOL CheckMeleeAttack2( float flDot, float flDist ); // Lay a crab - BOOL CheckRangeAttack1( float flDot, float flDist ); // Mortar launch - - static const char *pChildDieSounds[]; - static const char *pSackSounds[]; - static const char *pDeathSounds[]; - static const char *pAttackSounds[]; - static const char *pAttackHitSounds[]; - static const char *pBirthSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pFootSounds[]; - - CUSTOM_SCHEDULES; - -private: - float m_nodeTime; - float m_crabTime; - float m_mortarTime; - float m_painSoundTime; - int m_crabCount; -}; - - -class CMGargantua : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - void SetYawSpeed( void ); - int Classify ( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe - BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames - BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -80, -80, 0 ); - pev->absmax = pev->origin + Vector( 80, 80, 214 ); - } - - Schedule_t *GetScheduleOfType( int Type ); - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - - void PrescheduleThink( void ); - - void Killed( entvars_t *pevAttacker, int iGib ); - void DeathEffect( void ); - - void EyeOff( void ); - void EyeOn( int level ); - void EyeUpdate( void ); - void Leap( void ); - void StompAttack( void ); - void FlameCreate( void ); - void FlameUpdate( void ); - void FlameControls( float angleX, float angleY ); - void FlameDestroy( void ); - inline BOOL FlameIsOn( void ) { return m_pFlame[0] != NULL; } - - void FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); - - CUSTOM_SCHEDULES; - - static const char *pAttackHitSounds[]; - static const char *pBeamAttackSounds[]; - static const char *pAttackMissSounds[]; - static const char *pRicSounds[]; - static const char *pFootSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackSounds[]; - static const char *pStompSounds[]; - static const char *pBreatheSounds[]; - -private: - edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); - -protected: - CMSprite *m_pEyeGlow; // Glow around the eyes - CMBeam *m_pFlame[4]; // Flame beams - - int m_eyeBrightness; // Brightness target - float m_seeTime; // Time to attack (when I see the enemy, I set this) - float m_flameTime; // Time of next flame attack - float m_painSoundTime; // Time of next pain sound - float m_streakTime; // streak timer (don't send too many) - float m_flameX; // Flame thrower aim - float m_flameY; -}; - - -// maybe put this on a separate header file? -typedef enum -{ - TURRET_ANIM_NONE = 0, - TURRET_ANIM_FIRE, - TURRET_ANIM_SPIN, - TURRET_ANIM_DEPLOY, - TURRET_ANIM_RETIRE, - TURRET_ANIM_DIE, -} TURRET_ANIM; - -class CMBaseTurret : public CMBaseMonster -{ -public: - void Spawn(void); - virtual void Precache(void); - void KeyValue( KeyValueData *pkvd ); - void EXPORT TurretUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - - virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - virtual int Classify(void); - - int BloodColor( void ) { return DONT_BLEED; } - void GibMonster( void ) {} // UNDONE: Throw turret gibs? - - // Think functions - - void EXPORT ActiveThink(void); - void EXPORT SearchThink(void); - void EXPORT AutoSearchThink(void); - void EXPORT TurretDeath(void); - - virtual void EXPORT SpinDownCall(void) { m_iSpin = 0; } - virtual void EXPORT SpinUpCall(void) { m_iSpin = 1; } - - // void SpinDown(void); - // float EXPORT SpinDownCall( void ) { return SpinDown(); } - - // virtual float SpinDown(void) { return 0;} - // virtual float Retire(void) { return 0;} - - void EXPORT Deploy(void); - void EXPORT Retire(void); - - void EXPORT Initialize(void); - - virtual void Ping(void); - virtual void EyeOn(void); - virtual void EyeOff(void); - - // other functions - void SetTurretAnim(TURRET_ANIM anim); - int MoveTurret(void); - virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { }; - - float m_flMaxSpin; // Max time to spin the barrel w/o a target - int m_iSpin; - - CMSprite *m_pEyeGlow; - int m_eyeBrightness; - - int m_iDeployHeight; - int m_iRetractHeight; - int m_iMinPitch; - - int m_iBaseTurnRate; // angles per second - float m_fTurnRate; // actual turn rate - int m_iOrientation; // 0 = floor, 1 = Ceiling - int m_iOn; - int m_fBeserk; // Sometimes this bitch will just freak out - int m_iAutoStart; // true if the turret auto deploys when a target - // enters its range - - Vector m_vecLastSight; - float m_flLastSight; // Last time we saw a target - float m_flMaxWait; // Max time to seach w/o a target - int m_iSearchSpeed; // Not Used! - - // movement - float m_flStartYaw; - Vector m_vecCurAngles; - Vector m_vecGoalAngles; - - - float m_flPingTime; // Time until the next ping, used when searching - float m_flSpinUpTime; // Amount of time until the barrel should spin down when searching -}; - - -class CMTurret : public CMBaseTurret -{ -public: - void Spawn(void); - void Precache(void); - // Think functions - void SpinUpCall(void); - void SpinDownCall(void); - - // other functions - void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); - void Killed( entvars_t *pevAttacker, int iGib ); - -private: - int m_iStartSpin; - -}; - - -class CMMiniTurret : public CMBaseTurret -{ -public: - void Spawn( ); - void Precache(void); - // other functions - void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); - void Killed( entvars_t *pevAttacker, int iGib ); -}; - - -//========================================================= -// Sentry gun - smallest turret, placed near grunt entrenchments -//========================================================= -class CMSentry : public CMBaseTurret -{ -public: - void Spawn( ); - void Precache(void); - // other functions - void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); - int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - void Killed( entvars_t *pevAttacker, int iGib ); - void EXPORT SentryTouch( edict_t *pOther ); - void EXPORT SentryDeath( void ); - -}; - -// -// opposing force monsters -// - -//========================================================= -// Gonome's guts projectile -//========================================================= -class CGonomeGuts : public CMBaseEntity -{ -public: - void Spawn( void ); - - static edict_t *Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - void GutsTouch( edict_t *pOther ); - void EXPORT Animate( void ); - - int m_maxFrame; -}; - -//========================================================= -// Gonome -//========================================================= -class CMGonome : public CMBaseMonster -{ -public: - - void Spawn(void); - void Precache(void); - - int Classify(void); - void SetYawSpeed(); - void HandleAnimEvent(MonsterEvent_t *pEvent); - int IgnoreConditions(); - void IdleSound( void ); - void PainSound( void ); - void DeathSound( void ); - void AlertSound( void ); - void StartTask(Task_t *pTask); - - BOOL CheckMeleeAttack2(float flDot, float flDist); - BOOL CheckRangeAttack1(float flDot, float flDist); - void SetActivity( Activity NewActivity ); - - Schedule_t *GetSchedule(); - Schedule_t *GetScheduleOfType( int Type ); - void RunTask(Task_t* pTask); - - int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - void Killed(entvars_t *pevAttacker, int iGib); - - void UnlockPlayer(); - CGonomeGuts* GetGonomeGuts(entvars_t *pevOwner, const Vector& pos); - void ClearGuts(); - - CUSTOM_SCHEDULES; - - static const char* pPainSounds[]; - static const char* pIdleSounds[]; - static const char* pDeathSounds[]; - static const char *pAttackHitSounds[]; - static const char *pAttackMissSounds[]; - -protected: - float m_flNextFlinch; - float m_flNextThrowTime;// last time the gonome used the guts attack. - CGonomeGuts* m_pGonomeGuts; - - BOOL m_fPlayerLocked; - EHANDLE m_lockedPlayer; - - bool m_meleeAttack2; - bool m_playedAttackSound; -}; - -//========================================================= -// Male Assassin -//========================================================= -class CMMassn : public CMHGrunt -{ -public: - int Classify(void); - void HandleAnimEvent(MonsterEvent_t *pEvent); - void Sniperrifle(void); - - BOOL FOkToSpeak(void); - - void Spawn( void ); - void Precache( void ); - - void DeathSound(void); - void PainSound(void); - void IdleSound(void); -}; - -//========================================================= -// Otis -//========================================================= -class CMOtis : public CMBarney -{ -public: - void KeyValue(KeyValueData *pkvd); - - void Spawn(void); - void Precache(void); - void BarneyFirePistol(void); - void AlertSound(void); - void HandleAnimEvent(MonsterEvent_t *pEvent); - - int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); - - // Override these to set behavior - Schedule_t *GetSchedule(void); - - void TalkInit(void); - void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - void Killed(entvars_t *pevAttacker, int iGib); - - int head; - int bodystate; -}; - -//========================================================= -// Pit Drone's spit projectile -//========================================================= -class CPitdroneSpike : public CMBaseEntity -{ -public: - void Spawn(void); - void EXPORT SpikeTouch(edict_t *pOther); - void EXPORT StartTrail(); - static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles); -}; - -//========================================================= -// Pit Drone -//========================================================= -class CMPitdrone : public CMBaseMonster -{ -public: - void Spawn(void); - void Precache(void); - void HandleAnimEvent(MonsterEvent_t *pEvent); - void SetYawSpeed(void); - int ISoundMask(); - void KeyValue(KeyValueData *pkvd); - - int Classify(void); - - BOOL CheckMeleeAttack1(float flDot, float flDist); - BOOL CheckRangeAttack1(float flDot, float flDist); - void IdleSound(void); - void PainSound(void); - void AlertSound(void); - void DeathSound(void); - void BodyChange(float spikes); - int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - int IgnoreConditions(void); - Schedule_t* GetSchedule(void); - Schedule_t* GetScheduleOfType(int Type); - void StartTask(Task_t *pTask); - void RunTask(Task_t *pTask); - void RunAI(void); - void CheckAmmo(); - void GibMonster(); - CUSTOM_SCHEDULES; - - float m_flLastHurtTime; - float m_flNextSpitTime;// last time the PitDrone used the spit attack. - float m_flNextFlinch; - int m_iInitialAmmo; - bool shouldAttackWithLeftClaw; - - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pDieSounds[]; - static const char *pAttackMissSounds[]; -}; - -//========================================================= -// Shock Roach -//========================================================= -class CMShockRoach : public CMHeadCrab -{ -public: - void Spawn(void); - void Precache(void); - void EXPORT LeapTouch(edict_t *pOther); - void PainSound(void); - void DeathSound(void); - void IdleSound(void); - void AlertSound(void); - void MonsterThink(void); - void StartTask(Task_t* pTask); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackSounds[]; - static const char *pDeathSounds[]; - static const char *pBiteSounds[]; - - float m_flBirthTime; - BOOL m_fRoachSolid; - -protected: - void AttackSound(); -}; - -//========================================================= -// Shock Trooper -//========================================================= -class CMStrooper : public CMHGrunt -{ -public: - void Spawn(void); - void MonsterThink(); - void Precache(void); - int Classify(void); - BOOL CheckRangeAttack1(float flDot, float flDist); - BOOL CheckRangeAttack2(float flDot, float flDist); - void HandleAnimEvent(MonsterEvent_t *pEvent); - void SetObjectCollisionBox( void ) - { - pev->absmin = pev->origin + Vector( -24, -24, 0 ); - pev->absmax = pev->origin + Vector( 24, 24, 72 ); - } - - void SetActivity(Activity NewActivity); - - void DeathSound(void); - void PainSound(void); - void IdleSound(void); - void GibMonster(void); - - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); - - void DropShockRoach(bool gibbed); - - Schedule_t *GetSchedule(void); - Schedule_t *GetScheduleOfType(int Type); - - void SpeakSentence(); - - BOOL m_fRightClaw; - float m_rechargeTime; - float m_blinkTime; - float m_eyeChangeTime; - - static const char *pGruntSentences[]; -}; - -//========================================================= -// Voltigore's energy ball projectile -//========================================================= -#define VOLTIGORE_MAX_BEAMS 8 -class CMVoltigoreEnergyBall : public CMBaseEntity -{ -public: - void Spawn(void); - - static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); - void EXPORT BallTouch(edict_t *pOther); - void EXPORT FlyThink(void); - - void CreateBeams(); - void ClearBeams(); - void UpdateBeams(); - - CMBeam* m_pBeam[VOLTIGORE_MAX_BEAMS]; - int m_iBeams; - float m_timeToDie; - -protected: - - void CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness); - void UpdateBeam(int nIndex, const Vector& vecPos, bool show); - void ClearBeam(int nIndex); -}; - -//========================================================= -// Voltigore -//========================================================= -class CMVoltigore : public CMBaseMonster -{ -public: - virtual void Spawn(void); - virtual void Precache(void); - void SetYawSpeed(void); - virtual int Classify(void); - virtual void HandleAnimEvent(MonsterEvent_t *pEvent); - virtual void IdleSound(void); - virtual void PainSound(void); - virtual void DeathSound(void); - virtual void AlertSound(void); - void AttackSound(void); - virtual void StartTask(Task_t *pTask); - virtual BOOL CheckMeleeAttack1(float flDot, float flDist); - virtual BOOL CheckRangeAttack1(float flDot, float flDist); - virtual void RunAI(void); - virtual void GibMonster(); - Schedule_t *GetSchedule(void); - Schedule_t *GetScheduleOfType(int Type); - virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - virtual void Killed(entvars_t *pevAttacker, int iGib); - - CUSTOM_SCHEDULES - - float m_flNextZapTime; // last time the voltigore used the spit attack. - BOOL m_fShouldUpdateBeam; - CMBeam* m_pBeam[3]; - CMSprite* m_pBeamGlow; - int m_glowBrightness; - - static const char* pAlertSounds[]; - static const char* pAttackMeleeSounds[]; - static const char* pMeleeHitSounds[]; - static const char* pMeleeMissSounds[]; - static const char* pComSounds[]; - static const char* pDeathSounds[]; - static const char* pFootstepSounds[]; - static const char* pIdleSounds[]; - static const char* pPainSounds[]; - static const char* pGruntSounds[]; - - void CreateBeams(); - void DestroyBeams(); - void UpdateBeams(); - - void CreateGlow(); - void DestroyGlow(); - void GlowUpdate(); - void GlowOff(void); - void GlowOn(int level); -protected: - void GibBeamDamage(); - void PrecacheImpl(char* modelName); - int m_beamTexture; -}; - -//========================================================= -// Baby Voltigore -//========================================================= -class CMBabyVoltigore : public CMVoltigore -{ -public: - void Spawn(void); - void Precache(void); - void HandleAnimEvent(MonsterEvent_t* pEvent); - BOOL CheckMeleeAttack1(float flDot, float flDist); - BOOL CheckRangeAttack1(float flDot, float flDist); - void StartTask(Task_t *pTask); - void Killed(entvars_t *pevAttacker, int iGib); - void GibMonster(); - Schedule_t* GetSchedule(); - Schedule_t* GetScheduleOfType(int Type); -}; - -// -// sven co-op monsters -// - -//========================================================= -// Baby Gargantua -//========================================================= -class CMBabyGargantua : public CMGargantua -{ -public: - void Spawn( void ); - void Precache( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - - BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe - BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames - BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack - - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - - void StompAttack( void ); - void FlameCreate( void ); - void FlameUpdate( void ); - void FlameDestroy( void ); - - static const char *pBeamAttackSounds[]; - static const char *pFootSounds[]; - static const char *pIdleSounds[]; - static const char *pAlertSounds[]; - static const char *pPainSounds[]; - static const char *pAttackSounds[]; - static const char *pStompSounds[]; - static const char *pBreatheSounds[]; - static const char *pDieSounds[]; - -private: - edict_t *BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); -}; - -#endif // BASEMONSTER_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ + +#ifndef BASEMONSTER_H +#define BASEMONSTER_H + +#ifndef SCHEDULE_H +#include "schedule.h" +#endif + +#ifndef SKILL_H +#include "skill.h" +#endif + +// +// generic Monster +// +class CMBaseMonster : public CMBaseToggle +{ +private: + int m_afConditions; + +public: + + // these fields have been added in the process of reworking the state machine. (sjb) + EHANDLE m_hEnemy; // the entity that the monster is fighting. + EHANDLE m_hTargetEnt; // the entity that the monster is trying to reach + EHANDLE m_hOldEnemy[ MAX_OLD_ENEMIES ]; + Vector m_vecOldEnemy[ MAX_OLD_ENEMIES ]; + + float m_flFieldOfView;// width of monster's field of view ( dot product ) + float m_flWaitFinished;// if we're told to wait, this is the time that the wait will be over. + float m_flMoveWaitFinished; + + Activity m_Activity;// what the monster is doing (animation) + Activity m_IdealActivity;// monster should switch to this activity + + int m_LastHitGroup; // the last body region that took damage + + MONSTERSTATE m_MonsterState;// monster's current state + MONSTERSTATE m_IdealMonsterState;// monster should change to this state + + int m_iTaskStatus; + Schedule_t *m_pSchedule; + int m_iScheduleIndex; + + WayPoint_t m_Route[ ROUTE_SIZE ]; // Positions of movement + int m_movementGoal; // Goal that defines route + int m_iRouteIndex; // index into m_Route[] + float m_moveWaitTime; // How long I should wait for something to move + + Vector m_vecMoveGoal; // kept around for node graph moves, so we know our ultimate goal + Activity m_movementActivity; // When moving, set this activity + + int m_iAudibleList; // first index of a linked list of sounds that the monster can hear. + int m_afSoundTypes; + + Vector m_vecLastPosition;// monster sometimes wants to return to where it started after an operation. + + int m_iHintNode; // this is the hint node that the monster is moving towards or performing active idle on. + + int m_afMemory; + + int m_iMaxHealth;// keeps track of monster's maximum health value (for re-healing, etc) + + Vector m_vecEnemyLKP;// last known position of enemy. (enemy's origin) + + int m_cAmmoLoaded; // how much ammo is in the weapon (used to trigger reload anim sequences) + + int m_afCapability;// tells us what a monster can/can't do. + + float m_flNextAttack; // cannot attack again until this time + + int m_bitsDamageType; // what types of damage has monster (player) taken + BYTE m_rgbTimeBasedDamage[CDMG_TIMEBASED]; + + int m_lastDamageAmount;// how much damage did monster (player) last take + // time based damage counters, decr. 1 per 2 seconds + int m_bloodColor; // color of blood particless + + int m_failSchedule; // Schedule type to choose if current schedule fails + + float m_flHungryTime;// set this is a future time to stop the monster from eating for a while. + + float m_flDistTooFar; // if enemy farther away than this, bits_COND_ENEMY_TOOFAR set in CheckEnemy + float m_flDistLook; // distance monster sees (Default 2048) + + int m_iTriggerCondition;// for scripted AI, this is the condition that will cause the activation of the monster's TriggerTarget + string_t m_iszTriggerTarget;// name of target that should be fired. + + Vector m_HackedGunPos; // HACK until we can query end of gun + + string_t m_szMonsterName; // Monster name to display on HUD + int m_iClassifyOverride; // Overriden classification for this monster + + void KeyValue( KeyValueData *pkvd ); + +// monster use function + void EXPORT MonsterUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + void EXPORT CorpseUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + +// overrideable Monster member functions + + virtual int BloodColor( void ) { return m_bloodColor; } + + virtual CMBaseMonster *MyMonsterPointer( void ) { return this; } + virtual void Look ( int iDistance );// basic sight function for monsters + virtual void RunAI ( void );// core ai function! + void Listen ( void ); + + virtual BOOL IsAlive( void ) { return (pev->deadflag != DEAD_DEAD); } + virtual BOOL ShouldFadeOnDeath( void ); + +// Basic Monster AI functions + virtual float ChangeYaw ( int speed ); + float VecToYaw( Vector vecDir ); + float FlYawDiff ( void ); + + float DamageForce( float damage ); + +// stuff written for new state machine + virtual void MonsterThink( void ); + void EXPORT CallMonsterThink( void ) { this->MonsterThink(); } + virtual int IRelationship ( CMBaseEntity *pTarget ); + virtual void MonsterInit ( void ); + virtual void MonsterInitDead( void ); // Call after animation/pose is set up + virtual void BecomeDead( void ); + void EXPORT CorpseFallThink( void ); + + void EXPORT MonsterInitThink ( void ); + virtual void StartMonster ( void ); + virtual edict_t* BestVisibleEnemy ( void );// finds best visible enemy for attack + virtual void HandleAnimEvent( MonsterEvent_t *pEvent ); + + virtual int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist );// check validity of a straight move through space + virtual void Move( float flInterval = 0.1 ); + virtual void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); + virtual BOOL ShouldAdvanceRoute( float flWaypointDist ); + + virtual Activity GetStoppedActivity( void ) { return ACT_IDLE; } + virtual void Stop( void ) { m_IdealActivity = GetStoppedActivity(); } + + // This will stop animation until you call ResetSequenceInfo() at some point in the future + inline void StopAnimation( void ) { pev->framerate = 0; } + + // these functions will survey conditions and set appropriate conditions bits for attack types. + virtual BOOL CheckRangeAttack1( float flDot, float flDist ); + virtual BOOL CheckRangeAttack2( float flDot, float flDist ); + virtual BOOL CheckMeleeAttack1( float flDot, float flDist ); + virtual BOOL CheckMeleeAttack2( float flDot, float flDist ); + + BOOL FHaveSchedule( void ); + BOOL FScheduleValid ( void ); + void ClearSchedule( void ); + BOOL FScheduleDone ( void ); + void ChangeSchedule ( Schedule_t *pNewSchedule ); + void NextScheduledTask ( void ); + Schedule_t *ScheduleInList( const char *pName, Schedule_t **pList, int listCount ); + + virtual Schedule_t *ScheduleFromName( const char *pName ); + static Schedule_t *m_scheduleList[]; + + void MaintainSchedule ( void ); + virtual void StartTask ( Task_t *pTask ); + virtual void RunTask ( Task_t *pTask ); + virtual Schedule_t *GetScheduleOfType( int Type ); + virtual Schedule_t *GetSchedule( void ); + virtual void ScheduleChange( void ) {} + virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); } + virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); + virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CMBaseEntity *pListener ); + + virtual void SentenceStop( void ); + + Task_t *GetTask ( void ); + virtual MONSTERSTATE GetIdealState ( void ); + virtual void SetActivity ( Activity NewActivity ); + void SetSequenceByName ( char *szSequence ); + void SetState ( MONSTERSTATE State ); + virtual void ReportAIState( void ); + + void CheckAttacks ( edict_t *pTarget, float flDist ); + virtual int CheckEnemy ( edict_t *pEnemy ); + void PushEnemy( edict_t *pEnemy, Vector &vecLastKnownPos ); + BOOL PopEnemy( void ); + + BOOL FGetNodeRoute ( Vector vecDest ); + + inline void TaskComplete( void ) { if ( !HasConditions(bits_COND_TASK_FAILED) ) m_iTaskStatus = TASKSTATUS_COMPLETE; } + void MovementComplete( void ); + void TaskFail( void ); + inline void TaskBegin( void ) { m_iTaskStatus = TASKSTATUS_RUNNING; } + int TaskIsRunning( void ); + inline int TaskIsComplete( void ) { return (m_iTaskStatus == TASKSTATUS_COMPLETE); } + inline int MovementIsComplete( void ) { return (m_movementGoal == MOVEGOAL_NONE); } + + int IScheduleFlags ( void ); + BOOL FRefreshRoute( void ); + BOOL FRouteClear ( void ); + void RouteSimplify( edict_t *pTargetEnt ); + void AdvanceRoute ( float distance ); + virtual BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ); + void MakeIdealYaw( Vector vecTarget ); + virtual void SetYawSpeed ( void ) { return; }; // allows different yaw_speeds for each activity + BOOL BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget ); + virtual BOOL BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ); + int RouteClassify( int iMoveFlag ); + void InsertWaypoint ( Vector vecLocation, int afMoveFlags ); + + BOOL FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset ); + virtual BOOL FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ); + virtual BOOL FValidateCover ( const Vector &vecCoverLocation ) { return TRUE; }; + virtual float CoverRadius( void ) { return 784; } // Default cover radius + + virtual BOOL FCanCheckAttacks ( void ); + virtual void CheckAmmo( void ) { return; }; + virtual int IgnoreConditions ( void ); + + inline void SetConditions( int iConditions ) { m_afConditions |= iConditions; } + inline void ClearConditions( int iConditions ) { m_afConditions &= ~iConditions; } + inline BOOL HasConditions( int iConditions ) { if ( m_afConditions & iConditions ) return TRUE; return FALSE; } + inline BOOL HasAllConditions( int iConditions ) { if ( (m_afConditions & iConditions) == iConditions ) return TRUE; return FALSE; } + + virtual BOOL FValidateHintType( short sHint ); + int FindHintNode ( void ); + virtual BOOL FCanActiveIdle ( void ); + void SetTurnActivity ( void ); + + BOOL MoveToNode( Activity movementAct, float waitTime, const Vector &goal ); + BOOL MoveToTarget( Activity movementAct, float waitTime ); + BOOL MoveToLocation( Activity movementAct, float waitTime, const Vector &goal ); + BOOL MoveToEnemy( Activity movementAct, float waitTime ); + + BOOL FBecomeProne ( void ); + virtual void BarnacleVictimBitten( entvars_t *pevBarnacle ); + virtual void BarnacleVictimReleased( void ); + + void SetEyePosition ( void ); + + BOOL FShouldEat( void );// see if a monster is 'hungry' + void Eat ( float flFullDuration );// make the monster 'full' for a while. + + edict_t *CheckTraceHullAttack( float flDist, int iDamage, int iDmgType ); + BOOL FacingIdeal( void ); + + BOOL FCheckAITrigger( void );// checks and, if necessary, fires the monster's trigger target. + + BOOL BBoxFlat( void ); + + // PrescheduleThink + virtual void PrescheduleThink( void ) { return; }; + + BOOL GetEnemy ( void ); + void MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + + // combat functions + float UpdateTarget ( entvars_t *pevTarget ); + virtual Activity GetDeathActivity ( void ); + Activity GetSmallFlinchActivity( void ); + virtual void Killed( entvars_t *pevAttacker, int iGib ); + virtual void GibMonster( void ); + BOOL ShouldGibMonster( int iGib ); + void CallGibMonster( void ); + virtual BOOL HasHumanGibs( void ); + virtual BOOL HasAlienGibs( void ); + virtual void FadeMonster( void ); // Called instead of GibMonster() when gibs are disabled + + Vector ShootAtEnemy( const Vector &shootOrigin ); + virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ) * 0.75 + EyePosition() * 0.25; }; // position to shoot at + + virtual Vector GetGunPosition( void ); + + virtual int TakeHealth( float flHealth, int bitsDamageType ); + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + int DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + void RadiusDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); + void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); + virtual int IsMoving( void ) { return m_movementGoal != MOVEGOAL_NONE; } + + void RouteClear( void ); + void RouteNew( void ); + + virtual void DeathSound ( void ) { return; }; + virtual void AlertSound ( void ) { return; }; + virtual void IdleSound ( void ) { return; }; + virtual void PainSound ( void ) { return; }; + + virtual void StopFollowing( BOOL clearSchedule ) {} + + inline void Remember( int iMemory ) { m_afMemory |= iMemory; } + inline void Forget( int iMemory ) { m_afMemory &= ~iMemory; } + inline BOOL HasMemory( int iMemory ) { if ( m_afMemory & iMemory ) return TRUE; return FALSE; } + inline BOOL HasAllMemories( int iMemory ) { if ( (m_afMemory & iMemory) == iMemory ) return TRUE; return FALSE; } +}; + +// +// monster_plugin monsters +// + +#ifndef TALKMONSTER_H +#include "cmtalkmonster.h" +#endif + +#ifndef FLYINGMONSTER_H +#include "cmflyingmonster.h" +#endif + +#ifndef WEAPONS_H +#include "weapons.h" +#endif + +class CMHeadCrab : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void RunTask ( Task_t *pTask ); + void StartTask ( Task_t *pTask ); + void SetYawSpeed ( void ); + void EXPORT LeapTouch ( edict_t *pOther ); + Vector Center( void ); + Vector BodyTarget( const Vector &posSrc ); + void PainSound( void ); + void DeathSound( void ); + void IdleSound( void ); + void AlertSound( void ); + void PrescheduleThink( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + virtual float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite; } + virtual int GetVoicePitch( void ) { return 100; } + virtual float GetSoundVolume( void ) { return 1.0; } + Schedule_t* GetScheduleOfType ( int Type ); + + CUSTOM_SCHEDULES; + + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackSounds[]; + static const char *pDeathSounds[]; + static const char *pBiteSounds[]; +}; + +class CMBabyCrab : public CMHeadCrab +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + float GetDamageAmount( void ) { return gSkillData.headcrabDmgBite * 0.3; } + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + Schedule_t* GetScheduleOfType ( int Type ); + virtual int GetVoicePitch( void ) { return PITCH_NORM + RANDOM_LONG(40,50); } + virtual float GetSoundVolume( void ) { return 0.8; } +}; + + +class CMZombie : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + int IgnoreConditions ( void ); + + float m_flNextFlinch; + + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + void AttackSound( void ); + + static const char *pAttackSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + + // No range attacks + BOOL CheckRangeAttack1 ( float flDot, float flDist ) { return FALSE; } + BOOL CheckRangeAttack2 ( float flDot, float flDist ) { return FALSE; } + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); +}; + + +class CMSqueakGrenade : public CMGrenade +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify( void ); + void EXPORT SuperBounceTouch( edict_t *pOther ); + void EXPORT HuntThink( void ); + int BloodColor( void ) { return BLOOD_COLOR_YELLOW; } + void Killed( entvars_t *pevAttacker, int iGib ); + void GibMonster( void ); + + static float m_flNextBounceSoundTime; + + // CMBaseEntity *m_pTarget; + float m_flDie; + Vector m_vecTarget; + float m_flNextHunt; + float m_flNextHit; + Vector m_posPrev; +}; + + +class CMBullsquid : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void IdleSound( void ); + void PainSound( void ); + void DeathSound( void ); + void AlertSound ( void ); + void AttackSound( void ); + void StartTask ( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); + BOOL CheckMeleeAttack2 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + void RunAI( void ); + BOOL FValidateHintType ( short sHint ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType ( int Type ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + int IRelationship ( CMBaseEntity *pTarget ); + int IgnoreConditions ( void ); + MONSTERSTATE GetIdealState ( void ); + + CUSTOM_SCHEDULES; + + float m_flLastHurtTime;// we keep track of this, because if something hurts a squid, it will forget about its love of headcrabs for a while. + float m_flNextSpitTime;// last time the bullsquid used the spit attack. +}; + + +class CMHoundeye : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void SetYawSpeed ( void ); + void WarmUpSound ( void ); + void AlertSound( void ); + void DeathSound( void ); + void WarnSound( void ); + void PainSound( void ); + void IdleSound( void ); + void StartTask( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + void SonicAttack( void ); + void PrescheduleThink( void ); + void SetActivity ( Activity NewActivity ); + void WriteBeamColor ( void ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL FValidateHintType ( short sHint ); + BOOL FCanActiveIdle ( void ); + Schedule_t *GetScheduleOfType ( int Type ); + Schedule_t *GetSchedule( void ); + + CUSTOM_SCHEDULES; + + int m_iSpriteTexture; + BOOL m_fAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down + BOOL m_fDontBlink;// don't try to open/close eye if this bit is set! + Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members. +}; + + +class CMHGrunt : public CMTalkMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL FCanCheckAttacks ( void ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + void CheckAmmo ( void ); + void SetActivity ( Activity NewActivity ); + void StartTask ( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + void DeathSound( void ); + void PainSound( void ); + void IdleSound ( void ); + Vector GetGunPosition( void ); + void Shoot ( void ); + void Shotgun ( void ); + void PrescheduleThink ( void ); + void GibMonster( void ); + void SpeakSentence( void ); + + edict_t *Kick( void ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType ( int Type ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + int IRelationship ( CMBaseEntity *pTarget ); + + BOOL FOkToSpeak( void ); + void JustSpoke( void ); + + CUSTOM_SCHEDULES; + + // checking the feasibility of a grenade toss is kind of costly, so we do it every couple of seconds, + // not every server frame. + float m_flNextGrenadeCheck; + float m_flNextPainTime; + float m_flLastEnemySightTime; + + Vector m_vecTossVelocity; + + BOOL m_fThrowGrenade; + BOOL m_fStanding; + BOOL m_fFirstEncounter;// only put on the handsign show in the squad's first encounter. + int m_cClipSize; + + int m_voicePitch; + + int m_iBrassShell; + int m_iShotgunShell; + + int m_iSentence; + + static const char *pGruntSentences[]; +}; + + +class CMHAssassin : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void); + void Shoot( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + Schedule_t* GetSchedule ( void ); + Schedule_t* GetScheduleOfType ( int Type ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // jump + // BOOL CheckMeleeAttack2 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); // shoot + BOOL CheckRangeAttack2 ( float flDot, float flDist ); // throw grenade + void StartTask ( Task_t *pTask ); + void RunAI( void ); + void RunTask ( Task_t *pTask ); + void DeathSound ( void ); + void IdleSound ( void ); + CUSTOM_SCHEDULES; + + float m_flLastShot; + float m_flDiviation; + + float m_flNextJump; + Vector m_vecJumpVelocity; + + float m_flNextGrenadeCheck; + Vector m_vecTossVelocity; + BOOL m_fThrowGrenade; + + int m_iTargetRanderamt; + + int m_iFrustration; + + int m_iShell; +}; + + +#define ISLAVE_MAX_BEAMS 8 + +class CMISlave : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int ISoundMask( void ); + int Classify ( void ); + int IRelationship( CMBaseEntity *pTarget ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack2 ( float flDot, float flDist ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + + void DeathSound( void ); + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + + void Killed( entvars_t *pevAttacker, int iGib ); + + void StartTask ( Task_t *pTask ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType ( int Type ); + CUSTOM_SCHEDULES; + + void ClearBeams( ); + void ArmBeam( int side ); + void WackBeam( int side, edict_t *pEntity ); + void ZapBeam( int side ); + void BeamGlow( void ); + + int m_iBravery; + + CMBeam *m_pBeam[ISLAVE_MAX_BEAMS]; + + int m_iBeams; + float m_flNextAttack; + + int m_voicePitch; + + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + static const char *pPainSounds[]; + static const char *pDeathSounds[]; +}; + + +class CMBarney : public CMTalkMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int ISoundMask( void ); + void BarneyFirePistol( void ); + void AlertSound( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + void RunTask( Task_t *pTask ); + void StartTask( Task_t *pTask ); + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + + // Override these to set behavior + Schedule_t *GetScheduleOfType ( int Type ); + Schedule_t *GetSchedule ( void ); + MONSTERSTATE GetIdealState ( void ); + + void DeathSound( void ); + void PainSound( void ); + + void TalkInit( void ); + + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + void Killed( entvars_t *pevAttacker, int iGib ); + + BOOL m_fGunDrawn; + float m_painTime; + float m_checkAttackTime; + BOOL m_lastAttackCheck; + + CUSTOM_SCHEDULES; +}; + + +class CMAGrunt : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed ( void ); + int Classify ( void ); + int ISoundMask ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -32, -32, 0 ); + pev->absmax = pev->origin + Vector( 32, 32, 85 ); + } + + Schedule_t* GetSchedule ( void ); + Schedule_t* GetScheduleOfType ( int Type ); + BOOL FCanCheckAttacks ( void ); + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); + void StartTask ( Task_t *pTask ); + void AlertSound( void ); + void DeathSound ( void ); + void PainSound ( void ); + void AttackSound ( void ); + void PrescheduleThink ( void ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + int IRelationship( CMBaseEntity *pTarget ); + void StopTalking ( void ); + BOOL ShouldSpeak( void ); + CUSTOM_SCHEDULES; + + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + static const char *pAttackSounds[]; + static const char *pDieSounds[]; + static const char *pPainSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + + BOOL m_fCanHornetAttack; + float m_flNextHornetAttackCheck; + + float m_flNextPainTime; + + // three hacky fields for speech stuff. These don't really need to be saved. + float m_flNextSpeakTime; + float m_flNextWordTime; + int m_iLastWord; +}; + + +class CMScientist : public CMTalkMonster +{ +public: + void Spawn( void ); + void Precache( void ); + + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void RunTask( Task_t *pTask ); + void StartTask( Task_t *pTask ); + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + virtual int FriendNumber( int arrayNumber ); + void SetActivity ( Activity newActivity ); + Activity GetStoppedActivity( void ); + int ISoundMask( void ); + void DeclineFollowing( void ); + + float CoverRadius( void ) { return 1200; } // Need more room for cover because scientists want to get far away! + BOOL DisregardEnemy( edict_t *pEnemy ); + + BOOL CanHeal( void ); + void Heal( void ); + void Scream( void ); + + // Override these to set behavior + Schedule_t *GetScheduleOfType ( int Type ); + Schedule_t *GetSchedule ( void ); + MONSTERSTATE GetIdealState ( void ); + + void DeathSound( void ); + void PainSound( void ); + + void TalkInit( void ); + + void Killed( entvars_t *pevAttacker, int iGib ); + + CUSTOM_SCHEDULES; + +private: + float m_painTime; + float m_healTime; + float m_fearTime; +}; + + +class CMApache : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify( void ); + int BloodColor( void ) { return DONT_BLEED; } + void Killed( entvars_t *pevAttacker, int iGib ); + void GibMonster( void ); + + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -300, -300, -172); + pev->absmax = pev->origin + Vector(300, 300, 8); + } + + void EXPORT HuntThink( void ); + void EXPORT FlyTouch( edict_t *pOther ); + void EXPORT CrashTouch( edict_t *pOther ); + void EXPORT DyingThink( void ); + void EXPORT StartupUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + void EXPORT NullThink( void ); + + void ShowDamage( void ); + void Flight( void ); + void FireRocket( void ); + BOOL FireGun( void ); + + int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + + int m_iRockets; + float m_flForce; + float m_flNextRocket; + + Vector m_vecTarget; + Vector m_posTarget; + + Vector m_vecDesired; + Vector m_posDesired; + + Vector m_vecGoal; + + Vector m_angGun; + float m_flLastSeen; + float m_flPrevSeen; + + int m_iSoundState; // don't save this + + int m_iSpriteTexture; + int m_iExplode; + int m_iBodyGibs; + + float m_flGoalSpeed; + + int m_iDoSmokePuff; + CMBeam *m_pBeam; +}; + + +class CMApacheHVR : public CMGrenade +{ +public: + void Spawn( void ); + void Precache( void ); + void EXPORT IgniteThink( void ); + void EXPORT AccelerateThink( void ); + + int m_iTrail; + Vector m_vecForward; +}; + + +class CMController : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + void RunAI( void ); + BOOL CheckRangeAttack1 ( float flDot, float flDist ); // balls + BOOL CheckRangeAttack2 ( float flDot, float flDist ); // head + BOOL CheckMeleeAttack1 ( float flDot, float flDist ); // block, throw + Schedule_t* GetSchedule ( void ); + Schedule_t* GetScheduleOfType ( int Type ); + void StartTask ( Task_t *pTask ); + void RunTask ( Task_t *pTask ); + CUSTOM_SCHEDULES; + + void Stop( void ); + void Move ( float flInterval ); + int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ); + void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); + void SetActivity ( Activity NewActivity ); + BOOL ShouldAdvanceRoute( float flWaypointDist ); + int LookupFloat( ); + + float m_flNextFlinch; + + float m_flShootTime; + float m_flShootEnd; + + void PainSound( void ); + void AlertSound( void ); + void IdleSound( void ); + void AttackSound( void ); + void DeathSound( void ); + + static const char *pAttackSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pDeathSounds[]; + + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + void Killed( entvars_t *pevAttacker, int iGib ); + void GibMonster( void ); + + CMSprite *m_pBall[2]; // hand balls + int m_iBall[2]; // how bright it should be + float m_iBallTime[2]; // when it should be that color + int m_iBallCurrent[2]; // current brightness + + Vector m_vecEstVelocity; + + Vector m_velocity; + int m_fInCombat; +}; + +//========================================================= +// Controller bouncy ball attack +//========================================================= +class CMControllerHeadBall : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void EXPORT HuntThink( void ); + void EXPORT DieThink( void ); + void EXPORT BounceTouch( edict_t *pOther ); + void MovetoTarget( Vector vecTarget ); + void Crawl( void ); + int m_iTrail; + int m_flNextAttack; + Vector m_vecIdeal; + EHANDLE m_hOwner; +}; + +class CMControllerZapBall : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void EXPORT AnimateThink( void ); + void EXPORT ExplodeTouch( edict_t *pOther ); + + EHANDLE m_hOwner; +}; + + +class CInfoBM; + +class CMBigMomma : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + void Activate( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + void RunTask( Task_t *pTask ); + void StartTask( Task_t *pTask ); + Schedule_t *GetSchedule( void ); + Schedule_t *GetScheduleOfType( int Type ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + + void NodeStart( int iszNextNode ); + void NodeReach( void ); + BOOL ShouldGoToNode( void ); + + void SetYawSpeed( void ); + int Classify ( void ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void LayHeadcrab( void ); + + int GetNodeSequence( void ); + int GetNodePresequence( void ); + float GetNodeDelay( void ); + float GetNodeRange( void ); + float GetNodeYaw( void ); + + // Restart the crab count on each new level + void OverrideReset( void ) + { + m_crabCount = 0; + } + + void DeathNotice( entvars_t *pevChild ); + + BOOL CanLayCrab( void ); + + void LaunchMortar( void ); + + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -95, -95, 0 ); + pev->absmax = pev->origin + Vector( 95, 95, 190 ); + } + + BOOL CheckMeleeAttack1( float flDot, float flDist ); // Slash + BOOL CheckMeleeAttack2( float flDot, float flDist ); // Lay a crab + BOOL CheckRangeAttack1( float flDot, float flDist ); // Mortar launch + + static const char *pChildDieSounds[]; + static const char *pSackSounds[]; + static const char *pDeathSounds[]; + static const char *pAttackSounds[]; + static const char *pAttackHitSounds[]; + static const char *pBirthSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pFootSounds[]; + + CUSTOM_SCHEDULES; + +private: + float m_nodeTime; + float m_crabTime; + float m_mortarTime; + float m_painSoundTime; + int m_crabCount; +}; + + +class CMGargantua : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + void SetYawSpeed( void ); + int Classify ( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe + BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames + BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -80, -80, 0 ); + pev->absmax = pev->origin + Vector( 80, 80, 214 ); + } + + Schedule_t *GetScheduleOfType( int Type ); + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + + void PrescheduleThink( void ); + + void Killed( entvars_t *pevAttacker, int iGib ); + void DeathEffect( void ); + + void EyeOff( void ); + void EyeOn( int level ); + void EyeUpdate( void ); + void Leap( void ); + void StompAttack( void ); + void FlameCreate( void ); + void FlameUpdate( void ); + void FlameControls( float angleX, float angleY ); + void FlameDestroy( void ); + inline BOOL FlameIsOn( void ) { return m_pFlame[0] != NULL; } + + void FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ); + + CUSTOM_SCHEDULES; + + static const char *pAttackHitSounds[]; + static const char *pBeamAttackSounds[]; + static const char *pAttackMissSounds[]; + static const char *pRicSounds[]; + static const char *pFootSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackSounds[]; + static const char *pStompSounds[]; + static const char *pBreatheSounds[]; + +private: + edict_t *GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); + +protected: + CMSprite *m_pEyeGlow; // Glow around the eyes + CMBeam *m_pFlame[4]; // Flame beams + + int m_eyeBrightness; // Brightness target + float m_seeTime; // Time to attack (when I see the enemy, I set this) + float m_flameTime; // Time of next flame attack + float m_painSoundTime; // Time of next pain sound + float m_streakTime; // streak timer (don't send too many) + float m_flameX; // Flame thrower aim + float m_flameY; +}; + + +// maybe put this on a separate header file? +typedef enum +{ + TURRET_ANIM_NONE = 0, + TURRET_ANIM_FIRE, + TURRET_ANIM_SPIN, + TURRET_ANIM_DEPLOY, + TURRET_ANIM_RETIRE, + TURRET_ANIM_DIE, +} TURRET_ANIM; + +class CMBaseTurret : public CMBaseMonster +{ +public: + void Spawn(void); + virtual void Precache(void); + void KeyValue( KeyValueData *pkvd ); + void EXPORT TurretUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + + virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + virtual int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + virtual int Classify(void); + + int BloodColor( void ) { return DONT_BLEED; } + void GibMonster( void ) {} // UNDONE: Throw turret gibs? + + // Think functions + + void EXPORT ActiveThink(void); + void EXPORT SearchThink(void); + void EXPORT AutoSearchThink(void); + void EXPORT TurretDeath(void); + + virtual void EXPORT SpinDownCall(void) { m_iSpin = 0; } + virtual void EXPORT SpinUpCall(void) { m_iSpin = 1; } + + // void SpinDown(void); + // float EXPORT SpinDownCall( void ) { return SpinDown(); } + + // virtual float SpinDown(void) { return 0;} + // virtual float Retire(void) { return 0;} + + void EXPORT Deploy(void); + void EXPORT Retire(void); + + void EXPORT Initialize(void); + + virtual void Ping(void); + virtual void EyeOn(void); + virtual void EyeOff(void); + + // other functions + void SetTurretAnim(TURRET_ANIM anim); + int MoveTurret(void); + virtual void Shoot(Vector &vecSrc, Vector &vecDirToEnemy) { }; + + float m_flMaxSpin; // Max time to spin the barrel w/o a target + int m_iSpin; + + CMSprite *m_pEyeGlow; + int m_eyeBrightness; + + int m_iDeployHeight; + int m_iRetractHeight; + int m_iMinPitch; + + int m_iBaseTurnRate; // angles per second + float m_fTurnRate; // actual turn rate + int m_iOrientation; // 0 = floor, 1 = Ceiling + int m_iOn; + int m_fBeserk; // Sometimes this bitch will just freak out + int m_iAutoStart; // true if the turret auto deploys when a target + // enters its range + + Vector m_vecLastSight; + float m_flLastSight; // Last time we saw a target + float m_flMaxWait; // Max time to seach w/o a target + int m_iSearchSpeed; // Not Used! + + // movement + float m_flStartYaw; + Vector m_vecCurAngles; + Vector m_vecGoalAngles; + + + float m_flPingTime; // Time until the next ping, used when searching + float m_flSpinUpTime; // Amount of time until the barrel should spin down when searching +}; + + +class CMTurret : public CMBaseTurret +{ +public: + void Spawn(void); + void Precache(void); + // Think functions + void SpinUpCall(void); + void SpinDownCall(void); + + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); + void Killed( entvars_t *pevAttacker, int iGib ); + +private: + int m_iStartSpin; + +}; + + +class CMMiniTurret : public CMBaseTurret +{ +public: + void Spawn( ); + void Precache(void); + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); + void Killed( entvars_t *pevAttacker, int iGib ); +}; + + +//========================================================= +// Sentry gun - smallest turret, placed near grunt entrenchments +//========================================================= +class CMSentry : public CMBaseTurret +{ +public: + void Spawn( ); + void Precache(void); + // other functions + void Shoot(Vector &vecSrc, Vector &vecDirToEnemy); + int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + void Killed( entvars_t *pevAttacker, int iGib ); + void EXPORT SentryTouch( edict_t *pOther ); + void EXPORT SentryDeath( void ); + +}; + +// +// opposing force monsters +// + +//========================================================= +// Gonome's guts projectile +//========================================================= +class CGonomeGuts : public CMBaseEntity +{ +public: + void Spawn( void ); + + static edict_t *Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); + void GutsTouch( edict_t *pOther ); + void EXPORT Animate( void ); + + int m_maxFrame; +}; + +//========================================================= +// Gonome +//========================================================= +class CMGonome : public CMBaseMonster +{ +public: + + void Spawn(void); + void Precache(void); + + int Classify(void); + void SetYawSpeed(); + void HandleAnimEvent(MonsterEvent_t *pEvent); + int IgnoreConditions(); + void IdleSound( void ); + void PainSound( void ); + void DeathSound( void ); + void AlertSound( void ); + void StartTask(Task_t *pTask); + + BOOL CheckMeleeAttack2(float flDot, float flDist); + BOOL CheckRangeAttack1(float flDot, float flDist); + void SetActivity( Activity NewActivity ); + + Schedule_t *GetSchedule(); + Schedule_t *GetScheduleOfType( int Type ); + void RunTask(Task_t* pTask); + + int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + void Killed(entvars_t *pevAttacker, int iGib); + + void UnlockPlayer(); + CGonomeGuts* GetGonomeGuts(entvars_t *pevOwner, const Vector& pos); + void ClearGuts(); + + CUSTOM_SCHEDULES; + + static const char* pPainSounds[]; + static const char* pIdleSounds[]; + static const char* pDeathSounds[]; + static const char *pAttackHitSounds[]; + static const char *pAttackMissSounds[]; + +protected: + float m_flNextFlinch; + float m_flNextThrowTime;// last time the gonome used the guts attack. + CGonomeGuts* m_pGonomeGuts; + + BOOL m_fPlayerLocked; + EHANDLE m_lockedPlayer; + + bool m_meleeAttack2; + bool m_playedAttackSound; +}; + +//========================================================= +// Male Assassin +//========================================================= +class CMMassn : public CMHGrunt +{ +public: + int Classify(void); + void HandleAnimEvent(MonsterEvent_t *pEvent); + void Sniperrifle(void); + + BOOL FOkToSpeak(void); + + void Spawn( void ); + void Precache( void ); + + void DeathSound(void); + void PainSound(void); + void IdleSound(void); +}; + +//========================================================= +// Otis +//========================================================= +class CMOtis : public CMBarney +{ +public: + void KeyValue(KeyValueData *pkvd); + + void Spawn(void); + void Precache(void); + void BarneyFirePistol(void); + void AlertSound(void); + void HandleAnimEvent(MonsterEvent_t *pEvent); + + int TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType); + + // Override these to set behavior + Schedule_t *GetSchedule(void); + + void TalkInit(void); + void TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + void Killed(entvars_t *pevAttacker, int iGib); + + int head; + int bodystate; +}; + +//========================================================= +// Pit Drone's spit projectile +//========================================================= +class CPitdroneSpike : public CMBaseEntity +{ +public: + void Spawn(void); + void EXPORT SpikeTouch(edict_t *pOther); + void EXPORT StartTrail(); + static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles); +}; + +//========================================================= +// Pit Drone +//========================================================= +class CMPitdrone : public CMBaseMonster +{ +public: + void Spawn(void); + void Precache(void); + void HandleAnimEvent(MonsterEvent_t *pEvent); + void SetYawSpeed(void); + int ISoundMask(); + void KeyValue(KeyValueData *pkvd); + + int Classify(void); + + BOOL CheckMeleeAttack1(float flDot, float flDist); + BOOL CheckRangeAttack1(float flDot, float flDist); + void IdleSound(void); + void PainSound(void); + void AlertSound(void); + void DeathSound(void); + void BodyChange(float spikes); + int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + int IgnoreConditions(void); + Schedule_t* GetSchedule(void); + Schedule_t* GetScheduleOfType(int Type); + void StartTask(Task_t *pTask); + void RunTask(Task_t *pTask); + void RunAI(void); + void CheckAmmo(); + void GibMonster(); + CUSTOM_SCHEDULES; + + float m_flLastHurtTime; + float m_flNextSpitTime;// last time the PitDrone used the spit attack. + float m_flNextFlinch; + int m_iInitialAmmo; + bool shouldAttackWithLeftClaw; + + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pDieSounds[]; + static const char *pAttackMissSounds[]; +}; + +//========================================================= +// Shock Roach +//========================================================= +class CMShockRoach : public CMHeadCrab +{ +public: + void Spawn(void); + void Precache(void); + void EXPORT LeapTouch(edict_t *pOther); + void PainSound(void); + void DeathSound(void); + void IdleSound(void); + void AlertSound(void); + void MonsterThink(void); + void StartTask(Task_t* pTask); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackSounds[]; + static const char *pDeathSounds[]; + static const char *pBiteSounds[]; + + float m_flBirthTime; + BOOL m_fRoachSolid; + +protected: + void AttackSound(); +}; + +//========================================================= +// Shock Trooper +//========================================================= +class CMStrooper : public CMHGrunt +{ +public: + void Spawn(void); + void MonsterThink(); + void Precache(void); + int Classify(void); + BOOL CheckRangeAttack1(float flDot, float flDist); + BOOL CheckRangeAttack2(float flDot, float flDist); + void HandleAnimEvent(MonsterEvent_t *pEvent); + void SetObjectCollisionBox( void ) + { + pev->absmin = pev->origin + Vector( -24, -24, 0 ); + pev->absmax = pev->origin + Vector( 24, 24, 72 ); + } + + void SetActivity(Activity NewActivity); + + void DeathSound(void); + void PainSound(void); + void IdleSound(void); + void GibMonster(void); + + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); + + void DropShockRoach(bool gibbed); + + Schedule_t *GetSchedule(void); + Schedule_t *GetScheduleOfType(int Type); + + void SpeakSentence(); + + BOOL m_fRightClaw; + float m_rechargeTime; + float m_blinkTime; + float m_eyeChangeTime; + + static const char *pGruntSentences[]; +}; + +//========================================================= +// Voltigore's energy ball projectile +//========================================================= +#define VOLTIGORE_MAX_BEAMS 8 +class CMVoltigoreEnergyBall : public CMBaseEntity +{ +public: + void Spawn(void); + + static edict_t *Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); + void EXPORT BallTouch(edict_t *pOther); + void EXPORT FlyThink(void); + + void CreateBeams(); + void ClearBeams(); + void UpdateBeams(); + + CMBeam* m_pBeam[VOLTIGORE_MAX_BEAMS]; + int m_iBeams; + float m_timeToDie; + +protected: + + void CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness); + void UpdateBeam(int nIndex, const Vector& vecPos, bool show); + void ClearBeam(int nIndex); +}; + +//========================================================= +// Voltigore +//========================================================= +class CMVoltigore : public CMBaseMonster +{ +public: + virtual void Spawn(void); + virtual void Precache(void); + void SetYawSpeed(void); + virtual int Classify(void); + virtual void HandleAnimEvent(MonsterEvent_t *pEvent); + virtual void IdleSound(void); + virtual void PainSound(void); + virtual void DeathSound(void); + virtual void AlertSound(void); + void AttackSound(void); + virtual void StartTask(Task_t *pTask); + virtual BOOL CheckMeleeAttack1(float flDot, float flDist); + virtual BOOL CheckRangeAttack1(float flDot, float flDist); + virtual void RunAI(void); + virtual void GibMonster(); + Schedule_t *GetSchedule(void); + Schedule_t *GetScheduleOfType(int Type); + virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + virtual void Killed(entvars_t *pevAttacker, int iGib); + + CUSTOM_SCHEDULES + + float m_flNextZapTime; // last time the voltigore used the spit attack. + BOOL m_fShouldUpdateBeam; + CMBeam* m_pBeam[3]; + CMSprite* m_pBeamGlow; + int m_glowBrightness; + + static const char* pAlertSounds[]; + static const char* pAttackMeleeSounds[]; + static const char* pMeleeHitSounds[]; + static const char* pMeleeMissSounds[]; + static const char* pComSounds[]; + static const char* pDeathSounds[]; + static const char* pFootstepSounds[]; + static const char* pIdleSounds[]; + static const char* pPainSounds[]; + static const char* pGruntSounds[]; + + void CreateBeams(); + void DestroyBeams(); + void UpdateBeams(); + + void CreateGlow(); + void DestroyGlow(); + void GlowUpdate(); + void GlowOff(void); + void GlowOn(int level); +protected: + void GibBeamDamage(); + void PrecacheImpl(char* modelName); + int m_beamTexture; +}; + +//========================================================= +// Baby Voltigore +//========================================================= +class CMBabyVoltigore : public CMVoltigore +{ +public: + void Spawn(void); + void Precache(void); + void HandleAnimEvent(MonsterEvent_t* pEvent); + BOOL CheckMeleeAttack1(float flDot, float flDist); + BOOL CheckRangeAttack1(float flDot, float flDist); + void StartTask(Task_t *pTask); + void Killed(entvars_t *pevAttacker, int iGib); + void GibMonster(); + Schedule_t* GetSchedule(); + Schedule_t* GetScheduleOfType(int Type); +}; + +// +// sven co-op monsters +// + +//========================================================= +// Baby Gargantua +//========================================================= +class CMBabyGargantua : public CMGargantua +{ +public: + void Spawn( void ); + void Precache( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + + BOOL CheckMeleeAttack1( float flDot, float flDist ); // Swipe + BOOL CheckMeleeAttack2( float flDot, float flDist ); // Flames + BOOL CheckRangeAttack1( float flDot, float flDist ); // Stomp attack + + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + + void StompAttack( void ); + void FlameCreate( void ); + void FlameUpdate( void ); + void FlameDestroy( void ); + + static const char *pBeamAttackSounds[]; + static const char *pFootSounds[]; + static const char *pIdleSounds[]; + static const char *pAlertSounds[]; + static const char *pPainSounds[]; + static const char *pAttackSounds[]; + static const char *pStompSounds[]; + static const char *pBreatheSounds[]; + static const char *pDieSounds[]; + +private: + edict_t *BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType); +}; + +#endif // BASEMONSTER_H diff --git a/src/dlls/cmflyingmonster.h b/src/dlls/cmflyingmonster.h index 0f0b315..435474f 100644 --- a/src/dlls/cmflyingmonster.h +++ b/src/dlls/cmflyingmonster.h @@ -1,53 +1,53 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster - -#ifndef FLYINGMONSTER_H -#define FLYINGMONSTER_H - -class CMFlyingMonster : public CMBaseMonster -{ -public: - int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist );// check validity of a straight move through space - BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ); - Activity GetStoppedActivity( void ); - void Killed( entvars_t *pevAttacker, int iGib ); - void Stop( void ); - float ChangeYaw( int speed ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); - void Move( float flInterval = 0.1 ); - BOOL ShouldAdvanceRoute( float flWaypointDist ); - - inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; } - inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; } - inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; } - float CeilingZ( const Vector &position ); - float FloorZ( const Vector &position ); - BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction ); - - - // UNDONE: Save/restore this stuff!!! -protected: - Vector m_vecTravel; // Current direction - float m_flightSpeed; // Current flight speed (decays when not flapping or gliding) - float m_stopTime; // Last time we stopped (to avoid switching states too soon) - float m_momentum; // Weight for desired vs. momentum velocity - const char *m_pFlapSound; -}; - - -#endif //FLYINGMONSTER_H - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +// Base class for flying monsters. This overrides the movement test & execution code from CBaseMonster + +#ifndef FLYINGMONSTER_H +#define FLYINGMONSTER_H + +class CMFlyingMonster : public CMBaseMonster +{ +public: + int CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist );// check validity of a straight move through space + BOOL FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ); + Activity GetStoppedActivity( void ); + void Killed( entvars_t *pevAttacker, int iGib ); + void Stop( void ); + float ChangeYaw( int speed ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ); + void Move( float flInterval = 0.1 ); + BOOL ShouldAdvanceRoute( float flWaypointDist ); + + inline void SetFlyingMomentum( float momentum ) { m_momentum = momentum; } + inline void SetFlyingFlapSound( const char *pFlapSound ) { m_pFlapSound = pFlapSound; } + inline void SetFlyingSpeed( float speed ) { m_flightSpeed = speed; } + float CeilingZ( const Vector &position ); + float FloorZ( const Vector &position ); + BOOL ProbeZ( const Vector &position, const Vector &probe, float *pFraction ); + + + // UNDONE: Save/restore this stuff!!! +protected: + Vector m_vecTravel; // Current direction + float m_flightSpeed; // Current flight speed (decays when not flapping or gliding) + float m_stopTime; // Last time we stopped (to avoid switching states too soon) + float m_momentum; // Weight for desired vs. momentum velocity + const char *m_pFlapSound; +}; + + +#endif //FLYINGMONSTER_H + diff --git a/src/dlls/cmtalkmonster.h b/src/dlls/cmtalkmonster.h index e6f6135..374d5ee 100644 --- a/src/dlls/cmtalkmonster.h +++ b/src/dlls/cmtalkmonster.h @@ -1,178 +1,178 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef TALKMONSTER_H -#define TALKMONSTER_H - -#ifndef MONSTERS_H -#include "monsters.h" -#endif - -//========================================================= -// Talking monster base class -// Used for scientists and barneys -//========================================================= - -#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this - -#define TLK_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me. - -#define bit_saidDamageLight (1<<0) // bits so we don't repeat key sentences -#define bit_saidDamageMedium (1<<1) -#define bit_saidDamageHeavy (1<<2) -#define bit_saidHelloPlayer (1<<3) -#define bit_saidWoundLight (1<<4) -#define bit_saidWoundHeavy (1<<5) -#define bit_saidHeard (1<<6) -#define bit_saidSmelled (1<<7) - -#define TLK_CFRIENDS 3 - -typedef enum -{ - TLK_ANSWER = 0, - TLK_QUESTION, - TLK_IDLE, - TLK_STARE, - TLK_USE, - TLK_UNUSE, - TLK_STOP, - TLK_NOSHOOT, - TLK_HELLO, - TLK_PHELLO, - TLK_PIDLE, - TLK_PQUESTION, - TLK_PLHURT1, - TLK_PLHURT2, - TLK_PLHURT3, - TLK_SMELL, - TLK_WOUND, - TLK_MORTAL, - - TLK_CGROUPS, // MUST be last entry -} TALKGROUPNAMES; - - -enum -{ - SCHED_CANT_FOLLOW = LAST_COMMON_SCHEDULE + 1, - SCHED_MOVE_AWAY, // Try to get out of the player's way - SCHED_MOVE_AWAY_FOLLOW, // same, but follow afterward - SCHED_MOVE_AWAY_FAIL, // Turn back toward player - - LAST_TALKMONSTER_SCHEDULE, // MUST be last -}; - -enum -{ - TASK_CANT_FOLLOW = LAST_COMMON_TASK + 1, - TASK_MOVE_AWAY_PATH, - TASK_WALK_PATH_FOR_UNITS, - - TASK_TLK_RESPOND, // say my response - TASK_TLK_SPEAK, // question or remark - TASK_TLK_HELLO, // Try to say hello to player - TASK_TLK_HEADRESET, // reset head position - TASK_TLK_STOPSHOOTING, // tell player to stop shooting friend - TASK_TLK_STARE, // let the player know I know he's staring at me. - TASK_TLK_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle. - TASK_TLK_CLIENT_STARE, // same as look at client, but says something if the player stares. - TASK_TLK_EYECONTACT, // maintain eyecontact with person who I'm talking to - TASK_TLK_IDEALYAW, // set ideal yaw to face who I'm talking to - TASK_FACE_PLAYER, // Face the player - - LAST_TALKMONSTER_TASK, // MUST be last -}; - -class CMTalkMonster : public CMBaseMonster -{ -public: - void TalkInit( void ); - edict_t *FindNearestFriend(BOOL fPlayer); - float TargetDistance( void ); - void StopTalking( void ) { SentenceStop(); } - - // Base Monster functions - void Precache( void ); - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); - void TalkTouch( edict_t *pOther ); - void Killed( entvars_t *pevAttacker, int iGib ); - int IRelationship ( CMBaseEntity *pTarget ); - virtual int CanPlaySentence( BOOL fDisregardState ); - virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); - void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, edict_t *pListener ); - void KeyValue( KeyValueData *pkvd ); - - // AI functions - void SetActivity ( Activity newActivity ); - Schedule_t *GetScheduleOfType ( int Type ); - void StartTask( Task_t *pTask ); - void RunTask( Task_t *pTask ); - void HandleAnimEvent( MonsterEvent_t *pEvent ); - void PrescheduleThink( void ); - - - // Conversations / communication - int GetVoicePitch( void ); - void IdleRespond( void ); - int FIdleSpeak( void ); - int FIdleStare( void ); - int FIdleHello( void ); - void IdleHeadTurn( Vector &vecFriend ); - int FOkToSpeak( void ); - void TrySmellTalk( void ); - edict_t *EnumFriends( edict_t *pentPrevious, int listNumber, BOOL bTrace ); - void AlertFriends( void ); - void ShutUpFriends( void ); - BOOL IsTalking( void ); - void Talk( float flDuration ); - // For following - BOOL CanFollow( void ); - BOOL IsFollowing( void ) { return m_hTargetEnt != NULL && UTIL_IsPlayer(m_hTargetEnt); } - void StopFollowing( BOOL clearSchedule ); - void StartFollowing( edict_t *pLeader ); - virtual void DeclineFollowing( void ) {} - void LimitFollowers( edict_t *pPlayer, int maxFollowers ); - - void EXPORT FollowerUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - - virtual void SetAnswerQuestion( edict_t *pSpeaker ); - virtual int FriendNumber( int arrayNumber ) { return arrayNumber; } - - static char *m_szFriends[TLK_CFRIENDS]; // array of friend names - static float g_talkWaitTime; - - int m_bitsSaid; // set bits for sentences we don't want repeated - int m_nSpeak; // number of times initiated talking - int m_voicePitch; // pitch of voice for this head - const char *m_szGrp[TLK_CGROUPS]; // sentence group names - float m_useTime; // Don't allow +USE until this time - int m_iszUse; // Custom +USE sentence group (follow) - int m_iszUnUse; // Custom +USE sentence group (stop following) - - float m_flLastSaidSmelled;// last time we talked about something that stinks - float m_flStopTalkTime;// when in the future that I'll be done saying this sentence. - - EHANDLE m_hTalkTarget; // who to look at while talking - CUSTOM_SCHEDULES; -}; - - -// Clients can push talkmonsters out of their way -#define bits_COND_CLIENT_PUSH ( bits_COND_SPECIAL1 ) -// Don't see a client right now. -#define bits_COND_CLIENT_UNSEEN ( bits_COND_SPECIAL2 ) - - -#endif //TALKMONSTER_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef TALKMONSTER_H +#define TALKMONSTER_H + +#ifndef MONSTERS_H +#include "monsters.h" +#endif + +//========================================================= +// Talking monster base class +// Used for scientists and barneys +//========================================================= + +#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this + +#define TLK_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me. + +#define bit_saidDamageLight (1<<0) // bits so we don't repeat key sentences +#define bit_saidDamageMedium (1<<1) +#define bit_saidDamageHeavy (1<<2) +#define bit_saidHelloPlayer (1<<3) +#define bit_saidWoundLight (1<<4) +#define bit_saidWoundHeavy (1<<5) +#define bit_saidHeard (1<<6) +#define bit_saidSmelled (1<<7) + +#define TLK_CFRIENDS 3 + +typedef enum +{ + TLK_ANSWER = 0, + TLK_QUESTION, + TLK_IDLE, + TLK_STARE, + TLK_USE, + TLK_UNUSE, + TLK_STOP, + TLK_NOSHOOT, + TLK_HELLO, + TLK_PHELLO, + TLK_PIDLE, + TLK_PQUESTION, + TLK_PLHURT1, + TLK_PLHURT2, + TLK_PLHURT3, + TLK_SMELL, + TLK_WOUND, + TLK_MORTAL, + + TLK_CGROUPS, // MUST be last entry +} TALKGROUPNAMES; + + +enum +{ + SCHED_CANT_FOLLOW = LAST_COMMON_SCHEDULE + 1, + SCHED_MOVE_AWAY, // Try to get out of the player's way + SCHED_MOVE_AWAY_FOLLOW, // same, but follow afterward + SCHED_MOVE_AWAY_FAIL, // Turn back toward player + + LAST_TALKMONSTER_SCHEDULE, // MUST be last +}; + +enum +{ + TASK_CANT_FOLLOW = LAST_COMMON_TASK + 1, + TASK_MOVE_AWAY_PATH, + TASK_WALK_PATH_FOR_UNITS, + + TASK_TLK_RESPOND, // say my response + TASK_TLK_SPEAK, // question or remark + TASK_TLK_HELLO, // Try to say hello to player + TASK_TLK_HEADRESET, // reset head position + TASK_TLK_STOPSHOOTING, // tell player to stop shooting friend + TASK_TLK_STARE, // let the player know I know he's staring at me. + TASK_TLK_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle. + TASK_TLK_CLIENT_STARE, // same as look at client, but says something if the player stares. + TASK_TLK_EYECONTACT, // maintain eyecontact with person who I'm talking to + TASK_TLK_IDEALYAW, // set ideal yaw to face who I'm talking to + TASK_FACE_PLAYER, // Face the player + + LAST_TALKMONSTER_TASK, // MUST be last +}; + +class CMTalkMonster : public CMBaseMonster +{ +public: + void TalkInit( void ); + edict_t *FindNearestFriend(BOOL fPlayer); + float TargetDistance( void ); + void StopTalking( void ) { SentenceStop(); } + + // Base Monster functions + void Precache( void ); + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType); + void TalkTouch( edict_t *pOther ); + void Killed( entvars_t *pevAttacker, int iGib ); + int IRelationship ( CMBaseEntity *pTarget ); + virtual int CanPlaySentence( BOOL fDisregardState ); + virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ); + void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, edict_t *pListener ); + void KeyValue( KeyValueData *pkvd ); + + // AI functions + void SetActivity ( Activity newActivity ); + Schedule_t *GetScheduleOfType ( int Type ); + void StartTask( Task_t *pTask ); + void RunTask( Task_t *pTask ); + void HandleAnimEvent( MonsterEvent_t *pEvent ); + void PrescheduleThink( void ); + + + // Conversations / communication + int GetVoicePitch( void ); + void IdleRespond( void ); + int FIdleSpeak( void ); + int FIdleStare( void ); + int FIdleHello( void ); + void IdleHeadTurn( Vector &vecFriend ); + int FOkToSpeak( void ); + void TrySmellTalk( void ); + edict_t *EnumFriends( edict_t *pentPrevious, int listNumber, BOOL bTrace ); + void AlertFriends( void ); + void ShutUpFriends( void ); + BOOL IsTalking( void ); + void Talk( float flDuration ); + // For following + BOOL CanFollow( void ); + BOOL IsFollowing( void ) { return m_hTargetEnt != NULL && UTIL_IsPlayer(m_hTargetEnt); } + void StopFollowing( BOOL clearSchedule ); + void StartFollowing( edict_t *pLeader ); + virtual void DeclineFollowing( void ) {} + void LimitFollowers( edict_t *pPlayer, int maxFollowers ); + + void EXPORT FollowerUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + + virtual void SetAnswerQuestion( edict_t *pSpeaker ); + virtual int FriendNumber( int arrayNumber ) { return arrayNumber; } + + static char *m_szFriends[TLK_CFRIENDS]; // array of friend names + static float g_talkWaitTime; + + int m_bitsSaid; // set bits for sentences we don't want repeated + int m_nSpeak; // number of times initiated talking + int m_voicePitch; // pitch of voice for this head + const char *m_szGrp[TLK_CGROUPS]; // sentence group names + float m_useTime; // Don't allow +USE until this time + int m_iszUse; // Custom +USE sentence group (follow) + int m_iszUnUse; // Custom +USE sentence group (stop following) + + float m_flLastSaidSmelled;// last time we talked about something that stinks + float m_flStopTalkTime;// when in the future that I'll be done saying this sentence. + + EHANDLE m_hTalkTarget; // who to look at while talking + CUSTOM_SCHEDULES; +}; + + +// Clients can push talkmonsters out of their way +#define bits_COND_CLIENT_PUSH ( bits_COND_SPECIAL1 ) +// Don't see a client right now. +#define bits_COND_CLIENT_UNSEEN ( bits_COND_SPECIAL2 ) + + +#endif //TALKMONSTER_H diff --git a/src/dlls/combat.cpp b/src/dlls/combat.cpp index 0fe4d4a..5afbd27 100644 --- a/src/dlls/combat.cpp +++ b/src/dlls/combat.cpp @@ -1,1639 +1,1639 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== combat.cpp ======================================================== - - functions dealing with damage infliction & death - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "decals.h" -#include "animation.h" -#include "weapons.h" -#include "func_break.h" - -const Vector g_vecZero = Vector(0,0,0); -Vector g_vecAttackDir; -entvars_t *g_pevLastInflictor; - -#define HUMAN_GIB_COUNT 6 -#define ALIEN_GIB_COUNT 4 - - -// HACKHACK -- The gib velocity equations don't work -void CMGib :: LimitVelocity( void ) -{ - float length = pev->velocity.Length(); - - // ceiling at 1500. The gib velocity equation is not bounded properly. Rather than tune it - // in 3 separate places again, I'll just limit it here. - if ( length > 1500.0 ) - pev->velocity = pev->velocity.Normalize() * 1500; // This should really be sv_maxvelocity * 0.75 or something -} - - -void CMGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ) -{ - int i; - - for ( i = 0 ; i < cGibs ; i++ ) - { - CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); - - if (pGib == NULL) // no free monster edicts left? - continue; - - pGib->Spawn( "models/stickygib.mdl" ); - pGib->pev->body = RANDOM_LONG(0,2); - - if ( pevVictim ) - { - pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT( -3, 3 ); - pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT( -3, 3 ); - pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT( -3, 3 ); - - /* - pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ); - */ - - // make the gib fly away from the attack vector - pGib->pev->velocity = g_vecAttackDir * -1; - - // mix in some noise - pGib->pev->velocity.x += RANDOM_FLOAT ( -0.15, 0.15 ); - pGib->pev->velocity.y += RANDOM_FLOAT ( -0.15, 0.15 ); - pGib->pev->velocity.z += RANDOM_FLOAT ( -0.15, 0.15 ); - - pGib->pev->velocity = pGib->pev->velocity * 900; - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 250, 400 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 250, 400 ); - - // copy owner's blood color - CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); - if (pBlood != NULL) - pGib->m_bloodColor = pBlood->BloodColor(); - else - pGib->m_bloodColor = BLOOD_COLOR_RED; - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - - - pGib->pev->movetype = MOVETYPE_TOSS; - pGib->pev->solid = SOLID_BBOX; - UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); - pGib->SetTouch ( &CMGib::StickyGibTouch ); - pGib->SetThink (NULL); - } - pGib->LimitVelocity(); - } -} - -void CMGib :: SpawnHeadGib( entvars_t *pevVictim ) -{ - CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); - - if (pGib == NULL) // no free monster edicts left? - return; - - pGib->Spawn( "models/hgibs.mdl" );// throw one head - pGib->pev->body = 0; - - if ( pevVictim ) - { - pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; - - edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); - - if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer ) - { - // 5% chance head will be thrown at player's face. - entvars_t *pevPlayer; - - pevPlayer = VARS( pentPlayer ); - pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300; - pGib->pev->velocity.z += 100; - } - else - { - pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); - } - - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - // copy owner's blood color - CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); - if (pBlood != NULL) - pGib->m_bloodColor = pBlood->BloodColor(); - else - pGib->m_bloodColor = BLOOD_COLOR_RED; - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - } - pGib->LimitVelocity(); -} - -// Overload -void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) -{ - if ( human ) - CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/hgibs.mdl", human ); - else - CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/agibs.mdl", human ); -} -void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human ) -{ - int cSplat; - - for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ ) - { - CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); - - if (pGib == NULL) // no free monster edicts left? - continue; - - if ( human ) - { - // human pieces - pGib->Spawn( pGibModel ); - pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) - } - else - { - // aliens - pGib->Spawn( pGibModel ); - pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); - } - - if ( pevVictim ) - { - // spawn the gib somewhere in the monster's bounding volume - pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); - pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ) + 1; // absmin.z is in the floor because the engine subtracts 1 to enlarge the box - - // make the gib fly away from the attack vector - pGib->pev->velocity = g_vecAttackDir * -1; - - // mix in some noise - pGib->pev->velocity.x += RANDOM_FLOAT ( -0.25, 0.25 ); - pGib->pev->velocity.y += RANDOM_FLOAT ( -0.25, 0.25 ); - pGib->pev->velocity.z += RANDOM_FLOAT ( -0.25, 0.25 ); - - pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT ( 300, 400 ); - - pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); - pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); - - // copy owner's blood color - CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); - if (pBlood != NULL) - pGib->m_bloodColor = pBlood->BloodColor(); - else - pGib->m_bloodColor = BLOOD_COLOR_RED; - - if ( pevVictim->health > -50) - { - pGib->pev->velocity = pGib->pev->velocity * 0.7; - } - else if ( pevVictim->health > -200) - { - pGib->pev->velocity = pGib->pev->velocity * 2; - } - else - { - pGib->pev->velocity = pGib->pev->velocity * 4; - } - - pGib->pev->solid = SOLID_BBOX; - UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) ); - } - pGib->LimitVelocity(); - } -} - - -BOOL CMBaseMonster :: HasHumanGibs( void ) -{ - int myClass = Classify(); - - if ( myClass == CLASS_HUMAN_MILITARY || - myClass == CLASS_PLAYER_ALLY || - myClass == CLASS_HUMAN_PASSIVE || - myClass == CLASS_PLAYER ) - - return TRUE; - - return FALSE; -} - - -BOOL CMBaseMonster :: HasAlienGibs( void ) -{ - int myClass = Classify(); - - if ( myClass == CLASS_ALIEN_MILITARY || - myClass == CLASS_ALIEN_MONSTER || - myClass == CLASS_ALIEN_PASSIVE || - myClass == CLASS_INSECT || - myClass == CLASS_ALIEN_PREDATOR || - myClass == CLASS_ALIEN_PREY ) - - return TRUE; - - return FALSE; -} - - -void CMBaseMonster::FadeMonster( void ) -{ - StopAnimation(); - pev->velocity = g_vecZero; - pev->movetype = MOVETYPE_NONE; - pev->avelocity = g_vecZero; - pev->animtime = gpGlobals->time; - pev->effects |= EF_NOINTERP; - SUB_StartFadeOut(); -} - -//========================================================= -// GibMonster - create some gore and get rid of a monster's -// model. -//========================================================= -void CMBaseMonster :: GibMonster( void ) -{ - TraceResult tr; - BOOL gibbed = FALSE; - - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM); - - // only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs - if ( HasHumanGibs() ) - { - if ( CVAR_GET_FLOAT("violence_hgibs") != 0 ) // Only the player will ever get here - { - CMGib::SpawnHeadGib( pev ); - CMGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs. - } - gibbed = TRUE; - } - else if ( HasAlienGibs() ) - { - if ( CVAR_GET_FLOAT("violence_agibs") != 0 ) // Should never get here, but someone might call it directly - { - CMGib::SpawnRandomGibs( pev, 4, 0 ); // Throw alien gibs - } - gibbed = TRUE; - } - - if ( !IsPlayer() ) - { - if ( gibbed ) - { - // don't remove players! - SetThink ( &CMBaseMonster::SUB_Remove ); - pev->nextthink = gpGlobals->time; - } - else - { - FadeMonster(); - } - } -} - -//========================================================= -// GetDeathActivity - determines the best type of death -// anim to play. -//========================================================= -Activity CMBaseMonster :: GetDeathActivity ( void ) -{ - Activity deathActivity; - BOOL fTriedDirection; - float flDot; - TraceResult tr; - Vector vecSrc; - - if ( pev->deadflag != DEAD_NO ) - { - // don't run this while dying. - return m_IdealActivity; - } - - vecSrc = Center(); - - fTriedDirection = FALSE; - deathActivity = ACT_DIESIMPLE;// in case we can't find any special deaths to do. - - UTIL_MakeVectors ( pev->angles ); - flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); - - switch ( m_LastHitGroup ) - { - // try to pick a region-specific death. - case HITGROUP_HEAD: - deathActivity = ACT_DIE_HEADSHOT; - break; - - case HITGROUP_STOMACH: - deathActivity = ACT_DIE_GUTSHOT; - break; - - case HITGROUP_GENERIC: - // try to pick a death based on attack direction - fTriedDirection = TRUE; - - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - break; - - default: - // try to pick a death based on attack direction - fTriedDirection = TRUE; - - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - break; - } - - - // can we perform the prescribed death? - if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - // no! did we fail to perform a directional death? - if ( fTriedDirection ) - { - // if yes, we're out of options. Go simple. - deathActivity = ACT_DIESIMPLE; - } - else - { - // cannot perform the ideal region-specific death, so try a direction. - if ( flDot > 0.3 ) - { - deathActivity = ACT_DIEFORWARD; - } - else if ( flDot <= -0.3 ) - { - deathActivity = ACT_DIEBACKWARD; - } - } - } - - if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - // if we're still invalid, simple is our only option. - deathActivity = ACT_DIESIMPLE; - } - - if ( deathActivity == ACT_DIEFORWARD ) - { - // make sure there's room to fall forward - UTIL_TraceHull ( vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); - - if ( tr.flFraction != 1.0 ) - { - deathActivity = ACT_DIESIMPLE; - } - } - - if ( deathActivity == ACT_DIEBACKWARD ) - { - // make sure there's room to fall backward - UTIL_TraceHull ( vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); - - if ( tr.flFraction != 1.0 ) - { - deathActivity = ACT_DIESIMPLE; - } - } - - return deathActivity; -} - -//========================================================= -// GetSmallFlinchActivity - determines the best type of flinch -// anim to play. -//========================================================= -Activity CMBaseMonster :: GetSmallFlinchActivity ( void ) -{ - Activity flinchActivity; - BOOL fTriedDirection; - float flDot; - - fTriedDirection = FALSE; - UTIL_MakeVectors ( pev->angles ); - flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); - - switch ( m_LastHitGroup ) - { - // pick a region-specific flinch - case HITGROUP_HEAD: - flinchActivity = ACT_FLINCH_HEAD; - break; - case HITGROUP_STOMACH: - flinchActivity = ACT_FLINCH_STOMACH; - break; - case HITGROUP_LEFTARM: - flinchActivity = ACT_FLINCH_LEFTARM; - break; - case HITGROUP_RIGHTARM: - flinchActivity = ACT_FLINCH_RIGHTARM; - break; - case HITGROUP_LEFTLEG: - flinchActivity = ACT_FLINCH_LEFTLEG; - break; - case HITGROUP_RIGHTLEG: - flinchActivity = ACT_FLINCH_RIGHTLEG; - break; - case HITGROUP_GENERIC: - default: - // just get a generic flinch. - flinchActivity = ACT_SMALL_FLINCH; - break; - } - - - // do we have a sequence for the ideal activity? - if ( LookupActivity ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) - { - flinchActivity = ACT_SMALL_FLINCH; - } - - return flinchActivity; -} - - -void CMBaseMonster::BecomeDead( void ) -{ - pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses. - - // give the corpse half of the monster's original maximum health. - pev->health = pev->max_health / 2; - pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place. - - // make the corpse fly away from the attack vector - pev->movetype = MOVETYPE_TOSS; - //pev->flags &= ~FL_ONGROUND; - //pev->origin.z += 2; - //pev->velocity = g_vecAttackDir * -1; - //pev->velocity = pev->velocity * RANDOM_FLOAT( 300, 400 ); -} - - -BOOL CMBaseMonster::ShouldGibMonster( int iGib ) -{ - if ( ( iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE ) || ( iGib == GIB_ALWAYS ) ) - return TRUE; - - return FALSE; -} - - -void CMBaseMonster::CallGibMonster( void ) -{ - BOOL fade = FALSE; - - if ( HasHumanGibs() ) - { - if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) - fade = TRUE; - } - else if ( HasAlienGibs() ) - { - if ( CVAR_GET_FLOAT("violence_agibs") == 0 ) - fade = TRUE; - } - - pev->takedamage = DAMAGE_NO; - pev->solid = SOLID_NOT;// do something with the body. while monster blows up - - if ( fade ) - { - FadeMonster(); - } - else - { - pev->effects = EF_NODRAW; // make the model invisible. - GibMonster(); - } - - pev->deadflag = DEAD_DEAD; - FCheckAITrigger(); - - // don't let the status bar glitch for players.with <0 health. - if (pev->health < -99) - { - pev->health = 0; - pev->fuser4 = pev->health; - } - - if ( ShouldFadeOnDeath() && !fade ) - UTIL_Remove(this->edict()); -} - - -/* -============ -Killed -============ -*/ -void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - unsigned int cCount = 0; - BOOL fDone = FALSE; - - // If a player killed this monster, add score - if ( UTIL_IsPlayer( ENT( pevAttacker ) ) ) - pevAttacker->frags += 1.0; - - if ( HasMemory( bits_MEMORY_KILLED ) ) - { - if ( ShouldGibMonster( iGib ) ) - CallGibMonster(); - return; - } - - Remember( bits_MEMORY_KILLED ); - - // clear the deceased's sound channels.(may have been firing or reloading when killed) - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/null.wav", 1, ATTN_NORM); - m_IdealMonsterState = MONSTERSTATE_DEAD; - // Make sure this condition is fired too (TakeDamage breaks out before this happens on death) - SetConditions( bits_COND_LIGHT_DAMAGE ); - - // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. -/*jlb monstermaker - CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner); - if ( pOwner ) - { -//jlb it crashes here sometimes!!! - pOwner->DeathNotice( pev ); - } -jlb*/ - - if ( ShouldGibMonster( iGib ) ) - { - CallGibMonster(); - return; - } - else if ( pev->flags & FL_MONSTER ) - { - SetTouch( NULL ); - BecomeDead(); - } - - // don't let the status bar glitch for players.with <0 health. - if (pev->health < -99) - { - pev->health = 0; - pev->fuser4 = pev->health; - } - - //pev->enemy = ENT( pevAttacker );//why? (sjb) - - m_IdealMonsterState = MONSTERSTATE_DEAD; -} - -// -// fade out - slowly fades a entity out, then removes it. -// -// DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER! -// SET A FUTURE THINK AND A RENDERMODE!! -void CMBaseEntity :: SUB_StartFadeOut ( void ) -{ - if (pev->rendermode == kRenderNormal) - { - pev->renderamt = 255; - pev->rendermode = kRenderTransTexture; - } - - pev->solid = SOLID_NOT; - pev->avelocity = g_vecZero; - - pev->nextthink = gpGlobals->time + 0.1; - SetThink ( &CMBaseEntity::SUB_FadeOut ); -} - -void CMBaseEntity :: SUB_FadeOut ( void ) -{ - if ( pev->renderamt > 7 ) - { - pev->renderamt -= 7; - pev->nextthink = gpGlobals->time + 0.1; - } - else - { - pev->renderamt = 0; - pev->nextthink = gpGlobals->time + 0.2; - SetThink ( &CMBaseEntity::SUB_Remove ); - } -} - -//========================================================= -// WaitTillLand - in order to emit their meaty scent from -// the proper location, gibs should wait until they stop -// bouncing to emit their scent. That's what this function -// does. -//========================================================= -void CMGib :: WaitTillLand ( void ) -{ - if (!IsInWorld()) - { - UTIL_Remove( this->edict() ); - return; - } - - if ( pev->velocity == g_vecZero ) - { - SetThink ( &CMGib::SUB_StartFadeOut ); - pev->nextthink = gpGlobals->time + m_lifeTime; - } - else - { - // wait and check again in another half second. - pev->nextthink = gpGlobals->time + 0.5; - } -} - -// -// Gib bounces on the ground or wall, sponges some blood down, too! -// -void CMGib :: BounceGibTouch ( edict_t *pOther ) -{ - Vector vecSpot; - TraceResult tr; - - //if ( RANDOM_LONG(0,1) ) - // return;// don't bleed everytime - - if (pev->flags & FL_ONGROUND) - { - pev->velocity = pev->velocity * 0.9; - pev->angles.x = 0; - pev->angles.z = 0; - pev->avelocity.x = 0; - pev->avelocity.z = 0; - } - else - { - if ( m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED ) - { - vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down. - UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); - - UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - m_cBloodDecals--; - } - } -} - -// -// Sticky gib puts blood on the wall and stays put. -// -void CMGib :: StickyGibTouch ( edict_t *pOther ) -{ - Vector vecSpot; - TraceResult tr; - - SetThink ( &CMGib::SUB_Remove ); - pev->nextthink = gpGlobals->time + 5; - - if (!FStrEq(STRING(pOther->v.classname), "worldspawn")) - { - pev->nextthink = gpGlobals->time; - return; - } - - UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), & tr); - - UTIL_BloodDecalTrace( &tr, m_bloodColor ); - - pev->velocity = tr.vecPlaneNormal * -1; - pev->angles = UTIL_VecToAngles ( pev->velocity ); - pev->velocity = g_vecZero; - pev->avelocity = g_vecZero; - pev->movetype = MOVETYPE_NONE; -} - -// -// Throw a chunk -// -void CMGib :: Spawn( const char *szGibModel ) -{ - pev->movetype = MOVETYPE_BOUNCE; - pev->friction = 0.55; // deading the bounce a bit - - // sometimes an entity inherits the edict from a former piece of glass, - // and will spawn using the same render FX or rendermode! bad! - pev->renderamt = 255; - pev->rendermode = kRenderNormal; - pev->renderfx = kRenderFxNone; - pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap - pev->classname = MAKE_STRING("gib"); - - SET_MODEL(ENT(pev), szGibModel); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - - pev->nextthink = gpGlobals->time + 4; - m_lifeTime = 10; - SetThink ( &CMGib::WaitTillLand ); - SetTouch ( &CMGib::BounceGibTouch ); - - m_material = matNone; - m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). -} - -// take health -int CMBaseMonster :: TakeHealth (float flHealth, int bitsDamageType) -{ - if (!pev->takedamage) - return 0; - - // clear out any damage types we healed. - // UNDONE: generic health should not heal any - // UNDONE: time-based damage - - m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); - - return CMBaseEntity::TakeHealth(flHealth, bitsDamageType); -} - -/* -============ -TakeDamage - -The damage is coming from inflictor, but get mad at attacker -This should be the only function that ever reduces health. -bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK - -Time-based damage: only occurs while the monster is within the trigger_hurt. -When a monster is poisoned via an arrow etc it takes all the poison damage at once. - - - -============ -*/ -int CMBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - float flTake; - Vector vecDir; - - if (!pev->takedamage) - return 0; - - if ( !IsAlive() ) - { - return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); - } - - if ( pev->deadflag == DEAD_NO ) - { - // no pain sound during death animation. - PainSound();// "Ouch!" - } - - //!!!LATER - make armor consideration here! - flTake = flDamage; - - // set damage type sustained - m_bitsDamageType |= bitsDamageType; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = Vector( 0, 0, 0 ); - - if (!FNullEnt( pevInflictor )) - { - edict_t *pInflictor = ENT(pevInflictor); - if (pInflictor) - { - vecDir = ( UTIL_Center(pInflictor) - Vector ( 0, 0, 10 ) - Center() ).Normalize(); - vecDir = g_vecAttackDir = vecDir.Normalize(); - } - } - - // add to the damage total for clients, which will be sent as a single - // message at the end of the frame - // todo: remove after combining shotgun blasts? - if ( IsPlayer() ) - { - if ( pevInflictor ) - pev->dmg_inflictor = ENT(pevInflictor); - - pev->dmg_take += flTake; - - // check for godmode or invincibility - if ( pev->flags & FL_GODMODE ) - { - return 0; - } - } - - // if this is a player, move him around! - if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) ) - { - pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); - } - - // do the damage - pev->health -= flTake; - - if (pev->flags & FL_MONSTER) - pev->fuser4 = pev->health; - - // HACKHACK Don't kill monsters in a script. Let them break their scripts first - if ( m_MonsterState == MONSTERSTATE_SCRIPT ) - { - SetConditions( bits_COND_LIGHT_DAMAGE ); - return 0; - } - - if ( pev->health <= 0 ) - { - g_pevLastInflictor = pevInflictor; - - if ( bitsDamageType & DMG_ALWAYSGIB ) - { - Killed( pevAttacker, GIB_ALWAYS ); - } - else if ( bitsDamageType & DMG_NEVERGIB ) - { - Killed( pevAttacker, GIB_NEVER ); - } - else - { - Killed( pevAttacker, GIB_NORMAL ); - } - - g_pevLastInflictor = NULL; - - return 0; - } - - // react to the damage (get mad) - if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) - { - if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) - {// only if the attack was a monster or client! - - // enemy's last known position is somewhere down the vector that the attack came from. - if (pevInflictor) - { - edict_t *pEdict = m_hEnemy; - if (m_hEnemy == NULL || pevInflictor == VARS(pEdict) || !HasConditions(bits_COND_SEE_ENEMY)) - { - m_vecEnemyLKP = pevInflictor->origin; - } - } - else - { - m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); - } - - MakeIdealYaw( m_vecEnemyLKP ); - - // add pain to the conditions - // !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and - // heavy damage per monster class? - if ( flDamage > 0 ) - { - SetConditions(bits_COND_LIGHT_DAMAGE); - } - - if ( flDamage >= 20 ) - { - SetConditions(bits_COND_HEAVY_DAMAGE); - } - } - } - - return 1; -} - -//========================================================= -// DeadTakeDamage - takedamage function called when a monster's -// corpse is damaged. -//========================================================= -int CMBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - Vector vecDir; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = Vector( 0, 0, 0 ); - if (!FNullEnt( pevInflictor )) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pevInflictor)); - if (pMonster) - { - vecDir = ( pMonster->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); - vecDir = g_vecAttackDir = vecDir.Normalize(); - } - } - -#if 0// turn this back on when the bounding box issues are resolved. - - pev->flags &= ~FL_ONGROUND; - pev->origin.z += 1; - - // let the damage scoot the corpse around a bit. - if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) ) - { - pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); - } - -#endif - - // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. - if ( bitsDamageType & DMG_GIB_CORPSE ) - { - if ( pev->health <= flDamage ) - { - pev->health = -50; - pev->fuser4 = pev->health; - Killed( pevAttacker, GIB_ALWAYS ); - return 0; - } - // Accumulate corpse gibbing damage, so you can gib with multiple hits - pev->health -= flDamage * 0.1; - pev->fuser4 = pev->health; - } - - return 1; -} - - -float CMBaseMonster :: DamageForce( float damage ) -{ - float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; - - if ( force > 1000.0) - { - force = 1000.0; - } - - return force; -} - -// -// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range. -// -// only damage ents that can clearly be seen by the explosion! - - -void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType ) -{ - edict_t *pEntity = NULL; - TraceResult tr; - float flAdjustedDamage, falloff; - Vector vecSpot; - - if ( flRadius ) - falloff = flDamage / flRadius; - else - falloff = 1.0; - - int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER); - - vecSrc.z += 1;// in case grenade is lying on the ground - - if ( !pevAttacker ) - pevAttacker = pevInflictor; - - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) - { - if ( pEntity->v.takedamage != DAMAGE_NO ) - { - if (UTIL_IsPlayer(pEntity)) - { - // blast's don't tavel into or out of water - if (bInWater && pEntity->v.waterlevel == 0) - continue; - if (!bInWater && pEntity->v.waterlevel == 3) - continue; - - vecSpot = UTIL_BodyTarget( pEntity, vecSrc ); - - UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); - - if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) - {// the explosion can 'see' this entity, so hurt them! - if (tr.fStartSolid) - { - // if we're stuck inside them, fixup the position and distance - tr.vecEndPos = vecSrc; - tr.flFraction = 0.0; - } - - // decrease damage for an ent that's farther from the bomb. - flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; - flAdjustedDamage = flDamage - flAdjustedDamage; - - if ( flAdjustedDamage < 0 ) - { - flAdjustedDamage = 0; - } - - // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) ); - if (tr.flFraction != 1.0) - { - ClearMultiDamage( ); - UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType ); - ApplyMultiDamage( pevInflictor, pevAttacker ); - } - else - { - UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); - } - } - } - else if (pEntity->v.euser4 != NULL) - { - // UNDONE: this should check a damage mask, not an ignore - CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity)); - - if ( iClassIgnore != CLASS_NONE && pMonster->Classify() == iClassIgnore ) - {// houndeyes don't hurt other houndeyes with their attack - continue; - } - - // blast's don't tavel into or out of water - if (bInWater && pEntity->v.waterlevel == 0) - continue; - if (!bInWater && pEntity->v.waterlevel == 3) - continue; - - vecSpot = pMonster->BodyTarget( vecSrc ); - - UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); - - if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) - {// the explosion can 'see' this entity, so hurt them! - if (tr.fStartSolid) - { - // if we're stuck inside them, fixup the position and distance - tr.vecEndPos = vecSrc; - tr.flFraction = 0.0; - } - - // decrease damage for an ent that's farther from the bomb. - flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; - flAdjustedDamage = flDamage - flAdjustedDamage; - - if ( flAdjustedDamage < 0 ) - { - flAdjustedDamage = 0; - } - - // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) ); - if (tr.flFraction != 1.0) - { - ClearMultiDamage( ); - pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType ); - ApplyMultiDamage( pevInflictor, pevAttacker ); - } - else - { - pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); - } - } - } - } - } -} - - -void CMBaseMonster :: RadiusDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) -{ - ::RadiusDamage( pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); -} - - -void CMBaseMonster :: RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) -{ - ::RadiusDamage( vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); -} - - -//========================================================= -// CheckTraceHullAttack - expects a length to trace, amount -// of damage to do, and damage type. Returns a pointer to -// the damaged entity in case the monster wishes to do -// other stuff to the victim (punchangle, etc) -// -// Used for many contact-range melee attacks. Bites, claws, etc. -//========================================================= -edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int iDmgType ) -{ - TraceResult tr; - - if (IsPlayer()) - UTIL_MakeVectors( pev->angles ); - else - UTIL_MakeAimVectors( pev->angles ); - - Vector vecStart = pev->origin; - vecStart.z += pev->size.z * 0.5; - Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist ); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit ) - { - edict_t *pEntity = tr.pHit; - - if ( iDamage > 0 ) - { - if (UTIL_IsPlayer(pEntity)) - UTIL_TakeDamage( pEntity, pev, pev, iDamage, iDmgType ); - else if (pEntity->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); - } - } - - return pEntity; - } - - return NULL; -} - - -/* -================ -TraceAttack -================ -*/ -void CMBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - Vector vecOrigin = ptr->vecEndPos - vecDir * 4; - - if ( pev->takedamage ) - { - AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); - - int blood = BloodColor(); - - if ( blood != DONT_BLEED ) - { - SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - } - } -} - - -/* -//========================================================= -// TraceAttack -//========================================================= -void CMBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - Vector vecOrigin = ptr->vecEndPos - vecDir * 4; - - ALERT ( at_console, "%d\n", ptr->iHitgroup ); - - - if ( pev->takedamage ) - { - AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); - - int blood = BloodColor(); - - if ( blood != DONT_BLEED ) - { - SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. - } - } -} -*/ - -//========================================================= -// TraceAttack -//========================================================= -void CMBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( pev->takedamage ) - { - m_LastHitGroup = ptr->iHitgroup; - -/*jlb - switch ( ptr->iHitgroup ) - { - case HITGROUP_GENERIC: - break; - case HITGROUP_HEAD: - flDamage *= gSkillData.monHead; - break; - case HITGROUP_CHEST: - flDamage *= gSkillData.monChest; - break; - case HITGROUP_STOMACH: - flDamage *= gSkillData.monStomach; - break; - case HITGROUP_LEFTARM: - case HITGROUP_RIGHTARM: - flDamage *= gSkillData.monArm; - break; - case HITGROUP_LEFTLEG: - case HITGROUP_RIGHTLEG: - flDamage *= gSkillData.monLeg; - break; - default: - break; - } -jlb*/ - SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. - TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); - AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); - } -} - -/* -================ -FireBullets - -Go to the trouble of combining multiple pellets into a single damage call. - -This version is used by Monsters. -================ -*/ -void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker ) -{ - static int tracerCount; - int tracer; - TraceResult tr; - Vector vecRight = gpGlobals->v_right; - Vector vecUp = gpGlobals->v_up; - - if ( pevAttacker == NULL ) - pevAttacker = pev; // the default attacker is ourselves - - ClearMultiDamage(); - gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB; - - for (ULONG iShot = 1; iShot <= cShots; iShot++) - { - // get circular gaussian spread - float x, y, z; - 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 = vecDirShooting + - x * vecSpread.x * vecRight + - y * vecSpread.y * vecUp; - Vector vecEnd; - - vecEnd = vecSrc + vecDir * flDistance; - UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); - - tracer = 0; - if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) - { - Vector vecTracerSrc; - - if ( IsPlayer() ) - {// adjust tracer position for player - vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; - } - else - { - vecTracerSrc = vecSrc; - } - - if ( iTracerFreq != 1 ) // guns that always trace also always decal - tracer = 1; - switch( iBulletType ) - { - case BULLET_MONSTER_MP5: - case BULLET_MONSTER_9MM: - case BULLET_MONSTER_12MM: - case BULLET_MONSTER_762: - case BULLET_MONSTER_357: - default: - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); - WRITE_BYTE( TE_TRACER ); - WRITE_COORD( vecTracerSrc.x ); - WRITE_COORD( vecTracerSrc.y ); - WRITE_COORD( vecTracerSrc.z ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - MESSAGE_END(); - break; - } - } - // do damage, paint decals - if (tr.flFraction != 1.0) - { - if (UTIL_IsPlayer(tr.pHit)) // is this a player? - { - edict_t *pPlayer = tr.pHit; - - if ( iDamage ) - { - UTIL_TraceAttack(pPlayer, pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - } - else switch(iBulletType) - { - default: - case BULLET_MONSTER_9MM: - UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_MP5: - UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_12MM: - UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); - if ( !tracer ) - { - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - } - break; - - case BULLET_MONSTER_762: - UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_357: - UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_NONE: // FIX - UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - // only decal glass - if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) - { - UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); - } - - break; - } - } - else if (tr.pHit->v.euser4 != NULL) - { - CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); - - if ( iDamage ) - { - pMonster->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - } - else switch(iBulletType) - { - default: - case BULLET_MONSTER_9MM: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_MP5: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_12MM: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); - if ( !tracer ) - { - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - } - break; - - case BULLET_MONSTER_762: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_MONSTER_357: - pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - DecalGunshot( &tr, iBulletType ); - - break; - - case BULLET_NONE: // FIX - pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); - TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); - // only decal glass - if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) - { - UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); - } - - break; - } - } - } - // make bullet trails - UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); - } - ApplyMultiDamage(pev, pevAttacker); -} - - -void CMBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - if (BloodColor() == DONT_BLEED) - return; - - if (flDamage == 0) - return; - - if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR))) - return; - - // make blood decal on the wall! - TraceResult Bloodtr; - Vector vecTraceDir; - float flNoise; - int cCount; - int i; - -/* - if ( !IsAlive() ) - { - // dealing with a dead monster. - if ( pev->max_health <= 0 ) - { - // no blood decal for a monster that has already decalled its limit. - return; - } - else - { - pev->max_health--; - } - } -*/ - - if (flDamage < 10) - { - flNoise = 0.1; - cCount = 1; - } - else if (flDamage < 25) - { - flNoise = 0.2; - cCount = 2; - } - else - { - flNoise = 0.3; - cCount = 4; - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) - - vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); - - UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr); - - if ( Bloodtr.flFraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - } - } -} - -//========================================================= -//========================================================= -void CMBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ) -{ - // make blood decal on the wall! - TraceResult Bloodtr; - Vector vecTraceDir; - int i; - - if ( !IsAlive() ) - { - // dealing with a dead monster. - if ( pev->max_health <= 0 ) - { - // no blood decal for a monster that has already decalled its limit. - return; - } - else - { - pev->max_health--; - } - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir; - - vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); - - UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr); - -/* - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - WRITE_COORD( ptr->vecEndPos.x ); - WRITE_COORD( ptr->vecEndPos.y ); - WRITE_COORD( ptr->vecEndPos.z ); - - WRITE_COORD( Bloodtr.vecEndPos.x ); - WRITE_COORD( Bloodtr.vecEndPos.y ); - WRITE_COORD( Bloodtr.vecEndPos.z ); - MESSAGE_END(); -*/ - - if ( Bloodtr.flFraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); - } - } -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== combat.cpp ======================================================== + + functions dealing with damage infliction & death + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "decals.h" +#include "animation.h" +#include "weapons.h" +#include "func_break.h" + +const Vector g_vecZero = Vector(0,0,0); +Vector g_vecAttackDir; +entvars_t *g_pevLastInflictor; + +#define HUMAN_GIB_COUNT 6 +#define ALIEN_GIB_COUNT 4 + + +// HACKHACK -- The gib velocity equations don't work +void CMGib :: LimitVelocity( void ) +{ + float length = pev->velocity.Length(); + + // ceiling at 1500. The gib velocity equation is not bounded properly. Rather than tune it + // in 3 separate places again, I'll just limit it here. + if ( length > 1500.0 ) + pev->velocity = pev->velocity.Normalize() * 1500; // This should really be sv_maxvelocity * 0.75 or something +} + + +void CMGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ) +{ + int i; + + for ( i = 0 ; i < cGibs ; i++ ) + { + CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); + + if (pGib == NULL) // no free monster edicts left? + continue; + + pGib->Spawn( "models/stickygib.mdl" ); + pGib->pev->body = RANDOM_LONG(0,2); + + if ( pevVictim ) + { + pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT( -3, 3 ); + pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT( -3, 3 ); + pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT( -3, 3 ); + + /* + pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ); + */ + + // make the gib fly away from the attack vector + pGib->pev->velocity = g_vecAttackDir * -1; + + // mix in some noise + pGib->pev->velocity.x += RANDOM_FLOAT ( -0.15, 0.15 ); + pGib->pev->velocity.y += RANDOM_FLOAT ( -0.15, 0.15 ); + pGib->pev->velocity.z += RANDOM_FLOAT ( -0.15, 0.15 ); + + pGib->pev->velocity = pGib->pev->velocity * 900; + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 250, 400 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 250, 400 ); + + // copy owner's blood color + CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); + if (pBlood != NULL) + pGib->m_bloodColor = pBlood->BloodColor(); + else + pGib->m_bloodColor = BLOOD_COLOR_RED; + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + + + pGib->pev->movetype = MOVETYPE_TOSS; + pGib->pev->solid = SOLID_BBOX; + UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) ); + pGib->SetTouch ( &CMGib::StickyGibTouch ); + pGib->SetThink (NULL); + } + pGib->LimitVelocity(); + } +} + +void CMGib :: SpawnHeadGib( entvars_t *pevVictim ) +{ + CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); + + if (pGib == NULL) // no free monster edicts left? + return; + + pGib->Spawn( "models/hgibs.mdl" );// throw one head + pGib->pev->body = 0; + + if ( pevVictim ) + { + pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs; + + edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() ); + + if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer ) + { + // 5% chance head will be thrown at player's face. + entvars_t *pevPlayer; + + pevPlayer = VARS( pentPlayer ); + pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300; + pGib->pev->velocity.z += 100; + } + else + { + pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300)); + } + + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); + + // copy owner's blood color + CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); + if (pBlood != NULL) + pGib->m_bloodColor = pBlood->BloodColor(); + else + pGib->m_bloodColor = BLOOD_COLOR_RED; + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + } + pGib->LimitVelocity(); +} + +// Overload +void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ) +{ + if ( human ) + CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/hgibs.mdl", human ); + else + CMGib::SpawnRandomGibs( pevVictim, cGibs, "models/agibs.mdl", human ); +} +void CMGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human ) +{ + int cSplat; + + for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ ) + { + CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); + + if (pGib == NULL) // no free monster edicts left? + continue; + + if ( human ) + { + // human pieces + pGib->Spawn( pGibModel ); + pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib) + } + else + { + // aliens + pGib->Spawn( pGibModel ); + pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1); + } + + if ( pevVictim ) + { + // spawn the gib somewhere in the monster's bounding volume + pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) ); + pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ) + 1; // absmin.z is in the floor because the engine subtracts 1 to enlarge the box + + // make the gib fly away from the attack vector + pGib->pev->velocity = g_vecAttackDir * -1; + + // mix in some noise + pGib->pev->velocity.x += RANDOM_FLOAT ( -0.25, 0.25 ); + pGib->pev->velocity.y += RANDOM_FLOAT ( -0.25, 0.25 ); + pGib->pev->velocity.z += RANDOM_FLOAT ( -0.25, 0.25 ); + + pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT ( 300, 400 ); + + pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 ); + pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 ); + + // copy owner's blood color + CMBaseMonster *pBlood = GetClassPtr((CMBaseMonster *)VARS(pevVictim)); + if (pBlood != NULL) + pGib->m_bloodColor = pBlood->BloodColor(); + else + pGib->m_bloodColor = BLOOD_COLOR_RED; + + if ( pevVictim->health > -50) + { + pGib->pev->velocity = pGib->pev->velocity * 0.7; + } + else if ( pevVictim->health > -200) + { + pGib->pev->velocity = pGib->pev->velocity * 2; + } + else + { + pGib->pev->velocity = pGib->pev->velocity * 4; + } + + pGib->pev->solid = SOLID_BBOX; + UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) ); + } + pGib->LimitVelocity(); + } +} + + +BOOL CMBaseMonster :: HasHumanGibs( void ) +{ + int myClass = Classify(); + + if ( myClass == CLASS_HUMAN_MILITARY || + myClass == CLASS_PLAYER_ALLY || + myClass == CLASS_HUMAN_PASSIVE || + myClass == CLASS_PLAYER ) + + return TRUE; + + return FALSE; +} + + +BOOL CMBaseMonster :: HasAlienGibs( void ) +{ + int myClass = Classify(); + + if ( myClass == CLASS_ALIEN_MILITARY || + myClass == CLASS_ALIEN_MONSTER || + myClass == CLASS_ALIEN_PASSIVE || + myClass == CLASS_INSECT || + myClass == CLASS_ALIEN_PREDATOR || + myClass == CLASS_ALIEN_PREY ) + + return TRUE; + + return FALSE; +} + + +void CMBaseMonster::FadeMonster( void ) +{ + StopAnimation(); + pev->velocity = g_vecZero; + pev->movetype = MOVETYPE_NONE; + pev->avelocity = g_vecZero; + pev->animtime = gpGlobals->time; + pev->effects |= EF_NOINTERP; + SUB_StartFadeOut(); +} + +//========================================================= +// GibMonster - create some gore and get rid of a monster's +// model. +//========================================================= +void CMBaseMonster :: GibMonster( void ) +{ + TraceResult tr; + BOOL gibbed = FALSE; + + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM); + + // only humans throw skulls !!!UNDONE - eventually monsters will have their own sets of gibs + if ( HasHumanGibs() ) + { + if ( CVAR_GET_FLOAT("violence_hgibs") != 0 ) // Only the player will ever get here + { + CMGib::SpawnHeadGib( pev ); + CMGib::SpawnRandomGibs( pev, 4, 1 ); // throw some human gibs. + } + gibbed = TRUE; + } + else if ( HasAlienGibs() ) + { + if ( CVAR_GET_FLOAT("violence_agibs") != 0 ) // Should never get here, but someone might call it directly + { + CMGib::SpawnRandomGibs( pev, 4, 0 ); // Throw alien gibs + } + gibbed = TRUE; + } + + if ( !IsPlayer() ) + { + if ( gibbed ) + { + // don't remove players! + SetThink ( &CMBaseMonster::SUB_Remove ); + pev->nextthink = gpGlobals->time; + } + else + { + FadeMonster(); + } + } +} + +//========================================================= +// GetDeathActivity - determines the best type of death +// anim to play. +//========================================================= +Activity CMBaseMonster :: GetDeathActivity ( void ) +{ + Activity deathActivity; + BOOL fTriedDirection; + float flDot; + TraceResult tr; + Vector vecSrc; + + if ( pev->deadflag != DEAD_NO ) + { + // don't run this while dying. + return m_IdealActivity; + } + + vecSrc = Center(); + + fTriedDirection = FALSE; + deathActivity = ACT_DIESIMPLE;// in case we can't find any special deaths to do. + + UTIL_MakeVectors ( pev->angles ); + flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); + + switch ( m_LastHitGroup ) + { + // try to pick a region-specific death. + case HITGROUP_HEAD: + deathActivity = ACT_DIE_HEADSHOT; + break; + + case HITGROUP_STOMACH: + deathActivity = ACT_DIE_GUTSHOT; + break; + + case HITGROUP_GENERIC: + // try to pick a death based on attack direction + fTriedDirection = TRUE; + + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + break; + + default: + // try to pick a death based on attack direction + fTriedDirection = TRUE; + + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + break; + } + + + // can we perform the prescribed death? + if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + // no! did we fail to perform a directional death? + if ( fTriedDirection ) + { + // if yes, we're out of options. Go simple. + deathActivity = ACT_DIESIMPLE; + } + else + { + // cannot perform the ideal region-specific death, so try a direction. + if ( flDot > 0.3 ) + { + deathActivity = ACT_DIEFORWARD; + } + else if ( flDot <= -0.3 ) + { + deathActivity = ACT_DIEBACKWARD; + } + } + } + + if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + // if we're still invalid, simple is our only option. + deathActivity = ACT_DIESIMPLE; + } + + if ( deathActivity == ACT_DIEFORWARD ) + { + // make sure there's room to fall forward + UTIL_TraceHull ( vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); + + if ( tr.flFraction != 1.0 ) + { + deathActivity = ACT_DIESIMPLE; + } + } + + if ( deathActivity == ACT_DIEBACKWARD ) + { + // make sure there's room to fall backward + UTIL_TraceHull ( vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr ); + + if ( tr.flFraction != 1.0 ) + { + deathActivity = ACT_DIESIMPLE; + } + } + + return deathActivity; +} + +//========================================================= +// GetSmallFlinchActivity - determines the best type of flinch +// anim to play. +//========================================================= +Activity CMBaseMonster :: GetSmallFlinchActivity ( void ) +{ + Activity flinchActivity; + BOOL fTriedDirection; + float flDot; + + fTriedDirection = FALSE; + UTIL_MakeVectors ( pev->angles ); + flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 ); + + switch ( m_LastHitGroup ) + { + // pick a region-specific flinch + case HITGROUP_HEAD: + flinchActivity = ACT_FLINCH_HEAD; + break; + case HITGROUP_STOMACH: + flinchActivity = ACT_FLINCH_STOMACH; + break; + case HITGROUP_LEFTARM: + flinchActivity = ACT_FLINCH_LEFTARM; + break; + case HITGROUP_RIGHTARM: + flinchActivity = ACT_FLINCH_RIGHTARM; + break; + case HITGROUP_LEFTLEG: + flinchActivity = ACT_FLINCH_LEFTLEG; + break; + case HITGROUP_RIGHTLEG: + flinchActivity = ACT_FLINCH_RIGHTLEG; + break; + case HITGROUP_GENERIC: + default: + // just get a generic flinch. + flinchActivity = ACT_SMALL_FLINCH; + break; + } + + + // do we have a sequence for the ideal activity? + if ( LookupActivity ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE ) + { + flinchActivity = ACT_SMALL_FLINCH; + } + + return flinchActivity; +} + + +void CMBaseMonster::BecomeDead( void ) +{ + pev->takedamage = DAMAGE_YES;// don't let autoaim aim at corpses. + + // give the corpse half of the monster's original maximum health. + pev->health = pev->max_health / 2; + pev->max_health = 5; // max_health now becomes a counter for how many blood decals the corpse can place. + + // make the corpse fly away from the attack vector + pev->movetype = MOVETYPE_TOSS; + //pev->flags &= ~FL_ONGROUND; + //pev->origin.z += 2; + //pev->velocity = g_vecAttackDir * -1; + //pev->velocity = pev->velocity * RANDOM_FLOAT( 300, 400 ); +} + + +BOOL CMBaseMonster::ShouldGibMonster( int iGib ) +{ + if ( ( iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE ) || ( iGib == GIB_ALWAYS ) ) + return TRUE; + + return FALSE; +} + + +void CMBaseMonster::CallGibMonster( void ) +{ + BOOL fade = FALSE; + + if ( HasHumanGibs() ) + { + if ( CVAR_GET_FLOAT("violence_hgibs") == 0 ) + fade = TRUE; + } + else if ( HasAlienGibs() ) + { + if ( CVAR_GET_FLOAT("violence_agibs") == 0 ) + fade = TRUE; + } + + pev->takedamage = DAMAGE_NO; + pev->solid = SOLID_NOT;// do something with the body. while monster blows up + + if ( fade ) + { + FadeMonster(); + } + else + { + pev->effects = EF_NODRAW; // make the model invisible. + GibMonster(); + } + + pev->deadflag = DEAD_DEAD; + FCheckAITrigger(); + + // don't let the status bar glitch for players.with <0 health. + if (pev->health < -99) + { + pev->health = 0; + pev->fuser4 = pev->health; + } + + if ( ShouldFadeOnDeath() && !fade ) + UTIL_Remove(this->edict()); +} + + +/* +============ +Killed +============ +*/ +void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + unsigned int cCount = 0; + BOOL fDone = FALSE; + + // If a player killed this monster, add score + if ( UTIL_IsPlayer( ENT( pevAttacker ) ) ) + pevAttacker->frags += 1.0; + + if ( HasMemory( bits_MEMORY_KILLED ) ) + { + if ( ShouldGibMonster( iGib ) ) + CallGibMonster(); + return; + } + + Remember( bits_MEMORY_KILLED ); + + // clear the deceased's sound channels.(may have been firing or reloading when killed) + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/null.wav", 1, ATTN_NORM); + m_IdealMonsterState = MONSTERSTATE_DEAD; + // Make sure this condition is fired too (TakeDamage breaks out before this happens on death) + SetConditions( bits_COND_LIGHT_DAMAGE ); + + // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. +/*jlb monstermaker + CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner); + if ( pOwner ) + { +//jlb it crashes here sometimes!!! + pOwner->DeathNotice( pev ); + } +jlb*/ + + if ( ShouldGibMonster( iGib ) ) + { + CallGibMonster(); + return; + } + else if ( pev->flags & FL_MONSTER ) + { + SetTouch( NULL ); + BecomeDead(); + } + + // don't let the status bar glitch for players.with <0 health. + if (pev->health < -99) + { + pev->health = 0; + pev->fuser4 = pev->health; + } + + //pev->enemy = ENT( pevAttacker );//why? (sjb) + + m_IdealMonsterState = MONSTERSTATE_DEAD; +} + +// +// fade out - slowly fades a entity out, then removes it. +// +// DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER! +// SET A FUTURE THINK AND A RENDERMODE!! +void CMBaseEntity :: SUB_StartFadeOut ( void ) +{ + if (pev->rendermode == kRenderNormal) + { + pev->renderamt = 255; + pev->rendermode = kRenderTransTexture; + } + + pev->solid = SOLID_NOT; + pev->avelocity = g_vecZero; + + pev->nextthink = gpGlobals->time + 0.1; + SetThink ( &CMBaseEntity::SUB_FadeOut ); +} + +void CMBaseEntity :: SUB_FadeOut ( void ) +{ + if ( pev->renderamt > 7 ) + { + pev->renderamt -= 7; + pev->nextthink = gpGlobals->time + 0.1; + } + else + { + pev->renderamt = 0; + pev->nextthink = gpGlobals->time + 0.2; + SetThink ( &CMBaseEntity::SUB_Remove ); + } +} + +//========================================================= +// WaitTillLand - in order to emit their meaty scent from +// the proper location, gibs should wait until they stop +// bouncing to emit their scent. That's what this function +// does. +//========================================================= +void CMGib :: WaitTillLand ( void ) +{ + if (!IsInWorld()) + { + UTIL_Remove( this->edict() ); + return; + } + + if ( pev->velocity == g_vecZero ) + { + SetThink ( &CMGib::SUB_StartFadeOut ); + pev->nextthink = gpGlobals->time + m_lifeTime; + } + else + { + // wait and check again in another half second. + pev->nextthink = gpGlobals->time + 0.5; + } +} + +// +// Gib bounces on the ground or wall, sponges some blood down, too! +// +void CMGib :: BounceGibTouch ( edict_t *pOther ) +{ + Vector vecSpot; + TraceResult tr; + + //if ( RANDOM_LONG(0,1) ) + // return;// don't bleed everytime + + if (pev->flags & FL_ONGROUND) + { + pev->velocity = pev->velocity * 0.9; + pev->angles.x = 0; + pev->angles.z = 0; + pev->avelocity.x = 0; + pev->avelocity.z = 0; + } + else + { + if ( m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED ) + { + vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down. + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr); + + UTIL_BloodDecalTrace( &tr, m_bloodColor ); + + m_cBloodDecals--; + } + } +} + +// +// Sticky gib puts blood on the wall and stays put. +// +void CMGib :: StickyGibTouch ( edict_t *pOther ) +{ + Vector vecSpot; + TraceResult tr; + + SetThink ( &CMGib::SUB_Remove ); + pev->nextthink = gpGlobals->time + 5; + + if (!FStrEq(STRING(pOther->v.classname), "worldspawn")) + { + pev->nextthink = gpGlobals->time; + return; + } + + UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32, ignore_monsters, ENT(pev), & tr); + + UTIL_BloodDecalTrace( &tr, m_bloodColor ); + + pev->velocity = tr.vecPlaneNormal * -1; + pev->angles = UTIL_VecToAngles ( pev->velocity ); + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + pev->movetype = MOVETYPE_NONE; +} + +// +// Throw a chunk +// +void CMGib :: Spawn( const char *szGibModel ) +{ + pev->movetype = MOVETYPE_BOUNCE; + pev->friction = 0.55; // deading the bounce a bit + + // sometimes an entity inherits the edict from a former piece of glass, + // and will spawn using the same render FX or rendermode! bad! + pev->renderamt = 255; + pev->rendermode = kRenderNormal; + pev->renderfx = kRenderFxNone; + pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap + pev->classname = MAKE_STRING("gib"); + + SET_MODEL(ENT(pev), szGibModel); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + + pev->nextthink = gpGlobals->time + 4; + m_lifeTime = 10; + SetThink ( &CMGib::WaitTillLand ); + SetTouch ( &CMGib::BounceGibTouch ); + + m_material = matNone; + m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). +} + +// take health +int CMBaseMonster :: TakeHealth (float flHealth, int bitsDamageType) +{ + if (!pev->takedamage) + return 0; + + // clear out any damage types we healed. + // UNDONE: generic health should not heal any + // UNDONE: time-based damage + + m_bitsDamageType &= ~(bitsDamageType & ~DMG_TIMEBASED); + + return CMBaseEntity::TakeHealth(flHealth, bitsDamageType); +} + +/* +============ +TakeDamage + +The damage is coming from inflictor, but get mad at attacker +This should be the only function that ever reduces health. +bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK + +Time-based damage: only occurs while the monster is within the trigger_hurt. +When a monster is poisoned via an arrow etc it takes all the poison damage at once. + + + +============ +*/ +int CMBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + float flTake; + Vector vecDir; + + if (!pev->takedamage) + return 0; + + if ( !IsAlive() ) + { + return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); + } + + if ( pev->deadflag == DEAD_NO ) + { + // no pain sound during death animation. + PainSound();// "Ouch!" + } + + //!!!LATER - make armor consideration here! + flTake = flDamage; + + // set damage type sustained + m_bitsDamageType |= bitsDamageType; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = Vector( 0, 0, 0 ); + + if (!FNullEnt( pevInflictor )) + { + edict_t *pInflictor = ENT(pevInflictor); + if (pInflictor) + { + vecDir = ( UTIL_Center(pInflictor) - Vector ( 0, 0, 10 ) - Center() ).Normalize(); + vecDir = g_vecAttackDir = vecDir.Normalize(); + } + } + + // add to the damage total for clients, which will be sent as a single + // message at the end of the frame + // todo: remove after combining shotgun blasts? + if ( IsPlayer() ) + { + if ( pevInflictor ) + pev->dmg_inflictor = ENT(pevInflictor); + + pev->dmg_take += flTake; + + // check for godmode or invincibility + if ( pev->flags & FL_GODMODE ) + { + return 0; + } + } + + // if this is a player, move him around! + if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) ) + { + pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); + } + + // do the damage + pev->health -= flTake; + + if (pev->flags & FL_MONSTER) + pev->fuser4 = pev->health; + + // HACKHACK Don't kill monsters in a script. Let them break their scripts first + if ( m_MonsterState == MONSTERSTATE_SCRIPT ) + { + SetConditions( bits_COND_LIGHT_DAMAGE ); + return 0; + } + + if ( pev->health <= 0 ) + { + g_pevLastInflictor = pevInflictor; + + if ( bitsDamageType & DMG_ALWAYSGIB ) + { + Killed( pevAttacker, GIB_ALWAYS ); + } + else if ( bitsDamageType & DMG_NEVERGIB ) + { + Killed( pevAttacker, GIB_NEVER ); + } + else + { + Killed( pevAttacker, GIB_NORMAL ); + } + + g_pevLastInflictor = NULL; + + return 0; + } + + // react to the damage (get mad) + if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) ) + { + if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) ) + {// only if the attack was a monster or client! + + // enemy's last known position is somewhere down the vector that the attack came from. + if (pevInflictor) + { + edict_t *pEdict = m_hEnemy; + if (m_hEnemy == NULL || pevInflictor == VARS(pEdict) || !HasConditions(bits_COND_SEE_ENEMY)) + { + m_vecEnemyLKP = pevInflictor->origin; + } + } + else + { + m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); + } + + MakeIdealYaw( m_vecEnemyLKP ); + + // add pain to the conditions + // !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and + // heavy damage per monster class? + if ( flDamage > 0 ) + { + SetConditions(bits_COND_LIGHT_DAMAGE); + } + + if ( flDamage >= 20 ) + { + SetConditions(bits_COND_HEAVY_DAMAGE); + } + } + } + + return 1; +} + +//========================================================= +// DeadTakeDamage - takedamage function called when a monster's +// corpse is damaged. +//========================================================= +int CMBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + Vector vecDir; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = Vector( 0, 0, 0 ); + if (!FNullEnt( pevInflictor )) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pevInflictor)); + if (pMonster) + { + vecDir = ( pMonster->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize(); + vecDir = g_vecAttackDir = vecDir.Normalize(); + } + } + +#if 0// turn this back on when the bounding box issues are resolved. + + pev->flags &= ~FL_ONGROUND; + pev->origin.z += 1; + + // let the damage scoot the corpse around a bit. + if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) ) + { + pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage ); + } + +#endif + + // kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse. + if ( bitsDamageType & DMG_GIB_CORPSE ) + { + if ( pev->health <= flDamage ) + { + pev->health = -50; + pev->fuser4 = pev->health; + Killed( pevAttacker, GIB_ALWAYS ); + return 0; + } + // Accumulate corpse gibbing damage, so you can gib with multiple hits + pev->health -= flDamage * 0.1; + pev->fuser4 = pev->health; + } + + return 1; +} + + +float CMBaseMonster :: DamageForce( float damage ) +{ + float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5; + + if ( force > 1000.0) + { + force = 1000.0; + } + + return force; +} + +// +// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range. +// +// only damage ents that can clearly be seen by the explosion! + + +void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType ) +{ + edict_t *pEntity = NULL; + TraceResult tr; + float flAdjustedDamage, falloff; + Vector vecSpot; + + if ( flRadius ) + falloff = flDamage / flRadius; + else + falloff = 1.0; + + int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER); + + vecSrc.z += 1;// in case grenade is lying on the ground + + if ( !pevAttacker ) + pevAttacker = pevInflictor; + + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL) + { + if ( pEntity->v.takedamage != DAMAGE_NO ) + { + if (UTIL_IsPlayer(pEntity)) + { + // blast's don't tavel into or out of water + if (bInWater && pEntity->v.waterlevel == 0) + continue; + if (!bInWater && pEntity->v.waterlevel == 3) + continue; + + vecSpot = UTIL_BodyTarget( pEntity, vecSrc ); + + UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); + + if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) + {// the explosion can 'see' this entity, so hurt them! + if (tr.fStartSolid) + { + // if we're stuck inside them, fixup the position and distance + tr.vecEndPos = vecSrc; + tr.flFraction = 0.0; + } + + // decrease damage for an ent that's farther from the bomb. + flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; + flAdjustedDamage = flDamage - flAdjustedDamage; + + if ( flAdjustedDamage < 0 ) + { + flAdjustedDamage = 0; + } + + // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) ); + if (tr.flFraction != 1.0) + { + ClearMultiDamage( ); + UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType ); + ApplyMultiDamage( pevInflictor, pevAttacker ); + } + else + { + UTIL_TakeDamage ( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); + } + } + } + else if (pEntity->v.euser4 != NULL) + { + // UNDONE: this should check a damage mask, not an ignore + CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(pEntity)); + + if ( iClassIgnore != CLASS_NONE && pMonster->Classify() == iClassIgnore ) + {// houndeyes don't hurt other houndeyes with their attack + continue; + } + + // blast's don't tavel into or out of water + if (bInWater && pEntity->v.waterlevel == 0) + continue; + if (!bInWater && pEntity->v.waterlevel == 3) + continue; + + vecSpot = pMonster->BodyTarget( vecSrc ); + + UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr ); + + if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) + {// the explosion can 'see' this entity, so hurt them! + if (tr.fStartSolid) + { + // if we're stuck inside them, fixup the position and distance + tr.vecEndPos = vecSrc; + tr.flFraction = 0.0; + } + + // decrease damage for an ent that's farther from the bomb. + flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff; + flAdjustedDamage = flDamage - flAdjustedDamage; + + if ( flAdjustedDamage < 0 ) + { + flAdjustedDamage = 0; + } + + // ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) ); + if (tr.flFraction != 1.0) + { + ClearMultiDamage( ); + pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType ); + ApplyMultiDamage( pevInflictor, pevAttacker ); + } + else + { + pMonster->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); + } + } + } + } + } +} + + +void CMBaseMonster :: RadiusDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) +{ + ::RadiusDamage( pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); +} + + +void CMBaseMonster :: RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) +{ + ::RadiusDamage( vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType ); +} + + +//========================================================= +// CheckTraceHullAttack - expects a length to trace, amount +// of damage to do, and damage type. Returns a pointer to +// the damaged entity in case the monster wishes to do +// other stuff to the victim (punchangle, etc) +// +// Used for many contact-range melee attacks. Bites, claws, etc. +//========================================================= +edict_t* CMBaseMonster :: CheckTraceHullAttack( float flDist, int iDamage, int iDmgType ) +{ + TraceResult tr; + + if (IsPlayer()) + UTIL_MakeVectors( pev->angles ); + else + UTIL_MakeAimVectors( pev->angles ); + + Vector vecStart = pev->origin; + vecStart.z += pev->size.z * 0.5; + Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist ); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit ) + { + edict_t *pEntity = tr.pHit; + + if ( iDamage > 0 ) + { + if (UTIL_IsPlayer(pEntity)) + UTIL_TakeDamage( pEntity, pev, pev, iDamage, iDmgType ); + else if (pEntity->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); + } + } + + return pEntity; + } + + return NULL; +} + + +/* +================ +TraceAttack +================ +*/ +void CMBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + Vector vecOrigin = ptr->vecEndPos - vecDir * 4; + + if ( pev->takedamage ) + { + AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); + + int blood = BloodColor(); + + if ( blood != DONT_BLEED ) + { + SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + } + } +} + + +/* +//========================================================= +// TraceAttack +//========================================================= +void CMBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + Vector vecOrigin = ptr->vecEndPos - vecDir * 4; + + ALERT ( at_console, "%d\n", ptr->iHitgroup ); + + + if ( pev->takedamage ) + { + AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType ); + + int blood = BloodColor(); + + if ( blood != DONT_BLEED ) + { + SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood. + } + } +} +*/ + +//========================================================= +// TraceAttack +//========================================================= +void CMBaseMonster :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( pev->takedamage ) + { + m_LastHitGroup = ptr->iHitgroup; + +/*jlb + switch ( ptr->iHitgroup ) + { + case HITGROUP_GENERIC: + break; + case HITGROUP_HEAD: + flDamage *= gSkillData.monHead; + break; + case HITGROUP_CHEST: + flDamage *= gSkillData.monChest; + break; + case HITGROUP_STOMACH: + flDamage *= gSkillData.monStomach; + break; + case HITGROUP_LEFTARM: + case HITGROUP_RIGHTARM: + flDamage *= gSkillData.monArm; + break; + case HITGROUP_LEFTLEG: + case HITGROUP_RIGHTLEG: + flDamage *= gSkillData.monLeg; + break; + default: + break; + } +jlb*/ + SpawnBlood(ptr->vecEndPos, BloodColor(), flDamage);// a little surface blood. + TraceBleed( flDamage, vecDir, ptr, bitsDamageType ); + AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); + } +} + +/* +================ +FireBullets + +Go to the trouble of combining multiple pellets into a single damage call. + +This version is used by Monsters. +================ +*/ +void CMBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker ) +{ + static int tracerCount; + int tracer; + TraceResult tr; + Vector vecRight = gpGlobals->v_right; + Vector vecUp = gpGlobals->v_up; + + if ( pevAttacker == NULL ) + pevAttacker = pev; // the default attacker is ourselves + + ClearMultiDamage(); + gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB; + + for (ULONG iShot = 1; iShot <= cShots; iShot++) + { + // get circular gaussian spread + float x, y, z; + 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 = vecDirShooting + + x * vecSpread.x * vecRight + + y * vecSpread.y * vecUp; + Vector vecEnd; + + vecEnd = vecSrc + vecDir * flDistance; + UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr); + + tracer = 0; + if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0) + { + Vector vecTracerSrc; + + if ( IsPlayer() ) + {// adjust tracer position for player + vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16; + } + else + { + vecTracerSrc = vecSrc; + } + + if ( iTracerFreq != 1 ) // guns that always trace also always decal + tracer = 1; + switch( iBulletType ) + { + case BULLET_MONSTER_MP5: + case BULLET_MONSTER_9MM: + case BULLET_MONSTER_12MM: + case BULLET_MONSTER_762: + case BULLET_MONSTER_357: + default: + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc ); + WRITE_BYTE( TE_TRACER ); + WRITE_COORD( vecTracerSrc.x ); + WRITE_COORD( vecTracerSrc.y ); + WRITE_COORD( vecTracerSrc.z ); + WRITE_COORD( tr.vecEndPos.x ); + WRITE_COORD( tr.vecEndPos.y ); + WRITE_COORD( tr.vecEndPos.z ); + MESSAGE_END(); + break; + } + } + // do damage, paint decals + if (tr.flFraction != 1.0) + { + if (UTIL_IsPlayer(tr.pHit)) // is this a player? + { + edict_t *pPlayer = tr.pHit; + + if ( iDamage ) + { + UTIL_TraceAttack(pPlayer, pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + } + else switch(iBulletType) + { + default: + case BULLET_MONSTER_9MM: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_MP5: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_12MM: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); + if ( !tracer ) + { + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + } + break; + + case BULLET_MONSTER_762: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_357: + UTIL_TraceAttack(pPlayer, pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_NONE: // FIX + UTIL_TraceAttack(pPlayer, pevAttacker, 50, vecDir, &tr, DMG_CLUB); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + // only decal glass + if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) + { + UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); + } + + break; + } + } + else if (tr.pHit->v.euser4 != NULL) + { + CMBaseEntity *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); + + if ( iDamage ) + { + pMonster->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) ); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + } + else switch(iBulletType) + { + default: + case BULLET_MONSTER_9MM: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg9MM, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_MP5: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmgMP5, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_12MM: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg12MM, vecDir, &tr, DMG_BULLET); + if ( !tracer ) + { + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + } + break; + + case BULLET_MONSTER_762: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg762, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_MONSTER_357: + pMonster->TraceAttack(pevAttacker, gSkillData.monDmg357, vecDir, &tr, DMG_BULLET); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + DecalGunshot( &tr, iBulletType ); + + break; + + case BULLET_NONE: // FIX + pMonster->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB); + TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType); + // only decal glass + if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0) + { + UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) ); + } + + break; + } + } + } + // make bullet trails + UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 ); + } + ApplyMultiDamage(pev, pevAttacker); +} + + +void CMBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + if (BloodColor() == DONT_BLEED) + return; + + if (flDamage == 0) + return; + + if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR))) + return; + + // make blood decal on the wall! + TraceResult Bloodtr; + Vector vecTraceDir; + float flNoise; + int cCount; + int i; + +/* + if ( !IsAlive() ) + { + // dealing with a dead monster. + if ( pev->max_health <= 0 ) + { + // no blood decal for a monster that has already decalled its limit. + return; + } + else + { + pev->max_health--; + } + } +*/ + + if (flDamage < 10) + { + flNoise = 0.1; + cCount = 1; + } + else if (flDamage < 25) + { + flNoise = 0.2; + cCount = 2; + } + else + { + flNoise = 0.3; + cCount = 4; + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) + + vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); + + UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr); + + if ( Bloodtr.flFraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); + } + } +} + +//========================================================= +//========================================================= +void CMBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir ) +{ + // make blood decal on the wall! + TraceResult Bloodtr; + Vector vecTraceDir; + int i; + + if ( !IsAlive() ) + { + // dealing with a dead monster. + if ( pev->max_health <= 0 ) + { + // no blood decal for a monster that has already decalled its limit. + return; + } + else + { + pev->max_health--; + } + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir; + + vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); + + UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr); + +/* + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + WRITE_COORD( ptr->vecEndPos.x ); + WRITE_COORD( ptr->vecEndPos.y ); + WRITE_COORD( ptr->vecEndPos.z ); + + WRITE_COORD( Bloodtr.vecEndPos.x ); + WRITE_COORD( Bloodtr.vecEndPos.y ); + WRITE_COORD( Bloodtr.vecEndPos.z ); + MESSAGE_END(); +*/ + + if ( Bloodtr.flFraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BloodColor() ); + } + } +} diff --git a/src/dlls/controller.cpp b/src/dlls/controller.cpp index fcb8bf1..54cc85e 100644 --- a/src/dlls/controller.cpp +++ b/src/dlls/controller.cpp @@ -1,1374 +1,1374 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -//========================================================= -// CONTROLLER -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "effects.h" -#include "schedule.h" -#include "weapons.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define CONTROLLER_AE_HEAD_OPEN 1 -#define CONTROLLER_AE_BALL_SHOOT 2 -#define CONTROLLER_AE_SMALL_SHOOT 3 -#define CONTROLLER_AE_POWERUP_FULL 4 -#define CONTROLLER_AE_POWERUP_HALF 5 - -#define CONTROLLER_FLINCH_DELAY 2 // at most one flinch every n secs - -const char *CMController::pAttackSounds[] = -{ - "controller/con_attack1.wav", - "controller/con_attack2.wav", - "controller/con_attack3.wav", -}; - -const char *CMController::pIdleSounds[] = -{ - "controller/con_idle1.wav", - "controller/con_idle2.wav", - "controller/con_idle3.wav", - "controller/con_idle4.wav", - "controller/con_idle5.wav", -}; - -const char *CMController::pAlertSounds[] = -{ - "controller/con_alert1.wav", - "controller/con_alert2.wav", - "controller/con_alert3.wav", -}; - -const char *CMController::pPainSounds[] = -{ - "controller/con_pain1.wav", - "controller/con_pain2.wav", - "controller/con_pain3.wav", -}; - -const char *CMController::pDeathSounds[] = -{ - "controller/con_die1.wav", - "controller/con_die2.wav", -}; - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMController :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MILITARY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMController :: SetYawSpeed ( void ) -{ - int ys; - - ys = 120; - -#if 0 - switch ( m_Activity ) - { - } -#endif - - pev->yaw_speed = ys; -} - -int CMController :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // HACK HACK -- until we fix this. - if ( IsAlive() ) - PainSound(); - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - - -void CMController::Killed( entvars_t *pevAttacker, int iGib ) -{ - // shut off balls - /* - m_iBall[0] = 0; - m_iBallTime[0] = gpGlobals->time + 4.0; - m_iBall[1] = 0; - m_iBallTime[1] = gpGlobals->time + 4.0; - */ - - // fade balls - if (m_pBall[0]) - { - m_pBall[0]->SUB_StartFadeOut(); - m_pBall[0] = NULL; - } - if (m_pBall[1]) - { - m_pBall[1]->SUB_StartFadeOut(); - m_pBall[1] = NULL; - } - - CMBaseMonster::Killed( pevAttacker, iGib ); -} - - -void CMController::GibMonster( void ) -{ - // delete balls - if (m_pBall[0]) - { - UTIL_Remove( m_pBall[0]->edict() ); - m_pBall[0] = NULL; - } - if (m_pBall[1]) - { - UTIL_Remove( m_pBall[1]->edict() ); - m_pBall[1] = NULL; - } - CMBaseMonster::GibMonster( ); -} - - - - -void CMController :: PainSound( void ) -{ - if (RANDOM_LONG(0,5) < 2) - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); -} - -void CMController :: AlertSound( void ) -{ - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); -} - -void CMController :: IdleSound( void ) -{ - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pIdleSounds ); -} - -void CMController :: AttackSound( void ) -{ - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAttackSounds ); -} - -void CMController :: DeathSound( void ) -{ - EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMController :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case CONTROLLER_AE_HEAD_OPEN: - { - Vector vecStart, angleGun; - - GetAttachment( 0, vecStart, angleGun ); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( 1 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 20 ); // life * 10 - WRITE_COORD( -32 ); // decay - MESSAGE_END(); - - m_iBall[0] = 192; - m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - m_iBall[1] = 255; - m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - - } - break; - - case CONTROLLER_AE_BALL_SHOOT: - { - Vector vecStart, angleGun; - - GetAttachment( 0, vecStart, angleGun ); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( 32 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 10 ); // life * 10 - WRITE_COORD( 32 ); // decay - MESSAGE_END(); - - CMControllerHeadBall *pBall = CreateClassPtr((CMControllerHeadBall *)NULL); - - if (pBall != NULL) - { - pBall->pev->origin = vecStart; - pBall->pev->angles = pev->angles; - pBall->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pBall->pev->absmin = pBall->pev->origin + Vector(-1,-1,-1); - pBall->pev->absmax = pBall->pev->origin + Vector(1,1,1); - - pBall->Spawn(); - - pBall->pev->velocity = Vector( 0, 0, 32 ); - pBall->m_hEnemy = m_hEnemy; - } - m_iBall[0] = 0; - m_iBall[1] = 0; - } - break; - - case CONTROLLER_AE_SMALL_SHOOT: - { - AttackSound( ); - m_flShootTime = gpGlobals->time; - m_flShootEnd = m_flShootTime + atoi( pEvent->options ) / 15.0; - } - break; - case CONTROLLER_AE_POWERUP_FULL: - { - m_iBall[0] = 255; - m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - m_iBall[1] = 255; - m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - } - break; - case CONTROLLER_AE_POWERUP_HALF: - { - m_iBall[0] = 192; - m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - m_iBall[1] = 192; - m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; - } - break; - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMController :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/controller.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 )); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_FLY; - pev->flags |= FL_FLY; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = gSkillData.controllerHealth; - pev->view_ofs = Vector( 0, 0, -2 );// position of the eyes relative to monster's origin. - m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_alien_controller" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Alien Controller" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMController :: Precache() -{ - PRECACHE_MODEL("models/controller.mdl"); - - PRECACHE_SOUND_ARRAY( pAttackSounds ); - PRECACHE_SOUND_ARRAY( pIdleSounds ); - PRECACHE_SOUND_ARRAY( pAlertSounds ); - PRECACHE_SOUND_ARRAY( pPainSounds ); - PRECACHE_SOUND_ARRAY( pDeathSounds ); - - PRECACHE_MODEL( "sprites/xspark4.spr"); - - CMControllerZapBall ZapBall; - CMControllerHeadBall HeadBall; - - ZapBall.Precache(); - HeadBall.Precache(); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - -// Chase enemy schedule -Task_t tlControllerChaseEnemy[] = -{ - { TASK_GET_PATH_TO_ENEMY, (float)128 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - -}; - -Schedule_t slControllerChaseEnemy[] = -{ - { - tlControllerChaseEnemy, - ARRAYSIZE ( tlControllerChaseEnemy ), - bits_COND_NEW_ENEMY | - bits_COND_TASK_FAILED, - 0, - "ControllerChaseEnemy" - }, -}; - - - -Task_t tlControllerStrafe[] = -{ - { TASK_WAIT, (float)0.2 }, - { TASK_GET_PATH_TO_ENEMY, (float)128 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slControllerStrafe[] = -{ - { - tlControllerStrafe, - ARRAYSIZE ( tlControllerStrafe ), - bits_COND_NEW_ENEMY, - 0, - "ControllerStrafe" - }, -}; - - -Task_t tlControllerTakeCover[] = -{ - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slControllerTakeCover[] = -{ - { - tlControllerTakeCover, - ARRAYSIZE ( tlControllerTakeCover ), - bits_COND_NEW_ENEMY, - 0, - "ControllerTakeCover" - }, -}; - - -Task_t tlControllerFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slControllerFail[] = -{ - { - tlControllerFail, - ARRAYSIZE ( tlControllerFail ), - 0, - 0, - "ControllerFail" - }, -}; - - - -DEFINE_CUSTOM_SCHEDULES( CMController ) -{ - slControllerChaseEnemy, - slControllerStrafe, - slControllerTakeCover, - slControllerFail, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMController, CMBaseMonster ); - - - -//========================================================= -// StartTask -//========================================================= -void CMController :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - CMBaseMonster :: StartTask ( pTask ); - break; - case TASK_GET_PATH_TO_ENEMY_LKP: - { - if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, pTask->flData, (m_vecEnemyLKP - pev->origin).Length() + 1024 )) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" ); - TaskFail(); - } - break; - } - case TASK_GET_PATH_TO_ENEMY: - { - edict_t *pEnemy = m_hEnemy; - - if ( pEnemy == NULL ) - { - TaskFail(); - return; - } - - if (BuildNearestRoute( pEnemy->v.origin, pEnemy->v.view_ofs, pTask->flData, (pEnemy->v.origin - pev->origin).Length() + 1024 )) - { - TaskComplete(); - } - else - { - // no way to get there =( - ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); - TaskFail(); - } - break; - } - default: - CMBaseMonster :: StartTask ( pTask ); - break; - } -} - - -Vector Intersect( Vector vecSrc, Vector vecDst, Vector vecMove, float flSpeed ) -{ - Vector vecTo = vecDst - vecSrc; - - float a = DotProduct( vecMove, vecMove ) - flSpeed * flSpeed; - float b = 0 * DotProduct(vecTo, vecMove); // why does this work? - float c = DotProduct( vecTo, vecTo ); - - float t; - if (a == 0) - { - t = c / (flSpeed * flSpeed); - } - else - { - t = b * b - 4 * a * c; - t = sqrt( t ) / (2.0 * a); - float t1 = -b +t; - float t2 = -b -t; - - if (t1 < 0 || t2 < t1) - t = t2; - else - t = t1; - } - - // ALERT( at_console, "Intersect %f\n", t ); - - if (t < 0.1) - t = 0.1; - if (t > 10.0) - t = 10.0; - - Vector vecHit = vecTo + vecMove * t; - return vecHit.Normalize( ) * flSpeed; -} - - -int CMController::LookupFloat( ) -{ - if (m_velocity.Length( ) < 32.0) - { - return LookupSequence( "up" ); - } - - UTIL_MakeAimVectors( pev->angles ); - float x = DotProduct( gpGlobals->v_forward, m_velocity ); - float y = DotProduct( gpGlobals->v_right, m_velocity ); - float z = DotProduct( gpGlobals->v_up, m_velocity ); - - if (fabs(x) > fabs(y) && fabs(x) > fabs(z)) - { - if (x > 0) - return LookupSequence( "forward"); - else - return LookupSequence( "backward"); - } - else if (fabs(y) > fabs(z)) - { - if (y > 0) - return LookupSequence( "right"); - else - return LookupSequence( "left"); - } - else - { - if (z > 0) - return LookupSequence( "up"); - else - return LookupSequence( "down"); - } -} - - -//========================================================= -// RunTask -//========================================================= -void CMController :: RunTask ( Task_t *pTask ) -{ - - if (m_flShootEnd > gpGlobals->time) - { - Vector vecHand, vecAngle; - - GetAttachment( 2, vecHand, vecAngle ); - - while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->time) - { - Vector vecSrc = vecHand + pev->velocity * (m_flShootTime - gpGlobals->time); - Vector vecDir; - - if (m_hEnemy != NULL) - { - if (HasConditions( bits_COND_SEE_ENEMY )) - { - m_vecEstVelocity = m_vecEstVelocity * 0.5 + m_hEnemy->v.velocity * 0.5; - } - else - { - m_vecEstVelocity = m_vecEstVelocity * 0.8; - } - vecDir = Intersect( vecSrc, UTIL_BodyTarget( m_hEnemy, pev->origin ), m_vecEstVelocity, gSkillData.controllerSpeedBall ); - float delta = 0.03490; // +-2 degree - vecDir = vecDir + Vector( RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ) ) * gSkillData.controllerSpeedBall; - - vecSrc = vecSrc + vecDir * (gpGlobals->time - m_flShootTime); - - CMControllerZapBall *pBall = CreateClassPtr((CMControllerZapBall *)NULL); - - if (pBall != NULL) - { - pBall->pev->origin = vecSrc; - pBall->pev->angles = pev->angles; - pBall->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pBall->pev->absmin = pBall->pev->origin + Vector(-1,-1,-1); - pBall->pev->absmax = pBall->pev->origin + Vector(1,1,1); - - pBall->Spawn(); - - pBall->pev->velocity = vecDir; - } - } - m_flShootTime += 0.2; - } - - if (m_flShootTime > m_flShootEnd) - { - m_iBall[0] = 64; - m_iBallTime[0] = m_flShootEnd; - m_iBall[1] = 64; - m_iBallTime[1] = m_flShootEnd; - m_fInCombat = FALSE; - } - } - - switch ( pTask->iTask ) - { - case TASK_WAIT_FOR_MOVEMENT: - case TASK_WAIT: - case TASK_WAIT_FACE_ENEMY: - case TASK_WAIT_PVS: - MakeIdealYaw( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if (m_fSequenceFinished) - { - m_fInCombat = FALSE; - } - - CMBaseMonster :: RunTask ( pTask ); - - if (!m_fInCombat) - { - if (HasConditions ( bits_COND_CAN_RANGE_ATTACK1 )) - { - pev->sequence = LookupActivity( ACT_RANGE_ATTACK1 ); - pev->frame = 0; - ResetSequenceInfo( ); - m_fInCombat = TRUE; - } - else if (HasConditions ( bits_COND_CAN_RANGE_ATTACK2 )) - { - pev->sequence = LookupActivity( ACT_RANGE_ATTACK2 ); - pev->frame = 0; - ResetSequenceInfo( ); - m_fInCombat = TRUE; - } - else - { - int iFloat = LookupFloat( ); - if (m_fSequenceFinished || iFloat != pev->sequence) - { - pev->sequence = iFloat; - pev->frame = 0; - ResetSequenceInfo( ); - } - } - } - break; - default: - CMBaseMonster :: RunTask ( pTask ); - break; - } -} - - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMController :: GetSchedule ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_IDLE: - break; - - case MONSTERSTATE_ALERT: - break; - - case MONSTERSTATE_COMBAT: - { - Vector vecTmp = Intersect( Vector( 0, 0, 0 ), Vector( 100, 4, 7 ), Vector( 2, 10, -3 ), 20.0 ); - - // dead enemy - if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) - { - // m_iFrustration++; - } - if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) - { - // m_iFrustration++; - } - } - break; - } - - return CMBaseMonster :: GetSchedule(); -} - - - -//========================================================= -//========================================================= -Schedule_t* CMController :: GetScheduleOfType ( int Type ) -{ - // ALERT( at_console, "%d\n", m_iFrustration ); - switch ( Type ) - { - case SCHED_CHASE_ENEMY: - return slControllerChaseEnemy; - case SCHED_RANGE_ATTACK1: - return slControllerStrafe; - case SCHED_RANGE_ATTACK2: - case SCHED_MELEE_ATTACK1: - case SCHED_MELEE_ATTACK2: - case SCHED_TAKE_COVER_FROM_ENEMY: - return slControllerTakeCover; - case SCHED_FAIL: - return slControllerFail; - } - - return CMBaseMonster :: GetScheduleOfType( Type ); -} - - - - - -//========================================================= -// CheckRangeAttack1 - shoot a bigass energy ball out of their head -// -//========================================================= -BOOL CMController :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( flDot > 0.5 && flDist > 256 && flDist <= 2048 ) - { - return TRUE; - } - return FALSE; -} - - -BOOL CMController :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - if ( flDot > 0.5 && flDist > 64 && flDist <= 2048 ) - { - return TRUE; - } - return FALSE; -} - - -BOOL CMController :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - return FALSE; -} - - -void CMController :: SetActivity ( Activity NewActivity ) -{ - CMBaseMonster::SetActivity( NewActivity ); - - switch ( m_Activity) - { - case ACT_WALK: - m_flGroundSpeed = 100; - break; - default: - m_flGroundSpeed = 100; - break; - } -} - - - -//========================================================= -// RunAI -//========================================================= -void CMController :: RunAI( void ) -{ - CMBaseMonster :: RunAI(); - Vector vecStart, angleGun; - - if ( HasMemory( bits_MEMORY_KILLED ) ) - return; - - for (int i = 0; i < 2; i++) - { - if (m_pBall[i] == NULL) - { - m_pBall[i] = CMSprite::SpriteCreate( "sprites/xspark4.spr", pev->origin, TRUE ); - if (m_pBall[i] != NULL) - { - m_pBall[i]->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); - m_pBall[i]->SetAttachment( edict(), (i + 3) ); - m_pBall[i]->SetScale( 1.0 ); - } - else - continue; - } - - float t = m_iBallTime[i] - gpGlobals->time; - if (t > 0.1) - t = 0.1 / t; - else - t = 1.0; - - m_iBallCurrent[i] += (m_iBall[i] - m_iBallCurrent[i]) * t; - - m_pBall[i]->SetBrightness( m_iBallCurrent[i] ); - - GetAttachment( i + 2, vecStart, angleGun ); - UTIL_SetOrigin( m_pBall[i]->pev, vecStart ); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) + 0x1000 * (i + 3) ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( m_iBallCurrent[i] / 8 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 5 ); // life * 10 - WRITE_COORD( 0 ); // decay - MESSAGE_END(); - } -} - - -extern void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, int g, int b ); - -void CMController::Stop( void ) -{ - m_IdealActivity = GetStoppedActivity(); -} - - -#define DIST_TO_CHECK 200 -void CMController :: Move ( float flInterval ) -{ - float flWaypointDist; - float flCheckDist; - float flDist;// how far the lookahead check got before hitting an object. - float flMoveDist; - Vector vecDir; - Vector vecApex; - edict_t *pTargetEnt; - - // Don't move if no valid route - if ( FRouteClear() ) - { - ALERT( at_aiconsole, "Tried to move with no route!\n" ); - TaskFail(); - return; - } - - if ( m_flMoveWaitFinished > gpGlobals->time ) - return; - -// Debug, test movement code -#if 0 -// if ( CVAR_GET_FLOAT("stopmove" ) != 0 ) - { - if ( m_movementGoal == MOVEGOAL_ENEMY ) - RouteSimplify( m_hEnemy ); - else - RouteSimplify( m_hTargetEnt ); - FRefreshRoute(); - return; - } -#else -// Debug, draw the route -// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 0, 255 ); -#endif - - // if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer - // to that entity for the CheckLocalMove and Triangulate functions. - pTargetEnt = NULL; - - if (m_flGroundSpeed == 0) - { - m_flGroundSpeed = 100; - // TaskFail( ); - // return; - } - - flMoveDist = m_flGroundSpeed * flInterval; - - do - { - // local move to waypoint. - vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize(); - flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); - - // MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation ); - // ChangeYaw ( pev->yaw_speed ); - - // if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint - if ( flWaypointDist < DIST_TO_CHECK ) - { - flCheckDist = flWaypointDist; - } - else - { - flCheckDist = DIST_TO_CHECK; - } - - if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY ) - { - // only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR ) - pTargetEnt = m_hEnemy; - } - else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT ) - { - pTargetEnt = m_hTargetEnt; - } - - // !!!BUGBUG - CheckDist should be derived from ground speed. - // If this fails, it should be because of some dynamic entity blocking this guy. - // We've already checked this path, so we should wait and time out if the entity doesn't move - flDist = 0; - if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID ) - { - // Can't move, stop - Stop(); - // Blocking entity is in global trace_ent - edict_t *pBlocker = gpGlobals->trace_ent; - if (pBlocker != NULL) - { - Blocked( pBlocker ); - } - if ( pBlocker && m_moveWaitTime > 0 && UTIL_IsMoving(pBlocker) && !UTIL_IsPlayer(pBlocker) && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 ) - { - // Can we still move toward our target? - if ( flDist < m_flGroundSpeed ) - { - // Wait for a second - m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime; - // ALERT( at_aiconsole, "Move %s!!!\n", STRING( pBlocker->pev->classname ) ); - return; - } - } - else - { - // try to triangulate around whatever is in the way. - if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) ) - { - InsertWaypoint( vecApex, bits_MF_TO_DETOUR ); - RouteSimplify( pTargetEnt ); - } - else - { - ALERT ( at_aiconsole, "Couldn't Triangulate\n" ); - Stop(); - if ( m_moveWaitTime > 0 ) - { - FRefreshRoute(); - m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime * 0.5; - } - else - { - TaskFail(); - ALERT( at_aiconsole, "Failed to move!\n" ); - //ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z ); - } - return; - } - } - } - - // UNDONE: this is a hack to quit moving farther than it has looked ahead. - if (flCheckDist < flMoveDist) - { - MoveExecute( pTargetEnt, vecDir, flCheckDist / m_flGroundSpeed ); - - // ALERT( at_console, "%.02f\n", flInterval ); - AdvanceRoute( flWaypointDist ); - flMoveDist -= flCheckDist; - } - else - { - MoveExecute( pTargetEnt, vecDir, flMoveDist / m_flGroundSpeed ); - - if ( ShouldAdvanceRoute( flWaypointDist - flMoveDist ) ) - { - AdvanceRoute( flWaypointDist ); - } - flMoveDist = 0; - } - - if ( MovementIsComplete() ) - { - Stop(); - RouteClear(); - } - } while (flMoveDist > 0 && flCheckDist > 0); - - // cut corner? - if (flWaypointDist < 128) - { - if ( m_movementGoal == MOVEGOAL_ENEMY ) - RouteSimplify( m_hEnemy ); - else - RouteSimplify( m_hTargetEnt ); - FRefreshRoute(); - - if (m_flGroundSpeed > 100) - m_flGroundSpeed -= 40; - } - else - { - if (m_flGroundSpeed < 400) - m_flGroundSpeed += 10; - } -} - - - -BOOL CMController:: ShouldAdvanceRoute( float flWaypointDist ) -{ - if ( flWaypointDist <= 32 ) - { - return TRUE; - } - - return FALSE; -} - - -int CMController :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) -{ - TraceResult tr; - - UTIL_TraceHull( vecStart + Vector( 0, 0, 32), vecEnd + Vector( 0, 0, 32), dont_ignore_monsters, large_hull, edict(), &tr ); - - // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); - // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); - - if (pflDist) - { - *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. - } - - // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); - if (tr.fStartSolid || tr.flFraction < 1.0) - { - if ( pTarget && (pTarget == gpGlobals->trace_ent) ) - return LOCALMOVE_VALID; - return LOCALMOVE_INVALID; - } - - return LOCALMOVE_VALID; -} - - -void CMController::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) -{ - if ( m_IdealActivity != m_movementActivity ) - m_IdealActivity = m_movementActivity; - - // ALERT( at_console, "move %.4f %.4f %.4f : %f\n", vecDir.x, vecDir.y, vecDir.z, flInterval ); - - // float flTotal = m_flGroundSpeed * pev->framerate * flInterval; - // UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flTotal, MOVE_STRAFE ); - - m_velocity = m_velocity * 0.8 + m_flGroundSpeed * vecDir * 0.2; - - UTIL_MoveToOrigin ( ENT(pev), pev->origin + m_velocity, m_velocity.Length() * flInterval, MOVE_STRAFE ); - -} - - -void CMControllerHeadBall :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "sprites/xspark4.spr"); - pev->rendermode = kRenderTransAdd; - pev->rendercolor.x = 255; - pev->rendercolor.y = 255; - pev->rendercolor.z = 255; - pev->renderamt = 255; - pev->scale = 2.0; - - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( pev, pev->origin ); - - SetThink( &CMControllerHeadBall::HuntThink ); - SetTouch( &CMControllerHeadBall::BounceTouch ); - - m_vecIdeal = Vector( 0, 0, 0 ); - - pev->nextthink = gpGlobals->time + 0.1; - - m_hOwner = pev->owner; - pev->dmgtime = gpGlobals->time; - - pev->classname = MAKE_STRING( "controller_head_ball" ); -} - - -void CMControllerHeadBall :: Precache( void ) -{ - PRECACHE_MODEL("sprites/xspark1.spr"); - PRECACHE_SOUND("debris/zap4.wav"); - PRECACHE_SOUND("weapons/electro4.wav"); -} - - -void CMControllerHeadBall :: HuntThink( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - pev->renderamt -= 5; - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) ); // entity, attachment - WRITE_COORD( pev->origin.x ); // origin - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( pev->renderamt / 16 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B - WRITE_BYTE( 2 ); // life * 10 - WRITE_COORD( 0 ); // decay - MESSAGE_END(); - - // check world boundaries - if (gpGlobals->time - pev->dmgtime > 5 || pev->renderamt < 64 || m_hEnemy == NULL || m_hOwner == NULL || pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) - { - SetTouch( NULL ); - UTIL_Remove( this->edict() ); - return; - } - - MovetoTarget( UTIL_Center( m_hEnemy ) ); - - if ((UTIL_Center(m_hEnemy) - pev->origin).Length() < 64) - { - TraceResult tr; - - UTIL_TraceLine( pev->origin, UTIL_Center(m_hEnemy), dont_ignore_monsters, ENT(pev), &tr ); - - if (tr.pHit != NULL && tr.pHit->v.takedamage) - { - ClearMultiDamage( ); - - if (UTIL_IsPlayer(tr.pHit)) - UTIL_TraceAttack( tr.pHit, VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK ); - else if (tr.pHit->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); - pMonster->TraceAttack( VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK ); - } - - ApplyMultiDamage( pev, VARS(m_hOwner) ); - } - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( entindex() ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_SHORT( g_sModelIndexLaser ); - WRITE_BYTE( 0 ); // frame start - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 3 ); // life - WRITE_BYTE( 20 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); - - UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); - - m_flNextAttack = gpGlobals->time + 3.0; - - SetThink( &CMControllerHeadBall::DieThink ); - pev->nextthink = gpGlobals->time + 0.3; - } - - // Crawl( ); -} - - -void CMControllerHeadBall :: DieThink( void ) -{ - UTIL_Remove( this->edict() ); -} - - -void CMControllerHeadBall :: MovetoTarget( Vector vecTarget ) -{ - // accelerate - float flSpeed = m_vecIdeal.Length(); - if (flSpeed == 0) - { - m_vecIdeal = pev->velocity; - flSpeed = m_vecIdeal.Length(); - } - - if (flSpeed > 400) - { - m_vecIdeal = m_vecIdeal.Normalize( ) * 400; - } - m_vecIdeal = m_vecIdeal + (vecTarget - pev->origin).Normalize() * 100; - pev->velocity = m_vecIdeal; -} - - - -void CMControllerHeadBall :: Crawl( void ) -{ - - Vector vecAim = Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize( ); - Vector vecPnt = pev->origin + pev->velocity * 0.3 + vecAim * 64; - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( entindex() ); - WRITE_COORD( vecPnt.x); - WRITE_COORD( vecPnt.y); - WRITE_COORD( vecPnt.z); - WRITE_SHORT( g_sModelIndexLaser ); - WRITE_BYTE( 0 ); // frame start - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 3 ); // life - WRITE_BYTE( 20 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); -} - - -void CMControllerHeadBall::BounceTouch( edict_t *pOther ) -{ - Vector vecDir = m_vecIdeal.Normalize( ); - - TraceResult tr = UTIL_GetGlobalTrace( ); - - float n = -DotProduct(tr.vecPlaneNormal, vecDir); - - vecDir = 2.0 * tr.vecPlaneNormal * n + vecDir; - - m_vecIdeal = vecDir * m_vecIdeal.Length(); -} - - -void CMControllerZapBall :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "sprites/xspark4.spr"); - pev->rendermode = kRenderTransAdd; - pev->rendercolor.x = 255; - pev->rendercolor.y = 255; - pev->rendercolor.z = 255; - pev->renderamt = 255; - pev->scale = 0.5; - - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - UTIL_SetOrigin( pev, pev->origin ); - - SetThink( &CMControllerZapBall::AnimateThink ); - SetTouch( &CMControllerZapBall::ExplodeTouch ); - - m_hOwner = pev->owner; - pev->dmgtime = gpGlobals->time; // keep track of when ball spawned - pev->nextthink = gpGlobals->time + 0.1; - - pev->classname = MAKE_STRING( "controller_energy_ball" ); -} - -void CMControllerZapBall :: Precache( void ) -{ - PRECACHE_MODEL("sprites/xspark4.spr"); - // PRECACHE_SOUND("debris/zap4.wav"); - // PRECACHE_SOUND("weapons/electro4.wav"); -} - - -void CMControllerZapBall :: AnimateThink( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - pev->frame = ((int)pev->frame + 1) % 11; - - if (gpGlobals->time - pev->dmgtime > 5 || pev->velocity.Length() < 10) - { - SetTouch( NULL ); - UTIL_Remove( this->edict() ); - } -} - - -void CMControllerZapBall::ExplodeTouch( edict_t *pOther ) -{ - if (pOther->v.takedamage) - { - TraceResult tr = UTIL_GetGlobalTrace( ); - - entvars_t *pevOwner; - if (m_hOwner != NULL) - { - pevOwner = VARS(m_hOwner); - } - else - { - pevOwner = pev; - } - - ClearMultiDamage( ); - if (UTIL_IsPlayer(pOther)) - UTIL_TraceAttack(pOther, pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TraceAttack(pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM ); - } - ApplyMultiDamage( pevOwner, pevOwner ); - - UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) ); - - } - - UTIL_Remove( this->edict() ); -} - - -#endif // !OEM && !HLDEMO +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +//========================================================= +// CONTROLLER +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "effects.h" +#include "schedule.h" +#include "weapons.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define CONTROLLER_AE_HEAD_OPEN 1 +#define CONTROLLER_AE_BALL_SHOOT 2 +#define CONTROLLER_AE_SMALL_SHOOT 3 +#define CONTROLLER_AE_POWERUP_FULL 4 +#define CONTROLLER_AE_POWERUP_HALF 5 + +#define CONTROLLER_FLINCH_DELAY 2 // at most one flinch every n secs + +const char *CMController::pAttackSounds[] = +{ + "controller/con_attack1.wav", + "controller/con_attack2.wav", + "controller/con_attack3.wav", +}; + +const char *CMController::pIdleSounds[] = +{ + "controller/con_idle1.wav", + "controller/con_idle2.wav", + "controller/con_idle3.wav", + "controller/con_idle4.wav", + "controller/con_idle5.wav", +}; + +const char *CMController::pAlertSounds[] = +{ + "controller/con_alert1.wav", + "controller/con_alert2.wav", + "controller/con_alert3.wav", +}; + +const char *CMController::pPainSounds[] = +{ + "controller/con_pain1.wav", + "controller/con_pain2.wav", + "controller/con_pain3.wav", +}; + +const char *CMController::pDeathSounds[] = +{ + "controller/con_die1.wav", + "controller/con_die2.wav", +}; + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMController :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MILITARY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMController :: SetYawSpeed ( void ) +{ + int ys; + + ys = 120; + +#if 0 + switch ( m_Activity ) + { + } +#endif + + pev->yaw_speed = ys; +} + +int CMController :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // HACK HACK -- until we fix this. + if ( IsAlive() ) + PainSound(); + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + + +void CMController::Killed( entvars_t *pevAttacker, int iGib ) +{ + // shut off balls + /* + m_iBall[0] = 0; + m_iBallTime[0] = gpGlobals->time + 4.0; + m_iBall[1] = 0; + m_iBallTime[1] = gpGlobals->time + 4.0; + */ + + // fade balls + if (m_pBall[0]) + { + m_pBall[0]->SUB_StartFadeOut(); + m_pBall[0] = NULL; + } + if (m_pBall[1]) + { + m_pBall[1]->SUB_StartFadeOut(); + m_pBall[1] = NULL; + } + + CMBaseMonster::Killed( pevAttacker, iGib ); +} + + +void CMController::GibMonster( void ) +{ + // delete balls + if (m_pBall[0]) + { + UTIL_Remove( m_pBall[0]->edict() ); + m_pBall[0] = NULL; + } + if (m_pBall[1]) + { + UTIL_Remove( m_pBall[1]->edict() ); + m_pBall[1] = NULL; + } + CMBaseMonster::GibMonster( ); +} + + + + +void CMController :: PainSound( void ) +{ + if (RANDOM_LONG(0,5) < 2) + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pPainSounds ); +} + +void CMController :: AlertSound( void ) +{ + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAlertSounds ); +} + +void CMController :: IdleSound( void ) +{ + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pIdleSounds ); +} + +void CMController :: AttackSound( void ) +{ + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pAttackSounds ); +} + +void CMController :: DeathSound( void ) +{ + EMIT_SOUND_ARRAY_DYN( CHAN_VOICE, pDeathSounds ); +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMController :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case CONTROLLER_AE_HEAD_OPEN: + { + Vector vecStart, angleGun; + + GetAttachment( 0, vecStart, angleGun ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment + WRITE_COORD( vecStart.x ); // origin + WRITE_COORD( vecStart.y ); + WRITE_COORD( vecStart.z ); + WRITE_COORD( 1 ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 192 ); // G + WRITE_BYTE( 64 ); // B + WRITE_BYTE( 20 ); // life * 10 + WRITE_COORD( -32 ); // decay + MESSAGE_END(); + + m_iBall[0] = 192; + m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + m_iBall[1] = 255; + m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + + } + break; + + case CONTROLLER_AE_BALL_SHOOT: + { + Vector vecStart, angleGun; + + GetAttachment( 0, vecStart, angleGun ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) + 0x1000 ); // entity, attachment + WRITE_COORD( vecStart.x ); // origin + WRITE_COORD( vecStart.y ); + WRITE_COORD( vecStart.z ); + WRITE_COORD( 32 ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 192 ); // G + WRITE_BYTE( 64 ); // B + WRITE_BYTE( 10 ); // life * 10 + WRITE_COORD( 32 ); // decay + MESSAGE_END(); + + CMControllerHeadBall *pBall = CreateClassPtr((CMControllerHeadBall *)NULL); + + if (pBall != NULL) + { + pBall->pev->origin = vecStart; + pBall->pev->angles = pev->angles; + pBall->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pBall->pev->absmin = pBall->pev->origin + Vector(-1,-1,-1); + pBall->pev->absmax = pBall->pev->origin + Vector(1,1,1); + + pBall->Spawn(); + + pBall->pev->velocity = Vector( 0, 0, 32 ); + pBall->m_hEnemy = m_hEnemy; + } + m_iBall[0] = 0; + m_iBall[1] = 0; + } + break; + + case CONTROLLER_AE_SMALL_SHOOT: + { + AttackSound( ); + m_flShootTime = gpGlobals->time; + m_flShootEnd = m_flShootTime + atoi( pEvent->options ) / 15.0; + } + break; + case CONTROLLER_AE_POWERUP_FULL: + { + m_iBall[0] = 255; + m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + m_iBall[1] = 255; + m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + } + break; + case CONTROLLER_AE_POWERUP_HALF: + { + m_iBall[0] = 192; + m_iBallTime[0] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + m_iBall[1] = 192; + m_iBallTime[1] = gpGlobals->time + atoi( pEvent->options ) / 15.0; + } + break; + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMController :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/controller.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 )); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_FLY; + pev->flags |= FL_FLY; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = gSkillData.controllerHealth; + pev->view_ofs = Vector( 0, 0, -2 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_alien_controller" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Alien Controller" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMController :: Precache() +{ + PRECACHE_MODEL("models/controller.mdl"); + + PRECACHE_SOUND_ARRAY( pAttackSounds ); + PRECACHE_SOUND_ARRAY( pIdleSounds ); + PRECACHE_SOUND_ARRAY( pAlertSounds ); + PRECACHE_SOUND_ARRAY( pPainSounds ); + PRECACHE_SOUND_ARRAY( pDeathSounds ); + + PRECACHE_MODEL( "sprites/xspark4.spr"); + + CMControllerZapBall ZapBall; + CMControllerHeadBall HeadBall; + + ZapBall.Precache(); + HeadBall.Precache(); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + +// Chase enemy schedule +Task_t tlControllerChaseEnemy[] = +{ + { TASK_GET_PATH_TO_ENEMY, (float)128 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + +}; + +Schedule_t slControllerChaseEnemy[] = +{ + { + tlControllerChaseEnemy, + ARRAYSIZE ( tlControllerChaseEnemy ), + bits_COND_NEW_ENEMY | + bits_COND_TASK_FAILED, + 0, + "ControllerChaseEnemy" + }, +}; + + + +Task_t tlControllerStrafe[] = +{ + { TASK_WAIT, (float)0.2 }, + { TASK_GET_PATH_TO_ENEMY, (float)128 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slControllerStrafe[] = +{ + { + tlControllerStrafe, + ARRAYSIZE ( tlControllerStrafe ), + bits_COND_NEW_ENEMY, + 0, + "ControllerStrafe" + }, +}; + + +Task_t tlControllerTakeCover[] = +{ + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slControllerTakeCover[] = +{ + { + tlControllerTakeCover, + ARRAYSIZE ( tlControllerTakeCover ), + bits_COND_NEW_ENEMY, + 0, + "ControllerTakeCover" + }, +}; + + +Task_t tlControllerFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slControllerFail[] = +{ + { + tlControllerFail, + ARRAYSIZE ( tlControllerFail ), + 0, + 0, + "ControllerFail" + }, +}; + + + +DEFINE_CUSTOM_SCHEDULES( CMController ) +{ + slControllerChaseEnemy, + slControllerStrafe, + slControllerTakeCover, + slControllerFail, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMController, CMBaseMonster ); + + + +//========================================================= +// StartTask +//========================================================= +void CMController :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + CMBaseMonster :: StartTask ( pTask ); + break; + case TASK_GET_PATH_TO_ENEMY_LKP: + { + if (BuildNearestRoute( m_vecEnemyLKP, pev->view_ofs, pTask->flData, (m_vecEnemyLKP - pev->origin).Length() + 1024 )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemyLKP failed!!\n" ); + TaskFail(); + } + break; + } + case TASK_GET_PATH_TO_ENEMY: + { + edict_t *pEnemy = m_hEnemy; + + if ( pEnemy == NULL ) + { + TaskFail(); + return; + } + + if (BuildNearestRoute( pEnemy->v.origin, pEnemy->v.view_ofs, pTask->flData, (pEnemy->v.origin - pev->origin).Length() + 1024 )) + { + TaskComplete(); + } + else + { + // no way to get there =( + ALERT ( at_aiconsole, "GetPathToEnemy failed!!\n" ); + TaskFail(); + } + break; + } + default: + CMBaseMonster :: StartTask ( pTask ); + break; + } +} + + +Vector Intersect( Vector vecSrc, Vector vecDst, Vector vecMove, float flSpeed ) +{ + Vector vecTo = vecDst - vecSrc; + + float a = DotProduct( vecMove, vecMove ) - flSpeed * flSpeed; + float b = 0 * DotProduct(vecTo, vecMove); // why does this work? + float c = DotProduct( vecTo, vecTo ); + + float t; + if (a == 0) + { + t = c / (flSpeed * flSpeed); + } + else + { + t = b * b - 4 * a * c; + t = sqrt( t ) / (2.0 * a); + float t1 = -b +t; + float t2 = -b -t; + + if (t1 < 0 || t2 < t1) + t = t2; + else + t = t1; + } + + // ALERT( at_console, "Intersect %f\n", t ); + + if (t < 0.1) + t = 0.1; + if (t > 10.0) + t = 10.0; + + Vector vecHit = vecTo + vecMove * t; + return vecHit.Normalize( ) * flSpeed; +} + + +int CMController::LookupFloat( ) +{ + if (m_velocity.Length( ) < 32.0) + { + return LookupSequence( "up" ); + } + + UTIL_MakeAimVectors( pev->angles ); + float x = DotProduct( gpGlobals->v_forward, m_velocity ); + float y = DotProduct( gpGlobals->v_right, m_velocity ); + float z = DotProduct( gpGlobals->v_up, m_velocity ); + + if (fabs(x) > fabs(y) && fabs(x) > fabs(z)) + { + if (x > 0) + return LookupSequence( "forward"); + else + return LookupSequence( "backward"); + } + else if (fabs(y) > fabs(z)) + { + if (y > 0) + return LookupSequence( "right"); + else + return LookupSequence( "left"); + } + else + { + if (z > 0) + return LookupSequence( "up"); + else + return LookupSequence( "down"); + } +} + + +//========================================================= +// RunTask +//========================================================= +void CMController :: RunTask ( Task_t *pTask ) +{ + + if (m_flShootEnd > gpGlobals->time) + { + Vector vecHand, vecAngle; + + GetAttachment( 2, vecHand, vecAngle ); + + while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->time) + { + Vector vecSrc = vecHand + pev->velocity * (m_flShootTime - gpGlobals->time); + Vector vecDir; + + if (m_hEnemy != NULL) + { + if (HasConditions( bits_COND_SEE_ENEMY )) + { + m_vecEstVelocity = m_vecEstVelocity * 0.5 + m_hEnemy->v.velocity * 0.5; + } + else + { + m_vecEstVelocity = m_vecEstVelocity * 0.8; + } + vecDir = Intersect( vecSrc, UTIL_BodyTarget( m_hEnemy, pev->origin ), m_vecEstVelocity, gSkillData.controllerSpeedBall ); + float delta = 0.03490; // +-2 degree + vecDir = vecDir + Vector( RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ) ) * gSkillData.controllerSpeedBall; + + vecSrc = vecSrc + vecDir * (gpGlobals->time - m_flShootTime); + + CMControllerZapBall *pBall = CreateClassPtr((CMControllerZapBall *)NULL); + + if (pBall != NULL) + { + pBall->pev->origin = vecSrc; + pBall->pev->angles = pev->angles; + pBall->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pBall->pev->absmin = pBall->pev->origin + Vector(-1,-1,-1); + pBall->pev->absmax = pBall->pev->origin + Vector(1,1,1); + + pBall->Spawn(); + + pBall->pev->velocity = vecDir; + } + } + m_flShootTime += 0.2; + } + + if (m_flShootTime > m_flShootEnd) + { + m_iBall[0] = 64; + m_iBallTime[0] = m_flShootEnd; + m_iBall[1] = 64; + m_iBallTime[1] = m_flShootEnd; + m_fInCombat = FALSE; + } + } + + switch ( pTask->iTask ) + { + case TASK_WAIT_FOR_MOVEMENT: + case TASK_WAIT: + case TASK_WAIT_FACE_ENEMY: + case TASK_WAIT_PVS: + MakeIdealYaw( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if (m_fSequenceFinished) + { + m_fInCombat = FALSE; + } + + CMBaseMonster :: RunTask ( pTask ); + + if (!m_fInCombat) + { + if (HasConditions ( bits_COND_CAN_RANGE_ATTACK1 )) + { + pev->sequence = LookupActivity( ACT_RANGE_ATTACK1 ); + pev->frame = 0; + ResetSequenceInfo( ); + m_fInCombat = TRUE; + } + else if (HasConditions ( bits_COND_CAN_RANGE_ATTACK2 )) + { + pev->sequence = LookupActivity( ACT_RANGE_ATTACK2 ); + pev->frame = 0; + ResetSequenceInfo( ); + m_fInCombat = TRUE; + } + else + { + int iFloat = LookupFloat( ); + if (m_fSequenceFinished || iFloat != pev->sequence) + { + pev->sequence = iFloat; + pev->frame = 0; + ResetSequenceInfo( ); + } + } + } + break; + default: + CMBaseMonster :: RunTask ( pTask ); + break; + } +} + + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMController :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_IDLE: + break; + + case MONSTERSTATE_ALERT: + break; + + case MONSTERSTATE_COMBAT: + { + Vector vecTmp = Intersect( Vector( 0, 0, 0 ), Vector( 100, 4, 7 ), Vector( 2, 10, -3 ), 20.0 ); + + // dead enemy + if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) + { + // m_iFrustration++; + } + if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) + { + // m_iFrustration++; + } + } + break; + } + + return CMBaseMonster :: GetSchedule(); +} + + + +//========================================================= +//========================================================= +Schedule_t* CMController :: GetScheduleOfType ( int Type ) +{ + // ALERT( at_console, "%d\n", m_iFrustration ); + switch ( Type ) + { + case SCHED_CHASE_ENEMY: + return slControllerChaseEnemy; + case SCHED_RANGE_ATTACK1: + return slControllerStrafe; + case SCHED_RANGE_ATTACK2: + case SCHED_MELEE_ATTACK1: + case SCHED_MELEE_ATTACK2: + case SCHED_TAKE_COVER_FROM_ENEMY: + return slControllerTakeCover; + case SCHED_FAIL: + return slControllerFail; + } + + return CMBaseMonster :: GetScheduleOfType( Type ); +} + + + + + +//========================================================= +// CheckRangeAttack1 - shoot a bigass energy ball out of their head +// +//========================================================= +BOOL CMController :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( flDot > 0.5 && flDist > 256 && flDist <= 2048 ) + { + return TRUE; + } + return FALSE; +} + + +BOOL CMController :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + if ( flDot > 0.5 && flDist > 64 && flDist <= 2048 ) + { + return TRUE; + } + return FALSE; +} + + +BOOL CMController :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + return FALSE; +} + + +void CMController :: SetActivity ( Activity NewActivity ) +{ + CMBaseMonster::SetActivity( NewActivity ); + + switch ( m_Activity) + { + case ACT_WALK: + m_flGroundSpeed = 100; + break; + default: + m_flGroundSpeed = 100; + break; + } +} + + + +//========================================================= +// RunAI +//========================================================= +void CMController :: RunAI( void ) +{ + CMBaseMonster :: RunAI(); + Vector vecStart, angleGun; + + if ( HasMemory( bits_MEMORY_KILLED ) ) + return; + + for (int i = 0; i < 2; i++) + { + if (m_pBall[i] == NULL) + { + m_pBall[i] = CMSprite::SpriteCreate( "sprites/xspark4.spr", pev->origin, TRUE ); + if (m_pBall[i] != NULL) + { + m_pBall[i]->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); + m_pBall[i]->SetAttachment( edict(), (i + 3) ); + m_pBall[i]->SetScale( 1.0 ); + } + else + continue; + } + + float t = m_iBallTime[i] - gpGlobals->time; + if (t > 0.1) + t = 0.1 / t; + else + t = 1.0; + + m_iBallCurrent[i] += (m_iBall[i] - m_iBallCurrent[i]) * t; + + m_pBall[i]->SetBrightness( m_iBallCurrent[i] ); + + GetAttachment( i + 2, vecStart, angleGun ); + UTIL_SetOrigin( m_pBall[i]->pev, vecStart ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) + 0x1000 * (i + 3) ); // entity, attachment + WRITE_COORD( vecStart.x ); // origin + WRITE_COORD( vecStart.y ); + WRITE_COORD( vecStart.z ); + WRITE_COORD( m_iBallCurrent[i] / 8 ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 192 ); // G + WRITE_BYTE( 64 ); // B + WRITE_BYTE( 5 ); // life * 10 + WRITE_COORD( 0 ); // decay + MESSAGE_END(); + } +} + + +extern void DrawRoute( entvars_t *pev, WayPoint_t *m_Route, int m_iRouteIndex, int r, int g, int b ); + +void CMController::Stop( void ) +{ + m_IdealActivity = GetStoppedActivity(); +} + + +#define DIST_TO_CHECK 200 +void CMController :: Move ( float flInterval ) +{ + float flWaypointDist; + float flCheckDist; + float flDist;// how far the lookahead check got before hitting an object. + float flMoveDist; + Vector vecDir; + Vector vecApex; + edict_t *pTargetEnt; + + // Don't move if no valid route + if ( FRouteClear() ) + { + ALERT( at_aiconsole, "Tried to move with no route!\n" ); + TaskFail(); + return; + } + + if ( m_flMoveWaitFinished > gpGlobals->time ) + return; + +// Debug, test movement code +#if 0 +// if ( CVAR_GET_FLOAT("stopmove" ) != 0 ) + { + if ( m_movementGoal == MOVEGOAL_ENEMY ) + RouteSimplify( m_hEnemy ); + else + RouteSimplify( m_hTargetEnt ); + FRefreshRoute(); + return; + } +#else +// Debug, draw the route +// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 0, 255 ); +#endif + + // if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer + // to that entity for the CheckLocalMove and Triangulate functions. + pTargetEnt = NULL; + + if (m_flGroundSpeed == 0) + { + m_flGroundSpeed = 100; + // TaskFail( ); + // return; + } + + flMoveDist = m_flGroundSpeed * flInterval; + + do + { + // local move to waypoint. + vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize(); + flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); + + // MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation ); + // ChangeYaw ( pev->yaw_speed ); + + // if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint + if ( flWaypointDist < DIST_TO_CHECK ) + { + flCheckDist = flWaypointDist; + } + else + { + flCheckDist = DIST_TO_CHECK; + } + + if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY ) + { + // only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR ) + pTargetEnt = m_hEnemy; + } + else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT ) + { + pTargetEnt = m_hTargetEnt; + } + + // !!!BUGBUG - CheckDist should be derived from ground speed. + // If this fails, it should be because of some dynamic entity blocking this guy. + // We've already checked this path, so we should wait and time out if the entity doesn't move + flDist = 0; + if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID ) + { + // Can't move, stop + Stop(); + // Blocking entity is in global trace_ent + edict_t *pBlocker = gpGlobals->trace_ent; + if (pBlocker != NULL) + { + Blocked( pBlocker ); + } + if ( pBlocker && m_moveWaitTime > 0 && UTIL_IsMoving(pBlocker) && !UTIL_IsPlayer(pBlocker) && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 ) + { + // Can we still move toward our target? + if ( flDist < m_flGroundSpeed ) + { + // Wait for a second + m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime; + // ALERT( at_aiconsole, "Move %s!!!\n", STRING( pBlocker->pev->classname ) ); + return; + } + } + else + { + // try to triangulate around whatever is in the way. + if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) ) + { + InsertWaypoint( vecApex, bits_MF_TO_DETOUR ); + RouteSimplify( pTargetEnt ); + } + else + { + ALERT ( at_aiconsole, "Couldn't Triangulate\n" ); + Stop(); + if ( m_moveWaitTime > 0 ) + { + FRefreshRoute(); + m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime * 0.5; + } + else + { + TaskFail(); + ALERT( at_aiconsole, "Failed to move!\n" ); + //ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z ); + } + return; + } + } + } + + // UNDONE: this is a hack to quit moving farther than it has looked ahead. + if (flCheckDist < flMoveDist) + { + MoveExecute( pTargetEnt, vecDir, flCheckDist / m_flGroundSpeed ); + + // ALERT( at_console, "%.02f\n", flInterval ); + AdvanceRoute( flWaypointDist ); + flMoveDist -= flCheckDist; + } + else + { + MoveExecute( pTargetEnt, vecDir, flMoveDist / m_flGroundSpeed ); + + if ( ShouldAdvanceRoute( flWaypointDist - flMoveDist ) ) + { + AdvanceRoute( flWaypointDist ); + } + flMoveDist = 0; + } + + if ( MovementIsComplete() ) + { + Stop(); + RouteClear(); + } + } while (flMoveDist > 0 && flCheckDist > 0); + + // cut corner? + if (flWaypointDist < 128) + { + if ( m_movementGoal == MOVEGOAL_ENEMY ) + RouteSimplify( m_hEnemy ); + else + RouteSimplify( m_hTargetEnt ); + FRefreshRoute(); + + if (m_flGroundSpeed > 100) + m_flGroundSpeed -= 40; + } + else + { + if (m_flGroundSpeed < 400) + m_flGroundSpeed += 10; + } +} + + + +BOOL CMController:: ShouldAdvanceRoute( float flWaypointDist ) +{ + if ( flWaypointDist <= 32 ) + { + return TRUE; + } + + return FALSE; +} + + +int CMController :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) +{ + TraceResult tr; + + UTIL_TraceHull( vecStart + Vector( 0, 0, 32), vecEnd + Vector( 0, 0, 32), dont_ignore_monsters, large_hull, edict(), &tr ); + + // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); + // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); + + if (pflDist) + { + *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. + } + + // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); + if (tr.fStartSolid || tr.flFraction < 1.0) + { + if ( pTarget && (pTarget == gpGlobals->trace_ent) ) + return LOCALMOVE_VALID; + return LOCALMOVE_INVALID; + } + + return LOCALMOVE_VALID; +} + + +void CMController::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) +{ + if ( m_IdealActivity != m_movementActivity ) + m_IdealActivity = m_movementActivity; + + // ALERT( at_console, "move %.4f %.4f %.4f : %f\n", vecDir.x, vecDir.y, vecDir.z, flInterval ); + + // float flTotal = m_flGroundSpeed * pev->framerate * flInterval; + // UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flTotal, MOVE_STRAFE ); + + m_velocity = m_velocity * 0.8 + m_flGroundSpeed * vecDir * 0.2; + + UTIL_MoveToOrigin ( ENT(pev), pev->origin + m_velocity, m_velocity.Length() * flInterval, MOVE_STRAFE ); + +} + + +void CMControllerHeadBall :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "sprites/xspark4.spr"); + pev->rendermode = kRenderTransAdd; + pev->rendercolor.x = 255; + pev->rendercolor.y = 255; + pev->rendercolor.z = 255; + pev->renderamt = 255; + pev->scale = 2.0; + + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( &CMControllerHeadBall::HuntThink ); + SetTouch( &CMControllerHeadBall::BounceTouch ); + + m_vecIdeal = Vector( 0, 0, 0 ); + + pev->nextthink = gpGlobals->time + 0.1; + + m_hOwner = pev->owner; + pev->dmgtime = gpGlobals->time; + + pev->classname = MAKE_STRING( "controller_head_ball" ); +} + + +void CMControllerHeadBall :: Precache( void ) +{ + PRECACHE_MODEL("sprites/xspark1.spr"); + PRECACHE_SOUND("debris/zap4.wav"); + PRECACHE_SOUND("weapons/electro4.wav"); +} + + +void CMControllerHeadBall :: HuntThink( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + pev->renderamt -= 5; + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) ); // entity, attachment + WRITE_COORD( pev->origin.x ); // origin + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_COORD( pev->renderamt / 16 ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 255 ); // G + WRITE_BYTE( 255 ); // B + WRITE_BYTE( 2 ); // life * 10 + WRITE_COORD( 0 ); // decay + MESSAGE_END(); + + // check world boundaries + if (gpGlobals->time - pev->dmgtime > 5 || pev->renderamt < 64 || m_hEnemy == NULL || m_hOwner == NULL || pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096) + { + SetTouch( NULL ); + UTIL_Remove( this->edict() ); + return; + } + + MovetoTarget( UTIL_Center( m_hEnemy ) ); + + if ((UTIL_Center(m_hEnemy) - pev->origin).Length() < 64) + { + TraceResult tr; + + UTIL_TraceLine( pev->origin, UTIL_Center(m_hEnemy), dont_ignore_monsters, ENT(pev), &tr ); + + if (tr.pHit != NULL && tr.pHit->v.takedamage) + { + ClearMultiDamage( ); + + if (UTIL_IsPlayer(tr.pHit)) + UTIL_TraceAttack( tr.pHit, VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK ); + else if (tr.pHit->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); + pMonster->TraceAttack( VARS(m_hOwner), gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK ); + } + + ApplyMultiDamage( pev, VARS(m_hOwner) ); + } + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMENTPOINT ); + WRITE_SHORT( entindex() ); + WRITE_COORD( tr.vecEndPos.x ); + WRITE_COORD( tr.vecEndPos.y ); + WRITE_COORD( tr.vecEndPos.z ); + WRITE_SHORT( g_sModelIndexLaser ); + WRITE_BYTE( 0 ); // frame start + WRITE_BYTE( 10 ); // framerate + WRITE_BYTE( 3 ); // life + WRITE_BYTE( 20 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // brightness + WRITE_BYTE( 10 ); // speed + MESSAGE_END(); + + UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); + + m_flNextAttack = gpGlobals->time + 3.0; + + SetThink( &CMControllerHeadBall::DieThink ); + pev->nextthink = gpGlobals->time + 0.3; + } + + // Crawl( ); +} + + +void CMControllerHeadBall :: DieThink( void ) +{ + UTIL_Remove( this->edict() ); +} + + +void CMControllerHeadBall :: MovetoTarget( Vector vecTarget ) +{ + // accelerate + float flSpeed = m_vecIdeal.Length(); + if (flSpeed == 0) + { + m_vecIdeal = pev->velocity; + flSpeed = m_vecIdeal.Length(); + } + + if (flSpeed > 400) + { + m_vecIdeal = m_vecIdeal.Normalize( ) * 400; + } + m_vecIdeal = m_vecIdeal + (vecTarget - pev->origin).Normalize() * 100; + pev->velocity = m_vecIdeal; +} + + + +void CMControllerHeadBall :: Crawl( void ) +{ + + Vector vecAim = Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize( ); + Vector vecPnt = pev->origin + pev->velocity * 0.3 + vecAim * 64; + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMENTPOINT ); + WRITE_SHORT( entindex() ); + WRITE_COORD( vecPnt.x); + WRITE_COORD( vecPnt.y); + WRITE_COORD( vecPnt.z); + WRITE_SHORT( g_sModelIndexLaser ); + WRITE_BYTE( 0 ); // frame start + WRITE_BYTE( 10 ); // framerate + WRITE_BYTE( 3 ); // life + WRITE_BYTE( 20 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 255 ); // brightness + WRITE_BYTE( 10 ); // speed + MESSAGE_END(); +} + + +void CMControllerHeadBall::BounceTouch( edict_t *pOther ) +{ + Vector vecDir = m_vecIdeal.Normalize( ); + + TraceResult tr = UTIL_GetGlobalTrace( ); + + float n = -DotProduct(tr.vecPlaneNormal, vecDir); + + vecDir = 2.0 * tr.vecPlaneNormal * n + vecDir; + + m_vecIdeal = vecDir * m_vecIdeal.Length(); +} + + +void CMControllerZapBall :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "sprites/xspark4.spr"); + pev->rendermode = kRenderTransAdd; + pev->rendercolor.x = 255; + pev->rendercolor.y = 255; + pev->rendercolor.z = 255; + pev->renderamt = 255; + pev->scale = 0.5; + + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + UTIL_SetOrigin( pev, pev->origin ); + + SetThink( &CMControllerZapBall::AnimateThink ); + SetTouch( &CMControllerZapBall::ExplodeTouch ); + + m_hOwner = pev->owner; + pev->dmgtime = gpGlobals->time; // keep track of when ball spawned + pev->nextthink = gpGlobals->time + 0.1; + + pev->classname = MAKE_STRING( "controller_energy_ball" ); +} + +void CMControllerZapBall :: Precache( void ) +{ + PRECACHE_MODEL("sprites/xspark4.spr"); + // PRECACHE_SOUND("debris/zap4.wav"); + // PRECACHE_SOUND("weapons/electro4.wav"); +} + + +void CMControllerZapBall :: AnimateThink( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + pev->frame = ((int)pev->frame + 1) % 11; + + if (gpGlobals->time - pev->dmgtime > 5 || pev->velocity.Length() < 10) + { + SetTouch( NULL ); + UTIL_Remove( this->edict() ); + } +} + + +void CMControllerZapBall::ExplodeTouch( edict_t *pOther ) +{ + if (pOther->v.takedamage) + { + TraceResult tr = UTIL_GetGlobalTrace( ); + + entvars_t *pevOwner; + if (m_hOwner != NULL) + { + pevOwner = VARS(m_hOwner); + } + else + { + pevOwner = pev; + } + + ClearMultiDamage( ); + if (UTIL_IsPlayer(pOther)) + UTIL_TraceAttack(pOther, pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TraceAttack(pevOwner, gSkillData.controllerDmgBall, pev->velocity.Normalize(), &tr, DMG_ENERGYBEAM ); + } + ApplyMultiDamage( pevOwner, pevOwner ); + + UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) ); + + } + + UTIL_Remove( this->edict() ); +} + + +#endif // !OEM && !HLDEMO diff --git a/src/dlls/decals.h b/src/dlls/decals.h index e18ab71..47d99c2 100644 --- a/src/dlls/decals.h +++ b/src/dlls/decals.h @@ -1,75 +1,75 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef DECALS_H -#define DECALS_H - -// -// Dynamic Decals -// -enum decal_e -{ - DECAL_GUNSHOT1 = 0, - DECAL_GUNSHOT2, - DECAL_GUNSHOT3, - DECAL_GUNSHOT4, - DECAL_GUNSHOT5, - DECAL_LAMBDA1, - DECAL_LAMBDA2, - DECAL_LAMBDA3, - DECAL_LAMBDA4, - DECAL_LAMBDA5, - DECAL_LAMBDA6, - DECAL_SCORCH1, - DECAL_SCORCH2, - DECAL_BLOOD1, - DECAL_BLOOD2, - DECAL_BLOOD3, - DECAL_BLOOD4, - DECAL_BLOOD5, - DECAL_BLOOD6, - DECAL_YBLOOD1, - DECAL_YBLOOD2, - DECAL_YBLOOD3, - DECAL_YBLOOD4, - DECAL_YBLOOD5, - DECAL_YBLOOD6, - DECAL_GLASSBREAK1, - DECAL_GLASSBREAK2, - DECAL_GLASSBREAK3, - DECAL_BIGSHOT1, - DECAL_BIGSHOT2, - DECAL_BIGSHOT3, - DECAL_BIGSHOT4, - DECAL_BIGSHOT5, - DECAL_SPIT1, - DECAL_SPIT2, - DECAL_BPROOF1, // Bulletproof glass decal - DECAL_GARGSTOMP1, // Gargantua stomp crack - DECAL_SMALLSCORCH1, // Small scorch mark - DECAL_SMALLSCORCH2, // Small scorch mark - DECAL_SMALLSCORCH3, // Small scorch mark - DECAL_MOMMABIRTH, // Big momma birth splatter - DECAL_MOMMASPLAT, -}; - -typedef struct -{ - char *name; - int index; -} DLL_DECALLIST; - -extern DLL_DECALLIST gDecals[]; - -#endif // DECALS_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef DECALS_H +#define DECALS_H + +// +// Dynamic Decals +// +enum decal_e +{ + DECAL_GUNSHOT1 = 0, + DECAL_GUNSHOT2, + DECAL_GUNSHOT3, + DECAL_GUNSHOT4, + DECAL_GUNSHOT5, + DECAL_LAMBDA1, + DECAL_LAMBDA2, + DECAL_LAMBDA3, + DECAL_LAMBDA4, + DECAL_LAMBDA5, + DECAL_LAMBDA6, + DECAL_SCORCH1, + DECAL_SCORCH2, + DECAL_BLOOD1, + DECAL_BLOOD2, + DECAL_BLOOD3, + DECAL_BLOOD4, + DECAL_BLOOD5, + DECAL_BLOOD6, + DECAL_YBLOOD1, + DECAL_YBLOOD2, + DECAL_YBLOOD3, + DECAL_YBLOOD4, + DECAL_YBLOOD5, + DECAL_YBLOOD6, + DECAL_GLASSBREAK1, + DECAL_GLASSBREAK2, + DECAL_GLASSBREAK3, + DECAL_BIGSHOT1, + DECAL_BIGSHOT2, + DECAL_BIGSHOT3, + DECAL_BIGSHOT4, + DECAL_BIGSHOT5, + DECAL_SPIT1, + DECAL_SPIT2, + DECAL_BPROOF1, // Bulletproof glass decal + DECAL_GARGSTOMP1, // Gargantua stomp crack + DECAL_SMALLSCORCH1, // Small scorch mark + DECAL_SMALLSCORCH2, // Small scorch mark + DECAL_SMALLSCORCH3, // Small scorch mark + DECAL_MOMMABIRTH, // Big momma birth splatter + DECAL_MOMMASPLAT, +}; + +typedef struct +{ + char *name; + int index; +} DLL_DECALLIST; + +extern DLL_DECALLIST gDecals[]; + +#endif // DECALS_H diff --git a/src/dlls/defaultai.cpp b/src/dlls/defaultai.cpp index 648dca3..503af5a 100644 --- a/src/dlls/defaultai.cpp +++ b/src/dlls/defaultai.cpp @@ -1,1084 +1,1084 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Default behaviors. -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "defaultai.h" -#include "nodes.h" - -//========================================================= -// Fail -//========================================================= -Task_t tlFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slFail[] = -{ - { - tlFail, - ARRAYSIZE ( tlFail ), - bits_COND_CAN_ATTACK, - 0, - "Fail" - }, -}; - -//========================================================= -// Idle Schedules -//========================================================= -Task_t tlIdleStand1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)5 },// repick IDLESTAND every five seconds. gives us a chance to pick an active idle, fidget, etc. -}; - -Schedule_t slIdleStand[] = -{ - { - tlIdleStand1, - ARRAYSIZE ( tlIdleStand1 ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL_FOOD | - bits_COND_SMELL | - bits_COND_PROVOKED, - 0, - "IdleStand" - }, -}; - -Schedule_t slIdleTrigger[] = -{ - { - tlIdleStand1, - ARRAYSIZE ( tlIdleStand1 ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Idle Trigger" - }, -}; - - -Task_t tlIdleWalk1[] = -{ - { TASK_WALK_PATH, (float)9999 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slIdleWalk[] = -{ - { - tlIdleWalk1, - ARRAYSIZE ( tlIdleWalk1 ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL_FOOD | - bits_COND_SMELL | - bits_COND_PROVOKED, - 0, - "Idle Walk" - }, -}; - -//========================================================= -// Ambush - monster stands in place and waits for a new -// enemy, or chance to attack an existing enemy. -//========================================================= -Task_t tlAmbush[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_INDEFINITE, (float)0 }, -}; - -Schedule_t slAmbush[] = -{ - { - tlAmbush, - ARRAYSIZE ( tlAmbush ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED, - - 0, - "Ambush" - }, -}; - -//========================================================= -// ActiveIdle schedule - !!!BUGBUG - if this schedule doesn't -// complete on its own, the monster's HintNode will not be -// cleared, and the rest of the monster's group will avoid -// that node because they think the group member that was -// previously interrupted is still using that node to active -// idle. -///========================================================= -Task_t tlActiveIdle[] = -{ - { TASK_FIND_HINTNODE, (float)0 }, - { TASK_GET_PATH_TO_HINTNODE, (float)0 }, - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_HINTNODE, (float)0 }, - { TASK_PLAY_ACTIVE_IDLE, (float)0 }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, - { TASK_CLEAR_HINTNODE, (float)0 }, -}; - -Schedule_t slActiveIdle[] = -{ - { - tlActiveIdle, - ARRAYSIZE( tlActiveIdle ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_HEAR_SOUND, - 0, - "Active Idle" - } -}; - -//========================================================= -// Wake Schedules -//========================================================= -Task_t tlWakeAngry1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_SOUND_WAKE, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, -}; - -Schedule_t slWakeAngry[] = -{ - { - tlWakeAngry1, - ARRAYSIZE ( tlWakeAngry1 ), - 0, - 0, - "Wake Angry" - } -}; - -//========================================================= -// AlertFace Schedules -//========================================================= -Task_t tlAlertFace1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_IDEAL, (float)0 }, -}; - -Schedule_t slAlertFace[] = -{ - { - tlAlertFace1, - ARRAYSIZE ( tlAlertFace1 ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED, - 0, - "Alert Face" - }, -}; - -//========================================================= -// AlertSmallFlinch Schedule - shot, but didn't see attacker, -// flinch then face -//========================================================= -Task_t tlAlertSmallFlinch[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_REMEMBER, (float)bits_MEMORY_FLINCHED }, - { TASK_SMALL_FLINCH, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_ALERT_FACE }, -}; - -Schedule_t slAlertSmallFlinch[] = -{ - { - tlAlertSmallFlinch, - ARRAYSIZE ( tlAlertSmallFlinch ), - 0, - 0, - "Alert Small Flinch" - }, -}; - -//========================================================= -// AlertIdle Schedules -//========================================================= -Task_t tlAlertStand1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)20 }, - { TASK_SUGGEST_STATE, (float)MONSTERSTATE_IDLE }, -}; - -Schedule_t slAlertStand[] = -{ - { - tlAlertStand1, - ARRAYSIZE ( tlAlertStand1 ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_SMELL | - bits_COND_SMELL_FOOD | - bits_COND_HEAR_SOUND, - - 0, - "Alert Stand" - }, -}; - -//========================================================= -// InvestigateSound - sends a monster to the location of the -// sound that was just heard, to check things out. -//========================================================= -Task_t tlInvestigateSound[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSOUND, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_IDLE }, - { TASK_WAIT, (float)10 }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slInvestigateSound[] = -{ - { - tlInvestigateSound, - ARRAYSIZE ( tlInvestigateSound ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "InvestigateSound" - }, -}; - -//========================================================= -// CombatIdle Schedule -//========================================================= -Task_t tlCombatStand1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_INDEFINITE, (float)0 }, -}; - -Schedule_t slCombatStand[] = -{ - { - tlCombatStand1, - ARRAYSIZE ( tlCombatStand1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_CAN_ATTACK, - 0, - "Combat Stand" - }, -}; - -//========================================================= -// CombatFace Schedule -//========================================================= -Task_t tlCombatFace1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slCombatFace[] = -{ - { - tlCombatFace1, - ARRAYSIZE ( tlCombatFace1 ), - bits_COND_CAN_ATTACK | - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD, - 0, - "Combat Face" - }, -}; - -//========================================================= -// Standoff schedule. Used in combat when a monster is -// hiding in cover or the enemy has moved out of sight. -// Should we look around in this schedule? -//========================================================= -Task_t tlStandoff[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, -}; - -Schedule_t slStandoff[] = -{ - { - tlStandoff, - ARRAYSIZE ( tlStandoff ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_ENEMY_DEAD | - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND, - 0, - "Standoff" - } -}; - -//========================================================= -// Arm weapon (draw gun) -//========================================================= -Task_t tlArmWeapon[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, (float) ACT_ARM } -}; - -Schedule_t slArmWeapon[] = -{ - { - tlArmWeapon, - ARRAYSIZE ( tlArmWeapon ), - 0, - 0, - "Arm Weapon" - } -}; - -//========================================================= -// reload schedule -//========================================================= -Task_t tlReload[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, float(ACT_RELOAD) }, -}; - -Schedule_t slReload[] = -{ - { - tlReload, - ARRAYSIZE ( tlReload ), - bits_COND_HEAVY_DAMAGE, - 0, - "Reload" - } -}; - -//========================================================= -// Attack Schedules -//========================================================= - -// primary range attack -Task_t tlRangeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slRangeAttack1[] = -{ - { - tlRangeAttack1, - ARRAYSIZE ( tlRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED | - bits_COND_HEAR_SOUND, - 0, - "Range Attack1" - }, -}; - -// secondary range attack -Task_t tlRangeAttack2[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK2, (float)0 }, -}; - -Schedule_t slRangeAttack2[] = -{ - { - tlRangeAttack2, - ARRAYSIZE ( tlRangeAttack2 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_HEAR_SOUND, - 0, - "Range Attack2" - }, -}; - -// primary melee attack -Task_t tlPrimaryMeleeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_MELEE_ATTACK1, (float)0 }, -}; - -Schedule_t slPrimaryMeleeAttack[] = -{ - { - tlPrimaryMeleeAttack1, - ARRAYSIZE ( tlPrimaryMeleeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED, - 0, - "Primary Melee Attack" - }, -}; - -// secondary melee attack -Task_t tlSecondaryMeleeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_MELEE_ATTACK2, (float)0 }, -}; - -Schedule_t slSecondaryMeleeAttack[] = -{ - { - tlSecondaryMeleeAttack1, - ARRAYSIZE ( tlSecondaryMeleeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED, - 0, - "Secondary Melee Attack" - }, -}; - -// special attack1 -Task_t tlSpecialAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SPECIAL_ATTACK1, (float)0 }, -}; - -Schedule_t slSpecialAttack1[] = -{ - { - tlSpecialAttack1, - ARRAYSIZE ( tlSpecialAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED | - bits_COND_HEAR_SOUND, - 0, - "Special Attack1" - }, -}; - -// special attack2 -Task_t tlSpecialAttack2[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SPECIAL_ATTACK2, (float)0 }, -}; - -Schedule_t slSpecialAttack2[] = -{ - { - tlSpecialAttack2, - ARRAYSIZE ( tlSpecialAttack2 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED | - bits_COND_HEAR_SOUND, - 0, - "Special Attack2" - }, -}; - -// Chase enemy schedule -Task_t tlChaseEnemy1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CHASE_ENEMY_FAILED }, - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slChaseEnemy[] = -{ - { - tlChaseEnemy1, - ARRAYSIZE ( tlChaseEnemy1 ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_TASK_FAILED | - bits_COND_HEAR_SOUND, - 0, - "Chase Enemy" - }, -}; - - -// Chase enemy failure schedule -Task_t tlChaseEnemyFailed[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, -// { TASK_TURN_LEFT, (float)179 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slChaseEnemyFailed[] = -{ - { - tlChaseEnemyFailed, - ARRAYSIZE ( tlChaseEnemyFailed ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_HEAR_SOUND, - 0, - "tlChaseEnemyFailed" - }, -}; - - -//========================================================= -// small flinch, played when minor damage is taken. -//========================================================= -Task_t tlSmallFlinch[] = -{ - { TASK_REMEMBER, (float)bits_MEMORY_FLINCHED }, - { TASK_STOP_MOVING, 0 }, - { TASK_SMALL_FLINCH, 0 }, -}; - -Schedule_t slSmallFlinch[] = -{ - { - tlSmallFlinch, - ARRAYSIZE ( tlSmallFlinch ), - 0, - 0, - "Small Flinch" - }, -}; - -//========================================================= -// Die! -//========================================================= -Task_t tlDie1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SOUND_DIE, (float)0 }, - { TASK_DIE, (float)0 }, -}; - -Schedule_t slDie[] = -{ - { - tlDie1, - ARRAYSIZE( tlDie1 ), - 0, - 0, - "Die" - }, -}; - -//========================================================= -// Victory Dance -//========================================================= -Task_t tlVictoryDance[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_WAIT, (float)0 }, -}; - -Schedule_t slVictoryDance[] = -{ - { - tlVictoryDance, - ARRAYSIZE( tlVictoryDance ), - 0, - 0, - "Victory Dance" - }, -}; - -//========================================================= -// BarnacleVictimGrab - barnacle tongue just hit the monster, -// so play a hit animation, then play a cycling pull animation -// as the creature is hoisting the monster. -//========================================================= -Task_t tlBarnacleVictimGrab[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_BARNACLE_HIT }, - { TASK_SET_ACTIVITY, (float)ACT_BARNACLE_PULL }, - { TASK_WAIT_INDEFINITE, (float)0 },// just cycle barnacle pull anim while barnacle hoists. -}; - -Schedule_t slBarnacleVictimGrab[] = -{ - { - tlBarnacleVictimGrab, - ARRAYSIZE ( tlBarnacleVictimGrab ), - 0, - 0, - "Barnacle Victim" - } -}; - -//========================================================= -// BarnacleVictimChomp - barnacle has pulled the prey to its -// mouth. Victim should play the BARNCLE_CHOMP animation -// once, then loop the BARNACLE_CHEW animation indefinitely -//========================================================= -Task_t tlBarnacleVictimChomp[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_BARNACLE_CHOMP }, - { TASK_SET_ACTIVITY, (float)ACT_BARNACLE_CHEW }, - { TASK_WAIT_INDEFINITE, (float)0 },// just cycle barnacle pull anim while barnacle hoists. -}; - -Schedule_t slBarnacleVictimChomp[] = -{ - { - tlBarnacleVictimChomp, - ARRAYSIZE ( tlBarnacleVictimChomp ), - 0, - 0, - "Barnacle Chomp" - } -}; - - -// Universal Error Schedule -Task_t tlError[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_WAIT_INDEFINITE, (float)0 }, -}; - -Schedule_t slError[] = -{ - { - tlError, - ARRAYSIZE ( tlError ), - 0, - 0, - "Error" - }, -}; - - -//========================================================= -// Cower - this is what is usually done when attempts -// to escape danger fail. -//========================================================= -Task_t tlCower[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_COWER }, -}; - -Schedule_t slCower[] = -{ - { - tlCower, - ARRAYSIZE ( tlCower ), - 0, - 0, - "Cower" - }, -}; - -//========================================================= -// move away from where you're currently standing. -//========================================================= -Task_t tlTakeCoverFromOrigin[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_ORIGIN, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slTakeCoverFromOrigin[] = -{ - { - tlTakeCoverFromOrigin, - ARRAYSIZE ( tlTakeCoverFromOrigin ), - bits_COND_NEW_ENEMY, - 0, - "TakeCoverFromOrigin" - }, -}; - -//========================================================= -// hide from the loudest sound source -//========================================================= -Task_t tlTakeCoverFromBestSound[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slTakeCoverFromBestSound[] = -{ - { - tlTakeCoverFromBestSound, - ARRAYSIZE ( tlTakeCoverFromBestSound ), - bits_COND_NEW_ENEMY, - 0, - "TakeCoverFromBestSound" - }, -}; - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlTakeCoverFromEnemy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, -// { TASK_TURN_LEFT, (float)179 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slTakeCoverFromEnemy[] = -{ - { - tlTakeCoverFromEnemy, - ARRAYSIZE ( tlTakeCoverFromEnemy ), - bits_COND_NEW_ENEMY, - 0, - "tlTakeCoverFromEnemy" - }, -}; - -Schedule_t *CMBaseMonster::m_scheduleList[] = -{ - slIdleStand, - slIdleTrigger, - slIdleWalk, - slAmbush, - slActiveIdle, - slWakeAngry, - slAlertFace, - slAlertSmallFlinch, - slAlertStand, - slInvestigateSound, - slCombatStand, - slCombatFace, - slStandoff, - slArmWeapon, - slReload, - slRangeAttack1, - slRangeAttack2, - slPrimaryMeleeAttack, - slSecondaryMeleeAttack, - slSpecialAttack1, - slSpecialAttack2, - slChaseEnemy, - slChaseEnemyFailed, - slSmallFlinch, - slDie, - slVictoryDance, - slBarnacleVictimGrab, - slBarnacleVictimChomp, - slError, - slCower, - slTakeCoverFromOrigin, - slTakeCoverFromBestSound, - slTakeCoverFromEnemy, - slFail -}; - -Schedule_t *CMBaseMonster::ScheduleFromName( const char *pName ) -{ - return ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) ); -} - - -Schedule_t *CMBaseMonster :: ScheduleInList( const char *pName, Schedule_t **pList, int listCount ) -{ - int i; - - if ( !pName ) - { - ALERT( at_console, "%s set to unnamed schedule!\n", STRING(pev->classname) ); - return NULL; - } - - - for ( i = 0; i < listCount; i++ ) - { - if ( !pList[i]->pName ) - { - ALERT( at_console, "Unnamed schedule!\n" ); - continue; - } - if ( stricmp( pName, pList[i]->pName ) == 0 ) - return pList[i]; - } - return NULL; -} - -//========================================================= -// GetScheduleOfType - returns a pointer to one of the -// monster's available schedules of the indicated type. -//========================================================= -Schedule_t* CMBaseMonster :: GetScheduleOfType ( int Type ) -{ -// ALERT ( at_console, "Sched Type:%d\n", Type ); - switch ( Type ) - { - case SCHED_IDLE_STAND: - { - if ( RANDOM_LONG(0,14) == 0 && FCanActiveIdle() ) - { - return &slActiveIdle[ 0 ]; - } - - return &slIdleStand[ 0 ]; - } - case SCHED_IDLE_WALK: - { - return &slIdleWalk[ 0 ]; - } - case SCHED_WAIT_TRIGGER: - { - return &slIdleTrigger[ 0 ]; - } - case SCHED_WAKE_ANGRY: - { - return &slWakeAngry[ 0 ]; - } - case SCHED_ALERT_FACE: - { - return &slAlertFace[ 0 ]; - } - case SCHED_ALERT_STAND: - { - return &slAlertStand[ 0 ]; - } - case SCHED_COMBAT_STAND: - { - return &slCombatStand[ 0 ]; - } - case SCHED_COMBAT_FACE: - { - return &slCombatFace[ 0 ]; - } - case SCHED_CHASE_ENEMY: - { - return &slChaseEnemy[ 0 ]; - } - case SCHED_CHASE_ENEMY_FAILED: - { - return &slFail[ 0 ]; - } - case SCHED_SMALL_FLINCH: - { - return &slSmallFlinch[ 0 ]; - } - case SCHED_ALERT_SMALL_FLINCH: - { - return &slAlertSmallFlinch[ 0 ]; - } - case SCHED_RELOAD: - { - return &slReload[ 0 ]; - } - case SCHED_ARM_WEAPON: - { - return &slArmWeapon[ 0 ]; - } - case SCHED_STANDOFF: - { - return &slStandoff[ 0 ]; - } - case SCHED_RANGE_ATTACK1: - { - return &slRangeAttack1[ 0 ]; - } - case SCHED_RANGE_ATTACK2: - { - return &slRangeAttack2[ 0 ]; - } - case SCHED_MELEE_ATTACK1: - { - return &slPrimaryMeleeAttack[ 0 ]; - } - case SCHED_MELEE_ATTACK2: - { - return &slSecondaryMeleeAttack[ 0 ]; - } - case SCHED_SPECIAL_ATTACK1: - { - return &slSpecialAttack1[ 0 ]; - } - case SCHED_SPECIAL_ATTACK2: - { - return &slSpecialAttack2[ 0 ]; - } - case SCHED_TAKE_COVER_FROM_BEST_SOUND: - { - return &slTakeCoverFromBestSound[ 0 ]; - } - case SCHED_TAKE_COVER_FROM_ENEMY: - { - return &slTakeCoverFromEnemy[ 0 ]; - } - case SCHED_COWER: - { - return &slCower[ 0 ]; - } - case SCHED_AMBUSH: - { - return &slAmbush[ 0 ]; - } - case SCHED_BARNACLE_VICTIM_GRAB: - { - return &slBarnacleVictimGrab[ 0 ]; - } - case SCHED_BARNACLE_VICTIM_CHOMP: - { - return &slBarnacleVictimChomp[ 0 ]; - } - case SCHED_INVESTIGATE_SOUND: - { - return &slInvestigateSound[ 0 ]; - } - case SCHED_DIE: - { - return &slDie[ 0 ]; - } - case SCHED_TAKE_COVER_FROM_ORIGIN: - { - return &slTakeCoverFromOrigin[ 0 ]; - } - case SCHED_VICTORY_DANCE: - { - return &slVictoryDance[ 0 ]; - } - case SCHED_FAIL: - { - return slFail; - } - default: - { - ALERT ( at_console, "GetScheduleOfType()\nNo CASE for Schedule Type %d!\n", Type ); - - return &slIdleStand[ 0 ]; - break; - } - } - - return NULL; -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Default behaviors. +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "defaultai.h" +#include "nodes.h" + +//========================================================= +// Fail +//========================================================= +Task_t tlFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slFail[] = +{ + { + tlFail, + ARRAYSIZE ( tlFail ), + bits_COND_CAN_ATTACK, + 0, + "Fail" + }, +}; + +//========================================================= +// Idle Schedules +//========================================================= +Task_t tlIdleStand1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)5 },// repick IDLESTAND every five seconds. gives us a chance to pick an active idle, fidget, etc. +}; + +Schedule_t slIdleStand[] = +{ + { + tlIdleStand1, + ARRAYSIZE ( tlIdleStand1 ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL_FOOD | + bits_COND_SMELL | + bits_COND_PROVOKED, + 0, + "IdleStand" + }, +}; + +Schedule_t slIdleTrigger[] = +{ + { + tlIdleStand1, + ARRAYSIZE ( tlIdleStand1 ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Idle Trigger" + }, +}; + + +Task_t tlIdleWalk1[] = +{ + { TASK_WALK_PATH, (float)9999 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slIdleWalk[] = +{ + { + tlIdleWalk1, + ARRAYSIZE ( tlIdleWalk1 ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL_FOOD | + bits_COND_SMELL | + bits_COND_PROVOKED, + 0, + "Idle Walk" + }, +}; + +//========================================================= +// Ambush - monster stands in place and waits for a new +// enemy, or chance to attack an existing enemy. +//========================================================= +Task_t tlAmbush[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_INDEFINITE, (float)0 }, +}; + +Schedule_t slAmbush[] = +{ + { + tlAmbush, + ARRAYSIZE ( tlAmbush ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED, + + 0, + "Ambush" + }, +}; + +//========================================================= +// ActiveIdle schedule - !!!BUGBUG - if this schedule doesn't +// complete on its own, the monster's HintNode will not be +// cleared, and the rest of the monster's group will avoid +// that node because they think the group member that was +// previously interrupted is still using that node to active +// idle. +///========================================================= +Task_t tlActiveIdle[] = +{ + { TASK_FIND_HINTNODE, (float)0 }, + { TASK_GET_PATH_TO_HINTNODE, (float)0 }, + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_HINTNODE, (float)0 }, + { TASK_PLAY_ACTIVE_IDLE, (float)0 }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, + { TASK_CLEAR_HINTNODE, (float)0 }, +}; + +Schedule_t slActiveIdle[] = +{ + { + tlActiveIdle, + ARRAYSIZE( tlActiveIdle ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_HEAR_SOUND, + 0, + "Active Idle" + } +}; + +//========================================================= +// Wake Schedules +//========================================================= +Task_t tlWakeAngry1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_SOUND_WAKE, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, +}; + +Schedule_t slWakeAngry[] = +{ + { + tlWakeAngry1, + ARRAYSIZE ( tlWakeAngry1 ), + 0, + 0, + "Wake Angry" + } +}; + +//========================================================= +// AlertFace Schedules +//========================================================= +Task_t tlAlertFace1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_IDEAL, (float)0 }, +}; + +Schedule_t slAlertFace[] = +{ + { + tlAlertFace1, + ARRAYSIZE ( tlAlertFace1 ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED, + 0, + "Alert Face" + }, +}; + +//========================================================= +// AlertSmallFlinch Schedule - shot, but didn't see attacker, +// flinch then face +//========================================================= +Task_t tlAlertSmallFlinch[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_REMEMBER, (float)bits_MEMORY_FLINCHED }, + { TASK_SMALL_FLINCH, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_ALERT_FACE }, +}; + +Schedule_t slAlertSmallFlinch[] = +{ + { + tlAlertSmallFlinch, + ARRAYSIZE ( tlAlertSmallFlinch ), + 0, + 0, + "Alert Small Flinch" + }, +}; + +//========================================================= +// AlertIdle Schedules +//========================================================= +Task_t tlAlertStand1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)20 }, + { TASK_SUGGEST_STATE, (float)MONSTERSTATE_IDLE }, +}; + +Schedule_t slAlertStand[] = +{ + { + tlAlertStand1, + ARRAYSIZE ( tlAlertStand1 ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_SMELL | + bits_COND_SMELL_FOOD | + bits_COND_HEAR_SOUND, + + 0, + "Alert Stand" + }, +}; + +//========================================================= +// InvestigateSound - sends a monster to the location of the +// sound that was just heard, to check things out. +//========================================================= +Task_t tlInvestigateSound[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSOUND, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_IDLE }, + { TASK_WAIT, (float)10 }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slInvestigateSound[] = +{ + { + tlInvestigateSound, + ARRAYSIZE ( tlInvestigateSound ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "InvestigateSound" + }, +}; + +//========================================================= +// CombatIdle Schedule +//========================================================= +Task_t tlCombatStand1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_INDEFINITE, (float)0 }, +}; + +Schedule_t slCombatStand[] = +{ + { + tlCombatStand1, + ARRAYSIZE ( tlCombatStand1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_CAN_ATTACK, + 0, + "Combat Stand" + }, +}; + +//========================================================= +// CombatFace Schedule +//========================================================= +Task_t tlCombatFace1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slCombatFace[] = +{ + { + tlCombatFace1, + ARRAYSIZE ( tlCombatFace1 ), + bits_COND_CAN_ATTACK | + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD, + 0, + "Combat Face" + }, +}; + +//========================================================= +// Standoff schedule. Used in combat when a monster is +// hiding in cover or the enemy has moved out of sight. +// Should we look around in this schedule? +//========================================================= +Task_t tlStandoff[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, +}; + +Schedule_t slStandoff[] = +{ + { + tlStandoff, + ARRAYSIZE ( tlStandoff ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_ENEMY_DEAD | + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND, + 0, + "Standoff" + } +}; + +//========================================================= +// Arm weapon (draw gun) +//========================================================= +Task_t tlArmWeapon[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, (float) ACT_ARM } +}; + +Schedule_t slArmWeapon[] = +{ + { + tlArmWeapon, + ARRAYSIZE ( tlArmWeapon ), + 0, + 0, + "Arm Weapon" + } +}; + +//========================================================= +// reload schedule +//========================================================= +Task_t tlReload[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, float(ACT_RELOAD) }, +}; + +Schedule_t slReload[] = +{ + { + tlReload, + ARRAYSIZE ( tlReload ), + bits_COND_HEAVY_DAMAGE, + 0, + "Reload" + } +}; + +//========================================================= +// Attack Schedules +//========================================================= + +// primary range attack +Task_t tlRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slRangeAttack1[] = +{ + { + tlRangeAttack1, + ARRAYSIZE ( tlRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED | + bits_COND_HEAR_SOUND, + 0, + "Range Attack1" + }, +}; + +// secondary range attack +Task_t tlRangeAttack2[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK2, (float)0 }, +}; + +Schedule_t slRangeAttack2[] = +{ + { + tlRangeAttack2, + ARRAYSIZE ( tlRangeAttack2 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_HEAR_SOUND, + 0, + "Range Attack2" + }, +}; + +// primary melee attack +Task_t tlPrimaryMeleeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_MELEE_ATTACK1, (float)0 }, +}; + +Schedule_t slPrimaryMeleeAttack[] = +{ + { + tlPrimaryMeleeAttack1, + ARRAYSIZE ( tlPrimaryMeleeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED, + 0, + "Primary Melee Attack" + }, +}; + +// secondary melee attack +Task_t tlSecondaryMeleeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_MELEE_ATTACK2, (float)0 }, +}; + +Schedule_t slSecondaryMeleeAttack[] = +{ + { + tlSecondaryMeleeAttack1, + ARRAYSIZE ( tlSecondaryMeleeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED, + 0, + "Secondary Melee Attack" + }, +}; + +// special attack1 +Task_t tlSpecialAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SPECIAL_ATTACK1, (float)0 }, +}; + +Schedule_t slSpecialAttack1[] = +{ + { + tlSpecialAttack1, + ARRAYSIZE ( tlSpecialAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED | + bits_COND_HEAR_SOUND, + 0, + "Special Attack1" + }, +}; + +// special attack2 +Task_t tlSpecialAttack2[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SPECIAL_ATTACK2, (float)0 }, +}; + +Schedule_t slSpecialAttack2[] = +{ + { + tlSpecialAttack2, + ARRAYSIZE ( tlSpecialAttack2 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED | + bits_COND_HEAR_SOUND, + 0, + "Special Attack2" + }, +}; + +// Chase enemy schedule +Task_t tlChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CHASE_ENEMY_FAILED }, + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slChaseEnemy[] = +{ + { + tlChaseEnemy1, + ARRAYSIZE ( tlChaseEnemy1 ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED | + bits_COND_HEAR_SOUND, + 0, + "Chase Enemy" + }, +}; + + +// Chase enemy failure schedule +Task_t tlChaseEnemyFailed[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, +// { TASK_TURN_LEFT, (float)179 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slChaseEnemyFailed[] = +{ + { + tlChaseEnemyFailed, + ARRAYSIZE ( tlChaseEnemyFailed ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_HEAR_SOUND, + 0, + "tlChaseEnemyFailed" + }, +}; + + +//========================================================= +// small flinch, played when minor damage is taken. +//========================================================= +Task_t tlSmallFlinch[] = +{ + { TASK_REMEMBER, (float)bits_MEMORY_FLINCHED }, + { TASK_STOP_MOVING, 0 }, + { TASK_SMALL_FLINCH, 0 }, +}; + +Schedule_t slSmallFlinch[] = +{ + { + tlSmallFlinch, + ARRAYSIZE ( tlSmallFlinch ), + 0, + 0, + "Small Flinch" + }, +}; + +//========================================================= +// Die! +//========================================================= +Task_t tlDie1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SOUND_DIE, (float)0 }, + { TASK_DIE, (float)0 }, +}; + +Schedule_t slDie[] = +{ + { + tlDie1, + ARRAYSIZE( tlDie1 ), + 0, + 0, + "Die" + }, +}; + +//========================================================= +// Victory Dance +//========================================================= +Task_t tlVictoryDance[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_WAIT, (float)0 }, +}; + +Schedule_t slVictoryDance[] = +{ + { + tlVictoryDance, + ARRAYSIZE( tlVictoryDance ), + 0, + 0, + "Victory Dance" + }, +}; + +//========================================================= +// BarnacleVictimGrab - barnacle tongue just hit the monster, +// so play a hit animation, then play a cycling pull animation +// as the creature is hoisting the monster. +//========================================================= +Task_t tlBarnacleVictimGrab[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_BARNACLE_HIT }, + { TASK_SET_ACTIVITY, (float)ACT_BARNACLE_PULL }, + { TASK_WAIT_INDEFINITE, (float)0 },// just cycle barnacle pull anim while barnacle hoists. +}; + +Schedule_t slBarnacleVictimGrab[] = +{ + { + tlBarnacleVictimGrab, + ARRAYSIZE ( tlBarnacleVictimGrab ), + 0, + 0, + "Barnacle Victim" + } +}; + +//========================================================= +// BarnacleVictimChomp - barnacle has pulled the prey to its +// mouth. Victim should play the BARNCLE_CHOMP animation +// once, then loop the BARNACLE_CHEW animation indefinitely +//========================================================= +Task_t tlBarnacleVictimChomp[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_BARNACLE_CHOMP }, + { TASK_SET_ACTIVITY, (float)ACT_BARNACLE_CHEW }, + { TASK_WAIT_INDEFINITE, (float)0 },// just cycle barnacle pull anim while barnacle hoists. +}; + +Schedule_t slBarnacleVictimChomp[] = +{ + { + tlBarnacleVictimChomp, + ARRAYSIZE ( tlBarnacleVictimChomp ), + 0, + 0, + "Barnacle Chomp" + } +}; + + +// Universal Error Schedule +Task_t tlError[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_WAIT_INDEFINITE, (float)0 }, +}; + +Schedule_t slError[] = +{ + { + tlError, + ARRAYSIZE ( tlError ), + 0, + 0, + "Error" + }, +}; + + +//========================================================= +// Cower - this is what is usually done when attempts +// to escape danger fail. +//========================================================= +Task_t tlCower[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_COWER }, +}; + +Schedule_t slCower[] = +{ + { + tlCower, + ARRAYSIZE ( tlCower ), + 0, + 0, + "Cower" + }, +}; + +//========================================================= +// move away from where you're currently standing. +//========================================================= +Task_t tlTakeCoverFromOrigin[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_ORIGIN, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slTakeCoverFromOrigin[] = +{ + { + tlTakeCoverFromOrigin, + ARRAYSIZE ( tlTakeCoverFromOrigin ), + bits_COND_NEW_ENEMY, + 0, + "TakeCoverFromOrigin" + }, +}; + +//========================================================= +// hide from the loudest sound source +//========================================================= +Task_t tlTakeCoverFromBestSound[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slTakeCoverFromBestSound[] = +{ + { + tlTakeCoverFromBestSound, + ARRAYSIZE ( tlTakeCoverFromBestSound ), + bits_COND_NEW_ENEMY, + 0, + "TakeCoverFromBestSound" + }, +}; + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlTakeCoverFromEnemy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, +// { TASK_TURN_LEFT, (float)179 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slTakeCoverFromEnemy[] = +{ + { + tlTakeCoverFromEnemy, + ARRAYSIZE ( tlTakeCoverFromEnemy ), + bits_COND_NEW_ENEMY, + 0, + "tlTakeCoverFromEnemy" + }, +}; + +Schedule_t *CMBaseMonster::m_scheduleList[] = +{ + slIdleStand, + slIdleTrigger, + slIdleWalk, + slAmbush, + slActiveIdle, + slWakeAngry, + slAlertFace, + slAlertSmallFlinch, + slAlertStand, + slInvestigateSound, + slCombatStand, + slCombatFace, + slStandoff, + slArmWeapon, + slReload, + slRangeAttack1, + slRangeAttack2, + slPrimaryMeleeAttack, + slSecondaryMeleeAttack, + slSpecialAttack1, + slSpecialAttack2, + slChaseEnemy, + slChaseEnemyFailed, + slSmallFlinch, + slDie, + slVictoryDance, + slBarnacleVictimGrab, + slBarnacleVictimChomp, + slError, + slCower, + slTakeCoverFromOrigin, + slTakeCoverFromBestSound, + slTakeCoverFromEnemy, + slFail +}; + +Schedule_t *CMBaseMonster::ScheduleFromName( const char *pName ) +{ + return ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) ); +} + + +Schedule_t *CMBaseMonster :: ScheduleInList( const char *pName, Schedule_t **pList, int listCount ) +{ + int i; + + if ( !pName ) + { + ALERT( at_console, "%s set to unnamed schedule!\n", STRING(pev->classname) ); + return NULL; + } + + + for ( i = 0; i < listCount; i++ ) + { + if ( !pList[i]->pName ) + { + ALERT( at_console, "Unnamed schedule!\n" ); + continue; + } + if ( stricmp( pName, pList[i]->pName ) == 0 ) + return pList[i]; + } + return NULL; +} + +//========================================================= +// GetScheduleOfType - returns a pointer to one of the +// monster's available schedules of the indicated type. +//========================================================= +Schedule_t* CMBaseMonster :: GetScheduleOfType ( int Type ) +{ +// ALERT ( at_console, "Sched Type:%d\n", Type ); + switch ( Type ) + { + case SCHED_IDLE_STAND: + { + if ( RANDOM_LONG(0,14) == 0 && FCanActiveIdle() ) + { + return &slActiveIdle[ 0 ]; + } + + return &slIdleStand[ 0 ]; + } + case SCHED_IDLE_WALK: + { + return &slIdleWalk[ 0 ]; + } + case SCHED_WAIT_TRIGGER: + { + return &slIdleTrigger[ 0 ]; + } + case SCHED_WAKE_ANGRY: + { + return &slWakeAngry[ 0 ]; + } + case SCHED_ALERT_FACE: + { + return &slAlertFace[ 0 ]; + } + case SCHED_ALERT_STAND: + { + return &slAlertStand[ 0 ]; + } + case SCHED_COMBAT_STAND: + { + return &slCombatStand[ 0 ]; + } + case SCHED_COMBAT_FACE: + { + return &slCombatFace[ 0 ]; + } + case SCHED_CHASE_ENEMY: + { + return &slChaseEnemy[ 0 ]; + } + case SCHED_CHASE_ENEMY_FAILED: + { + return &slFail[ 0 ]; + } + case SCHED_SMALL_FLINCH: + { + return &slSmallFlinch[ 0 ]; + } + case SCHED_ALERT_SMALL_FLINCH: + { + return &slAlertSmallFlinch[ 0 ]; + } + case SCHED_RELOAD: + { + return &slReload[ 0 ]; + } + case SCHED_ARM_WEAPON: + { + return &slArmWeapon[ 0 ]; + } + case SCHED_STANDOFF: + { + return &slStandoff[ 0 ]; + } + case SCHED_RANGE_ATTACK1: + { + return &slRangeAttack1[ 0 ]; + } + case SCHED_RANGE_ATTACK2: + { + return &slRangeAttack2[ 0 ]; + } + case SCHED_MELEE_ATTACK1: + { + return &slPrimaryMeleeAttack[ 0 ]; + } + case SCHED_MELEE_ATTACK2: + { + return &slSecondaryMeleeAttack[ 0 ]; + } + case SCHED_SPECIAL_ATTACK1: + { + return &slSpecialAttack1[ 0 ]; + } + case SCHED_SPECIAL_ATTACK2: + { + return &slSpecialAttack2[ 0 ]; + } + case SCHED_TAKE_COVER_FROM_BEST_SOUND: + { + return &slTakeCoverFromBestSound[ 0 ]; + } + case SCHED_TAKE_COVER_FROM_ENEMY: + { + return &slTakeCoverFromEnemy[ 0 ]; + } + case SCHED_COWER: + { + return &slCower[ 0 ]; + } + case SCHED_AMBUSH: + { + return &slAmbush[ 0 ]; + } + case SCHED_BARNACLE_VICTIM_GRAB: + { + return &slBarnacleVictimGrab[ 0 ]; + } + case SCHED_BARNACLE_VICTIM_CHOMP: + { + return &slBarnacleVictimChomp[ 0 ]; + } + case SCHED_INVESTIGATE_SOUND: + { + return &slInvestigateSound[ 0 ]; + } + case SCHED_DIE: + { + return &slDie[ 0 ]; + } + case SCHED_TAKE_COVER_FROM_ORIGIN: + { + return &slTakeCoverFromOrigin[ 0 ]; + } + case SCHED_VICTORY_DANCE: + { + return &slVictoryDance[ 0 ]; + } + case SCHED_FAIL: + { + return slFail; + } + default: + { + ALERT ( at_console, "GetScheduleOfType()\nNo CASE for Schedule Type %d!\n", Type ); + + return &slIdleStand[ 0 ]; + break; + } + } + + return NULL; +} diff --git a/src/dlls/defaultai.h b/src/dlls/defaultai.h index 32657ab..ae2e78c 100644 --- a/src/dlls/defaultai.h +++ b/src/dlls/defaultai.h @@ -1,98 +1,98 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef DEFAULTAI_H -#define DEFAULTAI_H - -//========================================================= -// Failed -//========================================================= -extern Schedule_t slFail[]; - -//========================================================= -// Idle Schedules -//========================================================= -extern Schedule_t slIdleStand[]; -extern Schedule_t slIdleTrigger[]; -extern Schedule_t slIdleWalk[]; - -//========================================================= -// Wake Schedules -//========================================================= -extern Schedule_t slWakeAngry[]; - -//========================================================= -// AlertTurn Schedules -//========================================================= -extern Schedule_t slAlertFace[]; - -//========================================================= -// AlertIdle Schedules -//========================================================= -extern Schedule_t slAlertStand[]; - -//========================================================= -// CombatIdle Schedule -//========================================================= -extern Schedule_t slCombatStand[]; - -//========================================================= -// CombatFace Schedule -//========================================================= -extern Schedule_t slCombatFace[]; - -//========================================================= -// reload schedule -//========================================================= -extern Schedule_t slReload[]; - -//========================================================= -// Attack Schedules -//========================================================= - -extern Schedule_t slRangeAttack1[]; -extern Schedule_t slRangeAttack2[]; - -extern Schedule_t slTakeCoverFromBestSound[]; - -// primary melee attack -extern Schedule_t slMeleeAttack[]; - -// Chase enemy schedule -extern Schedule_t slChaseEnemy[]; - -//========================================================= -// small flinch, used when a relatively minor bit of damage -// is inflicted. -//========================================================= -extern Schedule_t slSmallFlinch[]; - -//========================================================= -// Die! -//========================================================= -extern Schedule_t slDie[]; - -//========================================================= -// Universal Error Schedule -//========================================================= -extern Schedule_t slError[]; - -//========================================================= -// Scripted sequences -//========================================================= -extern Schedule_t slWalkToScript[]; -extern Schedule_t slRunToScript[]; -extern Schedule_t slWaitScript[]; - -#endif // DEFAULTAI_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef DEFAULTAI_H +#define DEFAULTAI_H + +//========================================================= +// Failed +//========================================================= +extern Schedule_t slFail[]; + +//========================================================= +// Idle Schedules +//========================================================= +extern Schedule_t slIdleStand[]; +extern Schedule_t slIdleTrigger[]; +extern Schedule_t slIdleWalk[]; + +//========================================================= +// Wake Schedules +//========================================================= +extern Schedule_t slWakeAngry[]; + +//========================================================= +// AlertTurn Schedules +//========================================================= +extern Schedule_t slAlertFace[]; + +//========================================================= +// AlertIdle Schedules +//========================================================= +extern Schedule_t slAlertStand[]; + +//========================================================= +// CombatIdle Schedule +//========================================================= +extern Schedule_t slCombatStand[]; + +//========================================================= +// CombatFace Schedule +//========================================================= +extern Schedule_t slCombatFace[]; + +//========================================================= +// reload schedule +//========================================================= +extern Schedule_t slReload[]; + +//========================================================= +// Attack Schedules +//========================================================= + +extern Schedule_t slRangeAttack1[]; +extern Schedule_t slRangeAttack2[]; + +extern Schedule_t slTakeCoverFromBestSound[]; + +// primary melee attack +extern Schedule_t slMeleeAttack[]; + +// Chase enemy schedule +extern Schedule_t slChaseEnemy[]; + +//========================================================= +// small flinch, used when a relatively minor bit of damage +// is inflicted. +//========================================================= +extern Schedule_t slSmallFlinch[]; + +//========================================================= +// Die! +//========================================================= +extern Schedule_t slDie[]; + +//========================================================= +// Universal Error Schedule +//========================================================= +extern Schedule_t slError[]; + +//========================================================= +// Scripted sequences +//========================================================= +extern Schedule_t slWalkToScript[]; +extern Schedule_t slRunToScript[]; +extern Schedule_t slWaitScript[]; + +#endif // DEFAULTAI_H diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index 01577c5..a49c9dc 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -1,2132 +1,2132 @@ -// -// Monster Mod is a modification based on Botman's original "Monster" plugin. -// The "forgotten" modification was made by Rick90. -// This is an attempt to recreate the plugin so it does not become lost again. -// -// Recreated by Giegue. -// -// dllapi.cpp -// - -/* - * This 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 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 code; 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 "extdll.h" -#include "dllapi.h" -#include "meta_api.h" - -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "hornet.h" -#include "decals.h" -#include "shake.h" -#include "skill.h" -#include "nodes.h" - -extern CGraph WorldGraph; - -extern globalvars_t *gpGlobals; -extern enginefuncs_t g_engfuncs; -extern gamedll_funcs_t *gpGamedllFuncs; - -extern cvar_t *dllapi_log; -extern cvar_t *monster_spawn; -extern cvar_t *monster_show_deaths; -extern cvar_t *monster_show_info; - -// Player TakeDamage and Killed -int g_DamageMsg; -bool g_DamageActive; -int g_DamageVictim; -edict_t *g_DamageAttacker[33]; -int g_DamageBits[33]; -bool g_PlayerKilled[33]; -float g_flWaitTillMessage[33]; - -// DeathMsg -int g_DeathMsg; -bool g_DeathActive; - -// TE_TEXTMESSAGE -float g_NextMessage[33]; - -cvar_t *g_psv_gravity = NULL; - -DLL_DECALLIST gDecals[] = { - { "{shot1", -1 }, // DECAL_GUNSHOT1 - { "{shot2", -1 }, // DECAL_GUNSHOT2 - { "{shot3", -1 }, // DECAL_GUNSHOT3 - { "{shot4", -1 }, // DECAL_GUNSHOT4 - { "{shot5", -1 }, // DECAL_GUNSHOT5 - { "{lambda01", -1 }, // DECAL_LAMBDA1 - { "{lambda02", -1 }, // DECAL_LAMBDA2 - { "{lambda03", -1 }, // DECAL_LAMBDA3 - { "{lambda04", -1 }, // DECAL_LAMBDA4 - { "{lambda05", -1 }, // DECAL_LAMBDA5 - { "{lambda06", -1 }, // DECAL_LAMBDA6 - { "{scorch1", -1 }, // DECAL_SCORCH1 - { "{scorch2", -1 }, // DECAL_SCORCH2 - { "{blood1", -1 }, // DECAL_BLOOD1 - { "{blood2", -1 }, // DECAL_BLOOD2 - { "{blood3", -1 }, // DECAL_BLOOD3 - { "{blood4", -1 }, // DECAL_BLOOD4 - { "{blood5", -1 }, // DECAL_BLOOD5 - { "{blood6", -1 }, // DECAL_BLOOD6 - { "{yblood1", -1 }, // DECAL_YBLOOD1 - { "{yblood2", -1 }, // DECAL_YBLOOD2 - { "{yblood3", -1 }, // DECAL_YBLOOD3 - { "{yblood4", -1 }, // DECAL_YBLOOD4 - { "{yblood5", -1 }, // DECAL_YBLOOD5 - { "{yblood6", -1 }, // DECAL_YBLOOD6 - { "{break1", -1 }, // DECAL_GLASSBREAK1 - { "{break2", -1 }, // DECAL_GLASSBREAK2 - { "{break3", -1 }, // DECAL_GLASSBREAK3 - { "{bigshot1", -1 }, // DECAL_BIGSHOT1 - { "{bigshot2", -1 }, // DECAL_BIGSHOT2 - { "{bigshot3", -1 }, // DECAL_BIGSHOT3 - { "{bigshot4", -1 }, // DECAL_BIGSHOT4 - { "{bigshot5", -1 }, // DECAL_BIGSHOT5 - { "{spit1", -1 }, // DECAL_SPIT1 - { "{spit2", -1 }, // DECAL_SPIT2 - { "{bproof1", -1 }, // DECAL_BPROOF1 - { "{gargstomp", -1 }, // DECAL_GARGSTOMP1, // Gargantua stomp crack - { "{smscorch1", -1 }, // DECAL_SMALLSCORCH1, // Small scorch mark - { "{smscorch2", -1 }, // DECAL_SMALLSCORCH2, // Small scorch mark - { "{smscorch3", -1 }, // DECAL_SMALLSCORCH3, // Small scorch mark - { "{mommablob", -1 }, // DECAL_MOMMABIRTH // BM Birth spray - { "{mommablob", -1 }, // DECAL_MOMMASPLAT // BM Mortar spray?? need decal -}; - -monster_type_t monster_types[]= -{ - // These are just names. But to keep it consistent - // with the new KVD format, ensure these are exactly - // like an actual, entity classname. - - // We are going to use this as a list of what entities - // can be spawned. Monsters should go first. - // DO NOT ALTER THE ORDER OF ELEMENTS! - - "monster_alien_grunt", FALSE, // Original Half-Life Monsters - "monster_apache", FALSE, - "monster_barney", FALSE, - "monster_bigmomma", FALSE, - "monster_bullsquid", FALSE, - "monster_alien_controller", FALSE, - "monster_human_assassin", FALSE, - "monster_headcrab", FALSE, - "monster_human_grunt", FALSE, - "monster_houndeye", FALSE, - "monster_alien_slave", FALSE, - "monster_scientist", FALSE, - "monster_snark", FALSE, - "monster_zombie", FALSE, - "monster_gargantua", FALSE, - "monster_turret", FALSE, - "monster_miniturret", FALSE, - "monster_sentry", FALSE, - "monster_gonome", FALSE, // Opposing Force Monsters - "monster_male_assassin", FALSE, - "monster_otis", FALSE, - "monster_pitdrone", FALSE, - "monster_shockroach", FALSE, - "monster_shocktrooper", FALSE, - "monster_voltigore", FALSE, - "monster_baby_voltigore", FALSE, - "monster_babygarg", FALSE, // Sven Co-op Monsters - "info_node", FALSE, // Nodes - "info_node_air", FALSE, - "", FALSE -}; - -monster_t monsters[MAX_MONSTER_ENTS]; -int monster_ents_used = 0; - -monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS]; -int monster_spawn_count = 0; - -node_spawnpoint_t node_spawnpoint[MAX_NODES]; -int node_spawn_count = 0; - -float check_respawn_time; - -bool process_monster_cfg(void); -bool process_monster_precache_cfg(void); - - -int GetMonsterIndex(void) -{ - int monster_index = -1; - - for (int index = 0; index < MAX_MONSTER_ENTS; index++) - { - if (monsters[index].monster_pent == 0) - { - monster_index = index; - break; - } - } - - if (monster_index == -1) - return -1; - - if (monster_index >= monster_ents_used) - monster_ents_used = monster_index + 1; // monster index is 0 based - - return monster_index; -} - - -void FreeMonsterIndex(int index) -{ - int idx = monsters[index].respawn_index; - - if (idx != -1) - { - monster_spawnpoint[idx].need_to_respawn = TRUE; - monster_spawnpoint[idx].respawn_time = gpGlobals->time + - monster_spawnpoint[idx].delay; - } - - delete monsters[index].pMonster; - - monsters[index].monster_index = 0; - monsters[index].monster_pent = NULL; - monsters[index].killed = FALSE; - monsters[index].pMonster = NULL; - - if (index == monster_ents_used-1) - { - while (monsters[index].monster_index == 0) - { - index--; - monster_ents_used--; - if (monster_ents_used == 0) - break; - } - } -} - - -void Remove_Entity(edict_t *pEdict) -{ - for (int index = 0; index < monster_ents_used; index++) - { - if (monsters[index].monster_pent == pEdict) - { - FreeMonsterIndex(index); - break; - } - } - - REMOVE_ENTITY(pEdict); -} - - -void monster_unload(void) -{ - // the plugin is being unloaded, remove any currently spawned monster... - - for (int index = 0; index < MAX_MONSTER_ENTS; index++) - { - if (monsters[index].pMonster != NULL) - { - monsters[index].monster_pent->v.flags |= FL_KILLME; - - delete monsters[index].pMonster; - - monsters[index].monster_index = 0; - monsters[index].monster_pent = NULL; - monsters[index].killed = FALSE; - monsters[index].pMonster = NULL; - } - } -} - - -void check_monster_hurt(edict_t *pAttacker) -{ - int index; - - for (index = 0; index < monster_ents_used; index++) - { - if (monsters[index].monster_index) - { - edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index); - - if (pent) - { - if (pent->v.health < pent->v.fuser4) - { - if (pent->v.takedamage != DAMAGE_NO) - { - TraceResult tr; - Vector vecSrc, vecSpot; - float distance, damage; - - // location of attacker and location of enemy... - vecSrc = pAttacker->v.origin + pAttacker->v.view_ofs; - vecSpot = pent->v.origin; - - // distance the blood can travel from the body... - distance = (vecSpot - vecSrc).Length() + 100.0f; - - // use aiming angles of attacker to trace blood splatter... - UTIL_MakeVectors(pAttacker->v.v_angle); - - // start just beyond the attacker's body... - vecSrc = vecSrc + gpGlobals->v_forward * 20; - vecSpot = vecSrc + gpGlobals->v_forward * distance; - - // trace a line ignoring enemies body... - UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, pent, &tr ); - - damage = pent->v.fuser4 - pent->v.health; - - // restore previous health, then do the damage (again) - pent->v.health = pent->v.fuser4; - - ClearMultiDamage( ); - monsters[index].pMonster->TraceAttack( VARS(pAttacker), damage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, DMG_BULLET ); - ApplyMultiDamage( VARS(pAttacker), VARS(pAttacker) ); - } - - // save the new current health as previous health... - pent->v.fuser4 = pent->v.health; - } - } - else - { - // the entity no longer exists and we didn't catch it dying - FreeMonsterIndex(index); - } - } - } -} - - -void check_monster_dead(edict_t *pAttacker) -{ - for (int index = 0; index < monster_ents_used; index++) - { - if (monsters[index].monster_index) - { - edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index); - - if (pent) - { - if (pent->v.flags & FL_KILLME) // func_wall was "killed" - { - if (pent->v.flags & FL_MONSTER) // is this a monster? - { - if (monsters[index].killed == FALSE) - { - pent->v.flags &= ~FL_KILLME; // clear FL_KILLME bit - - pent->v.deadflag = DEAD_NO; // bring back to life - - monsters[index].pMonster->Killed(VARS(pAttacker), 0); - - monsters[index].killed = TRUE; - } - } - else // normal entity - { - FreeMonsterIndex(index); - } - } - } - else - { - FreeMonsterIndex(index); - } - } - } -} - - -void check_player_dead( edict_t *pPlayer ) -{ - // Death messages are disabled - if (!monster_show_deaths->value) - return; - - int iPlayerIndex = ENTINDEX( pPlayer ); - - // Player died? - if ( !UTIL_IsAlive( pPlayer ) ) - { - edict_t *pAttacker = pPlayer->v.dmg_inflictor; - char szMessage[129]; // To allow exactly 128 characters - - // Attacker is NULL or message already shown, don't care - if ( pAttacker == NULL || g_PlayerKilled[ iPlayerIndex ] ) - return; - - // Get player's name - char szPlayerName[33]; - sprintf( szPlayerName, "%s", STRING( pPlayer->v.netname ) ); - - // Killed by a monster? - if ( pAttacker->v.flags & FL_MONSTER ) - { - // Check the first character for 'aeiou'. - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker)); - char szCheck[2]; - strncpy( szCheck, STRING( pMonster->m_szMonsterName ), 1 ); - - // Make the first character lowercase - szCheck[0] = tolower( szCheck[ 0 ] ); - if ( strncmp( szCheck, "a", 1 ) == 0 || strncmp( szCheck, "e", 1 ) == 0 || strncmp( szCheck, "i", 1 ) == 0 || strncmp( szCheck, "o", 1 ) == 0 || strncmp( szCheck, "u", 1 ) == 0 ) - sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) ); - else - sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) ); - } - else - { - // Suicide? - if ( pAttacker == pPlayer ) - sprintf( szMessage, "* %s commited suicide.\n", szPlayerName ); - // An entity killed this player. - else if ( ENTINDEX( pAttacker ) > 0 ) - { - // Gather damage type and format death message - if ( g_DamageBits[ iPlayerIndex ] == DMG_GENERIC ) - sprintf( szMessage, "* %s died mysteriously.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_CRUSH ) - sprintf( szMessage, "* %s was smashed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET ) - sprintf( szMessage, "* %s was shot.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH ) - sprintf( szMessage, "* %s lost its jelly.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN ) - sprintf( szMessage, "* %s burned to death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE ) - sprintf( szMessage, "* %s froze to death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL ) - sprintf( szMessage, "* %s broke its bones.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST ) - sprintf( szMessage, "* %s blew up.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB ) - sprintf( szMessage, "* %s was crowbared.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SHOCK ) - sprintf( szMessage, "* %s was electrocuted.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SONIC ) - sprintf( szMessage, "* %s ears popped.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_ENERGYBEAM ) - sprintf( szMessage, "* %s saw the pretty lights.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == DMG_NEVERGIB ) - sprintf( szMessage, "* %s had a painful death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == DMG_ALWAYSGIB ) - sprintf( szMessage, "* %s was gibbed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWN ) - sprintf( szMessage, "* %s became too drunk.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE ) - sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS ) - sprintf( szMessage, "* %s lost its brain.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON ) - sprintf( szMessage, "* %s had a slow death.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION ) - sprintf( szMessage, "* %s went nuclear.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWNRECOVER ) - sprintf( szMessage, "* %s used too much flex tape.\n", szPlayerName ); // is this type of death even possible? - else if ( g_DamageBits[ iPlayerIndex ] & DMG_ACID ) - sprintf( szMessage, "* %s was melted.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWBURN ) - sprintf( szMessage, "* %s became a cake.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE ) - sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR ) - sprintf( szMessage, "* %s blew its missile pet.\n", szPlayerName ); - else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages - sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) ); - else // other mods could have more DMG_ variants that aren't registered here. - sprintf( szMessage, "* %s deadly died.\n", szPlayerName ); - } - // the "world" killed this player - else - sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName ); - } - - // Print the message - if ( strlen( szMessage ) > 0 ) - UTIL_ClientPrintAll( HUD_PRINTTALK, szMessage ); - g_PlayerKilled[ iPlayerIndex ] = true; - } - else - g_PlayerKilled[ iPlayerIndex ] = false; -} - -void check_monster_info( edict_t *pPlayer ) -{ - // Monster Info is disabled - if (!monster_show_info->value) - return; - - // Player must be alive - if ( UTIL_IsAlive( pPlayer ) ) - { - // Don't overdo it! - if ( g_NextMessage[ ENTINDEX( pPlayer ) ] > gpGlobals->time ) - return; - - // Get player position and view angle - Vector origin = pPlayer->v.origin; - Vector view_angle = pPlayer->v.v_angle; - Vector view_offset = pPlayer->v.view_ofs; - - // Prepare Trace - TraceResult tr; - Vector v_src, v_dest; - - UTIL_MakeVectors(view_angle); - - v_src = origin + view_offset; // Player aiment - v_dest = v_src + gpGlobals->v_forward * 4096; // Should cover enough distance - - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - // Hit an entity? - if (tr.pHit != NULL) - { - // Must be a monster - if (tr.pHit->v.flags & FL_MONSTER) - { - // Get monster info - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); - - char szInfo[512]; - sprintf(szInfo, "Enemy: %s\nHealth: %.0f\nFrags: %.0f\n", STRING( pMonster->m_szMonsterName ), pMonster->pev->health, pMonster->pev->frags ); - - // Create a TE_TEXTMESSAGE and show the monster information - MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer ); - WRITE_BYTE( TE_TEXTMESSAGE ); - WRITE_BYTE( 3 ); // Channel - WRITE_SHORT( 327 ); // X - WRITE_SHORT( 4771 ); // Y - WRITE_BYTE( 0 ); // Effect - WRITE_BYTE( 171 ); // R1 - WRITE_BYTE( 23 ); // G1 - WRITE_BYTE( 7 ); // B1 - WRITE_BYTE( 0 ); // A1 - WRITE_BYTE( 207 ); // R2 - WRITE_BYTE( 23 ); // G2 - WRITE_BYTE( 7 ); // B2 - WRITE_BYTE( 255 ); // A2 - WRITE_SHORT( 0 ); // Fade-in Time - WRITE_SHORT( 15 ); // Fade-out Time - WRITE_SHORT( 448 ); // Hold time - WRITE_STRING( szInfo ); // Message - MESSAGE_END(); - - // Delay till next scan - g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.8; - } - } - } -} - -bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_index, int spawnflags, pKVD *keyvalue) -{ - int monster_index; - edict_t *monster_pent; - KeyValueData kvd; - - if ((monster_index = GetMonsterIndex()) == -1) - { - //META_CONS("[MONSTER] ERROR: No FREE Monster edicts!"); - LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!"); - return TRUE; - } - - // was this monster NOT precached? - if (monster_types[monster_type].need_to_precache == FALSE) - { - char msg[256]; - - //META_CONS("[MONSTER] ERROR: You can't spawn monster %s since it wasn't precached!", monster_types[monster_type].name); - LOG_MESSAGE(PLID, "ERROR: You can't spawn monster %s since it wasn't precached!", monster_types[monster_type].name); - - //META_CONS("[MONSTER] valid precached monster names are:"); - LOG_MESSAGE(PLID, "valid precached monster names are:"); - msg[0] = 0; - for (int index = 0; monster_types[index].name[0]; index++) - { - if (monster_types[index].need_to_precache == TRUE) - { - strcat(msg, monster_types[index].name); - strcat(msg, " "); - if (strlen(msg) > 60) - { - //META_CONS("[MONSTER] %s", msg); - LOG_MESSAGE(PLID, "%s", msg); - msg[0] = 0; - } - } - } - if (msg[0]) - { - //META_CONS("[MONSTER] %s", msg); - LOG_MESSAGE(PLID, "%s", msg); - } - - return TRUE; - } - - switch (monster_type) - { - case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break; - case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break; - case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break; - case 3: monsters[monster_index].pMonster = CreateClassPtr((CMBigMomma *)NULL); break; - case 4: monsters[monster_index].pMonster = CreateClassPtr((CMBullsquid *)NULL); break; - case 5: monsters[monster_index].pMonster = CreateClassPtr((CMController *)NULL); break; - case 6: monsters[monster_index].pMonster = CreateClassPtr((CMHAssassin *)NULL); break; - case 7: monsters[monster_index].pMonster = CreateClassPtr((CMHeadCrab *)NULL); break; - case 8: monsters[monster_index].pMonster = CreateClassPtr((CMHGrunt *)NULL); break; - case 9: monsters[monster_index].pMonster = CreateClassPtr((CMHoundeye *)NULL); break; - case 10: monsters[monster_index].pMonster = CreateClassPtr((CMISlave *)NULL); break; - case 11: monsters[monster_index].pMonster = CreateClassPtr((CMScientist *)NULL); break; - case 12: monsters[monster_index].pMonster = CreateClassPtr((CMSqueakGrenade *)NULL); break; - case 13: monsters[monster_index].pMonster = CreateClassPtr((CMZombie *)NULL); break; - case 14: monsters[monster_index].pMonster = CreateClassPtr((CMGargantua *)NULL); break; - case 15: monsters[monster_index].pMonster = CreateClassPtr((CMTurret *)NULL); break; - case 16: monsters[monster_index].pMonster = CreateClassPtr((CMMiniTurret *)NULL); break; - case 17: monsters[monster_index].pMonster = CreateClassPtr((CMSentry *)NULL); break; - case 18: monsters[monster_index].pMonster = CreateClassPtr((CMGonome *)NULL); break; - case 19: monsters[monster_index].pMonster = CreateClassPtr((CMMassn *)NULL); break; - case 20: monsters[monster_index].pMonster = CreateClassPtr((CMOtis *)NULL); break; - case 21: monsters[monster_index].pMonster = CreateClassPtr((CMPitdrone *)NULL); break; - case 22: monsters[monster_index].pMonster = CreateClassPtr((CMShockRoach *)NULL); break; - case 23: monsters[monster_index].pMonster = CreateClassPtr((CMStrooper *)NULL); break; - case 24: monsters[monster_index].pMonster = CreateClassPtr((CMVoltigore *)NULL); break; - case 25: monsters[monster_index].pMonster = CreateClassPtr((CMBabyVoltigore *)NULL); break; - case 26: monsters[monster_index].pMonster = CreateClassPtr((CMBabyGargantua *)NULL); break; - } - - if (monsters[monster_index].pMonster == NULL) - { - //META_CONS("[MONSTER] ERROR: Error Creating Monster!" ); - LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!"); - return TRUE; - } - - monsters[monster_index].respawn_index = respawn_index; - - monster_pent = ENT(monsters[monster_index].pMonster->pev); - monsters[monster_index].monster_pent = monster_pent; - - monsters[monster_index].monster_index = (*g_engfuncs.pfnIndexOfEdict)(monster_pent); - - monster_pent->v.origin = origin; - monster_pent->v.angles = angles; - - // Keyvalue data - if (keyvalue != NULL) - { - for (int index = 0; index < MAX_KEYVALUES; index++) - { - if (strlen(keyvalue[index].key) > 0) - { - kvd.szKeyName = keyvalue[index].key; - kvd.szValue = keyvalue[index].value; - monsters[monster_index].pMonster->KeyValue( &kvd ); - } - } - } - - monster_pent->v.spawnflags = spawnflags; - - monsters[monster_index].pMonster->Spawn(); - - // Reverse fadecorpse behaviour - if ( ( spawnflags & SF_MONSTER_FADECORPSE ) ) - monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE; - else - monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE; - - monster_pent->v.fuser4 = monster_pent->v.health; // save the original health - - return FALSE; -} - - -void check_respawn(void) -{ - int monster_type; - Vector origin; - Vector angles; - int spawnflags; - pKVD *keyvalue; - - if (!monster_spawn->value) - return; // monster_spawn is turned off, retry again later - - for (int index=0; index < monster_spawn_count; index++) - { - if (monster_spawnpoint[index].need_to_respawn && - (monster_spawnpoint[index].respawn_time <= gpGlobals->time)) - { - monster_spawnpoint[index].need_to_respawn = FALSE; - - monster_type = monster_spawnpoint[index].monster; - - origin = monster_spawnpoint[index].origin; - - angles = monster_spawnpoint[index].angles; - - spawnflags = monster_spawnpoint[index].spawnflags; - - keyvalue = monster_spawnpoint[index].keyvalue; - - if (spawn_monster(monster_type, origin, angles, index, spawnflags, keyvalue)) - { - // spawn_monster failed, retry again after delay... - monster_spawnpoint[index].need_to_respawn = TRUE; - monster_spawnpoint[index].respawn_time = gpGlobals->time + - monster_spawnpoint[index].delay; - } - } - } -} - - -DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball -DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud -DLL_GLOBAL short g_sModelIndexTinySpit;// holds the index for the spore grenade explosion -DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion -DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model -DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood -DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood -DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam -DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; -DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot - -void world_precache(void) -{ - g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball - g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke - g_sModelIndexTinySpit = PRECACHE_MODEL ("sprites/tinyspit.spr");// spore - g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball - 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_sModelIndexLaser = PRECACHE_MODEL( (char *)g_pModelNameLaser ); - g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr"); - - PRECACHE_MODEL ("models/w_grenade.mdl"); -} - - -void MonsterCommand(void) -{ - int index; - char msg[256]; - int monster_type = -1; - - if (CMD_ARGC() >= 3) - { - const char *parg1 = CMD_ARGV(1); - - // check for a valid monster name... - for (index = 0; monster_types[index].name[0]; index++) - { - // ensure it's an actual monster classname - if (strncmp(parg1, "monster", 7) == 0) - { - if (strcmp(parg1, monster_types[index].name) == 0) - { - monster_type = index; - break; - } - } - } - - if (monster_type != -1) - { - // check for a valid player name or index... - const char *parg2 = CMD_ARGV(2); - int player_index = -1; - edict_t *pPlayer; - const char *player_name; - - if (*parg2 == '#') // player index - { - if (sscanf(&parg2[1], "%d", &player_index) != 1) - player_index = -1; - - if ((player_index < 1) || (player_index > gpGlobals->maxClients)) - { - //META_CONS("[MONSTER] invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); - LOG_MESSAGE(PLID, "invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); - player_index = -1; - } - } - else - { - for (index = 1; index <= gpGlobals->maxClients; index++) - { - pPlayer = INDEXENT(index); - - if (pPlayer && !pPlayer->free) - { - if (stricmp(STRING(pPlayer->v.netname), parg2) == 0) - { - player_index = index; // found the matching player name - break; - } - } - } - - if (player_index == -1) - { - //META_CONS("[MONSTER] can't find player named \"%s\"!", parg2); - LOG_MESSAGE(PLID, "can't find player named \"%s\"!", parg2); - return; - } - } - - if (player_index != -1) - { - pPlayer = INDEXENT(player_index); - - if ((pPlayer == NULL) || (pPlayer->free)) - { - //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); - LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); - return; - } - - player_name = STRING(pPlayer->v.netname); - - if (player_name[0] == 0) - { - //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); - LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); - return; - } - - if (!UTIL_IsAlive(pPlayer)) - { - //META_CONS("[MONSTER] player \"%s\" is not alive or is an observer!", player_name); - LOG_MESSAGE(PLID, "player \"%s\" is not alive or is an observer!", player_name); - return; - } - - TraceResult tr; - Vector origin = pPlayer->v.origin; - Vector view_angle = pPlayer->v.v_angle; - Vector v_src, v_dest; - Vector monster_angle; - - // If spawning a turret, add autostart spawnflag. Zero otherwise - int spawnflags = 0; - if (monster_type >= 15 && monster_type <= 17) // Turret, Mini-Turret and Sentry - spawnflags = SF_MONSTER_TURRET_AUTOACTIVATE; - - // try to determine the best place to spawn the monster... - - view_angle.x = 0; // zero the pitch (level horizontally) - - UTIL_MakeVectors(view_angle); - - v_src = origin + Vector(0, 0, 20); // up a little bit - v_dest = v_src + gpGlobals->v_forward * 128; // in front of player - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y + 180.0f; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - v_src = origin + Vector(0, 0, 20); // up a little bit - // diagonally in front and to the left of the player... - v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * -90; - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y - 135.0f; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - v_src = origin + Vector(0, 0, 20); // up a little bit - // diagonally in front and to the right of the player... - v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * 90; - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y + 135.0f; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - v_src = origin + Vector(0, 0, 20); // up a little bit - v_dest = v_src + gpGlobals->v_right * 128; // to the right - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y + 90.0f; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - v_src = origin + Vector(0, 0, 20); // up a little bit - v_dest = v_src + gpGlobals->v_right * -128; // to the left - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y - 90.0f; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - v_src = origin + Vector(0, 0, 20); // up a little bit - v_dest = v_src + gpGlobals->v_forward * -128; // to the rear - - UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); - - if (tr.flFraction >= 1.0) - { - v_src = v_dest; - v_dest = v_dest + Vector(0, 0, -200); // down to ground - - // try to find the floor... - UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); - - if (tr.flFraction < 1.0) // hit something? - { - monster_angle.y = view_angle.y; // face the player - if (monster_angle.y > 360) - monster_angle.y -= 360; - if (monster_angle.y < 0) - monster_angle.y += 360; - - spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); - - return; - } - } - - //META_CONS("[MONSTER] there's no room to spawn a monster near player \"%s\"!", player_name); - LOG_MESSAGE(PLID, "there's no room to spawn a monster near player \"%s\"!", player_name); - - return; - } - } - } - - //META_CONS("[MONSTER] usage: monster monster_name player_name | #player_index"); - //META_CONS("[MONSTER] valid monster_names are:"); - LOG_MESSAGE(PLID, "usage: monster monster_name player_name | #player_index"); - LOG_MESSAGE(PLID, "valid monster_names are:"); - msg[0] = 0; - for (index = 0; monster_types[index].name[0]; index++) - { - // limit list to monster entities only - if (strncmp(monster_types[index].name, "monster", 7) == 0) - { - strcat(msg, monster_types[index].name); - strcat(msg, " "); - if (strlen(msg) > 60) - { - //META_CONS("[MONSTER] %s", msg); - LOG_MESSAGE(PLID, "%s", msg); - msg[0] = 0; - } - } - } - if (msg[0]) - { - //META_CONS("[MONSTER] %s", msg); - LOG_MESSAGE(PLID, "%s", msg); - } -} - -void SpawnViewerCommand(void) -{ - int index; - - // debug command to spawn a node_viewer at a player's location - if (CMD_ARGC() >= 2) - { - // check for a valid player name or index... - const char *parg2 = CMD_ARGV(1); - int player_index = -1; - edict_t *pPlayer; - const char *player_name; - - if (*parg2 == '#') // player index - { - if (sscanf(&parg2[1], "%d", &player_index) != 1) - player_index = -1; - - if ((player_index < 1) || (player_index > gpGlobals->maxClients)) - { - //META_CONS("[MONSTER] invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); - LOG_MESSAGE(PLID, "invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); - player_index = -1; - return; - } - } - else - { - for (index = 1; index <= gpGlobals->maxClients; index++) - { - pPlayer = INDEXENT(index); - - if (pPlayer && !pPlayer->free) - { - if (stricmp(STRING(pPlayer->v.netname), parg2) == 0) - { - player_index = index; // found the matching player name - break; - } - } - } - - if (player_index == -1) - { - //META_CONS("[MONSTER] can't find player named \"%s\"!", parg2); - LOG_MESSAGE(PLID, "can't find player named \"%s\"!", parg2); - return; - } - } - - if (player_index != -1) - { - pPlayer = INDEXENT(player_index); - - if ((pPlayer == NULL) || (pPlayer->free)) - { - //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); - LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); - return; - } - - player_name = STRING(pPlayer->v.netname); - - if (player_name[0] == 0) - { - //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); - LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); - return; - } - - if (!UTIL_IsAlive(pPlayer)) - { - //META_CONS("[MONSTER] player \"%s\" is not alive or is an observer!", player_name); - LOG_MESSAGE(PLID, "player \"%s\" is not alive or is an observer!", player_name); - return; - } - - Vector origin = pPlayer->v.origin; - - CMBaseEntity *pViewer = CreateClassPtr((CNodeViewer *)NULL); - if (pViewer == NULL) - { - //META_CONS("[MONSTER] ERROR: Error Creating Node!" ); - LOG_MESSAGE(PLID, "ERROR: Error Creating Viewer!"); - return; - } - - pViewer->pev->origin = origin; - pViewer->Spawn(); - return; - } - } - - LOG_MESSAGE(PLID, "usage: node_viewer player_name | #player_index"); - LOG_MESSAGE(PLID, "spawns a node viewer at the player's location"); -} - -void mmGameDLLInit( void ) -{ - // one time initialization stuff here... - - RETURN_META(MRES_IGNORED); -} - - -int mmDispatchSpawn( edict_t *pent ) -{ - int index; - char *pClassname = (char *)STRING(pent->v.classname); - - if (strcmp(pClassname, "worldspawn") == 0) - { - // free any monster class memory not previously freed... - for (index = 0; index < MAX_MONSTER_ENTS; index++) - { - if (monsters[index].pMonster != NULL) - delete monsters[index].pMonster; - } - - // free any allocated keyvalue memory - for (index = 0; index < monster_spawn_count; index++) - { - if (monster_spawnpoint[index].keyvalue) - free(monster_spawnpoint[index].keyvalue); - } - - // do level initialization stuff here... - - for (index = 0; monster_types[index].name[0]; index++) - monster_types[index].need_to_precache = FALSE; - - world_precache(); - - monster_spawn_count = 0; - node_spawn_count = 0; - - monster_skill_init(); - - process_monster_precache_cfg(); - - process_monster_cfg(); - - // node support. -Giegue - // init the WorldGraph. - WorldGraph.InitGraph(); - - // make sure the .NOD file is newer than the .BSP file. - if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) ) - { - // NOD file is not present, or is older than the BSP file. - WorldGraph.AllocNodes(); - } - else - { - // Load the node graph for this level - if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) ) - { - // couldn't load, so alloc and prepare to build a graph. - ALERT ( at_console, "*Error opening .NOD file\n" ); - WorldGraph.AllocNodes(); - } - else - { - ALERT ( at_console, "\n*Graph Loaded!\n" ); - } - } - - check_respawn_time = 0.0; - - for (index = 0; index < MAX_MONSTER_ENTS; index++) - { - monsters[index].monster_index = 0; - monsters[index].monster_pent = NULL; - monsters[index].killed = FALSE; // not killed yet - monsters[index].pMonster = NULL; - } - - monster_ents_used = 0; - - for (index = 0; index < ARRAYSIZE(gDecals); index++ ) - gDecals[index].index = DECAL_INDEX( gDecals[index].name ); - } - - // 0==Success, -1==Failure ? - RETURN_META_VALUE(MRES_IGNORED, 0); -} - -void mmDispatchThink( edict_t *pent ) -{ - for (int index=0; index < monster_ents_used; index++) - { - if (pent == monsters[index].monster_pent) - { - monsters[index].pMonster->Think(); - - check_monster_dead(pent); - - RETURN_META(MRES_SUPERCEDE); - } - } - - RETURN_META(MRES_IGNORED); -} -// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers) -// while it builds the graph -BOOL gTouchDisabled = FALSE; -void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther ) -{ - if (gTouchDisabled) - RETURN_META(MRES_SUPERCEDE); - - for (int index=0; index < monster_ents_used; index++) - { - if ((pentTouched != NULL) && (pentTouched == monsters[index].monster_pent)) - { - monsters[index].pMonster->Touch(pentOther); - - check_monster_dead(pentOther); - - RETURN_META(MRES_SUPERCEDE); - } - } - - RETURN_META(MRES_IGNORED); -} - - -void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) -{ - int index; - - CMAGrunt agrunt; - CMApache apache; - CMBarney barney; - CMBigMomma bigmomma; - CMBullsquid bullsquid; - CMController controller; - CMHAssassin hassassin; - CMHeadCrab headcrab; - CMHGrunt hgrunt; - CMHoundeye houndeye; - CMISlave islave; - CMScientist scientist; - CMSqueakGrenade snark; - CMZombie zombie; - CMGargantua gargantua; - CMTurret turret; - CMMiniTurret miniturret; - CMSentry sentry; - CMGonome gonome; - CMMassn massn; - CMOtis otis; - CMPitdrone pitdrone; - CMShockRoach shockroach; - CMStrooper strooper; - CMVoltigore voltigore; - CMBabyVoltigore babyvoltigore; - CMBabyGargantua babygargantua; - - g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); - - (g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand); - (g_engfuncs.pfnAddServerCommand)("node_viewer", SpawnViewerCommand); - - for (index = 0; monster_types[index].name[0]; index++) - { - if (monster_types[index].need_to_precache) - { - if (dllapi_log->value) - { - LOG_MESSAGE(PLID, "Precaching %s models & sounds...", monster_types[index].name); - } - - switch (index) - { - case 0: agrunt.Precache(); break; - case 1: apache.Precache(); break; - case 2: barney.Precache(); break; - case 3: bigmomma.Precache(); break; - case 4: bullsquid.Precache(); break; - case 5: controller.Precache(); break; - case 6: hassassin.Precache(); break; - case 7: headcrab.Precache(); break; - case 8: hgrunt.Precache(); break; - case 9: houndeye.Precache(); break; - case 10: islave.Precache(); break; - case 11: scientist.Precache(); break; - case 12: snark.Precache(); break; - case 13: zombie.Precache(); break; - case 14: gargantua.Precache(); break; - case 15: turret.Precache(); break; - case 16: miniturret.Precache(); break; - case 17: sentry.Precache(); break; - case 18: gonome.Precache(); break; - case 19: massn.Precache(); break; - case 20: otis.Precache(); break; - case 21: pitdrone.Precache(); break; - case 22: shockroach.Precache(); break; - case 23: strooper.Precache(); break; - case 24: voltigore.Precache(); break; - case 25: babyvoltigore.Precache(); break; - case 26: babygargantua.Precache(); break; - } - } - } - - for (index = 0; index < MAX_MONSTER_ENTS; index++) - { - monsters[index].monster_index = 0; - monsters[index].monster_pent = NULL; - monsters[index].killed = FALSE; // not killed yet - monsters[index].pMonster = NULL; - } - - monster_ents_used = 0; - - // spawn nodes - for (index = 0; index < node_spawn_count; index++) - { - CMBaseEntity *pNode; - pNode = CreateClassPtr((CNodeEnt *)NULL); - - if (pNode == NULL) - { - //META_CONS("[MONSTER] ERROR: Error Creating Node!" ); - LOG_MESSAGE(PLID, "ERROR: Error Creating Node!"); - } - else - { - pNode->pev->origin = node_spawnpoint[index].origin; - - if (node_spawnpoint[index].is_air_node) - pNode->pev->classname = MAKE_STRING("info_node_air"); - else - pNode->pev->classname = MAKE_STRING("info_node"); - - pNode->Spawn(); - } - } - - RETURN_META(MRES_IGNORED); -} - -void mmStartFrame( void ) -{ - if (check_respawn_time <= gpGlobals->time) - { - check_respawn_time = gpGlobals->time + 1.0; - - check_respawn(); - } - - RETURN_META(MRES_IGNORED); -} - -void mmClientKill( edict_t *pPlayer ) -{ - // Just to let the system know the player commited suicide - pPlayer->v.dmg_inflictor = pPlayer; - RETURN_META(MRES_IGNORED); -} - -static DLL_FUNCTIONS gFunctionTable = -{ - mmGameDLLInit, //! pfnGameInit() Initialize the game (one-time call after loading of game .dll) - mmDispatchSpawn, //! pfnSpawn() - mmDispatchThink, //! pfnThink - NULL, // pfnUse - mmDispatchTouch, //! pfnTouch - NULL, // pfnBlocked - NULL, // pfnKeyValue - NULL, // pfnSave - NULL, // pfnRestore - NULL, // pfnSetAbsBox - - NULL, // pfnSaveWriteFields - NULL, // pfnSaveReadFields - - NULL, // pfnSaveGlobalState - NULL, // pfnRestoreGlobalState - NULL, // pfnResetGlobalState - - NULL, // pfnClientConnect - NULL, // pfnClientDisconnect - mmClientKill, //! pfnClientKill - NULL, // pfnClientPutInServer - NULL, // pfnClientCommand - NULL, // pfnClientUserInfoChanged - mmServerActivate, //! pfnServerActivate() (wd) Server is starting a new map - NULL, // pfnServerDeactivate - - NULL, // pfnPlayerPreThink - NULL, // pfnPlayerPostThink - - mmStartFrame, //! pfnStartFrame - NULL, // pfnParmsNewLevel - NULL, // pfnParmsChangeLevel - - NULL, // pfnGetGameDescription - NULL, // pfnPlayerCustomization - - NULL, // pfnSpectatorConnect - NULL, // pfnSpectatorDisconnect - NULL, // pfnSpectatorThink - - NULL, // pfnSys_Error - - NULL, // pfnPM_Move - NULL, // pfnPM_Init - NULL, // pfnPM_FindTextureType - - NULL, // pfnSetupVisibility - NULL, // pfnUpdateClientData - NULL, // pfnAddToFullPack - NULL, // pfnCreateBaseline - NULL, // pfnRegisterEncoders - NULL, // pfnGetWeaponData - NULL, // pfnCmdStart - NULL, // pfnCmdEnd - NULL, // pfnConnectionlessPacket - NULL, // pfnGetHullBounds - NULL, // pfnCreateInstancedBaselines - NULL, // pfnInconsistentFile - NULL, // pfnAllowLagCompensation -}; - - -C_DLLEXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) -{ - if(!pFunctionTable) { - UTIL_LogPrintf("GetEntityAPI2 called with null pFunctionTable"); - return(FALSE); - } - else if(*interfaceVersion != INTERFACE_VERSION) { - UTIL_LogPrintf("GetEntityAPI2 version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION); - //! Tell engine what version we had, so it can figure out who is out of date. - *interfaceVersion = INTERFACE_VERSION; - return(FALSE); - } - memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) ); - return(TRUE); -} - - -void mmDispatchThink_Post( edict_t *pent ) -{ - check_monster_hurt(pent); - check_monster_dead(pent); - - RETURN_META(MRES_IGNORED); -} - -void mmPlayerPostThink_Post( edict_t *pEntity ) -{ - check_monster_hurt(pEntity); - check_monster_dead(pEntity); - check_player_dead(pEntity); - check_monster_info(pEntity); - - RETURN_META(MRES_IGNORED); -} - -static DLL_FUNCTIONS gFunctionTable_Post = -{ - NULL, // pfnGameInit() Initialize the game (one-time call after loading of game .dll) - NULL, // pfnSpawn() - mmDispatchThink_Post, //! pfnThink - NULL, // pfnUse - NULL, // pfnTouch - NULL, // pfnBlocked - NULL, // pfnKeyValue - NULL, // pfnSave - NULL, // pfnRestore - NULL, // pfnSetAbsBox - - NULL, // pfnSaveWriteFields - NULL, // pfnSaveReadFields - - NULL, // pfnSaveGlobalState - NULL, // pfnRestoreGlobalState - NULL, // pfnResetGlobalState - - NULL, // pfnClientConnect - NULL, // pfnClientDisconnect - NULL, // pfnClientKill - NULL, // pfnClientPutInServer - NULL, // pfnClientCommand - NULL, // pfnClientUserInfoChanged - NULL, // pfnServerActivate() (wd) Server is starting a new map - NULL, // pfnServerDeactivate - - NULL, // pfnPlayerPreThink - mmPlayerPostThink_Post, //! pfnPlayerPostThink - - NULL, // pfnStartFrame - NULL, // pfnParmsNewLevel - NULL, // pfnParmsChangeLevel - - NULL, // pfnGetGameDescription - NULL, // pfnPlayerCustomization - - NULL, // pfnSpectatorConnect - NULL, // pfnSpectatorDisconnect - NULL, // pfnSpectatorThink - - NULL, // pfnSys_Error - - NULL, // pfnPM_Move - NULL, // pfnPM_Init - NULL, // pfnPM_FindTextureType - - NULL, // pfnSetupVisibility - NULL, // pfnUpdateClientData - NULL, // pfnAddToFullPack - NULL, // pfnCreateBaseline - NULL, // pfnRegisterEncoders - NULL, // pfnGetWeaponData - NULL, // pfnCmdStart - NULL, // pfnCmdEnd - NULL, // pfnConnectionlessPacket - NULL, // pfnGetHullBounds - NULL, // pfnCreateInstancedBaselines - NULL, // pfnInconsistentFile - NULL, // pfnAllowLagCompensation -}; - -C_DLLEXPORT int GetEntityAPI2_Post( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) -{ - if(!pFunctionTable) { - UTIL_LogPrintf("GetEntityAPI2_Post called with null pFunctionTable"); - return(FALSE); - } - else if(*interfaceVersion != INTERFACE_VERSION) { - UTIL_LogPrintf("GetEntityAPI2_Post version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION); - //! Tell engine what version we had, so it can figure out who is out of date. - *interfaceVersion = INTERFACE_VERSION; - return(FALSE); - } - memcpy( pFunctionTable, &gFunctionTable_Post, sizeof( DLL_FUNCTIONS ) ); - return(TRUE); -} - - -// Some messages seems to be offset by 1. Linux specific? CStrike specific? -int IsCSServer( void ) -{ - char mod[16]; - sprintf( mod, "%s", GET_GAME_INFO( PLID, GINFO_NAME ) ); - - if ( strcmp( mod, "cstrike" ) == 0 || strcmp( mod, "czero" ) == 0 ) - return 1; - - return 0; -} - -int mmRegUserMsg_Post( const char *pName, int iSize ) -{ - int cs_server = IsCSServer(); - - if ( strcmp( pName, "Damage" ) == 0 ) - g_DamageMsg = META_RESULT_ORIG_RET( int ) - cs_server; - else if ( strcmp( pName, "DeathMsg" ) == 0 ) - g_DeathMsg = META_RESULT_ORIG_RET( int );// - cs_server; - - RETURN_META_VALUE( MRES_IGNORED, 0 ); -} - -void mmMessageBegin_Post( int msg_dest, int msg_type, const float *pOrigin, edict_t *ed ) -{ - if ( msg_type == g_DamageMsg ) - { - // Whatever hurting us must be a valid entity - if (ed->v.dmg_inflictor != NULL ) - { - g_DamageActive = true; - g_DamageVictim = ENTINDEX( ed ); - g_DamageAttacker[ g_DamageVictim ] = ed->v.dmg_inflictor; - } - } - else if ( msg_type == g_DeathMsg ) - { - // Prepare to update deathmsg - g_DeathActive = true; - } - - RETURN_META( MRES_IGNORED ); -} - -void mmWriteLong_Post( int iValue ) -{ - if ( g_DamageActive ) - g_DamageBits[ g_DamageVictim ] = iValue; - - RETURN_META( MRES_IGNORED ); -} - -// This cannot be done on post! -void mmWriteString( const char *szValue ) -{ - if ( g_DeathActive ) - { - // Prevent recursion - g_DeathActive = false; - - // Ensure whatever killed the player is a valid entity - if (g_DamageAttacker[ g_DamageVictim ] != NULL) - { - // Send a new WriteString with the killer's classname - WRITE_STRING( STRING( g_DamageAttacker[ g_DamageVictim ]->v.classname ) ); - - // Supercede the old message - RETURN_META( MRES_SUPERCEDE ); - } - } - - RETURN_META( MRES_IGNORED ); -} - -void mmMessageEnd_Post( void ) -{ - g_DamageActive = false; - - RETURN_META( MRES_IGNORED ); -} - -enginefuncs_t meta_engfuncs = -{ - NULL, // pfnPrecacheModel() - NULL, // pfnPrecacheSound() - NULL, // pfnSetModel() - NULL, // pfnModelIndex() - NULL, // pfnModelFrames() - - NULL, // pfnSetSize() - NULL, // pfnChangeLevel() - NULL, // pfnGetSpawnParms() - NULL, // pfnSaveSpawnParms() - - NULL, // pfnVecToYaw() - NULL, // pfnVecToAngles() - NULL, // pfnMoveToOrigin() - NULL, // pfnChangeYaw() - NULL, // pfnChangePitch() - - NULL, // pfnFindEntityByString() - NULL, // pfnGetEntityIllum() - NULL, // pfnFindEntityInSphere() - NULL, // pfnFindClientInPVS() - NULL, // pfnEntitiesInPVS() - - NULL, // pfnMakeVectors() - NULL, // pfnAngleVectors() - - NULL, // pfnCreateEntity() - NULL, // pfnRemoveEntity() - NULL, // pfnCreateNamedEntity() - - NULL, // pfnMakeStatic() - NULL, // pfnEntIsOnFloor() - NULL, // pfnDropToFloor() - - NULL, // pfnWalkMove() - NULL, // pfnSetOrigin() - - NULL, // pfnEmitSound() - NULL, // pfnEmitAmbientSound() - - NULL, // pfnTraceLine() - NULL, // pfnTraceToss() - NULL, // pfnTraceMonsterHull() - NULL, // pfnTraceHull() - NULL, // pfnTraceModel() - NULL, // pfnTraceTexture() - NULL, // pfnTraceSphere() - NULL, // pfnGetAimVector() - - NULL, // pfnServerCommand() - NULL, // pfnServerExecute() - NULL, // pfnClientCommand() - - NULL, // pfnParticleEffect() - NULL, // pfnLightStyle() - NULL, // pfnDecalIndex() - NULL, // pfnPointContents() - - NULL, // pfnMessageBegin() - NULL, // pfnMessageEnd() - - NULL, // pfnWriteByte() - NULL, // pfnWriteChar() - NULL, // pfnWriteShort() - NULL, // pfnWriteLong() - NULL, // pfnWriteAngle() - NULL, // pfnWriteCoord() - mmWriteString, //! pfnWriteString() - NULL, // pfnWriteEntity() - - NULL, // pfnCVarRegister() - NULL, // pfnCVarGetFloat() - NULL, // pfnCVarGetString() - NULL, // pfnCVarSetFloat() - NULL, // pfnCVarSetString() - - NULL, // pfnAlertMessage() - NULL, // pfnEngineFprintf() - - NULL, // pfnPvAllocEntPrivateData() - NULL, // pfnPvEntPrivateData() - NULL, // pfnFreeEntPrivateData() - - NULL, // pfnSzFromIndex() - NULL, // pfnAllocString() - - NULL, // pfnGetVarsOfEnt() - NULL, // pfnPEntityOfEntOffset() - NULL, // pfnEntOffsetOfPEntity() - NULL, // pfnIndexOfEdict() - NULL, // pfnPEntityOfEntIndex() - NULL, // pfnFindEntityByVars() - NULL, // pfnGetModelPtr() - - NULL, // pfnRegUserMsg() - - NULL, // pfnAnimationAutomove() - NULL, // pfnGetBonePosition() - - NULL, // pfnFunctionFromName() - NULL, // pfnNameForFunction() - - NULL, // pfnClientPrintf() - NULL, // pfnServerPrint() - - NULL, // pfnCmd_Args() - NULL, // pfnCmd_Argv() - NULL, // pfnCmd_Argc() - - NULL, // pfnGetAttachment() - - NULL, // pfnCRC32_Init() - NULL, // pfnCRC32_ProcessBuffer() - NULL, // pfnCRC32_ProcessByte() - NULL, // pfnCRC32_Final() - - NULL, // pfnRandomLong() - NULL, // pfnRandomFloat() - - NULL, // pfnSetView() - NULL, // pfnTime() - NULL, // pfnCrosshairAngle() - - NULL, // pfnLoadFileForMe() - NULL, // pfnFreeFile() - - NULL, // pfnEndSection() - NULL, // pfnCompareFileTime() - NULL, // pfnGetGameDir() - NULL, // pfnCvar_RegisterVariable() - NULL, // pfnFadeClientVolume() - NULL, // pfnSetClientMaxspeed() - NULL, // pfnCreateFakeClient() - NULL, // pfnRunPlayerMove() - NULL, // pfnNumberOfEntities() - - NULL, // pfnGetInfoKeyBuffer() - NULL, // pfnInfoKeyValue() - NULL, // pfnSetKeyValue() - NULL, // pfnSetClientKeyValue() - - NULL, // pfnIsMapValid() - NULL, // pfnStaticDecal() - NULL, // pfnPrecacheGeneric() - NULL, // pfnGetPlayerUserId() - NULL, // pfnBuildSoundMsg() - NULL, // pfnIsDedicatedServer() - NULL, // pfnCVarGetPointer() - NULL, // pfnGetPlayerWONId() - - NULL, // pfnInfo_RemoveKey() - NULL, // pfnGetPhysicsKeyValue() - NULL, // pfnSetPhysicsKeyValue() - NULL, // pfnGetPhysicsInfoString() - NULL, // pfnPrecacheEvent() - NULL, // pfnPlaybackEvent() - - NULL, // pfnSetFatPVS() - NULL, // pfnSetFatPAS() - - NULL, // pfnCheckVisibility() - - NULL, // pfnDeltaSetField() - NULL, // pfnDeltaUnsetField() - NULL, // pfnDeltaAddEncoder() - NULL, // pfnGetCurrentPlayer() - NULL, // pfnCanSkipPlayer() - NULL, // pfnDeltaFindField() - NULL, // pfnDeltaSetFieldByIndex() - NULL, // pfnDeltaUnsetFieldByIndex() - - NULL, // pfnSetGroupMask() - - NULL, // pfnCreateInstancedBaseline() - NULL, // pfnCvar_DirectSet() - - NULL, // pfnForceUnmodified() - - NULL, // pfnGetPlayerStats() - - NULL, // pfnAddServerCommand() - - NULL, // pfnVoice_GetClientListening() - NULL, // pfnVoice_SetClientListening() - - NULL, // pfnGetPlayerAuthId() - - NULL, // pfnSequenceGet() - NULL, // pfnSequencePickSentence() - NULL, // pfnGetFileSize() - NULL, // pfnGetApproxWavePlayLen() - NULL, // pfnIsCareerMatch() - NULL, // pfnGetLocalizedStringLength() - NULL, // pfnRegisterTutorMessageShown() - NULL, // pfnGetTimesTutorMessageShown() - NULL, // pfnProcessTutorMessageDecayBuffer() - NULL, // pfnConstructTutorMessageDecayBuffer() - NULL, // pfnResetTutorMessageDecayData() - NULL, // pfnQueryClientCvarValue() - NULL, // pfnQueryClientCvarValue2() - NULL, // pfnEngCheckParm() -}; - -C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion) -{ - if(!pengfuncsFromEngine) - { - LOG_ERROR(PLID, "GetEngineFunctions called with null pengfuncsFromEngine"); - return(FALSE); - } - else if(*interfaceVersion != ENGINE_INTERFACE_VERSION) - { - LOG_ERROR(PLID, "GetEngineFunctions version mismatch; requested=%d ours=%d", *interfaceVersion, ENGINE_INTERFACE_VERSION); - // Tell metamod what version we had, so it can figure out who is - // out of date. - *interfaceVersion = ENGINE_INTERFACE_VERSION; - return(FALSE); - } - memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t)); - return TRUE; -} - -enginefuncs_t meta_engfuncs_post = -{ - NULL, // pfnPrecacheModel() - NULL, // pfnPrecacheSound() - NULL, // pfnSetModel() - NULL, // pfnModelIndex() - NULL, // pfnModelFrames() - - NULL, // pfnSetSize() - NULL, // pfnChangeLevel() - NULL, // pfnGetSpawnParms() - NULL, // pfnSaveSpawnParms() - - NULL, // pfnVecToYaw() - NULL, // pfnVecToAngles() - NULL, // pfnMoveToOrigin() - NULL, // pfnChangeYaw() - NULL, // pfnChangePitch() - - NULL, // pfnFindEntityByString() - NULL, // pfnGetEntityIllum() - NULL, // pfnFindEntityInSphere() - NULL, // pfnFindClientInPVS() - NULL, // pfnEntitiesInPVS() - - NULL, // pfnMakeVectors() - NULL, // pfnAngleVectors() - - NULL, // pfnCreateEntity() - NULL, // pfnRemoveEntity() - NULL, // pfnCreateNamedEntity() - - NULL, // pfnMakeStatic() - NULL, // pfnEntIsOnFloor() - NULL, // pfnDropToFloor() - - NULL, // pfnWalkMove() - NULL, // pfnSetOrigin() - - NULL, // pfnEmitSound() - NULL, // pfnEmitAmbientSound() - - NULL, // pfnTraceLine() - NULL, // pfnTraceToss() - NULL, // pfnTraceMonsterHull() - NULL, // pfnTraceHull() - NULL, // pfnTraceModel() - NULL, // pfnTraceTexture() - NULL, // pfnTraceSphere() - NULL, // pfnGetAimVector() - - NULL, // pfnServerCommand() - NULL, // pfnServerExecute() - NULL, // pfnClientCommand() - - NULL, // pfnParticleEffect() - NULL, // pfnLightStyle() - NULL, // pfnDecalIndex() - NULL, // pfnPointContents() - - mmMessageBegin_Post, //! pfnMessageBegin() - mmMessageEnd_Post, //! pfnMessageEnd() - - NULL, // pfnWriteByte() - NULL, // pfnWriteChar() - NULL, // pfnWriteShort() - mmWriteLong_Post, //! pfnWriteLong() - NULL, // pfnWriteAngle() - NULL, // pfnWriteCoord() - NULL, // pfnWriteString() - NULL, // pfnWriteEntity() - - NULL, // pfnCVarRegister() - NULL, // pfnCVarGetFloat() - NULL, // pfnCVarGetString() - NULL, // pfnCVarSetFloat() - NULL, // pfnCVarSetString() - - NULL, // pfnAlertMessage() - NULL, // pfnEngineFprintf() - - NULL, // pfnPvAllocEntPrivateData() - NULL, // pfnPvEntPrivateData() - NULL, // pfnFreeEntPrivateData() - - NULL, // pfnSzFromIndex() - NULL, // pfnAllocString() - - NULL, // pfnGetVarsOfEnt() - NULL, // pfnPEntityOfEntOffset() - NULL, // pfnEntOffsetOfPEntity() - NULL, // pfnIndexOfEdict() - NULL, // pfnPEntityOfEntIndex() - NULL, // pfnFindEntityByVars() - NULL, // pfnGetModelPtr() - - mmRegUserMsg_Post, //! pfnRegUserMsg() - - NULL, // pfnAnimationAutomove() - NULL, // pfnGetBonePosition() - - NULL, // pfnFunctionFromName() - NULL, // pfnNameForFunction() - - NULL, // pfnClientPrintf() - NULL, // pfnServerPrint() - - NULL, // pfnCmd_Args() - NULL, // pfnCmd_Argv() - NULL, // pfnCmd_Argc() - - NULL, // pfnGetAttachment() - - NULL, // pfnCRC32_Init() - NULL, // pfnCRC32_ProcessBuffer() - NULL, // pfnCRC32_ProcessByte() - NULL, // pfnCRC32_Final() - - NULL, // pfnRandomLong() - NULL, // pfnRandomFloat() - - NULL, // pfnSetView() - NULL, // pfnTime() - NULL, // pfnCrosshairAngle() - - NULL, // pfnLoadFileForMe() - NULL, // pfnFreeFile() - - NULL, // pfnEndSection() - NULL, // pfnCompareFileTime() - NULL, // pfnGetGameDir() - NULL, // pfnCvar_RegisterVariable() - NULL, // pfnFadeClientVolume() - NULL, // pfnSetClientMaxspeed() - NULL, // pfnCreateFakeClient() - NULL, // pfnRunPlayerMove() - NULL, // pfnNumberOfEntities() - - NULL, // pfnGetInfoKeyBuffer() - NULL, // pfnInfoKeyValue() - NULL, // pfnSetKeyValue() - NULL, // pfnSetClientKeyValue() - - NULL, // pfnIsMapValid() - NULL, // pfnStaticDecal() - NULL, // pfnPrecacheGeneric() - NULL, // pfnGetPlayerUserId() - NULL, // pfnBuildSoundMsg() - NULL, // pfnIsDedicatedServer() - NULL, // pfnCVarGetPointer() - NULL, // pfnGetPlayerWONId() - - NULL, // pfnInfo_RemoveKey() - NULL, // pfnGetPhysicsKeyValue() - NULL, // pfnSetPhysicsKeyValue() - NULL, // pfnGetPhysicsInfoString() - NULL, // pfnPrecacheEvent() - NULL, // pfnPlaybackEvent() - - NULL, // pfnSetFatPVS() - NULL, // pfnSetFatPAS() - - NULL, // pfnCheckVisibility() - - NULL, // pfnDeltaSetField() - NULL, // pfnDeltaUnsetField() - NULL, // pfnDeltaAddEncoder() - NULL, // pfnGetCurrentPlayer() - NULL, // pfnCanSkipPlayer() - NULL, // pfnDeltaFindField() - NULL, // pfnDeltaSetFieldByIndex() - NULL, // pfnDeltaUnsetFieldByIndex() - - NULL, // pfnSetGroupMask() - - NULL, // pfnCreateInstancedBaseline() - NULL, // pfnCvar_DirectSet() - - NULL, // pfnForceUnmodified() - - NULL, // pfnGetPlayerStats() - - NULL, // pfnAddServerCommand() - - NULL, // pfnVoice_GetClientListening() - NULL, // pfnVoice_SetClientListening() - - NULL, // pfnGetPlayerAuthId() - - NULL, // pfnSequenceGet() - NULL, // pfnSequencePickSentence() - NULL, // pfnGetFileSize() - NULL, // pfnGetApproxWavePlayLen() - NULL, // pfnIsCareerMatch() - NULL, // pfnGetLocalizedStringLength() - NULL, // pfnRegisterTutorMessageShown() - NULL, // pfnGetTimesTutorMessageShown() - NULL, // pfnProcessTutorMessageDecayBuffer() - NULL, // pfnConstructTutorMessageDecayBuffer() - NULL, // pfnResetTutorMessageDecayData() - NULL, // pfnQueryClientCvarValue() - NULL, // pfnQueryClientCvarValue2() - NULL, // pfnEngCheckParm() -}; - -C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion) -{ - if(!pengfuncsFromEngine) - { - LOG_ERROR(PLID, "GetEngineFunctions_Post called with null pengfuncsFromEngine"); - return(FALSE); - } - else if(*interfaceVersion != ENGINE_INTERFACE_VERSION) - { - LOG_ERROR(PLID, "GetEngineFunctions_Post version mismatch; requested=%d ours=%d", *interfaceVersion, ENGINE_INTERFACE_VERSION); - // Tell metamod what version we had, so it can figure out who is - // out of date. - *interfaceVersion = ENGINE_INTERFACE_VERSION; - return(FALSE); - } - memcpy(pengfuncsFromEngine, &meta_engfuncs_post, sizeof(enginefuncs_t)); - return TRUE; -} +// +// Monster Mod is a modification based on Botman's original "Monster" plugin. +// The "forgotten" modification was made by Rick90. +// This is an attempt to recreate the plugin so it does not become lost again. +// +// Recreated by Giegue. +// +// dllapi.cpp +// + +/* + * This 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 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 code; 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 "extdll.h" +#include "dllapi.h" +#include "meta_api.h" + +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "hornet.h" +#include "decals.h" +#include "shake.h" +#include "skill.h" +#include "nodes.h" + +extern CGraph WorldGraph; + +extern globalvars_t *gpGlobals; +extern enginefuncs_t g_engfuncs; +extern gamedll_funcs_t *gpGamedllFuncs; + +extern cvar_t *dllapi_log; +extern cvar_t *monster_spawn; +extern cvar_t *monster_show_deaths; +extern cvar_t *monster_show_info; + +// Player TakeDamage and Killed +int g_DamageMsg; +bool g_DamageActive; +int g_DamageVictim; +edict_t *g_DamageAttacker[33]; +int g_DamageBits[33]; +bool g_PlayerKilled[33]; +float g_flWaitTillMessage[33]; + +// DeathMsg +int g_DeathMsg; +bool g_DeathActive; + +// TE_TEXTMESSAGE +float g_NextMessage[33]; + +cvar_t *g_psv_gravity = NULL; + +DLL_DECALLIST gDecals[] = { + { "{shot1", -1 }, // DECAL_GUNSHOT1 + { "{shot2", -1 }, // DECAL_GUNSHOT2 + { "{shot3", -1 }, // DECAL_GUNSHOT3 + { "{shot4", -1 }, // DECAL_GUNSHOT4 + { "{shot5", -1 }, // DECAL_GUNSHOT5 + { "{lambda01", -1 }, // DECAL_LAMBDA1 + { "{lambda02", -1 }, // DECAL_LAMBDA2 + { "{lambda03", -1 }, // DECAL_LAMBDA3 + { "{lambda04", -1 }, // DECAL_LAMBDA4 + { "{lambda05", -1 }, // DECAL_LAMBDA5 + { "{lambda06", -1 }, // DECAL_LAMBDA6 + { "{scorch1", -1 }, // DECAL_SCORCH1 + { "{scorch2", -1 }, // DECAL_SCORCH2 + { "{blood1", -1 }, // DECAL_BLOOD1 + { "{blood2", -1 }, // DECAL_BLOOD2 + { "{blood3", -1 }, // DECAL_BLOOD3 + { "{blood4", -1 }, // DECAL_BLOOD4 + { "{blood5", -1 }, // DECAL_BLOOD5 + { "{blood6", -1 }, // DECAL_BLOOD6 + { "{yblood1", -1 }, // DECAL_YBLOOD1 + { "{yblood2", -1 }, // DECAL_YBLOOD2 + { "{yblood3", -1 }, // DECAL_YBLOOD3 + { "{yblood4", -1 }, // DECAL_YBLOOD4 + { "{yblood5", -1 }, // DECAL_YBLOOD5 + { "{yblood6", -1 }, // DECAL_YBLOOD6 + { "{break1", -1 }, // DECAL_GLASSBREAK1 + { "{break2", -1 }, // DECAL_GLASSBREAK2 + { "{break3", -1 }, // DECAL_GLASSBREAK3 + { "{bigshot1", -1 }, // DECAL_BIGSHOT1 + { "{bigshot2", -1 }, // DECAL_BIGSHOT2 + { "{bigshot3", -1 }, // DECAL_BIGSHOT3 + { "{bigshot4", -1 }, // DECAL_BIGSHOT4 + { "{bigshot5", -1 }, // DECAL_BIGSHOT5 + { "{spit1", -1 }, // DECAL_SPIT1 + { "{spit2", -1 }, // DECAL_SPIT2 + { "{bproof1", -1 }, // DECAL_BPROOF1 + { "{gargstomp", -1 }, // DECAL_GARGSTOMP1, // Gargantua stomp crack + { "{smscorch1", -1 }, // DECAL_SMALLSCORCH1, // Small scorch mark + { "{smscorch2", -1 }, // DECAL_SMALLSCORCH2, // Small scorch mark + { "{smscorch3", -1 }, // DECAL_SMALLSCORCH3, // Small scorch mark + { "{mommablob", -1 }, // DECAL_MOMMABIRTH // BM Birth spray + { "{mommablob", -1 }, // DECAL_MOMMASPLAT // BM Mortar spray?? need decal +}; + +monster_type_t monster_types[]= +{ + // These are just names. But to keep it consistent + // with the new KVD format, ensure these are exactly + // like an actual, entity classname. + + // We are going to use this as a list of what entities + // can be spawned. Monsters should go first. + // DO NOT ALTER THE ORDER OF ELEMENTS! + + "monster_alien_grunt", FALSE, // Original Half-Life Monsters + "monster_apache", FALSE, + "monster_barney", FALSE, + "monster_bigmomma", FALSE, + "monster_bullsquid", FALSE, + "monster_alien_controller", FALSE, + "monster_human_assassin", FALSE, + "monster_headcrab", FALSE, + "monster_human_grunt", FALSE, + "monster_houndeye", FALSE, + "monster_alien_slave", FALSE, + "monster_scientist", FALSE, + "monster_snark", FALSE, + "monster_zombie", FALSE, + "monster_gargantua", FALSE, + "monster_turret", FALSE, + "monster_miniturret", FALSE, + "monster_sentry", FALSE, + "monster_gonome", FALSE, // Opposing Force Monsters + "monster_male_assassin", FALSE, + "monster_otis", FALSE, + "monster_pitdrone", FALSE, + "monster_shockroach", FALSE, + "monster_shocktrooper", FALSE, + "monster_voltigore", FALSE, + "monster_baby_voltigore", FALSE, + "monster_babygarg", FALSE, // Sven Co-op Monsters + "info_node", FALSE, // Nodes + "info_node_air", FALSE, + "", FALSE +}; + +monster_t monsters[MAX_MONSTER_ENTS]; +int monster_ents_used = 0; + +monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS]; +int monster_spawn_count = 0; + +node_spawnpoint_t node_spawnpoint[MAX_NODES]; +int node_spawn_count = 0; + +float check_respawn_time; + +bool process_monster_cfg(void); +bool process_monster_precache_cfg(void); + + +int GetMonsterIndex(void) +{ + int monster_index = -1; + + for (int index = 0; index < MAX_MONSTER_ENTS; index++) + { + if (monsters[index].monster_pent == 0) + { + monster_index = index; + break; + } + } + + if (monster_index == -1) + return -1; + + if (monster_index >= monster_ents_used) + monster_ents_used = monster_index + 1; // monster index is 0 based + + return monster_index; +} + + +void FreeMonsterIndex(int index) +{ + int idx = monsters[index].respawn_index; + + if (idx != -1) + { + monster_spawnpoint[idx].need_to_respawn = TRUE; + monster_spawnpoint[idx].respawn_time = gpGlobals->time + + monster_spawnpoint[idx].delay; + } + + delete monsters[index].pMonster; + + monsters[index].monster_index = 0; + monsters[index].monster_pent = NULL; + monsters[index].killed = FALSE; + monsters[index].pMonster = NULL; + + if (index == monster_ents_used-1) + { + while (monsters[index].monster_index == 0) + { + index--; + monster_ents_used--; + if (monster_ents_used == 0) + break; + } + } +} + + +void Remove_Entity(edict_t *pEdict) +{ + for (int index = 0; index < monster_ents_used; index++) + { + if (monsters[index].monster_pent == pEdict) + { + FreeMonsterIndex(index); + break; + } + } + + REMOVE_ENTITY(pEdict); +} + + +void monster_unload(void) +{ + // the plugin is being unloaded, remove any currently spawned monster... + + for (int index = 0; index < MAX_MONSTER_ENTS; index++) + { + if (monsters[index].pMonster != NULL) + { + monsters[index].monster_pent->v.flags |= FL_KILLME; + + delete monsters[index].pMonster; + + monsters[index].monster_index = 0; + monsters[index].monster_pent = NULL; + monsters[index].killed = FALSE; + monsters[index].pMonster = NULL; + } + } +} + + +void check_monster_hurt(edict_t *pAttacker) +{ + int index; + + for (index = 0; index < monster_ents_used; index++) + { + if (monsters[index].monster_index) + { + edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index); + + if (pent) + { + if (pent->v.health < pent->v.fuser4) + { + if (pent->v.takedamage != DAMAGE_NO) + { + TraceResult tr; + Vector vecSrc, vecSpot; + float distance, damage; + + // location of attacker and location of enemy... + vecSrc = pAttacker->v.origin + pAttacker->v.view_ofs; + vecSpot = pent->v.origin; + + // distance the blood can travel from the body... + distance = (vecSpot - vecSrc).Length() + 100.0f; + + // use aiming angles of attacker to trace blood splatter... + UTIL_MakeVectors(pAttacker->v.v_angle); + + // start just beyond the attacker's body... + vecSrc = vecSrc + gpGlobals->v_forward * 20; + vecSpot = vecSrc + gpGlobals->v_forward * distance; + + // trace a line ignoring enemies body... + UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, pent, &tr ); + + damage = pent->v.fuser4 - pent->v.health; + + // restore previous health, then do the damage (again) + pent->v.health = pent->v.fuser4; + + ClearMultiDamage( ); + monsters[index].pMonster->TraceAttack( VARS(pAttacker), damage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, DMG_BULLET ); + ApplyMultiDamage( VARS(pAttacker), VARS(pAttacker) ); + } + + // save the new current health as previous health... + pent->v.fuser4 = pent->v.health; + } + } + else + { + // the entity no longer exists and we didn't catch it dying + FreeMonsterIndex(index); + } + } + } +} + + +void check_monster_dead(edict_t *pAttacker) +{ + for (int index = 0; index < monster_ents_used; index++) + { + if (monsters[index].monster_index) + { + edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index); + + if (pent) + { + if (pent->v.flags & FL_KILLME) // func_wall was "killed" + { + if (pent->v.flags & FL_MONSTER) // is this a monster? + { + if (monsters[index].killed == FALSE) + { + pent->v.flags &= ~FL_KILLME; // clear FL_KILLME bit + + pent->v.deadflag = DEAD_NO; // bring back to life + + monsters[index].pMonster->Killed(VARS(pAttacker), 0); + + monsters[index].killed = TRUE; + } + } + else // normal entity + { + FreeMonsterIndex(index); + } + } + } + else + { + FreeMonsterIndex(index); + } + } + } +} + + +void check_player_dead( edict_t *pPlayer ) +{ + // Death messages are disabled + if (!monster_show_deaths->value) + return; + + int iPlayerIndex = ENTINDEX( pPlayer ); + + // Player died? + if ( !UTIL_IsAlive( pPlayer ) ) + { + edict_t *pAttacker = pPlayer->v.dmg_inflictor; + char szMessage[129]; // To allow exactly 128 characters + + // Attacker is NULL or message already shown, don't care + if ( pAttacker == NULL || g_PlayerKilled[ iPlayerIndex ] ) + return; + + // Get player's name + char szPlayerName[33]; + sprintf( szPlayerName, "%s", STRING( pPlayer->v.netname ) ); + + // Killed by a monster? + if ( pAttacker->v.flags & FL_MONSTER ) + { + // Check the first character for 'aeiou'. + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pAttacker)); + char szCheck[2]; + strncpy( szCheck, STRING( pMonster->m_szMonsterName ), 1 ); + + // Make the first character lowercase + szCheck[0] = tolower( szCheck[ 0 ] ); + if ( strncmp( szCheck, "a", 1 ) == 0 || strncmp( szCheck, "e", 1 ) == 0 || strncmp( szCheck, "i", 1 ) == 0 || strncmp( szCheck, "o", 1 ) == 0 || strncmp( szCheck, "u", 1 ) == 0 ) + sprintf( szMessage, "* %s was killed by an %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) ); + else + sprintf( szMessage, "* %s was killed by a %s.\n", szPlayerName, STRING( pMonster->m_szMonsterName ) ); + } + else + { + // Suicide? + if ( pAttacker == pPlayer ) + sprintf( szMessage, "* %s commited suicide.\n", szPlayerName ); + // An entity killed this player. + else if ( ENTINDEX( pAttacker ) > 0 ) + { + // Gather damage type and format death message + if ( g_DamageBits[ iPlayerIndex ] == DMG_GENERIC ) + sprintf( szMessage, "* %s died mysteriously.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_CRUSH ) + sprintf( szMessage, "* %s was smashed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BULLET ) + sprintf( szMessage, "* %s was shot.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLASH ) + sprintf( szMessage, "* %s lost its jelly.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BURN ) + sprintf( szMessage, "* %s burned to death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_FREEZE ) + sprintf( szMessage, "* %s froze to death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_FALL ) + sprintf( szMessage, "* %s broke its bones.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_BLAST ) + sprintf( szMessage, "* %s blew up.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_CLUB ) + sprintf( szMessage, "* %s was crowbared.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SHOCK ) + sprintf( szMessage, "* %s was electrocuted.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SONIC ) + sprintf( szMessage, "* %s ears popped.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_ENERGYBEAM ) + sprintf( szMessage, "* %s saw the pretty lights.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == DMG_NEVERGIB ) + sprintf( szMessage, "* %s had a painful death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == DMG_ALWAYSGIB ) + sprintf( szMessage, "* %s was gibbed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWN ) + sprintf( szMessage, "* %s became too drunk.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_PARALYZE ) + sprintf( szMessage, "* %s was paralyzed.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_NERVEGAS ) + sprintf( szMessage, "* %s lost its brain.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_POISON ) + sprintf( szMessage, "* %s had a slow death.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_RADIATION ) + sprintf( szMessage, "* %s went nuclear.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_DROWNRECOVER ) + sprintf( szMessage, "* %s used too much flex tape.\n", szPlayerName ); // is this type of death even possible? + else if ( g_DamageBits[ iPlayerIndex ] & DMG_ACID ) + sprintf( szMessage, "* %s was melted.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWBURN ) + sprintf( szMessage, "* %s became a cake.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_SLOWFREEZE ) + sprintf( szMessage, "* %s died of hypothermia.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] & DMG_MORTAR ) + sprintf( szMessage, "* %s blew its missile pet.\n", szPlayerName ); + else if ( g_DamageBits[ iPlayerIndex ] == (1 << 30) ) // (1 << 30) = 1073741824. For custom death messages + sprintf( szMessage, "* %s %s.\n", szPlayerName, STRING( pAttacker->v.noise ) ); + else // other mods could have more DMG_ variants that aren't registered here. + sprintf( szMessage, "* %s deadly died.\n", szPlayerName ); + } + // the "world" killed this player + else + sprintf( szMessage, "* %s fell or drowned or something.\n", szPlayerName ); + } + + // Print the message + if ( strlen( szMessage ) > 0 ) + UTIL_ClientPrintAll( HUD_PRINTTALK, szMessage ); + g_PlayerKilled[ iPlayerIndex ] = true; + } + else + g_PlayerKilled[ iPlayerIndex ] = false; +} + +void check_monster_info( edict_t *pPlayer ) +{ + // Monster Info is disabled + if (!monster_show_info->value) + return; + + // Player must be alive + if ( UTIL_IsAlive( pPlayer ) ) + { + // Don't overdo it! + if ( g_NextMessage[ ENTINDEX( pPlayer ) ] > gpGlobals->time ) + return; + + // Get player position and view angle + Vector origin = pPlayer->v.origin; + Vector view_angle = pPlayer->v.v_angle; + Vector view_offset = pPlayer->v.view_ofs; + + // Prepare Trace + TraceResult tr; + Vector v_src, v_dest; + + UTIL_MakeVectors(view_angle); + + v_src = origin + view_offset; // Player aiment + v_dest = v_src + gpGlobals->v_forward * 4096; // Should cover enough distance + + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + // Hit an entity? + if (tr.pHit != NULL) + { + // Must be a monster + if (tr.pHit->v.flags & FL_MONSTER) + { + // Get monster info + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); + + char szInfo[512]; + sprintf(szInfo, "Enemy: %s\nHealth: %.0f\nFrags: %.0f\n", STRING( pMonster->m_szMonsterName ), pMonster->pev->health, pMonster->pev->frags ); + + // Create a TE_TEXTMESSAGE and show the monster information + MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pPlayer ); + WRITE_BYTE( TE_TEXTMESSAGE ); + WRITE_BYTE( 3 ); // Channel + WRITE_SHORT( 327 ); // X + WRITE_SHORT( 4771 ); // Y + WRITE_BYTE( 0 ); // Effect + WRITE_BYTE( 171 ); // R1 + WRITE_BYTE( 23 ); // G1 + WRITE_BYTE( 7 ); // B1 + WRITE_BYTE( 0 ); // A1 + WRITE_BYTE( 207 ); // R2 + WRITE_BYTE( 23 ); // G2 + WRITE_BYTE( 7 ); // B2 + WRITE_BYTE( 255 ); // A2 + WRITE_SHORT( 0 ); // Fade-in Time + WRITE_SHORT( 15 ); // Fade-out Time + WRITE_SHORT( 448 ); // Hold time + WRITE_STRING( szInfo ); // Message + MESSAGE_END(); + + // Delay till next scan + g_NextMessage[ ENTINDEX( pPlayer ) ] = gpGlobals->time + 0.8; + } + } + } +} + +bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_index, int spawnflags, pKVD *keyvalue) +{ + int monster_index; + edict_t *monster_pent; + KeyValueData kvd; + + if ((monster_index = GetMonsterIndex()) == -1) + { + //META_CONS("[MONSTER] ERROR: No FREE Monster edicts!"); + LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!"); + return TRUE; + } + + // was this monster NOT precached? + if (monster_types[monster_type].need_to_precache == FALSE) + { + char msg[256]; + + //META_CONS("[MONSTER] ERROR: You can't spawn monster %s since it wasn't precached!", monster_types[monster_type].name); + LOG_MESSAGE(PLID, "ERROR: You can't spawn monster %s since it wasn't precached!", monster_types[monster_type].name); + + //META_CONS("[MONSTER] valid precached monster names are:"); + LOG_MESSAGE(PLID, "valid precached monster names are:"); + msg[0] = 0; + for (int index = 0; monster_types[index].name[0]; index++) + { + if (monster_types[index].need_to_precache == TRUE) + { + strcat(msg, monster_types[index].name); + strcat(msg, " "); + if (strlen(msg) > 60) + { + //META_CONS("[MONSTER] %s", msg); + LOG_MESSAGE(PLID, "%s", msg); + msg[0] = 0; + } + } + } + if (msg[0]) + { + //META_CONS("[MONSTER] %s", msg); + LOG_MESSAGE(PLID, "%s", msg); + } + + return TRUE; + } + + switch (monster_type) + { + case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break; + case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break; + case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break; + case 3: monsters[monster_index].pMonster = CreateClassPtr((CMBigMomma *)NULL); break; + case 4: monsters[monster_index].pMonster = CreateClassPtr((CMBullsquid *)NULL); break; + case 5: monsters[monster_index].pMonster = CreateClassPtr((CMController *)NULL); break; + case 6: monsters[monster_index].pMonster = CreateClassPtr((CMHAssassin *)NULL); break; + case 7: monsters[monster_index].pMonster = CreateClassPtr((CMHeadCrab *)NULL); break; + case 8: monsters[monster_index].pMonster = CreateClassPtr((CMHGrunt *)NULL); break; + case 9: monsters[monster_index].pMonster = CreateClassPtr((CMHoundeye *)NULL); break; + case 10: monsters[monster_index].pMonster = CreateClassPtr((CMISlave *)NULL); break; + case 11: monsters[monster_index].pMonster = CreateClassPtr((CMScientist *)NULL); break; + case 12: monsters[monster_index].pMonster = CreateClassPtr((CMSqueakGrenade *)NULL); break; + case 13: monsters[monster_index].pMonster = CreateClassPtr((CMZombie *)NULL); break; + case 14: monsters[monster_index].pMonster = CreateClassPtr((CMGargantua *)NULL); break; + case 15: monsters[monster_index].pMonster = CreateClassPtr((CMTurret *)NULL); break; + case 16: monsters[monster_index].pMonster = CreateClassPtr((CMMiniTurret *)NULL); break; + case 17: monsters[monster_index].pMonster = CreateClassPtr((CMSentry *)NULL); break; + case 18: monsters[monster_index].pMonster = CreateClassPtr((CMGonome *)NULL); break; + case 19: monsters[monster_index].pMonster = CreateClassPtr((CMMassn *)NULL); break; + case 20: monsters[monster_index].pMonster = CreateClassPtr((CMOtis *)NULL); break; + case 21: monsters[monster_index].pMonster = CreateClassPtr((CMPitdrone *)NULL); break; + case 22: monsters[monster_index].pMonster = CreateClassPtr((CMShockRoach *)NULL); break; + case 23: monsters[monster_index].pMonster = CreateClassPtr((CMStrooper *)NULL); break; + case 24: monsters[monster_index].pMonster = CreateClassPtr((CMVoltigore *)NULL); break; + case 25: monsters[monster_index].pMonster = CreateClassPtr((CMBabyVoltigore *)NULL); break; + case 26: monsters[monster_index].pMonster = CreateClassPtr((CMBabyGargantua *)NULL); break; + } + + if (monsters[monster_index].pMonster == NULL) + { + //META_CONS("[MONSTER] ERROR: Error Creating Monster!" ); + LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!"); + return TRUE; + } + + monsters[monster_index].respawn_index = respawn_index; + + monster_pent = ENT(monsters[monster_index].pMonster->pev); + monsters[monster_index].monster_pent = monster_pent; + + monsters[monster_index].monster_index = (*g_engfuncs.pfnIndexOfEdict)(monster_pent); + + monster_pent->v.origin = origin; + monster_pent->v.angles = angles; + + // Keyvalue data + if (keyvalue != NULL) + { + for (int index = 0; index < MAX_KEYVALUES; index++) + { + if (strlen(keyvalue[index].key) > 0) + { + kvd.szKeyName = keyvalue[index].key; + kvd.szValue = keyvalue[index].value; + monsters[monster_index].pMonster->KeyValue( &kvd ); + } + } + } + + monster_pent->v.spawnflags = spawnflags; + + monsters[monster_index].pMonster->Spawn(); + + // Reverse fadecorpse behaviour + if ( ( spawnflags & SF_MONSTER_FADECORPSE ) ) + monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE; + else + monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE; + + monster_pent->v.fuser4 = monster_pent->v.health; // save the original health + + return FALSE; +} + + +void check_respawn(void) +{ + int monster_type; + Vector origin; + Vector angles; + int spawnflags; + pKVD *keyvalue; + + if (!monster_spawn->value) + return; // monster_spawn is turned off, retry again later + + for (int index=0; index < monster_spawn_count; index++) + { + if (monster_spawnpoint[index].need_to_respawn && + (monster_spawnpoint[index].respawn_time <= gpGlobals->time)) + { + monster_spawnpoint[index].need_to_respawn = FALSE; + + monster_type = monster_spawnpoint[index].monster; + + origin = monster_spawnpoint[index].origin; + + angles = monster_spawnpoint[index].angles; + + spawnflags = monster_spawnpoint[index].spawnflags; + + keyvalue = monster_spawnpoint[index].keyvalue; + + if (spawn_monster(monster_type, origin, angles, index, spawnflags, keyvalue)) + { + // spawn_monster failed, retry again after delay... + monster_spawnpoint[index].need_to_respawn = TRUE; + monster_spawnpoint[index].respawn_time = gpGlobals->time + + monster_spawnpoint[index].delay; + } + } + } +} + + +DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball +DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud +DLL_GLOBAL short g_sModelIndexTinySpit;// holds the index for the spore grenade explosion +DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion +DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model +DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood +DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood +DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam +DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr"; +DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot + +void world_precache(void) +{ + g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball + g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke + g_sModelIndexTinySpit = PRECACHE_MODEL ("sprites/tinyspit.spr");// spore + g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball + 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_sModelIndexLaser = PRECACHE_MODEL( (char *)g_pModelNameLaser ); + g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr"); + + PRECACHE_MODEL ("models/w_grenade.mdl"); +} + + +void MonsterCommand(void) +{ + int index; + char msg[256]; + int monster_type = -1; + + if (CMD_ARGC() >= 3) + { + const char *parg1 = CMD_ARGV(1); + + // check for a valid monster name... + for (index = 0; monster_types[index].name[0]; index++) + { + // ensure it's an actual monster classname + if (strncmp(parg1, "monster", 7) == 0) + { + if (strcmp(parg1, monster_types[index].name) == 0) + { + monster_type = index; + break; + } + } + } + + if (monster_type != -1) + { + // check for a valid player name or index... + const char *parg2 = CMD_ARGV(2); + int player_index = -1; + edict_t *pPlayer; + const char *player_name; + + if (*parg2 == '#') // player index + { + if (sscanf(&parg2[1], "%d", &player_index) != 1) + player_index = -1; + + if ((player_index < 1) || (player_index > gpGlobals->maxClients)) + { + //META_CONS("[MONSTER] invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); + LOG_MESSAGE(PLID, "invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); + player_index = -1; + } + } + else + { + for (index = 1; index <= gpGlobals->maxClients; index++) + { + pPlayer = INDEXENT(index); + + if (pPlayer && !pPlayer->free) + { + if (stricmp(STRING(pPlayer->v.netname), parg2) == 0) + { + player_index = index; // found the matching player name + break; + } + } + } + + if (player_index == -1) + { + //META_CONS("[MONSTER] can't find player named \"%s\"!", parg2); + LOG_MESSAGE(PLID, "can't find player named \"%s\"!", parg2); + return; + } + } + + if (player_index != -1) + { + pPlayer = INDEXENT(player_index); + + if ((pPlayer == NULL) || (pPlayer->free)) + { + //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); + LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); + return; + } + + player_name = STRING(pPlayer->v.netname); + + if (player_name[0] == 0) + { + //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); + LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); + return; + } + + if (!UTIL_IsAlive(pPlayer)) + { + //META_CONS("[MONSTER] player \"%s\" is not alive or is an observer!", player_name); + LOG_MESSAGE(PLID, "player \"%s\" is not alive or is an observer!", player_name); + return; + } + + TraceResult tr; + Vector origin = pPlayer->v.origin; + Vector view_angle = pPlayer->v.v_angle; + Vector v_src, v_dest; + Vector monster_angle; + + // If spawning a turret, add autostart spawnflag. Zero otherwise + int spawnflags = 0; + if (monster_type >= 15 && monster_type <= 17) // Turret, Mini-Turret and Sentry + spawnflags = SF_MONSTER_TURRET_AUTOACTIVATE; + + // try to determine the best place to spawn the monster... + + view_angle.x = 0; // zero the pitch (level horizontally) + + UTIL_MakeVectors(view_angle); + + v_src = origin + Vector(0, 0, 20); // up a little bit + v_dest = v_src + gpGlobals->v_forward * 128; // in front of player + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y + 180.0f; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + v_src = origin + Vector(0, 0, 20); // up a little bit + // diagonally in front and to the left of the player... + v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * -90; + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y - 135.0f; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + v_src = origin + Vector(0, 0, 20); // up a little bit + // diagonally in front and to the right of the player... + v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * 90; + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y + 135.0f; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + v_src = origin + Vector(0, 0, 20); // up a little bit + v_dest = v_src + gpGlobals->v_right * 128; // to the right + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y + 90.0f; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + v_src = origin + Vector(0, 0, 20); // up a little bit + v_dest = v_src + gpGlobals->v_right * -128; // to the left + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y - 90.0f; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + v_src = origin + Vector(0, 0, 20); // up a little bit + v_dest = v_src + gpGlobals->v_forward * -128; // to the rear + + UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr); + + if (tr.flFraction >= 1.0) + { + v_src = v_dest; + v_dest = v_dest + Vector(0, 0, -200); // down to ground + + // try to find the floor... + UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr); + + if (tr.flFraction < 1.0) // hit something? + { + monster_angle.y = view_angle.y; // face the player + if (monster_angle.y > 360) + monster_angle.y -= 360; + if (monster_angle.y < 0) + monster_angle.y += 360; + + spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); + + return; + } + } + + //META_CONS("[MONSTER] there's no room to spawn a monster near player \"%s\"!", player_name); + LOG_MESSAGE(PLID, "there's no room to spawn a monster near player \"%s\"!", player_name); + + return; + } + } + } + + //META_CONS("[MONSTER] usage: monster monster_name player_name | #player_index"); + //META_CONS("[MONSTER] valid monster_names are:"); + LOG_MESSAGE(PLID, "usage: monster monster_name player_name | #player_index"); + LOG_MESSAGE(PLID, "valid monster_names are:"); + msg[0] = 0; + for (index = 0; monster_types[index].name[0]; index++) + { + // limit list to monster entities only + if (strncmp(monster_types[index].name, "monster", 7) == 0) + { + strcat(msg, monster_types[index].name); + strcat(msg, " "); + if (strlen(msg) > 60) + { + //META_CONS("[MONSTER] %s", msg); + LOG_MESSAGE(PLID, "%s", msg); + msg[0] = 0; + } + } + } + if (msg[0]) + { + //META_CONS("[MONSTER] %s", msg); + LOG_MESSAGE(PLID, "%s", msg); + } +} + +void SpawnViewerCommand(void) +{ + int index; + + // debug command to spawn a node_viewer at a player's location + if (CMD_ARGC() >= 2) + { + // check for a valid player name or index... + const char *parg2 = CMD_ARGV(1); + int player_index = -1; + edict_t *pPlayer; + const char *player_name; + + if (*parg2 == '#') // player index + { + if (sscanf(&parg2[1], "%d", &player_index) != 1) + player_index = -1; + + if ((player_index < 1) || (player_index > gpGlobals->maxClients)) + { + //META_CONS("[MONSTER] invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); + LOG_MESSAGE(PLID, "invalid player index! (%d to %d allowed)", 1, gpGlobals->maxClients); + player_index = -1; + return; + } + } + else + { + for (index = 1; index <= gpGlobals->maxClients; index++) + { + pPlayer = INDEXENT(index); + + if (pPlayer && !pPlayer->free) + { + if (stricmp(STRING(pPlayer->v.netname), parg2) == 0) + { + player_index = index; // found the matching player name + break; + } + } + } + + if (player_index == -1) + { + //META_CONS("[MONSTER] can't find player named \"%s\"!", parg2); + LOG_MESSAGE(PLID, "can't find player named \"%s\"!", parg2); + return; + } + } + + if (player_index != -1) + { + pPlayer = INDEXENT(player_index); + + if ((pPlayer == NULL) || (pPlayer->free)) + { + //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); + LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); + return; + } + + player_name = STRING(pPlayer->v.netname); + + if (player_name[0] == 0) + { + //META_CONS("[MONSTER] player index %d is not a valid player!", player_index); + LOG_MESSAGE(PLID, "player index %d is not a valid player!", player_index); + return; + } + + if (!UTIL_IsAlive(pPlayer)) + { + //META_CONS("[MONSTER] player \"%s\" is not alive or is an observer!", player_name); + LOG_MESSAGE(PLID, "player \"%s\" is not alive or is an observer!", player_name); + return; + } + + Vector origin = pPlayer->v.origin; + + CMBaseEntity *pViewer = CreateClassPtr((CNodeViewer *)NULL); + if (pViewer == NULL) + { + //META_CONS("[MONSTER] ERROR: Error Creating Node!" ); + LOG_MESSAGE(PLID, "ERROR: Error Creating Viewer!"); + return; + } + + pViewer->pev->origin = origin; + pViewer->Spawn(); + return; + } + } + + LOG_MESSAGE(PLID, "usage: node_viewer player_name | #player_index"); + LOG_MESSAGE(PLID, "spawns a node viewer at the player's location"); +} + +void mmGameDLLInit( void ) +{ + // one time initialization stuff here... + + RETURN_META(MRES_IGNORED); +} + + +int mmDispatchSpawn( edict_t *pent ) +{ + int index; + char *pClassname = (char *)STRING(pent->v.classname); + + if (strcmp(pClassname, "worldspawn") == 0) + { + // free any monster class memory not previously freed... + for (index = 0; index < MAX_MONSTER_ENTS; index++) + { + if (monsters[index].pMonster != NULL) + delete monsters[index].pMonster; + } + + // free any allocated keyvalue memory + for (index = 0; index < monster_spawn_count; index++) + { + if (monster_spawnpoint[index].keyvalue) + free(monster_spawnpoint[index].keyvalue); + } + + // do level initialization stuff here... + + for (index = 0; monster_types[index].name[0]; index++) + monster_types[index].need_to_precache = FALSE; + + world_precache(); + + monster_spawn_count = 0; + node_spawn_count = 0; + + monster_skill_init(); + + process_monster_precache_cfg(); + + process_monster_cfg(); + + // node support. -Giegue + // init the WorldGraph. + WorldGraph.InitGraph(); + + // make sure the .NOD file is newer than the .BSP file. + if ( !WorldGraph.CheckNODFile ( ( char * )STRING( gpGlobals->mapname ) ) ) + { + // NOD file is not present, or is older than the BSP file. + WorldGraph.AllocNodes(); + } + else + { + // Load the node graph for this level + if ( !WorldGraph.FLoadGraph ( (char *)STRING( gpGlobals->mapname ) ) ) + { + // couldn't load, so alloc and prepare to build a graph. + ALERT ( at_console, "*Error opening .NOD file\n" ); + WorldGraph.AllocNodes(); + } + else + { + ALERT ( at_console, "\n*Graph Loaded!\n" ); + } + } + + check_respawn_time = 0.0; + + for (index = 0; index < MAX_MONSTER_ENTS; index++) + { + monsters[index].monster_index = 0; + monsters[index].monster_pent = NULL; + monsters[index].killed = FALSE; // not killed yet + monsters[index].pMonster = NULL; + } + + monster_ents_used = 0; + + for (index = 0; index < ARRAYSIZE(gDecals); index++ ) + gDecals[index].index = DECAL_INDEX( gDecals[index].name ); + } + + // 0==Success, -1==Failure ? + RETURN_META_VALUE(MRES_IGNORED, 0); +} + +void mmDispatchThink( edict_t *pent ) +{ + for (int index=0; index < monster_ents_used; index++) + { + if (pent == monsters[index].monster_pent) + { + monsters[index].pMonster->Think(); + + check_monster_dead(pent); + + RETURN_META(MRES_SUPERCEDE); + } + } + + RETURN_META(MRES_IGNORED); +} +// HACKHACK -- this is a hack to keep the node graph entity from "touching" things (like triggers) +// while it builds the graph +BOOL gTouchDisabled = FALSE; +void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther ) +{ + if (gTouchDisabled) + RETURN_META(MRES_SUPERCEDE); + + for (int index=0; index < monster_ents_used; index++) + { + if ((pentTouched != NULL) && (pentTouched == monsters[index].monster_pent)) + { + monsters[index].pMonster->Touch(pentOther); + + check_monster_dead(pentOther); + + RETURN_META(MRES_SUPERCEDE); + } + } + + RETURN_META(MRES_IGNORED); +} + + +void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) +{ + int index; + + CMAGrunt agrunt; + CMApache apache; + CMBarney barney; + CMBigMomma bigmomma; + CMBullsquid bullsquid; + CMController controller; + CMHAssassin hassassin; + CMHeadCrab headcrab; + CMHGrunt hgrunt; + CMHoundeye houndeye; + CMISlave islave; + CMScientist scientist; + CMSqueakGrenade snark; + CMZombie zombie; + CMGargantua gargantua; + CMTurret turret; + CMMiniTurret miniturret; + CMSentry sentry; + CMGonome gonome; + CMMassn massn; + CMOtis otis; + CMPitdrone pitdrone; + CMShockRoach shockroach; + CMStrooper strooper; + CMVoltigore voltigore; + CMBabyVoltigore babyvoltigore; + CMBabyGargantua babygargantua; + + g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); + + (g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand); + (g_engfuncs.pfnAddServerCommand)("node_viewer", SpawnViewerCommand); + + for (index = 0; monster_types[index].name[0]; index++) + { + if (monster_types[index].need_to_precache) + { + if (dllapi_log->value) + { + LOG_MESSAGE(PLID, "Precaching %s models & sounds...", monster_types[index].name); + } + + switch (index) + { + case 0: agrunt.Precache(); break; + case 1: apache.Precache(); break; + case 2: barney.Precache(); break; + case 3: bigmomma.Precache(); break; + case 4: bullsquid.Precache(); break; + case 5: controller.Precache(); break; + case 6: hassassin.Precache(); break; + case 7: headcrab.Precache(); break; + case 8: hgrunt.Precache(); break; + case 9: houndeye.Precache(); break; + case 10: islave.Precache(); break; + case 11: scientist.Precache(); break; + case 12: snark.Precache(); break; + case 13: zombie.Precache(); break; + case 14: gargantua.Precache(); break; + case 15: turret.Precache(); break; + case 16: miniturret.Precache(); break; + case 17: sentry.Precache(); break; + case 18: gonome.Precache(); break; + case 19: massn.Precache(); break; + case 20: otis.Precache(); break; + case 21: pitdrone.Precache(); break; + case 22: shockroach.Precache(); break; + case 23: strooper.Precache(); break; + case 24: voltigore.Precache(); break; + case 25: babyvoltigore.Precache(); break; + case 26: babygargantua.Precache(); break; + } + } + } + + for (index = 0; index < MAX_MONSTER_ENTS; index++) + { + monsters[index].monster_index = 0; + monsters[index].monster_pent = NULL; + monsters[index].killed = FALSE; // not killed yet + monsters[index].pMonster = NULL; + } + + monster_ents_used = 0; + + // spawn nodes + for (index = 0; index < node_spawn_count; index++) + { + CMBaseEntity *pNode; + pNode = CreateClassPtr((CNodeEnt *)NULL); + + if (pNode == NULL) + { + //META_CONS("[MONSTER] ERROR: Error Creating Node!" ); + LOG_MESSAGE(PLID, "ERROR: Error Creating Node!"); + } + else + { + pNode->pev->origin = node_spawnpoint[index].origin; + + if (node_spawnpoint[index].is_air_node) + pNode->pev->classname = MAKE_STRING("info_node_air"); + else + pNode->pev->classname = MAKE_STRING("info_node"); + + pNode->Spawn(); + } + } + + RETURN_META(MRES_IGNORED); +} + +void mmStartFrame( void ) +{ + if (check_respawn_time <= gpGlobals->time) + { + check_respawn_time = gpGlobals->time + 1.0; + + check_respawn(); + } + + RETURN_META(MRES_IGNORED); +} + +void mmClientKill( edict_t *pPlayer ) +{ + // Just to let the system know the player commited suicide + pPlayer->v.dmg_inflictor = pPlayer; + RETURN_META(MRES_IGNORED); +} + +static DLL_FUNCTIONS gFunctionTable = +{ + mmGameDLLInit, //! pfnGameInit() Initialize the game (one-time call after loading of game .dll) + mmDispatchSpawn, //! pfnSpawn() + mmDispatchThink, //! pfnThink + NULL, // pfnUse + mmDispatchTouch, //! pfnTouch + NULL, // pfnBlocked + NULL, // pfnKeyValue + NULL, // pfnSave + NULL, // pfnRestore + NULL, // pfnSetAbsBox + + NULL, // pfnSaveWriteFields + NULL, // pfnSaveReadFields + + NULL, // pfnSaveGlobalState + NULL, // pfnRestoreGlobalState + NULL, // pfnResetGlobalState + + NULL, // pfnClientConnect + NULL, // pfnClientDisconnect + mmClientKill, //! pfnClientKill + NULL, // pfnClientPutInServer + NULL, // pfnClientCommand + NULL, // pfnClientUserInfoChanged + mmServerActivate, //! pfnServerActivate() (wd) Server is starting a new map + NULL, // pfnServerDeactivate + + NULL, // pfnPlayerPreThink + NULL, // pfnPlayerPostThink + + mmStartFrame, //! pfnStartFrame + NULL, // pfnParmsNewLevel + NULL, // pfnParmsChangeLevel + + NULL, // pfnGetGameDescription + NULL, // pfnPlayerCustomization + + NULL, // pfnSpectatorConnect + NULL, // pfnSpectatorDisconnect + NULL, // pfnSpectatorThink + + NULL, // pfnSys_Error + + NULL, // pfnPM_Move + NULL, // pfnPM_Init + NULL, // pfnPM_FindTextureType + + NULL, // pfnSetupVisibility + NULL, // pfnUpdateClientData + NULL, // pfnAddToFullPack + NULL, // pfnCreateBaseline + NULL, // pfnRegisterEncoders + NULL, // pfnGetWeaponData + NULL, // pfnCmdStart + NULL, // pfnCmdEnd + NULL, // pfnConnectionlessPacket + NULL, // pfnGetHullBounds + NULL, // pfnCreateInstancedBaselines + NULL, // pfnInconsistentFile + NULL, // pfnAllowLagCompensation +}; + + +C_DLLEXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) +{ + if(!pFunctionTable) { + UTIL_LogPrintf("GetEntityAPI2 called with null pFunctionTable"); + return(FALSE); + } + else if(*interfaceVersion != INTERFACE_VERSION) { + UTIL_LogPrintf("GetEntityAPI2 version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION); + //! Tell engine what version we had, so it can figure out who is out of date. + *interfaceVersion = INTERFACE_VERSION; + return(FALSE); + } + memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) ); + return(TRUE); +} + + +void mmDispatchThink_Post( edict_t *pent ) +{ + check_monster_hurt(pent); + check_monster_dead(pent); + + RETURN_META(MRES_IGNORED); +} + +void mmPlayerPostThink_Post( edict_t *pEntity ) +{ + check_monster_hurt(pEntity); + check_monster_dead(pEntity); + check_player_dead(pEntity); + check_monster_info(pEntity); + + RETURN_META(MRES_IGNORED); +} + +static DLL_FUNCTIONS gFunctionTable_Post = +{ + NULL, // pfnGameInit() Initialize the game (one-time call after loading of game .dll) + NULL, // pfnSpawn() + mmDispatchThink_Post, //! pfnThink + NULL, // pfnUse + NULL, // pfnTouch + NULL, // pfnBlocked + NULL, // pfnKeyValue + NULL, // pfnSave + NULL, // pfnRestore + NULL, // pfnSetAbsBox + + NULL, // pfnSaveWriteFields + NULL, // pfnSaveReadFields + + NULL, // pfnSaveGlobalState + NULL, // pfnRestoreGlobalState + NULL, // pfnResetGlobalState + + NULL, // pfnClientConnect + NULL, // pfnClientDisconnect + NULL, // pfnClientKill + NULL, // pfnClientPutInServer + NULL, // pfnClientCommand + NULL, // pfnClientUserInfoChanged + NULL, // pfnServerActivate() (wd) Server is starting a new map + NULL, // pfnServerDeactivate + + NULL, // pfnPlayerPreThink + mmPlayerPostThink_Post, //! pfnPlayerPostThink + + NULL, // pfnStartFrame + NULL, // pfnParmsNewLevel + NULL, // pfnParmsChangeLevel + + NULL, // pfnGetGameDescription + NULL, // pfnPlayerCustomization + + NULL, // pfnSpectatorConnect + NULL, // pfnSpectatorDisconnect + NULL, // pfnSpectatorThink + + NULL, // pfnSys_Error + + NULL, // pfnPM_Move + NULL, // pfnPM_Init + NULL, // pfnPM_FindTextureType + + NULL, // pfnSetupVisibility + NULL, // pfnUpdateClientData + NULL, // pfnAddToFullPack + NULL, // pfnCreateBaseline + NULL, // pfnRegisterEncoders + NULL, // pfnGetWeaponData + NULL, // pfnCmdStart + NULL, // pfnCmdEnd + NULL, // pfnConnectionlessPacket + NULL, // pfnGetHullBounds + NULL, // pfnCreateInstancedBaselines + NULL, // pfnInconsistentFile + NULL, // pfnAllowLagCompensation +}; + +C_DLLEXPORT int GetEntityAPI2_Post( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ) +{ + if(!pFunctionTable) { + UTIL_LogPrintf("GetEntityAPI2_Post called with null pFunctionTable"); + return(FALSE); + } + else if(*interfaceVersion != INTERFACE_VERSION) { + UTIL_LogPrintf("GetEntityAPI2_Post version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION); + //! Tell engine what version we had, so it can figure out who is out of date. + *interfaceVersion = INTERFACE_VERSION; + return(FALSE); + } + memcpy( pFunctionTable, &gFunctionTable_Post, sizeof( DLL_FUNCTIONS ) ); + return(TRUE); +} + + +// Some messages seems to be offset by 1. Linux specific? CStrike specific? +int IsCSServer( void ) +{ + char mod[16]; + sprintf( mod, "%s", GET_GAME_INFO( PLID, GINFO_NAME ) ); + + if ( strcmp( mod, "cstrike" ) == 0 || strcmp( mod, "czero" ) == 0 ) + return 1; + + return 0; +} + +int mmRegUserMsg_Post( const char *pName, int iSize ) +{ + int cs_server = IsCSServer(); + + if ( strcmp( pName, "Damage" ) == 0 ) + g_DamageMsg = META_RESULT_ORIG_RET( int ) - cs_server; + else if ( strcmp( pName, "DeathMsg" ) == 0 ) + g_DeathMsg = META_RESULT_ORIG_RET( int );// - cs_server; + + RETURN_META_VALUE( MRES_IGNORED, 0 ); +} + +void mmMessageBegin_Post( int msg_dest, int msg_type, const float *pOrigin, edict_t *ed ) +{ + if ( msg_type == g_DamageMsg ) + { + // Whatever hurting us must be a valid entity + if (ed->v.dmg_inflictor != NULL ) + { + g_DamageActive = true; + g_DamageVictim = ENTINDEX( ed ); + g_DamageAttacker[ g_DamageVictim ] = ed->v.dmg_inflictor; + } + } + else if ( msg_type == g_DeathMsg ) + { + // Prepare to update deathmsg + g_DeathActive = true; + } + + RETURN_META( MRES_IGNORED ); +} + +void mmWriteLong_Post( int iValue ) +{ + if ( g_DamageActive ) + g_DamageBits[ g_DamageVictim ] = iValue; + + RETURN_META( MRES_IGNORED ); +} + +// This cannot be done on post! +void mmWriteString( const char *szValue ) +{ + if ( g_DeathActive ) + { + // Prevent recursion + g_DeathActive = false; + + // Ensure whatever killed the player is a valid entity + if (g_DamageAttacker[ g_DamageVictim ] != NULL) + { + // Send a new WriteString with the killer's classname + WRITE_STRING( STRING( g_DamageAttacker[ g_DamageVictim ]->v.classname ) ); + + // Supercede the old message + RETURN_META( MRES_SUPERCEDE ); + } + } + + RETURN_META( MRES_IGNORED ); +} + +void mmMessageEnd_Post( void ) +{ + g_DamageActive = false; + + RETURN_META( MRES_IGNORED ); +} + +enginefuncs_t meta_engfuncs = +{ + NULL, // pfnPrecacheModel() + NULL, // pfnPrecacheSound() + NULL, // pfnSetModel() + NULL, // pfnModelIndex() + NULL, // pfnModelFrames() + + NULL, // pfnSetSize() + NULL, // pfnChangeLevel() + NULL, // pfnGetSpawnParms() + NULL, // pfnSaveSpawnParms() + + NULL, // pfnVecToYaw() + NULL, // pfnVecToAngles() + NULL, // pfnMoveToOrigin() + NULL, // pfnChangeYaw() + NULL, // pfnChangePitch() + + NULL, // pfnFindEntityByString() + NULL, // pfnGetEntityIllum() + NULL, // pfnFindEntityInSphere() + NULL, // pfnFindClientInPVS() + NULL, // pfnEntitiesInPVS() + + NULL, // pfnMakeVectors() + NULL, // pfnAngleVectors() + + NULL, // pfnCreateEntity() + NULL, // pfnRemoveEntity() + NULL, // pfnCreateNamedEntity() + + NULL, // pfnMakeStatic() + NULL, // pfnEntIsOnFloor() + NULL, // pfnDropToFloor() + + NULL, // pfnWalkMove() + NULL, // pfnSetOrigin() + + NULL, // pfnEmitSound() + NULL, // pfnEmitAmbientSound() + + NULL, // pfnTraceLine() + NULL, // pfnTraceToss() + NULL, // pfnTraceMonsterHull() + NULL, // pfnTraceHull() + NULL, // pfnTraceModel() + NULL, // pfnTraceTexture() + NULL, // pfnTraceSphere() + NULL, // pfnGetAimVector() + + NULL, // pfnServerCommand() + NULL, // pfnServerExecute() + NULL, // pfnClientCommand() + + NULL, // pfnParticleEffect() + NULL, // pfnLightStyle() + NULL, // pfnDecalIndex() + NULL, // pfnPointContents() + + NULL, // pfnMessageBegin() + NULL, // pfnMessageEnd() + + NULL, // pfnWriteByte() + NULL, // pfnWriteChar() + NULL, // pfnWriteShort() + NULL, // pfnWriteLong() + NULL, // pfnWriteAngle() + NULL, // pfnWriteCoord() + mmWriteString, //! pfnWriteString() + NULL, // pfnWriteEntity() + + NULL, // pfnCVarRegister() + NULL, // pfnCVarGetFloat() + NULL, // pfnCVarGetString() + NULL, // pfnCVarSetFloat() + NULL, // pfnCVarSetString() + + NULL, // pfnAlertMessage() + NULL, // pfnEngineFprintf() + + NULL, // pfnPvAllocEntPrivateData() + NULL, // pfnPvEntPrivateData() + NULL, // pfnFreeEntPrivateData() + + NULL, // pfnSzFromIndex() + NULL, // pfnAllocString() + + NULL, // pfnGetVarsOfEnt() + NULL, // pfnPEntityOfEntOffset() + NULL, // pfnEntOffsetOfPEntity() + NULL, // pfnIndexOfEdict() + NULL, // pfnPEntityOfEntIndex() + NULL, // pfnFindEntityByVars() + NULL, // pfnGetModelPtr() + + NULL, // pfnRegUserMsg() + + NULL, // pfnAnimationAutomove() + NULL, // pfnGetBonePosition() + + NULL, // pfnFunctionFromName() + NULL, // pfnNameForFunction() + + NULL, // pfnClientPrintf() + NULL, // pfnServerPrint() + + NULL, // pfnCmd_Args() + NULL, // pfnCmd_Argv() + NULL, // pfnCmd_Argc() + + NULL, // pfnGetAttachment() + + NULL, // pfnCRC32_Init() + NULL, // pfnCRC32_ProcessBuffer() + NULL, // pfnCRC32_ProcessByte() + NULL, // pfnCRC32_Final() + + NULL, // pfnRandomLong() + NULL, // pfnRandomFloat() + + NULL, // pfnSetView() + NULL, // pfnTime() + NULL, // pfnCrosshairAngle() + + NULL, // pfnLoadFileForMe() + NULL, // pfnFreeFile() + + NULL, // pfnEndSection() + NULL, // pfnCompareFileTime() + NULL, // pfnGetGameDir() + NULL, // pfnCvar_RegisterVariable() + NULL, // pfnFadeClientVolume() + NULL, // pfnSetClientMaxspeed() + NULL, // pfnCreateFakeClient() + NULL, // pfnRunPlayerMove() + NULL, // pfnNumberOfEntities() + + NULL, // pfnGetInfoKeyBuffer() + NULL, // pfnInfoKeyValue() + NULL, // pfnSetKeyValue() + NULL, // pfnSetClientKeyValue() + + NULL, // pfnIsMapValid() + NULL, // pfnStaticDecal() + NULL, // pfnPrecacheGeneric() + NULL, // pfnGetPlayerUserId() + NULL, // pfnBuildSoundMsg() + NULL, // pfnIsDedicatedServer() + NULL, // pfnCVarGetPointer() + NULL, // pfnGetPlayerWONId() + + NULL, // pfnInfo_RemoveKey() + NULL, // pfnGetPhysicsKeyValue() + NULL, // pfnSetPhysicsKeyValue() + NULL, // pfnGetPhysicsInfoString() + NULL, // pfnPrecacheEvent() + NULL, // pfnPlaybackEvent() + + NULL, // pfnSetFatPVS() + NULL, // pfnSetFatPAS() + + NULL, // pfnCheckVisibility() + + NULL, // pfnDeltaSetField() + NULL, // pfnDeltaUnsetField() + NULL, // pfnDeltaAddEncoder() + NULL, // pfnGetCurrentPlayer() + NULL, // pfnCanSkipPlayer() + NULL, // pfnDeltaFindField() + NULL, // pfnDeltaSetFieldByIndex() + NULL, // pfnDeltaUnsetFieldByIndex() + + NULL, // pfnSetGroupMask() + + NULL, // pfnCreateInstancedBaseline() + NULL, // pfnCvar_DirectSet() + + NULL, // pfnForceUnmodified() + + NULL, // pfnGetPlayerStats() + + NULL, // pfnAddServerCommand() + + NULL, // pfnVoice_GetClientListening() + NULL, // pfnVoice_SetClientListening() + + NULL, // pfnGetPlayerAuthId() + + NULL, // pfnSequenceGet() + NULL, // pfnSequencePickSentence() + NULL, // pfnGetFileSize() + NULL, // pfnGetApproxWavePlayLen() + NULL, // pfnIsCareerMatch() + NULL, // pfnGetLocalizedStringLength() + NULL, // pfnRegisterTutorMessageShown() + NULL, // pfnGetTimesTutorMessageShown() + NULL, // pfnProcessTutorMessageDecayBuffer() + NULL, // pfnConstructTutorMessageDecayBuffer() + NULL, // pfnResetTutorMessageDecayData() + NULL, // pfnQueryClientCvarValue() + NULL, // pfnQueryClientCvarValue2() + NULL, // pfnEngCheckParm() +}; + +C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion) +{ + if(!pengfuncsFromEngine) + { + LOG_ERROR(PLID, "GetEngineFunctions called with null pengfuncsFromEngine"); + return(FALSE); + } + else if(*interfaceVersion != ENGINE_INTERFACE_VERSION) + { + LOG_ERROR(PLID, "GetEngineFunctions version mismatch; requested=%d ours=%d", *interfaceVersion, ENGINE_INTERFACE_VERSION); + // Tell metamod what version we had, so it can figure out who is + // out of date. + *interfaceVersion = ENGINE_INTERFACE_VERSION; + return(FALSE); + } + memcpy(pengfuncsFromEngine, &meta_engfuncs, sizeof(enginefuncs_t)); + return TRUE; +} + +enginefuncs_t meta_engfuncs_post = +{ + NULL, // pfnPrecacheModel() + NULL, // pfnPrecacheSound() + NULL, // pfnSetModel() + NULL, // pfnModelIndex() + NULL, // pfnModelFrames() + + NULL, // pfnSetSize() + NULL, // pfnChangeLevel() + NULL, // pfnGetSpawnParms() + NULL, // pfnSaveSpawnParms() + + NULL, // pfnVecToYaw() + NULL, // pfnVecToAngles() + NULL, // pfnMoveToOrigin() + NULL, // pfnChangeYaw() + NULL, // pfnChangePitch() + + NULL, // pfnFindEntityByString() + NULL, // pfnGetEntityIllum() + NULL, // pfnFindEntityInSphere() + NULL, // pfnFindClientInPVS() + NULL, // pfnEntitiesInPVS() + + NULL, // pfnMakeVectors() + NULL, // pfnAngleVectors() + + NULL, // pfnCreateEntity() + NULL, // pfnRemoveEntity() + NULL, // pfnCreateNamedEntity() + + NULL, // pfnMakeStatic() + NULL, // pfnEntIsOnFloor() + NULL, // pfnDropToFloor() + + NULL, // pfnWalkMove() + NULL, // pfnSetOrigin() + + NULL, // pfnEmitSound() + NULL, // pfnEmitAmbientSound() + + NULL, // pfnTraceLine() + NULL, // pfnTraceToss() + NULL, // pfnTraceMonsterHull() + NULL, // pfnTraceHull() + NULL, // pfnTraceModel() + NULL, // pfnTraceTexture() + NULL, // pfnTraceSphere() + NULL, // pfnGetAimVector() + + NULL, // pfnServerCommand() + NULL, // pfnServerExecute() + NULL, // pfnClientCommand() + + NULL, // pfnParticleEffect() + NULL, // pfnLightStyle() + NULL, // pfnDecalIndex() + NULL, // pfnPointContents() + + mmMessageBegin_Post, //! pfnMessageBegin() + mmMessageEnd_Post, //! pfnMessageEnd() + + NULL, // pfnWriteByte() + NULL, // pfnWriteChar() + NULL, // pfnWriteShort() + mmWriteLong_Post, //! pfnWriteLong() + NULL, // pfnWriteAngle() + NULL, // pfnWriteCoord() + NULL, // pfnWriteString() + NULL, // pfnWriteEntity() + + NULL, // pfnCVarRegister() + NULL, // pfnCVarGetFloat() + NULL, // pfnCVarGetString() + NULL, // pfnCVarSetFloat() + NULL, // pfnCVarSetString() + + NULL, // pfnAlertMessage() + NULL, // pfnEngineFprintf() + + NULL, // pfnPvAllocEntPrivateData() + NULL, // pfnPvEntPrivateData() + NULL, // pfnFreeEntPrivateData() + + NULL, // pfnSzFromIndex() + NULL, // pfnAllocString() + + NULL, // pfnGetVarsOfEnt() + NULL, // pfnPEntityOfEntOffset() + NULL, // pfnEntOffsetOfPEntity() + NULL, // pfnIndexOfEdict() + NULL, // pfnPEntityOfEntIndex() + NULL, // pfnFindEntityByVars() + NULL, // pfnGetModelPtr() + + mmRegUserMsg_Post, //! pfnRegUserMsg() + + NULL, // pfnAnimationAutomove() + NULL, // pfnGetBonePosition() + + NULL, // pfnFunctionFromName() + NULL, // pfnNameForFunction() + + NULL, // pfnClientPrintf() + NULL, // pfnServerPrint() + + NULL, // pfnCmd_Args() + NULL, // pfnCmd_Argv() + NULL, // pfnCmd_Argc() + + NULL, // pfnGetAttachment() + + NULL, // pfnCRC32_Init() + NULL, // pfnCRC32_ProcessBuffer() + NULL, // pfnCRC32_ProcessByte() + NULL, // pfnCRC32_Final() + + NULL, // pfnRandomLong() + NULL, // pfnRandomFloat() + + NULL, // pfnSetView() + NULL, // pfnTime() + NULL, // pfnCrosshairAngle() + + NULL, // pfnLoadFileForMe() + NULL, // pfnFreeFile() + + NULL, // pfnEndSection() + NULL, // pfnCompareFileTime() + NULL, // pfnGetGameDir() + NULL, // pfnCvar_RegisterVariable() + NULL, // pfnFadeClientVolume() + NULL, // pfnSetClientMaxspeed() + NULL, // pfnCreateFakeClient() + NULL, // pfnRunPlayerMove() + NULL, // pfnNumberOfEntities() + + NULL, // pfnGetInfoKeyBuffer() + NULL, // pfnInfoKeyValue() + NULL, // pfnSetKeyValue() + NULL, // pfnSetClientKeyValue() + + NULL, // pfnIsMapValid() + NULL, // pfnStaticDecal() + NULL, // pfnPrecacheGeneric() + NULL, // pfnGetPlayerUserId() + NULL, // pfnBuildSoundMsg() + NULL, // pfnIsDedicatedServer() + NULL, // pfnCVarGetPointer() + NULL, // pfnGetPlayerWONId() + + NULL, // pfnInfo_RemoveKey() + NULL, // pfnGetPhysicsKeyValue() + NULL, // pfnSetPhysicsKeyValue() + NULL, // pfnGetPhysicsInfoString() + NULL, // pfnPrecacheEvent() + NULL, // pfnPlaybackEvent() + + NULL, // pfnSetFatPVS() + NULL, // pfnSetFatPAS() + + NULL, // pfnCheckVisibility() + + NULL, // pfnDeltaSetField() + NULL, // pfnDeltaUnsetField() + NULL, // pfnDeltaAddEncoder() + NULL, // pfnGetCurrentPlayer() + NULL, // pfnCanSkipPlayer() + NULL, // pfnDeltaFindField() + NULL, // pfnDeltaSetFieldByIndex() + NULL, // pfnDeltaUnsetFieldByIndex() + + NULL, // pfnSetGroupMask() + + NULL, // pfnCreateInstancedBaseline() + NULL, // pfnCvar_DirectSet() + + NULL, // pfnForceUnmodified() + + NULL, // pfnGetPlayerStats() + + NULL, // pfnAddServerCommand() + + NULL, // pfnVoice_GetClientListening() + NULL, // pfnVoice_SetClientListening() + + NULL, // pfnGetPlayerAuthId() + + NULL, // pfnSequenceGet() + NULL, // pfnSequencePickSentence() + NULL, // pfnGetFileSize() + NULL, // pfnGetApproxWavePlayLen() + NULL, // pfnIsCareerMatch() + NULL, // pfnGetLocalizedStringLength() + NULL, // pfnRegisterTutorMessageShown() + NULL, // pfnGetTimesTutorMessageShown() + NULL, // pfnProcessTutorMessageDecayBuffer() + NULL, // pfnConstructTutorMessageDecayBuffer() + NULL, // pfnResetTutorMessageDecayData() + NULL, // pfnQueryClientCvarValue() + NULL, // pfnQueryClientCvarValue2() + NULL, // pfnEngCheckParm() +}; + +C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion) +{ + if(!pengfuncsFromEngine) + { + LOG_ERROR(PLID, "GetEngineFunctions_Post called with null pengfuncsFromEngine"); + return(FALSE); + } + else if(*interfaceVersion != ENGINE_INTERFACE_VERSION) + { + LOG_ERROR(PLID, "GetEngineFunctions_Post version mismatch; requested=%d ours=%d", *interfaceVersion, ENGINE_INTERFACE_VERSION); + // Tell metamod what version we had, so it can figure out who is + // out of date. + *interfaceVersion = ENGINE_INTERFACE_VERSION; + return(FALSE); + } + memcpy(pengfuncsFromEngine, &meta_engfuncs_post, sizeof(enginefuncs_t)); + return TRUE; +} diff --git a/src/dlls/doors.h b/src/dlls/doors.h index c9752c1..dcf571c 100644 --- a/src/dlls/doors.h +++ b/src/dlls/doors.h @@ -1,33 +1,33 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef DOORS_H -#define DOORS_H - -// doors -#define SF_DOOR_ROTATE_Y 0 -#define SF_DOOR_START_OPEN 1 -#define SF_DOOR_ROTATE_BACKWARDS 2 -#define SF_DOOR_PASSABLE 8 -#define SF_DOOR_ONEWAY 16 -#define SF_DOOR_NO_AUTO_RETURN 32 -#define SF_DOOR_ROTATE_Z 64 -#define SF_DOOR_ROTATE_X 128 -#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button. -#define SF_DOOR_NOMONSTERS 512 // Monster can't open -#define SF_DOOR_SILENT 0x80000000 - - - -#endif //DOORS_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef DOORS_H +#define DOORS_H + +// doors +#define SF_DOOR_ROTATE_Y 0 +#define SF_DOOR_START_OPEN 1 +#define SF_DOOR_ROTATE_BACKWARDS 2 +#define SF_DOOR_PASSABLE 8 +#define SF_DOOR_ONEWAY 16 +#define SF_DOOR_NO_AUTO_RETURN 32 +#define SF_DOOR_ROTATE_Z 64 +#define SF_DOOR_ROTATE_X 128 +#define SF_DOOR_USE_ONLY 256 // door must be opened by player's use button. +#define SF_DOOR_NOMONSTERS 512 // Monster can't open +#define SF_DOOR_SILENT 0x80000000 + + + +#endif //DOORS_H diff --git a/src/dlls/effects.cpp b/src/dlls/effects.cpp index 0a0209c..13583a6 100644 --- a/src/dlls/effects.cpp +++ b/src/dlls/effects.cpp @@ -1,446 +1,446 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "customentity.h" -#include "effects.h" -#include "weapons.h" -#include "decals.h" -#include "func_break.h" -#include "shake.h" - -#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired - -#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them. - - -// -------------------------------------------------- -// -// Beams -// -// -------------------------------------------------- - -void CMBeam::Spawn( void ) -{ - pev->solid = SOLID_NOT; // Remove model & collisions - Precache( ); -} - -void CMBeam::Precache( void ) -{ - if ( pev->owner ) - SetStartEntity( ENTINDEX( pev->owner ) ); - if ( pev->aiment ) - SetEndEntity( ENTINDEX( pev->aiment ) ); -} - -void CMBeam::SetStartEntity( int entityIndex ) -{ - pev->sequence = (entityIndex & 0x0FFF) | ((pev->sequence&0xF000)<<12); - pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); -} - -void CMBeam::SetEndEntity( int entityIndex ) -{ - pev->skin = (entityIndex & 0x0FFF) | ((pev->skin&0xF000)<<12); - pev->aiment = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); -} - - -// These don't take attachments into account -const Vector &CMBeam::GetStartPos( void ) -{ - if ( GetType() == BEAM_ENTS ) - { - edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetStartEntity() ); - return pent->v.origin; - } - return pev->origin; -} - - -const Vector &CMBeam::GetEndPos( void ) -{ - int type = GetType(); - if ( type == BEAM_POINTS || type == BEAM_HOSE ) - { - return pev->angles; - } - - edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetEndEntity() ); - if ( pent ) - return pent->v.origin; - return pev->angles; -} - - -CMBeam *CMBeam::BeamCreate( const char *pSpriteName, int width ) -{ - // Create a new entity with CMBeam private data - CMBeam *pBeam = CreateClassPtr( (CMBeam *)NULL ); - - if (pBeam == NULL) - return NULL; - - pBeam->pev->classname = MAKE_STRING("beam"); - - pBeam->BeamInit( pSpriteName, width ); - - return pBeam; -} - - -void CMBeam::BeamInit( const char *pSpriteName, int width ) -{ - pev->flags |= FL_CUSTOMENTITY; - SetColor( 255, 255, 255 ); - SetBrightness( 255 ); - SetNoise( 0 ); - SetFrame( 0 ); - SetScrollRate( 0 ); - pev->model = MAKE_STRING( pSpriteName ); - SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) ); - SetWidth( width ); - pev->skin = 0; - pev->sequence = 0; - pev->rendermode = 0; -} - - -void CMBeam::PointsInit( const Vector &start, const Vector &end ) -{ - SetType( BEAM_POINTS ); - SetStartPos( start ); - SetEndPos( end ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CMBeam::HoseInit( const Vector &start, const Vector &direction ) -{ - SetType( BEAM_HOSE ); - SetStartPos( start ); - SetEndPos( direction ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CMBeam::PointEntInit( const Vector &start, int endIndex ) -{ - SetType( BEAM_ENTPOINT ); - SetStartPos( start ); - SetEndEntity( endIndex ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - -void CMBeam::EntsInit( int startIndex, int endIndex ) -{ - SetType( BEAM_ENTS ); - SetStartEntity( startIndex ); - SetEndEntity( endIndex ); - SetStartAttachment( 0 ); - SetEndAttachment( 0 ); - RelinkBeam(); -} - - -void CMBeam::RelinkBeam( void ) -{ - const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); - - pev->mins.x = min( startPos.x, endPos.x ); - pev->mins.y = min( startPos.y, endPos.y ); - pev->mins.z = min( startPos.z, endPos.z ); - pev->maxs.x = max( startPos.x, endPos.x ); - pev->maxs.y = max( startPos.y, endPos.y ); - pev->maxs.z = max( startPos.z, endPos.z ); - pev->mins = pev->mins - pev->origin; - pev->maxs = pev->maxs - pev->origin; - - UTIL_SetSize( pev, pev->mins, pev->maxs ); - UTIL_SetOrigin( pev, pev->origin ); -} - -#if 0 -void CMBeam::SetObjectCollisionBox( void ) -{ - const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); - - pev->absmin.x = min( startPos.x, endPos.x ); - pev->absmin.y = min( startPos.y, endPos.y ); - pev->absmin.z = min( startPos.z, endPos.z ); - pev->absmax.x = max( startPos.x, endPos.x ); - pev->absmax.y = max( startPos.y, endPos.y ); - pev->absmax.z = max( startPos.z, endPos.z ); -} -#endif - - -void CMBeam::TriggerTouch( edict_t *pOther ) -{ - if ( pOther->v.flags & (FL_CLIENT | FL_MONSTER) ) - { - if ( pev->owner ) - { - if (pev->owner->v.euser4 != NULL) - { - CMBaseEntity *pOwner = GetClassPtr((CMBaseEntity *)VARS(pev->owner)); - pOwner->Use( pOther, this->edict(), USE_TOGGLE, 0 ); - } - } - ALERT( at_console, "Firing targets!!!\n" ); - } -} - - -edict_t *CMBeam::RandomTargetname( const char *szName ) -{ - int total = 0; - - edict_t *pEntity = NULL; - edict_t *pNewEntity = NULL; - while ((pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName )) != NULL) - { - total++; - if (RANDOM_LONG(0,total-1) < 1) - pEntity = pNewEntity; - } - return pEntity; -} - - -void CMBeam::DoSparks( const Vector &start, const Vector &end ) -{ - if ( pev->spawnflags & (SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) ) - { - if ( pev->spawnflags & SF_BEAM_SPARKSTART ) - { - UTIL_Sparks( start ); - } - if ( pev->spawnflags & SF_BEAM_SPARKEND ) - { - UTIL_Sparks( end ); - } - } -} - -void CMBeam::BeamDamage( TraceResult *ptr ) -{ - RelinkBeam(); - if ( ptr->flFraction != 1.0 && ptr->pHit != NULL ) - { - if (UTIL_IsPlayer(ptr->pHit)) - { - ClearMultiDamage(); - UTIL_TraceAttack(ptr->pHit, pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM ); - ApplyMultiDamage( pev, pev ); - - if ( pev->spawnflags & SF_BEAM_DECALS ) - { - if ( UTIL_IsBSPModel(ptr->pHit) ) - UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); - } - - } - else if (ptr->pHit->v.euser4 != NULL) - { - ClearMultiDamage(); - CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(ptr->pHit)); - pMonster->TraceAttack( pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM ); - ApplyMultiDamage( pev, pev ); - - if ( pev->spawnflags & SF_BEAM_DECALS ) - { - if ( pMonster->IsBSPModel() ) - UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); - } - } - } - pev->dmgtime = gpGlobals->time; -} - - -void CMSprite::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_NONE; - pev->effects = 0; - pev->frame = 0; - - Precache(); - SET_MODEL( ENT(pev), STRING(pev->model) ); - - m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; - if ( pev->targetname && !(pev->spawnflags & SF_SPRITE_STARTON) ) - TurnOff(); - else - TurnOn(); - - // Worldcraft only sets y rotation, copy to Z - if ( pev->angles.y != 0 && pev->angles.z == 0 ) - { - pev->angles.z = pev->angles.y; - pev->angles.y = 0; - } -} - - -void CMSprite::Precache( void ) -{ - PRECACHE_MODEL( (char *)STRING(pev->model) ); - - // Reset attachment after save/restore - if ( pev->aiment ) - SetAttachment( pev->aiment, pev->body ); - else - { - // Clear attachment - pev->skin = 0; - pev->body = 0; - } -} - - -void CMSprite::SpriteInit( const char *pSpriteName, const Vector &origin ) -{ - pev->model = MAKE_STRING(pSpriteName); - pev->origin = origin; - Spawn(); -} - -CMSprite *CMSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ) -{ - CMSprite *pSprite = CreateClassPtr( (CMSprite *)NULL ); - pSprite->SpriteInit( pSpriteName, origin ); - pSprite->pev->classname = MAKE_STRING("env_sprite"); - pSprite->pev->solid = SOLID_NOT; - pSprite->pev->movetype = MOVETYPE_NOCLIP; - if ( animate ) - pSprite->TurnOn(); - - return pSprite; -} - - -void CMSprite::AnimateThink( void ) -{ - Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); - - pev->nextthink = gpGlobals->time + 0.1; - m_lastTime = gpGlobals->time; -} - -void CMSprite::AnimateUntilDead( void ) -{ - if ( gpGlobals->time > pev->dmgtime ) - UTIL_Remove(this->edict()); - else - { - AnimateThink(); - pev->nextthink = gpGlobals->time; - } -} - -void CMSprite::Expand( float scaleSpeed, float fadeSpeed ) -{ - pev->speed = scaleSpeed; - pev->health = fadeSpeed; - SetThink( &CMSprite::ExpandThink ); - - pev->nextthink = gpGlobals->time; - m_lastTime = gpGlobals->time; -} - - -void CMSprite::ExpandThink( void ) -{ - float frametime = gpGlobals->time - m_lastTime; - pev->scale += pev->speed * frametime; - pev->renderamt -= pev->health * frametime; - if ( pev->renderamt <= 0 ) - { - pev->renderamt = 0; - UTIL_Remove( this->edict() ); - } - else - { - pev->nextthink = gpGlobals->time + 0.1; - m_lastTime = gpGlobals->time; - } -} - - -void CMSprite::Animate( float frames ) -{ - pev->frame += frames; - if ( pev->frame > m_maxFrame ) - { - if ( pev->spawnflags & SF_SPRITE_ONCE ) - { - TurnOff(); - } - else - { - if ( m_maxFrame > 0 ) - pev->frame = fmod( pev->frame, m_maxFrame ); - } - } -} - - -void CMSprite::TurnOff( void ) -{ - pev->effects = EF_NODRAW; - pev->nextthink = 0; -} - - -void CMSprite::TurnOn( void ) -{ - pev->effects = 0; - if ( (pev->framerate && m_maxFrame > 1.0) || (pev->spawnflags & SF_SPRITE_ONCE) ) - { - SetThink( &CMSprite::AnimateThink ); - pev->nextthink = gpGlobals->time; - m_lastTime = gpGlobals->time; - } - pev->frame = 0; -} - - -void CMSprite::Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - int on = pev->effects != EF_NODRAW; - if ( ShouldToggle( useType, on ) ) - { - if ( on ) - { - TurnOff(); - } - else - { - TurnOn(); - } - } -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "customentity.h" +#include "effects.h" +#include "weapons.h" +#include "decals.h" +#include "func_break.h" +#include "shake.h" + +#define SF_GIBSHOOTER_REPEATABLE 1 // allows a gibshooter to be refired + +#define SF_FUNNEL_REVERSE 1 // funnel effect repels particles instead of attracting them. + + +// -------------------------------------------------- +// +// Beams +// +// -------------------------------------------------- + +void CMBeam::Spawn( void ) +{ + pev->solid = SOLID_NOT; // Remove model & collisions + Precache( ); +} + +void CMBeam::Precache( void ) +{ + if ( pev->owner ) + SetStartEntity( ENTINDEX( pev->owner ) ); + if ( pev->aiment ) + SetEndEntity( ENTINDEX( pev->aiment ) ); +} + +void CMBeam::SetStartEntity( int entityIndex ) +{ + pev->sequence = (entityIndex & 0x0FFF) | ((pev->sequence&0xF000)<<12); + pev->owner = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); +} + +void CMBeam::SetEndEntity( int entityIndex ) +{ + pev->skin = (entityIndex & 0x0FFF) | ((pev->skin&0xF000)<<12); + pev->aiment = g_engfuncs.pfnPEntityOfEntIndex( entityIndex ); +} + + +// These don't take attachments into account +const Vector &CMBeam::GetStartPos( void ) +{ + if ( GetType() == BEAM_ENTS ) + { + edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetStartEntity() ); + return pent->v.origin; + } + return pev->origin; +} + + +const Vector &CMBeam::GetEndPos( void ) +{ + int type = GetType(); + if ( type == BEAM_POINTS || type == BEAM_HOSE ) + { + return pev->angles; + } + + edict_t *pent = g_engfuncs.pfnPEntityOfEntIndex( GetEndEntity() ); + if ( pent ) + return pent->v.origin; + return pev->angles; +} + + +CMBeam *CMBeam::BeamCreate( const char *pSpriteName, int width ) +{ + // Create a new entity with CMBeam private data + CMBeam *pBeam = CreateClassPtr( (CMBeam *)NULL ); + + if (pBeam == NULL) + return NULL; + + pBeam->pev->classname = MAKE_STRING("beam"); + + pBeam->BeamInit( pSpriteName, width ); + + return pBeam; +} + + +void CMBeam::BeamInit( const char *pSpriteName, int width ) +{ + pev->flags |= FL_CUSTOMENTITY; + SetColor( 255, 255, 255 ); + SetBrightness( 255 ); + SetNoise( 0 ); + SetFrame( 0 ); + SetScrollRate( 0 ); + pev->model = MAKE_STRING( pSpriteName ); + SetTexture( PRECACHE_MODEL( (char *)pSpriteName ) ); + SetWidth( width ); + pev->skin = 0; + pev->sequence = 0; + pev->rendermode = 0; +} + + +void CMBeam::PointsInit( const Vector &start, const Vector &end ) +{ + SetType( BEAM_POINTS ); + SetStartPos( start ); + SetEndPos( end ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CMBeam::HoseInit( const Vector &start, const Vector &direction ) +{ + SetType( BEAM_HOSE ); + SetStartPos( start ); + SetEndPos( direction ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CMBeam::PointEntInit( const Vector &start, int endIndex ) +{ + SetType( BEAM_ENTPOINT ); + SetStartPos( start ); + SetEndEntity( endIndex ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + +void CMBeam::EntsInit( int startIndex, int endIndex ) +{ + SetType( BEAM_ENTS ); + SetStartEntity( startIndex ); + SetEndEntity( endIndex ); + SetStartAttachment( 0 ); + SetEndAttachment( 0 ); + RelinkBeam(); +} + + +void CMBeam::RelinkBeam( void ) +{ + const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); + + pev->mins.x = min( startPos.x, endPos.x ); + pev->mins.y = min( startPos.y, endPos.y ); + pev->mins.z = min( startPos.z, endPos.z ); + pev->maxs.x = max( startPos.x, endPos.x ); + pev->maxs.y = max( startPos.y, endPos.y ); + pev->maxs.z = max( startPos.z, endPos.z ); + pev->mins = pev->mins - pev->origin; + pev->maxs = pev->maxs - pev->origin; + + UTIL_SetSize( pev, pev->mins, pev->maxs ); + UTIL_SetOrigin( pev, pev->origin ); +} + +#if 0 +void CMBeam::SetObjectCollisionBox( void ) +{ + const Vector &startPos = GetStartPos(), &endPos = GetEndPos(); + + pev->absmin.x = min( startPos.x, endPos.x ); + pev->absmin.y = min( startPos.y, endPos.y ); + pev->absmin.z = min( startPos.z, endPos.z ); + pev->absmax.x = max( startPos.x, endPos.x ); + pev->absmax.y = max( startPos.y, endPos.y ); + pev->absmax.z = max( startPos.z, endPos.z ); +} +#endif + + +void CMBeam::TriggerTouch( edict_t *pOther ) +{ + if ( pOther->v.flags & (FL_CLIENT | FL_MONSTER) ) + { + if ( pev->owner ) + { + if (pev->owner->v.euser4 != NULL) + { + CMBaseEntity *pOwner = GetClassPtr((CMBaseEntity *)VARS(pev->owner)); + pOwner->Use( pOther, this->edict(), USE_TOGGLE, 0 ); + } + } + ALERT( at_console, "Firing targets!!!\n" ); + } +} + + +edict_t *CMBeam::RandomTargetname( const char *szName ) +{ + int total = 0; + + edict_t *pEntity = NULL; + edict_t *pNewEntity = NULL; + while ((pNewEntity = UTIL_FindEntityByTargetname( pNewEntity, szName )) != NULL) + { + total++; + if (RANDOM_LONG(0,total-1) < 1) + pEntity = pNewEntity; + } + return pEntity; +} + + +void CMBeam::DoSparks( const Vector &start, const Vector &end ) +{ + if ( pev->spawnflags & (SF_BEAM_SPARKSTART|SF_BEAM_SPARKEND) ) + { + if ( pev->spawnflags & SF_BEAM_SPARKSTART ) + { + UTIL_Sparks( start ); + } + if ( pev->spawnflags & SF_BEAM_SPARKEND ) + { + UTIL_Sparks( end ); + } + } +} + +void CMBeam::BeamDamage( TraceResult *ptr ) +{ + RelinkBeam(); + if ( ptr->flFraction != 1.0 && ptr->pHit != NULL ) + { + if (UTIL_IsPlayer(ptr->pHit)) + { + ClearMultiDamage(); + UTIL_TraceAttack(ptr->pHit, pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM ); + ApplyMultiDamage( pev, pev ); + + if ( pev->spawnflags & SF_BEAM_DECALS ) + { + if ( UTIL_IsBSPModel(ptr->pHit) ) + UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); + } + + } + else if (ptr->pHit->v.euser4 != NULL) + { + ClearMultiDamage(); + CMBaseEntity *pMonster = GetClassPtr((CMBaseEntity *)VARS(ptr->pHit)); + pMonster->TraceAttack( pev, pev->dmg * (gpGlobals->time - pev->dmgtime), (ptr->vecEndPos - pev->origin).Normalize(), ptr, DMG_ENERGYBEAM ); + ApplyMultiDamage( pev, pev ); + + if ( pev->spawnflags & SF_BEAM_DECALS ) + { + if ( pMonster->IsBSPModel() ) + UTIL_DecalTrace( ptr, DECAL_BIGSHOT1 + RANDOM_LONG(0,4) ); + } + } + } + pev->dmgtime = gpGlobals->time; +} + + +void CMSprite::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_NONE; + pev->effects = 0; + pev->frame = 0; + + Precache(); + SET_MODEL( ENT(pev), STRING(pev->model) ); + + m_maxFrame = (float) MODEL_FRAMES( pev->modelindex ) - 1; + if ( pev->targetname && !(pev->spawnflags & SF_SPRITE_STARTON) ) + TurnOff(); + else + TurnOn(); + + // Worldcraft only sets y rotation, copy to Z + if ( pev->angles.y != 0 && pev->angles.z == 0 ) + { + pev->angles.z = pev->angles.y; + pev->angles.y = 0; + } +} + + +void CMSprite::Precache( void ) +{ + PRECACHE_MODEL( (char *)STRING(pev->model) ); + + // Reset attachment after save/restore + if ( pev->aiment ) + SetAttachment( pev->aiment, pev->body ); + else + { + // Clear attachment + pev->skin = 0; + pev->body = 0; + } +} + + +void CMSprite::SpriteInit( const char *pSpriteName, const Vector &origin ) +{ + pev->model = MAKE_STRING(pSpriteName); + pev->origin = origin; + Spawn(); +} + +CMSprite *CMSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ) +{ + CMSprite *pSprite = CreateClassPtr( (CMSprite *)NULL ); + pSprite->SpriteInit( pSpriteName, origin ); + pSprite->pev->classname = MAKE_STRING("env_sprite"); + pSprite->pev->solid = SOLID_NOT; + pSprite->pev->movetype = MOVETYPE_NOCLIP; + if ( animate ) + pSprite->TurnOn(); + + return pSprite; +} + + +void CMSprite::AnimateThink( void ) +{ + Animate( pev->framerate * (gpGlobals->time - m_lastTime) ); + + pev->nextthink = gpGlobals->time + 0.1; + m_lastTime = gpGlobals->time; +} + +void CMSprite::AnimateUntilDead( void ) +{ + if ( gpGlobals->time > pev->dmgtime ) + UTIL_Remove(this->edict()); + else + { + AnimateThink(); + pev->nextthink = gpGlobals->time; + } +} + +void CMSprite::Expand( float scaleSpeed, float fadeSpeed ) +{ + pev->speed = scaleSpeed; + pev->health = fadeSpeed; + SetThink( &CMSprite::ExpandThink ); + + pev->nextthink = gpGlobals->time; + m_lastTime = gpGlobals->time; +} + + +void CMSprite::ExpandThink( void ) +{ + float frametime = gpGlobals->time - m_lastTime; + pev->scale += pev->speed * frametime; + pev->renderamt -= pev->health * frametime; + if ( pev->renderamt <= 0 ) + { + pev->renderamt = 0; + UTIL_Remove( this->edict() ); + } + else + { + pev->nextthink = gpGlobals->time + 0.1; + m_lastTime = gpGlobals->time; + } +} + + +void CMSprite::Animate( float frames ) +{ + pev->frame += frames; + if ( pev->frame > m_maxFrame ) + { + if ( pev->spawnflags & SF_SPRITE_ONCE ) + { + TurnOff(); + } + else + { + if ( m_maxFrame > 0 ) + pev->frame = fmod( pev->frame, m_maxFrame ); + } + } +} + + +void CMSprite::TurnOff( void ) +{ + pev->effects = EF_NODRAW; + pev->nextthink = 0; +} + + +void CMSprite::TurnOn( void ) +{ + pev->effects = 0; + if ( (pev->framerate && m_maxFrame > 1.0) || (pev->spawnflags & SF_SPRITE_ONCE) ) + { + SetThink( &CMSprite::AnimateThink ); + pev->nextthink = gpGlobals->time; + m_lastTime = gpGlobals->time; + } + pev->frame = 0; +} + + +void CMSprite::Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + int on = pev->effects != EF_NODRAW; + if ( ShouldToggle( useType, on ) ) + { + if ( on ) + { + TurnOff(); + } + else + { + TurnOn(); + } + } +} diff --git a/src/dlls/effects.h b/src/dlls/effects.h index 3bcafb4..52de124 100644 --- a/src/dlls/effects.h +++ b/src/dlls/effects.h @@ -1,203 +1,203 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef EFFECTS_H -#define EFFECTS_H - -#define SF_BEAM_STARTON 0x0001 -#define SF_BEAM_TOGGLE 0x0002 -#define SF_BEAM_RANDOM 0x0004 -#define SF_BEAM_RING 0x0008 -#define SF_BEAM_SPARKSTART 0x0010 -#define SF_BEAM_SPARKEND 0x0020 -#define SF_BEAM_DECALS 0x0040 -#define SF_BEAM_SHADEIN 0x0080 -#define SF_BEAM_SHADEOUT 0x0100 -#define SF_BEAM_TEMPORARY 0x8000 - -#define SF_SPRITE_STARTON 0x0001 -#define SF_SPRITE_ONCE 0x0002 -#define SF_SPRITE_TEMPORARY 0x8000 - -class CMSprite : public CMPointEntity -{ -public: - void Spawn( void ); - void Precache( void ); - - int ObjectCaps( void ) - { - int flags = 0; - if ( pev->spawnflags & SF_SPRITE_TEMPORARY ) - flags = FCAP_DONT_SAVE; - return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags; - } - void EXPORT AnimateThink( void ); - void EXPORT ExpandThink( void ); - void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - void Animate( float frames ); - void Expand( float scaleSpeed, float fadeSpeed ); - void SpriteInit( const char *pSpriteName, const Vector &origin ); - - inline void SetAttachment( edict_t *pEntity, int attachment ) - { - if ( pEntity ) - { - pev->skin = ENTINDEX(pEntity); - pev->body = attachment; - pev->aiment = pEntity; - pev->movetype = MOVETYPE_FOLLOW; - } - } - void TurnOff( void ); - void TurnOn( void ); - inline float Frames( void ) { return m_maxFrame; } - inline void SetTransparency( int rendermode, int r, int g, int b, int a, int fx ) - { - pev->rendermode = rendermode; - pev->rendercolor.x = r; - pev->rendercolor.y = g; - pev->rendercolor.z = b; - pev->renderamt = a; - pev->renderfx = fx; - } - inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; } - inline void SetScale( float scale ) { pev->scale = scale; } - inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } - inline void SetBrightness( int brightness ) { pev->renderamt = brightness; } - - inline void AnimateAndDie( float framerate ) - { - SetThink(&CMSprite::AnimateUntilDead); - pev->framerate = framerate; - pev->dmgtime = gpGlobals->time + (m_maxFrame / framerate); - pev->nextthink = gpGlobals->time; - } - - void EXPORT AnimateUntilDead( void ); - - static CMSprite *SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ); - -private: - - float m_lastTime; - float m_maxFrame; -}; - - -class CMBeam : public CMBaseEntity -{ -public: - void Spawn( void ); - void Precache( void ); - int ObjectCaps( void ) - { - int flags = 0; - if ( pev->spawnflags & SF_BEAM_TEMPORARY ) - flags = FCAP_DONT_SAVE; - return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags; - } - - void EXPORT TriggerTouch( edict_t *pOther ); - - // These functions are here to show the way beams are encoded as entities. - // Encoding beams as entities simplifies their management in the client/server architecture - inline void SetType( int type ) { pev->rendermode = (pev->rendermode & 0xF0) | (type&0x0F); } - inline void SetFlags( int flags ) { pev->rendermode = (pev->rendermode & 0x0F) | (flags&0xF0); } - inline void SetStartPos( const Vector& pos ) { pev->origin = pos; } - inline void SetEndPos( const Vector& pos ) { pev->angles = pos; } - void SetStartEntity( int entityIndex ); - void SetEndEntity( int entityIndex ); - - inline void SetStartAttachment( int attachment ) { pev->sequence = (pev->sequence & 0x0FFF) | ((attachment&0xF)<<12); } - inline void SetEndAttachment( int attachment ) { pev->skin = (pev->skin & 0x0FFF) | ((attachment&0xF)<<12); } - - inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; } - inline void SetWidth( int width ) { pev->scale = width; } - inline void SetNoise( int amplitude ) { pev->body = amplitude; } - inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } - inline void SetBrightness( int brightness ) { pev->renderamt = brightness; } - inline void SetFrame( float frame ) { pev->frame = frame; } - inline void SetScrollRate( int speed ) { pev->animtime = speed; } - - inline int GetType( void ) { return pev->rendermode & 0x0F; } - inline int GetFlags( void ) { return pev->rendermode & 0xF0; } - inline int GetStartEntity( void ) { return pev->sequence & 0xFFF; } - inline int GetEndEntity( void ) { return pev->skin & 0xFFF; } - - const Vector &GetStartPos( void ); - const Vector &GetEndPos( void ); - - Vector Center( void ) { return (GetStartPos() + GetEndPos()) * 0.5; }; // center point of beam - - inline int GetTexture( void ) { return pev->modelindex; } - inline int GetWidth( void ) { return pev->scale; } - inline int GetNoise( void ) { return pev->body; } - // inline void GetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } - inline int GetBrightness( void ) { return pev->renderamt; } - inline int GetFrame( void ) { return pev->frame; } - inline int GetScrollRate( void ) { return pev->animtime; } - - // Call after you change start/end positions - void RelinkBeam( void ); -// void SetObjectCollisionBox( void ); - - void DoSparks( const Vector &start, const Vector &end ); - edict_t *RandomTargetname( const char *szName ); - void BeamDamage( TraceResult *ptr ); - // Init after BeamCreate() - void BeamInit( const char *pSpriteName, int width ); - void PointsInit( const Vector &start, const Vector &end ); - void PointEntInit( const Vector &start, int endIndex ); - void EntsInit( int startIndex, int endIndex ); - void HoseInit( const Vector &start, const Vector &direction ); - - static CMBeam *BeamCreate( const char *pSpriteName, int width ); - - inline void LiveForTime( float time ) { SetThink(&CMBeam::SUB_Remove); pev->nextthink = gpGlobals->time + time; } - inline void BeamDamageInstant( TraceResult *ptr, float damage ) - { - pev->dmg = damage; - pev->dmgtime = gpGlobals->time - 1; - BeamDamage(ptr); - } -}; - - -#define SF_MESSAGE_ONCE 0x0001 // Fade in, not out -#define SF_MESSAGE_ALL 0x0002 // Send to all clients - - -class CMLaser : public CMBeam -{ -public: - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData *pkvd ); - - void TurnOn( void ); - void TurnOff( void ); - int IsOn( void ); - - void FireAtPoint( TraceResult &point ); - - void EXPORT StrikeThink( void ); - void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - - CMSprite *m_pSprite; - int m_iszSpriteName; - Vector m_firePosition; -}; - -#endif //EFFECTS_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef EFFECTS_H +#define EFFECTS_H + +#define SF_BEAM_STARTON 0x0001 +#define SF_BEAM_TOGGLE 0x0002 +#define SF_BEAM_RANDOM 0x0004 +#define SF_BEAM_RING 0x0008 +#define SF_BEAM_SPARKSTART 0x0010 +#define SF_BEAM_SPARKEND 0x0020 +#define SF_BEAM_DECALS 0x0040 +#define SF_BEAM_SHADEIN 0x0080 +#define SF_BEAM_SHADEOUT 0x0100 +#define SF_BEAM_TEMPORARY 0x8000 + +#define SF_SPRITE_STARTON 0x0001 +#define SF_SPRITE_ONCE 0x0002 +#define SF_SPRITE_TEMPORARY 0x8000 + +class CMSprite : public CMPointEntity +{ +public: + void Spawn( void ); + void Precache( void ); + + int ObjectCaps( void ) + { + int flags = 0; + if ( pev->spawnflags & SF_SPRITE_TEMPORARY ) + flags = FCAP_DONT_SAVE; + return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags; + } + void EXPORT AnimateThink( void ); + void EXPORT ExpandThink( void ); + void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + void Animate( float frames ); + void Expand( float scaleSpeed, float fadeSpeed ); + void SpriteInit( const char *pSpriteName, const Vector &origin ); + + inline void SetAttachment( edict_t *pEntity, int attachment ) + { + if ( pEntity ) + { + pev->skin = ENTINDEX(pEntity); + pev->body = attachment; + pev->aiment = pEntity; + pev->movetype = MOVETYPE_FOLLOW; + } + } + void TurnOff( void ); + void TurnOn( void ); + inline float Frames( void ) { return m_maxFrame; } + inline void SetTransparency( int rendermode, int r, int g, int b, int a, int fx ) + { + pev->rendermode = rendermode; + pev->rendercolor.x = r; + pev->rendercolor.y = g; + pev->rendercolor.z = b; + pev->renderamt = a; + pev->renderfx = fx; + } + inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; } + inline void SetScale( float scale ) { pev->scale = scale; } + inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } + inline void SetBrightness( int brightness ) { pev->renderamt = brightness; } + + inline void AnimateAndDie( float framerate ) + { + SetThink(&CMSprite::AnimateUntilDead); + pev->framerate = framerate; + pev->dmgtime = gpGlobals->time + (m_maxFrame / framerate); + pev->nextthink = gpGlobals->time; + } + + void EXPORT AnimateUntilDead( void ); + + static CMSprite *SpriteCreate( const char *pSpriteName, const Vector &origin, BOOL animate ); + +private: + + float m_lastTime; + float m_maxFrame; +}; + + +class CMBeam : public CMBaseEntity +{ +public: + void Spawn( void ); + void Precache( void ); + int ObjectCaps( void ) + { + int flags = 0; + if ( pev->spawnflags & SF_BEAM_TEMPORARY ) + flags = FCAP_DONT_SAVE; + return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | flags; + } + + void EXPORT TriggerTouch( edict_t *pOther ); + + // These functions are here to show the way beams are encoded as entities. + // Encoding beams as entities simplifies their management in the client/server architecture + inline void SetType( int type ) { pev->rendermode = (pev->rendermode & 0xF0) | (type&0x0F); } + inline void SetFlags( int flags ) { pev->rendermode = (pev->rendermode & 0x0F) | (flags&0xF0); } + inline void SetStartPos( const Vector& pos ) { pev->origin = pos; } + inline void SetEndPos( const Vector& pos ) { pev->angles = pos; } + void SetStartEntity( int entityIndex ); + void SetEndEntity( int entityIndex ); + + inline void SetStartAttachment( int attachment ) { pev->sequence = (pev->sequence & 0x0FFF) | ((attachment&0xF)<<12); } + inline void SetEndAttachment( int attachment ) { pev->skin = (pev->skin & 0x0FFF) | ((attachment&0xF)<<12); } + + inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; } + inline void SetWidth( int width ) { pev->scale = width; } + inline void SetNoise( int amplitude ) { pev->body = amplitude; } + inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } + inline void SetBrightness( int brightness ) { pev->renderamt = brightness; } + inline void SetFrame( float frame ) { pev->frame = frame; } + inline void SetScrollRate( int speed ) { pev->animtime = speed; } + + inline int GetType( void ) { return pev->rendermode & 0x0F; } + inline int GetFlags( void ) { return pev->rendermode & 0xF0; } + inline int GetStartEntity( void ) { return pev->sequence & 0xFFF; } + inline int GetEndEntity( void ) { return pev->skin & 0xFFF; } + + const Vector &GetStartPos( void ); + const Vector &GetEndPos( void ); + + Vector Center( void ) { return (GetStartPos() + GetEndPos()) * 0.5; }; // center point of beam + + inline int GetTexture( void ) { return pev->modelindex; } + inline int GetWidth( void ) { return pev->scale; } + inline int GetNoise( void ) { return pev->body; } + // inline void GetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; } + inline int GetBrightness( void ) { return pev->renderamt; } + inline int GetFrame( void ) { return pev->frame; } + inline int GetScrollRate( void ) { return pev->animtime; } + + // Call after you change start/end positions + void RelinkBeam( void ); +// void SetObjectCollisionBox( void ); + + void DoSparks( const Vector &start, const Vector &end ); + edict_t *RandomTargetname( const char *szName ); + void BeamDamage( TraceResult *ptr ); + // Init after BeamCreate() + void BeamInit( const char *pSpriteName, int width ); + void PointsInit( const Vector &start, const Vector &end ); + void PointEntInit( const Vector &start, int endIndex ); + void EntsInit( int startIndex, int endIndex ); + void HoseInit( const Vector &start, const Vector &direction ); + + static CMBeam *BeamCreate( const char *pSpriteName, int width ); + + inline void LiveForTime( float time ) { SetThink(&CMBeam::SUB_Remove); pev->nextthink = gpGlobals->time + time; } + inline void BeamDamageInstant( TraceResult *ptr, float damage ) + { + pev->dmg = damage; + pev->dmgtime = gpGlobals->time - 1; + BeamDamage(ptr); + } +}; + + +#define SF_MESSAGE_ONCE 0x0001 // Fade in, not out +#define SF_MESSAGE_ALL 0x0002 // Send to all clients + + +class CMLaser : public CMBeam +{ +public: + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData *pkvd ); + + void TurnOn( void ); + void TurnOff( void ); + int IsOn( void ); + + void FireAtPoint( TraceResult &point ); + + void EXPORT StrikeThink( void ); + void Use( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); + + CMSprite *m_pSprite; + int m_iszSpriteName; + Vector m_firePosition; +}; + +#endif //EFFECTS_H diff --git a/src/dlls/enginecallback.h b/src/dlls/enginecallback.h index 31072fd..19eb34b 100644 --- a/src/dlls/enginecallback.h +++ b/src/dlls/enginecallback.h @@ -1,158 +1,158 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef ENGINECALLBACK_H -#define ENGINECALLBACK_H -#pragma once - -#include "event_flags.h" - -// Must be provided by user of this code -extern enginefuncs_t g_engfuncs; - -// The actual engine callbacks -#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) -#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) -#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) -#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) -#define SET_MODEL (*g_engfuncs.pfnSetModel) -#define MODEL_INDEX (*g_engfuncs.pfnModelIndex) -#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) -#define SET_SIZE (*g_engfuncs.pfnSetSize) -#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) -#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms) -#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms) -#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw) -#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles) -#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin) -#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw) -#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch) -#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors) -#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity) -#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity) -#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity) -#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic) -#define ENT_IS_ON_FLOOR (*g_engfuncs.pfnEntIsOnFloor) -#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor) -#define WALK_MOVE (*g_engfuncs.pfnWalkMove) -#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin) -#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound) -#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg) -#define TRACE_LINE (*g_engfuncs.pfnTraceLine) -#define TRACE_TOSS (*g_engfuncs.pfnTraceToss) -#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull) -#define TRACE_HULL (*g_engfuncs.pfnTraceHull) -#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector) -#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand) -#define SERVER_EXECUTE (*g_engfuncs.pfnServerExecute) -#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand) -#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect) -#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle) -#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex) -#define POINT_CONTENTS (*g_engfuncs.pfnPointContents) -#define CRC32_INIT (*g_engfuncs.pfnCRC32_Init) -#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC32_ProcessBuffer) -#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC32_ProcessByte) -#define CRC32_FINAL (*g_engfuncs.pfnCRC32_Final) -#define RANDOM_LONG (*g_engfuncs.pfnRandomLong) -#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) -#define GETPLAYERWONID (*g_engfuncs.pfnGetPlayerWONId) - -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) { - (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed); -} -#define MESSAGE_END (*g_engfuncs.pfnMessageEnd) -#define WRITE_BYTE (*g_engfuncs.pfnWriteByte) -#define WRITE_CHAR (*g_engfuncs.pfnWriteChar) -#define WRITE_SHORT (*g_engfuncs.pfnWriteShort) -#define WRITE_LONG (*g_engfuncs.pfnWriteLong) -#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle) -#define WRITE_COORD (*g_engfuncs.pfnWriteCoord) -#define WRITE_STRING (*g_engfuncs.pfnWriteString) -#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity) -#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister) -#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat) -#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) -#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) -#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) -#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer) -#define ALERT (*g_engfuncs.pfnAlertMessage) -#define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) -#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) -inline void *GET_PRIVATE( edict_t *pent ) -{ - if ( pent ) - return pent->pvPrivateData; - return NULL; -} - -#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData) -//#define STRING (*g_engfuncs.pfnSzFromIndex) -#define ALLOC_STRING (*g_engfuncs.pfnAllocString) -#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString) -#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum) -#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere) -#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS) -#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound) -#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr) -#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg) -#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition) -#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName) -#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction) -#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture) -#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf) -#define CMD_ARGS (*g_engfuncs.pfnCmd_Args) -#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) -#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) -#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment) -#define SET_VIEW (*g_engfuncs.pfnSetView) -#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle) -#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFileForMe) -#define FREE_FILE (*g_engfuncs.pfnFreeFile) -#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime) -#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) -#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) -#define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) -#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) - -#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent) -#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) - -#define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) -#define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) - -#define ENGINE_CHECK_VISIBILITY (*g_engfuncs.pfnCheckVisibility) - -#define DELTA_SET ( *g_engfuncs.pfnDeltaSetField ) -#define DELTA_UNSET ( *g_engfuncs.pfnDeltaUnsetField ) -#define DELTA_ADDENCODER ( *g_engfuncs.pfnDeltaAddEncoder ) -#define ENGINE_CURRENT_PLAYER ( *g_engfuncs.pfnGetCurrentPlayer ) - -#define ENGINE_CANSKIP ( *g_engfuncs.pfnCanSkipPlayer ) - -#define DELTA_FINDFIELD ( *g_engfuncs.pfnDeltaFindField ) -#define DELTA_SETBYINDEX ( *g_engfuncs.pfnDeltaSetFieldByIndex ) -#define DELTA_UNSETBYINDEX ( *g_engfuncs.pfnDeltaUnsetFieldByIndex ) - -#define ENGINE_GETPHYSINFO ( *g_engfuncs.pfnGetPhysicsInfoString ) - -#define ENGINE_SETGROUPMASK ( *g_engfuncs.pfnSetGroupMask ) - -#define ENGINE_INSTANCE_BASELINE ( *g_engfuncs.pfnCreateInstancedBaseline ) - -#define ENGINE_FORCE_UNMODIFIED ( *g_engfuncs.pfnForceUnmodified ) - -#define PLAYER_CNX_STATS ( *g_engfuncs.pfnGetPlayerStats ) - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef ENGINECALLBACK_H +#define ENGINECALLBACK_H +#pragma once + +#include "event_flags.h" + +// Must be provided by user of this code +extern enginefuncs_t g_engfuncs; + +// The actual engine callbacks +#define GETPLAYERUSERID (*g_engfuncs.pfnGetPlayerUserId) +#define PRECACHE_MODEL (*g_engfuncs.pfnPrecacheModel) +#define PRECACHE_SOUND (*g_engfuncs.pfnPrecacheSound) +#define PRECACHE_GENERIC (*g_engfuncs.pfnPrecacheGeneric) +#define SET_MODEL (*g_engfuncs.pfnSetModel) +#define MODEL_INDEX (*g_engfuncs.pfnModelIndex) +#define MODEL_FRAMES (*g_engfuncs.pfnModelFrames) +#define SET_SIZE (*g_engfuncs.pfnSetSize) +#define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) +#define GET_SPAWN_PARMS (*g_engfuncs.pfnGetSpawnParms) +#define SAVE_SPAWN_PARMS (*g_engfuncs.pfnSaveSpawnParms) +#define VEC_TO_YAW (*g_engfuncs.pfnVecToYaw) +#define VEC_TO_ANGLES (*g_engfuncs.pfnVecToAngles) +#define MOVE_TO_ORIGIN (*g_engfuncs.pfnMoveToOrigin) +#define oldCHANGE_YAW (*g_engfuncs.pfnChangeYaw) +#define CHANGE_PITCH (*g_engfuncs.pfnChangePitch) +#define MAKE_VECTORS (*g_engfuncs.pfnMakeVectors) +#define CREATE_ENTITY (*g_engfuncs.pfnCreateEntity) +#define REMOVE_ENTITY (*g_engfuncs.pfnRemoveEntity) +#define CREATE_NAMED_ENTITY (*g_engfuncs.pfnCreateNamedEntity) +#define MAKE_STATIC (*g_engfuncs.pfnMakeStatic) +#define ENT_IS_ON_FLOOR (*g_engfuncs.pfnEntIsOnFloor) +#define DROP_TO_FLOOR (*g_engfuncs.pfnDropToFloor) +#define WALK_MOVE (*g_engfuncs.pfnWalkMove) +#define SET_ORIGIN (*g_engfuncs.pfnSetOrigin) +#define EMIT_SOUND_DYN2 (*g_engfuncs.pfnEmitSound) +#define BUILD_SOUND_MSG (*g_engfuncs.pfnBuildSoundMsg) +#define TRACE_LINE (*g_engfuncs.pfnTraceLine) +#define TRACE_TOSS (*g_engfuncs.pfnTraceToss) +#define TRACE_MONSTER_HULL (*g_engfuncs.pfnTraceMonsterHull) +#define TRACE_HULL (*g_engfuncs.pfnTraceHull) +#define GET_AIM_VECTOR (*g_engfuncs.pfnGetAimVector) +#define SERVER_COMMAND (*g_engfuncs.pfnServerCommand) +#define SERVER_EXECUTE (*g_engfuncs.pfnServerExecute) +#define CLIENT_COMMAND (*g_engfuncs.pfnClientCommand) +#define PARTICLE_EFFECT (*g_engfuncs.pfnParticleEffect) +#define LIGHT_STYLE (*g_engfuncs.pfnLightStyle) +#define DECAL_INDEX (*g_engfuncs.pfnDecalIndex) +#define POINT_CONTENTS (*g_engfuncs.pfnPointContents) +#define CRC32_INIT (*g_engfuncs.pfnCRC32_Init) +#define CRC32_PROCESS_BUFFER (*g_engfuncs.pfnCRC32_ProcessBuffer) +#define CRC32_PROCESS_BYTE (*g_engfuncs.pfnCRC32_ProcessByte) +#define CRC32_FINAL (*g_engfuncs.pfnCRC32_Final) +#define RANDOM_LONG (*g_engfuncs.pfnRandomLong) +#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) +#define GETPLAYERWONID (*g_engfuncs.pfnGetPlayerWONId) + +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin = NULL, edict_t *ed = NULL ) { + (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ed); +} +#define MESSAGE_END (*g_engfuncs.pfnMessageEnd) +#define WRITE_BYTE (*g_engfuncs.pfnWriteByte) +#define WRITE_CHAR (*g_engfuncs.pfnWriteChar) +#define WRITE_SHORT (*g_engfuncs.pfnWriteShort) +#define WRITE_LONG (*g_engfuncs.pfnWriteLong) +#define WRITE_ANGLE (*g_engfuncs.pfnWriteAngle) +#define WRITE_COORD (*g_engfuncs.pfnWriteCoord) +#define WRITE_STRING (*g_engfuncs.pfnWriteString) +#define WRITE_ENTITY (*g_engfuncs.pfnWriteEntity) +#define CVAR_REGISTER (*g_engfuncs.pfnCVarRegister) +#define CVAR_GET_FLOAT (*g_engfuncs.pfnCVarGetFloat) +#define CVAR_GET_STRING (*g_engfuncs.pfnCVarGetString) +#define CVAR_SET_FLOAT (*g_engfuncs.pfnCVarSetFloat) +#define CVAR_SET_STRING (*g_engfuncs.pfnCVarSetString) +#define CVAR_GET_POINTER (*g_engfuncs.pfnCVarGetPointer) +#define ALERT (*g_engfuncs.pfnAlertMessage) +#define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) +#define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) +inline void *GET_PRIVATE( edict_t *pent ) +{ + if ( pent ) + return pent->pvPrivateData; + return NULL; +} + +#define FREE_PRIVATE (*g_engfuncs.pfnFreeEntPrivateData) +//#define STRING (*g_engfuncs.pfnSzFromIndex) +#define ALLOC_STRING (*g_engfuncs.pfnAllocString) +#define FIND_ENTITY_BY_STRING (*g_engfuncs.pfnFindEntityByString) +#define GETENTITYILLUM (*g_engfuncs.pfnGetEntityIllum) +#define FIND_ENTITY_IN_SPHERE (*g_engfuncs.pfnFindEntityInSphere) +#define FIND_CLIENT_IN_PVS (*g_engfuncs.pfnFindClientInPVS) +#define EMIT_AMBIENT_SOUND (*g_engfuncs.pfnEmitAmbientSound) +#define GET_MODEL_PTR (*g_engfuncs.pfnGetModelPtr) +#define REG_USER_MSG (*g_engfuncs.pfnRegUserMsg) +#define GET_BONE_POSITION (*g_engfuncs.pfnGetBonePosition) +#define FUNCTION_FROM_NAME (*g_engfuncs.pfnFunctionFromName) +#define NAME_FOR_FUNCTION (*g_engfuncs.pfnNameForFunction) +#define TRACE_TEXTURE (*g_engfuncs.pfnTraceTexture) +#define CLIENT_PRINTF (*g_engfuncs.pfnClientPrintf) +#define CMD_ARGS (*g_engfuncs.pfnCmd_Args) +#define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) +#define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) +#define GET_ATTACHMENT (*g_engfuncs.pfnGetAttachment) +#define SET_VIEW (*g_engfuncs.pfnSetView) +#define SET_CROSSHAIRANGLE (*g_engfuncs.pfnCrosshairAngle) +#define LOAD_FILE_FOR_ME (*g_engfuncs.pfnLoadFileForMe) +#define FREE_FILE (*g_engfuncs.pfnFreeFile) +#define COMPARE_FILE_TIME (*g_engfuncs.pfnCompareFileTime) +#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir) +#define IS_MAP_VALID (*g_engfuncs.pfnIsMapValid) +#define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) +#define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) + +#define PRECACHE_EVENT (*g_engfuncs.pfnPrecacheEvent) +#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) + +#define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) +#define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) + +#define ENGINE_CHECK_VISIBILITY (*g_engfuncs.pfnCheckVisibility) + +#define DELTA_SET ( *g_engfuncs.pfnDeltaSetField ) +#define DELTA_UNSET ( *g_engfuncs.pfnDeltaUnsetField ) +#define DELTA_ADDENCODER ( *g_engfuncs.pfnDeltaAddEncoder ) +#define ENGINE_CURRENT_PLAYER ( *g_engfuncs.pfnGetCurrentPlayer ) + +#define ENGINE_CANSKIP ( *g_engfuncs.pfnCanSkipPlayer ) + +#define DELTA_FINDFIELD ( *g_engfuncs.pfnDeltaFindField ) +#define DELTA_SETBYINDEX ( *g_engfuncs.pfnDeltaSetFieldByIndex ) +#define DELTA_UNSETBYINDEX ( *g_engfuncs.pfnDeltaUnsetFieldByIndex ) + +#define ENGINE_GETPHYSINFO ( *g_engfuncs.pfnGetPhysicsInfoString ) + +#define ENGINE_SETGROUPMASK ( *g_engfuncs.pfnSetGroupMask ) + +#define ENGINE_INSTANCE_BASELINE ( *g_engfuncs.pfnCreateInstancedBaseline ) + +#define ENGINE_FORCE_UNMODIFIED ( *g_engfuncs.pfnForceUnmodified ) + +#define PLAYER_CNX_STATS ( *g_engfuncs.pfnGetPlayerStats ) + #endif //ENGINECALLBACK_H \ No newline at end of file diff --git a/src/dlls/explode.cpp b/src/dlls/explode.cpp old mode 100755 new mode 100644 index dcc5cd6..4c3e358 --- a/src/dlls/explode.cpp +++ b/src/dlls/explode.cpp @@ -1,301 +1,301 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -/* - -===== explode.cpp ======================================================== - - Explosion-related code - -*/ -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "decals.h" -#include "explode.h" - -// Spark Shower -class CMShower : public CMBaseEntity -{ - void Spawn( void ); - void Think( void ); - void Touch( CMBaseEntity *pOther ); - int ObjectCaps( void ) { return FCAP_DONT_SAVE; } -}; - -void CMShower::Spawn( void ) -{ - pev->velocity = RANDOM_FLOAT( 200, 300 ) * pev->angles; - pev->velocity.x += RANDOM_FLOAT(-100.f,100.f); - pev->velocity.y += RANDOM_FLOAT(-100.f,100.f); - if ( pev->velocity.z >= 0 ) - pev->velocity.z += 200; - else - pev->velocity.z -= 200; - pev->movetype = MOVETYPE_BOUNCE; - pev->gravity = 0.5; - pev->nextthink = gpGlobals->time + 0.1; - pev->solid = SOLID_NOT; - SET_MODEL( edict(), "models/grenade.mdl"); // Need a model, just use the grenade, we don't draw it anyway - UTIL_SetSize(pev, g_vecZero, g_vecZero ); - pev->effects |= EF_NODRAW; - pev->speed = RANDOM_FLOAT( 0.5, 1.5 ); - - pev->angles = g_vecZero; - pev->classname = MAKE_STRING( "_spark_shower" ); -} - - -void CMShower::Think( void ) -{ - UTIL_Sparks( pev->origin ); - - pev->speed -= 0.1; - if ( pev->speed > 0 ) - pev->nextthink = gpGlobals->time + 0.1; - else - UTIL_Remove( this->edict() ); - pev->flags &= ~FL_ONGROUND; -} - -void CMShower::Touch( CMBaseEntity *pOther ) -{ - if ( pev->flags & FL_ONGROUND ) - pev->velocity = pev->velocity * 0.1; - else - pev->velocity = pev->velocity * 0.6; - - if ( (pev->velocity.x*pev->velocity.x+pev->velocity.y*pev->velocity.y) < 10.0 ) - pev->speed = 0; -} - -class CMEnvExplosion : public CMBaseMonster -{ -public: - void Spawn( ); - void EXPORT Smoke ( void ); - void KeyValue( KeyValueData *pkvd ); - void DelayUse( void ); - void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ); - - int m_iMagnitude;// how large is the fireball? how much damage? - int m_spriteScale; // what's the exact fireball sprite scale? -}; - -void CMEnvExplosion::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "iMagnitude")) - { - m_iMagnitude = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBaseEntity::KeyValue( pkvd ); -} - -void CMEnvExplosion::Spawn( void ) -{ - pev->solid = SOLID_NOT; - pev->effects = EF_NODRAW; - - pev->movetype = MOVETYPE_NONE; - /* - if ( m_iMagnitude > 250 ) - { - m_iMagnitude = 250; - } - */ - - float flSpriteScale; - flSpriteScale = ( m_iMagnitude - 50) * 0.6; - - /* - if ( flSpriteScale > 50 ) - { - flSpriteScale = 50; - } - */ - if ( flSpriteScale < 10 ) - { - flSpriteScale = 10; - } - - m_spriteScale = (int)flSpriteScale; - pev->classname = MAKE_STRING( "_env_explosion" ); -} - -void CMEnvExplosion::DelayUse( void ) -{ - Use( NULL, NULL, USE_TOGGLE, 0 ); -} - -void CMEnvExplosion::Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ) -{ - TraceResult tr; - - pev->model = iStringNull;//invisible - pev->solid = SOLID_NOT;// intangible - - Vector vecSpot;// trace starts here! - - vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); - - UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); - - // Pull out of the wall a bit - if ( tr.flFraction != 1.0 ) - { - pev->origin = tr.vecEndPos + (tr.vecPlaneNormal * (m_iMagnitude - 24) * 0.6); - } - else - { - pev->origin = pev->origin; - } - - // draw decal - if (! ( pev->spawnflags & SF_ENVEXPLOSION_NODECAL)) - { - if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) - { - UTIL_DecalTrace( &tr, DECAL_SCORCH1 ); - } - else - { - UTIL_DecalTrace( &tr, DECAL_SCORCH2 ); - } - } - - // draw fireball - if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) ) - { - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_EXPLOSION); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( g_sModelIndexFireball ); - WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10 - WRITE_BYTE( 15 ); // framerate - WRITE_BYTE( TE_EXPLFLAG_NONE ); - MESSAGE_END(); - } - else - { - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_EXPLOSION); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( g_sModelIndexFireball ); - WRITE_BYTE( 0 ); // no sprite - WRITE_BYTE( 15 ); // framerate - WRITE_BYTE( TE_EXPLFLAG_NONE ); - MESSAGE_END(); - } - - // do damage - if ( !( pev->spawnflags & SF_ENVEXPLOSION_NODAMAGE ) ) - { - RadiusDamage ( pev, pev->owner == NULL ? pev : VARS( pev->owner ), m_iMagnitude, CLASS_NONE, DMG_BLAST ); - } - - SetThink( &CMEnvExplosion::Smoke ); - pev->nextthink = gpGlobals->time + 0.3; - - // draw sparks - if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSPARKS ) ) - { - int sparkCount = RANDOM_LONG(0,3); - - for ( int i = 0; i < sparkCount; i++ ) - { - CMBaseEntity *pSpark = CreateClassPtr((CMShower *)NULL); - if ( pSpark == NULL ) - { - ALERT( at_console, "Failed to spawn spark_shower!" ); - } - else - { - UTIL_SetOrigin( pSpark->pev, pev->origin ); - pSpark->pev->angles = tr.vecPlaneNormal; - pSpark->Spawn(); - } - // Create( "spark_shower", pev->origin, tr.vecPlaneNormal, NULL ); - } - } -} - -void CMEnvExplosion::Smoke( void ) -{ - if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSMOKE ) ) - { - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - MESSAGE_END(); - } - - if ( !(pev->spawnflags & SF_ENVEXPLOSION_REPEATABLE) ) - { - UTIL_Remove( this->edict() ); - } -} - - -// Stock to quickly create a one-time explosion -void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ) -{ - KeyValueData kvd; - char buf[128]; - - //CMBaseEntity *pExplosion = CMBaseEntity::Create( "env_explosion", center, angles, pOwner ); - CMBaseEntity *pExplosion = CreateClassPtr((CMEnvExplosion *)NULL); - if ( pExplosion == NULL ) - { - ALERT( at_console, "Failed to create env_explosion!" ); - } - else - { - sprintf( buf, "%3d", magnitude ); - kvd.szKeyName = "iMagnitude"; - kvd.szValue = buf; - pExplosion->KeyValue( &kvd ); - pExplosion->pev->owner = pOwner; - pExplosion->pev->spawnflags |= flags; - - UTIL_SetOrigin( pExplosion->pev, center ); - pExplosion->pev->angles = angles; - - // This is a temporary entity, filter out the flag - pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE; - - pExplosion->Spawn(); - if ( delay > 0.0f ) - { - //pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); // i don't trust you - pExplosion->SetThink( &CMEnvExplosion::DelayUse ); - pExplosion->pev->nextthink = gpGlobals->time + delay; - } - else - { - pExplosion->Use( NULL, NULL, USE_TOGGLE, 0 ); - } - } -} +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +/* + +===== explode.cpp ======================================================== + + Explosion-related code + +*/ +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "decals.h" +#include "explode.h" + +// Spark Shower +class CMShower : public CMBaseEntity +{ + void Spawn( void ); + void Think( void ); + void Touch( CMBaseEntity *pOther ); + int ObjectCaps( void ) { return FCAP_DONT_SAVE; } +}; + +void CMShower::Spawn( void ) +{ + pev->velocity = RANDOM_FLOAT( 200, 300 ) * pev->angles; + pev->velocity.x += RANDOM_FLOAT(-100.f,100.f); + pev->velocity.y += RANDOM_FLOAT(-100.f,100.f); + if ( pev->velocity.z >= 0 ) + pev->velocity.z += 200; + else + pev->velocity.z -= 200; + pev->movetype = MOVETYPE_BOUNCE; + pev->gravity = 0.5; + pev->nextthink = gpGlobals->time + 0.1; + pev->solid = SOLID_NOT; + SET_MODEL( edict(), "models/grenade.mdl"); // Need a model, just use the grenade, we don't draw it anyway + UTIL_SetSize(pev, g_vecZero, g_vecZero ); + pev->effects |= EF_NODRAW; + pev->speed = RANDOM_FLOAT( 0.5, 1.5 ); + + pev->angles = g_vecZero; + pev->classname = MAKE_STRING( "_spark_shower" ); +} + + +void CMShower::Think( void ) +{ + UTIL_Sparks( pev->origin ); + + pev->speed -= 0.1; + if ( pev->speed > 0 ) + pev->nextthink = gpGlobals->time + 0.1; + else + UTIL_Remove( this->edict() ); + pev->flags &= ~FL_ONGROUND; +} + +void CMShower::Touch( CMBaseEntity *pOther ) +{ + if ( pev->flags & FL_ONGROUND ) + pev->velocity = pev->velocity * 0.1; + else + pev->velocity = pev->velocity * 0.6; + + if ( (pev->velocity.x*pev->velocity.x+pev->velocity.y*pev->velocity.y) < 10.0 ) + pev->speed = 0; +} + +class CMEnvExplosion : public CMBaseMonster +{ +public: + void Spawn( ); + void EXPORT Smoke ( void ); + void KeyValue( KeyValueData *pkvd ); + void DelayUse( void ); + void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ); + + int m_iMagnitude;// how large is the fireball? how much damage? + int m_spriteScale; // what's the exact fireball sprite scale? +}; + +void CMEnvExplosion::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "iMagnitude")) + { + m_iMagnitude = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseEntity::KeyValue( pkvd ); +} + +void CMEnvExplosion::Spawn( void ) +{ + pev->solid = SOLID_NOT; + pev->effects = EF_NODRAW; + + pev->movetype = MOVETYPE_NONE; + /* + if ( m_iMagnitude > 250 ) + { + m_iMagnitude = 250; + } + */ + + float flSpriteScale; + flSpriteScale = ( m_iMagnitude - 50) * 0.6; + + /* + if ( flSpriteScale > 50 ) + { + flSpriteScale = 50; + } + */ + if ( flSpriteScale < 10 ) + { + flSpriteScale = 10; + } + + m_spriteScale = (int)flSpriteScale; + pev->classname = MAKE_STRING( "_env_explosion" ); +} + +void CMEnvExplosion::DelayUse( void ) +{ + Use( NULL, NULL, USE_TOGGLE, 0 ); +} + +void CMEnvExplosion::Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + TraceResult tr; + + pev->model = iStringNull;//invisible + pev->solid = SOLID_NOT;// intangible + + Vector vecSpot;// trace starts here! + + vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); + + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); + + // Pull out of the wall a bit + if ( tr.flFraction != 1.0 ) + { + pev->origin = tr.vecEndPos + (tr.vecPlaneNormal * (m_iMagnitude - 24) * 0.6); + } + else + { + pev->origin = pev->origin; + } + + // draw decal + if (! ( pev->spawnflags & SF_ENVEXPLOSION_NODECAL)) + { + if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( &tr, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( &tr, DECAL_SCORCH2 ); + } + } + + // draw fireball + if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOFIREBALL ) ) + { + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_EXPLOSION); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( g_sModelIndexFireball ); + WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10 + WRITE_BYTE( 15 ); // framerate + WRITE_BYTE( TE_EXPLFLAG_NONE ); + MESSAGE_END(); + } + else + { + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_EXPLOSION); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( g_sModelIndexFireball ); + WRITE_BYTE( 0 ); // no sprite + WRITE_BYTE( 15 ); // framerate + WRITE_BYTE( TE_EXPLFLAG_NONE ); + MESSAGE_END(); + } + + // do damage + if ( !( pev->spawnflags & SF_ENVEXPLOSION_NODAMAGE ) ) + { + RadiusDamage ( pev, pev->owner == NULL ? pev : VARS( pev->owner ), m_iMagnitude, CLASS_NONE, DMG_BLAST ); + } + + SetThink( &CMEnvExplosion::Smoke ); + pev->nextthink = gpGlobals->time + 0.3; + + // draw sparks + if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSPARKS ) ) + { + int sparkCount = RANDOM_LONG(0,3); + + for ( int i = 0; i < sparkCount; i++ ) + { + CMBaseEntity *pSpark = CreateClassPtr((CMShower *)NULL); + if ( pSpark == NULL ) + { + ALERT( at_console, "Failed to spawn spark_shower!" ); + } + else + { + UTIL_SetOrigin( pSpark->pev, pev->origin ); + pSpark->pev->angles = tr.vecPlaneNormal; + pSpark->Spawn(); + } + // Create( "spark_shower", pev->origin, tr.vecPlaneNormal, NULL ); + } + } +} + +void CMEnvExplosion::Smoke( void ) +{ + if ( !( pev->spawnflags & SF_ENVEXPLOSION_NOSMOKE ) ) + { + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( (BYTE)m_spriteScale ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + MESSAGE_END(); + } + + if ( !(pev->spawnflags & SF_ENVEXPLOSION_REPEATABLE) ) + { + UTIL_Remove( this->edict() ); + } +} + + +// Stock to quickly create a one-time explosion +void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ) +{ + KeyValueData kvd; + char buf[128]; + + //CMBaseEntity *pExplosion = CMBaseEntity::Create( "env_explosion", center, angles, pOwner ); + CMBaseEntity *pExplosion = CreateClassPtr((CMEnvExplosion *)NULL); + if ( pExplosion == NULL ) + { + ALERT( at_console, "Failed to create env_explosion!" ); + } + else + { + sprintf( buf, "%3d", magnitude ); + kvd.szKeyName = "iMagnitude"; + kvd.szValue = buf; + pExplosion->KeyValue( &kvd ); + pExplosion->pev->owner = pOwner; + pExplosion->pev->spawnflags |= flags; + + UTIL_SetOrigin( pExplosion->pev, center ); + pExplosion->pev->angles = angles; + + // This is a temporary entity, filter out the flag + pExplosion->pev->spawnflags &= ~SF_ENVEXPLOSION_REPEATABLE; + + pExplosion->Spawn(); + if ( delay > 0.0f ) + { + //pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); // i don't trust you + pExplosion->SetThink( &CMEnvExplosion::DelayUse ); + pExplosion->pev->nextthink = gpGlobals->time + delay; + } + else + { + pExplosion->Use( NULL, NULL, USE_TOGGLE, 0 ); + } + } +} diff --git a/src/dlls/explode.h b/src/dlls/explode.h index 89ec96b..6b34159 100644 --- a/src/dlls/explode.h +++ b/src/dlls/explode.h @@ -1,32 +1,32 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef EXPLODE_H -#define EXPLODE_H - - -#define SF_ENVEXPLOSION_NODAMAGE ( 1 << 0 ) // when set, ENV_EXPLOSION will not actually inflict damage -#define SF_ENVEXPLOSION_REPEATABLE ( 1 << 1 ) // can this entity be refired? -#define SF_ENVEXPLOSION_NOFIREBALL ( 1 << 2 ) // don't draw the fireball -#define SF_ENVEXPLOSION_NOSMOKE ( 1 << 3 ) // don't draw the smoke -#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark -#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark - -extern DLL_GLOBAL short g_sModelIndexFireball; -extern DLL_GLOBAL short g_sModelIndexSmoke; -extern DLL_GLOBAL short g_sModelIndexTinySpit; - -extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ); - -#endif //EXPLODE_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef EXPLODE_H +#define EXPLODE_H + + +#define SF_ENVEXPLOSION_NODAMAGE ( 1 << 0 ) // when set, ENV_EXPLOSION will not actually inflict damage +#define SF_ENVEXPLOSION_REPEATABLE ( 1 << 1 ) // can this entity be refired? +#define SF_ENVEXPLOSION_NOFIREBALL ( 1 << 2 ) // don't draw the fireball +#define SF_ENVEXPLOSION_NOSMOKE ( 1 << 3 ) // don't draw the smoke +#define SF_ENVEXPLOSION_NODECAL ( 1 << 4 ) // don't make a scorch mark +#define SF_ENVEXPLOSION_NOSPARKS ( 1 << 5 ) // don't make a scorch mark + +extern DLL_GLOBAL short g_sModelIndexFireball; +extern DLL_GLOBAL short g_sModelIndexSmoke; +extern DLL_GLOBAL short g_sModelIndexTinySpit; + +extern void ExplosionCreate( const Vector ¢er, const Vector &angles, edict_t *pOwner, int magnitude, int flags, float delay ); + +#endif //EXPLODE_H diff --git a/src/dlls/extdll.h b/src/dlls/extdll.h index ceaefa5..a6d3ca2 100644 --- a/src/dlls/extdll.h +++ b/src/dlls/extdll.h @@ -1,87 +1,88 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef EXTDLL_H -#define EXTDLL_H - - -// -// Global header file for extension DLLs -// - -// Allow "DEBUG" in addition to default "_DEBUG" -#ifdef _DEBUG -#define DEBUG 1 -#endif - -// Silence certain warnings -#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 - -// Prevent tons of unused windows definitions -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOWINRES -#define NOSERVICE -#define NOMCX -#define NOIME -#include "windows.h" -#else // _WIN32 -#define FALSE 0 -#define TRUE (!FALSE) -typedef unsigned long ULONG; -typedef unsigned char BYTE; -typedef int BOOL; -#define MAX_PATH PATH_MAX -#include -#include -#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) -#endif //_WIN32 - -// Misc C-runtime library headers -#include "stdio.h" -#include "stdlib.h" -#include "math.h" - -// min/max is not on math.h library -// move outside win32 scope so it can compile on both platforms -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#define max(a,b) (((a) > (b)) ? (a) : (b)) - -// Header file containing definition of globalvars_t and entvars_t -typedef int func_t; // -typedef int string_t; // from engine's pr_comp.h; -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 - -// Shared engine/DLL constants -#include "const.h" -#include "progdefs.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" - -#endif //EXTDLL_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef EXTDLL_H +#define EXTDLL_H + + +// +// Global header file for extension DLLs +// + +// Allow "DEBUG" in addition to default "_DEBUG" +#ifdef _DEBUG +#define DEBUG 1 +#endif + +// Silence certain warnings +#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 +#pragma warning(disable : 4390) // empty controlled statement (seems to work fine? monster_api.cpp[101/115]) + +// Prevent tons of unused windows definitions +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOWINRES +#define NOSERVICE +#define NOMCX +#define NOIME +#include "windows.h" +#else // _WIN32 +#define FALSE 0 +#define TRUE (!FALSE) +typedef unsigned long ULONG; +typedef unsigned char BYTE; +typedef int BOOL; +#define MAX_PATH PATH_MAX +#include +#include +#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d) +#endif //_WIN32 + +// Misc C-runtime library headers +#include "stdio.h" +#include "stdlib.h" +#include "math.h" + +// min/max is not on math.h library +// move outside win32 scope so it can compile on both platforms +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +// Header file containing definition of globalvars_t and entvars_t +typedef int func_t; // +typedef int string_t; // from engine's pr_comp.h; +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 + +// Shared engine/DLL constants +#include "const.h" +#include "progdefs.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" + +#endif //EXTDLL_H diff --git a/src/dlls/flyingmonster.cpp b/src/dlls/flyingmonster.cpp index e00208e..41fa969 100644 --- a/src/dlls/flyingmonster.cpp +++ b/src/dlls/flyingmonster.cpp @@ -1,281 +1,281 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" - -#define FLYING_AE_FLAP (8) -#define FLYING_AE_FLAPSOUND (9) - - -extern DLL_GLOBAL edict_t *g_pBodyQueueHead; - -int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) -{ - // UNDONE: need to check more than the endpoint - if (FBitSet(pev->flags, FL_SWIM) && (UTIL_PointContents(vecEnd) != CONTENTS_WATER)) - { - // ALERT(at_aiconsole, "can't swim out of water\n"); - return FALSE; - } - - TraceResult tr; - - UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr ); - - // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); - // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); - - if (pflDist) - { - *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. - } - - // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); - if (tr.fStartSolid || tr.flFraction < 1.0) - { - if ( pTarget && (pTarget == gpGlobals->trace_ent) ) - return LOCALMOVE_VALID; - return LOCALMOVE_INVALID; - } - - return LOCALMOVE_VALID; -} - - -BOOL CMFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ) -{ - return CMBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex ); -} - - -Activity CMFlyingMonster :: GetStoppedActivity( void ) -{ - if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else - return ACT_IDLE; - - return ACT_HOVER; -} - - -void CMFlyingMonster :: Stop( void ) -{ - Activity stopped = GetStoppedActivity(); - if ( m_IdealActivity != stopped ) - { - m_flightSpeed = 0; - m_IdealActivity = stopped; - } - pev->angles.z = 0; - pev->angles.x = 0; - m_vecTravel = g_vecZero; -} - - -float CMFlyingMonster :: ChangeYaw( int speed ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - { - float diff = FlYawDiff(); - float target = 0; - - if ( m_IdealActivity != GetStoppedActivity() ) - { - if ( diff < -20 ) - target = 90; - else if ( diff > 20 ) - target = -90; - } - pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime ); - } - return CMBaseMonster::ChangeYaw( speed ); -} - - -void CMFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->movetype = MOVETYPE_STEP; - ClearBits( pev->flags, FL_ONGROUND ); - pev->angles.z = 0; - pev->angles.x = 0; - CMBaseMonster::Killed( pevAttacker, iGib ); -} - - -void CMFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case FLYING_AE_FLAP: - m_flightSpeed = 400; - break; - - case FLYING_AE_FLAPSOUND: - if ( m_pFlapSound ) - EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM ); - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - - -void CMFlyingMonster :: Move( float flInterval ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - m_flGroundSpeed = m_flightSpeed; - CMBaseMonster::Move( flInterval ); -} - - -BOOL CMFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist ) -{ - // Get true 3D distance to the goal so we actually reach the correct height - if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL ) - flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); - - if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) ) - return TRUE; - - return FALSE; -} - - -void CMFlyingMonster::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) -{ - if ( pev->movetype == MOVETYPE_FLY ) - { - if ( gpGlobals->time - m_stopTime > 1.0 ) - { - if ( m_IdealActivity != m_movementActivity ) - { - m_IdealActivity = m_movementActivity; - m_flGroundSpeed = m_flightSpeed = 200; - } - } - Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval)); - - if ( m_IdealActivity != m_movementActivity ) - { - m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime ); - if ( m_flightSpeed < 100 ) - m_stopTime = gpGlobals->time; - } - else - m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime ); - - if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) ) - { - m_vecTravel = (vecMove - pev->origin); - m_vecTravel = m_vecTravel.Normalize(); - UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE); - } - else - { - m_IdealActivity = GetStoppedActivity(); - m_stopTime = gpGlobals->time; - m_vecTravel = g_vecZero; - } - } - else - CMBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval ); -} - - -float CMFlyingMonster::CeilingZ( const Vector &position ) -{ - TraceResult tr; - - Vector minUp = position; - Vector maxUp = position; - maxUp.z += 4096.0; - - UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr); - if (tr.flFraction != 1.0) - maxUp.z = tr.vecEndPos.z; - - if ((pev->flags) & FL_SWIM) - { - return UTIL_WaterLevel( position, minUp.z, maxUp.z ); - } - return maxUp.z; -} - -BOOL CMFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction) -{ - int conPosition = UTIL_PointContents(position); - if ( (((pev->flags) & FL_SWIM) == FL_SWIM) ^ (conPosition == CONTENTS_WATER)) - { - // SWIMING & !WATER - // or FLYING & WATER - // - *pFraction = 0.0; - return TRUE; // We hit a water boundary because we are where we don't belong. - } - int conProbe = UTIL_PointContents(probe); - if (conProbe == conPosition) - { - // The probe is either entirely inside the water (for fish) or entirely - // outside the water (for birds). - // - *pFraction = 1.0; - return FALSE; - } - - Vector ProbeUnit = (probe-position).Normalize(); - float ProbeLength = (probe-position).Length(); - float maxProbeLength = ProbeLength; - float minProbeLength = 0; - - float diff = maxProbeLength - minProbeLength; - while (diff > 1.0) - { - float midProbeLength = minProbeLength + diff/2.0; - Vector midProbeVec = midProbeLength * ProbeUnit; - if (UTIL_PointContents(position+midProbeVec) == conPosition) - { - minProbeLength = midProbeLength; - } - else - { - maxProbeLength = midProbeLength; - } - diff = maxProbeLength - minProbeLength; - } - *pFraction = minProbeLength/ProbeLength; - - return TRUE; -} - -float CMFlyingMonster::FloorZ( const Vector &position ) -{ - TraceResult tr; - - Vector down = position; - down.z -= 2048; - - UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr ); - - if ( tr.flFraction != 1.0 ) - return tr.vecEndPos.z; - - return down.z; -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" + +#define FLYING_AE_FLAP (8) +#define FLYING_AE_FLAPSOUND (9) + + +extern DLL_GLOBAL edict_t *g_pBodyQueueHead; + +int CMFlyingMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) +{ + // UNDONE: need to check more than the endpoint + if (FBitSet(pev->flags, FL_SWIM) && (UTIL_PointContents(vecEnd) != CONTENTS_WATER)) + { + // ALERT(at_aiconsole, "can't swim out of water\n"); + return FALSE; + } + + TraceResult tr; + + UTIL_TraceHull( vecStart + Vector( 0, 0, 32 ), vecEnd + Vector( 0, 0, 32 ), dont_ignore_monsters, large_hull, edict(), &tr ); + + // ALERT( at_console, "%.0f %.0f %.0f : ", vecStart.x, vecStart.y, vecStart.z ); + // ALERT( at_console, "%.0f %.0f %.0f\n", vecEnd.x, vecEnd.y, vecEnd.z ); + + if (pflDist) + { + *pflDist = ( (tr.vecEndPos - Vector( 0, 0, 32 )) - vecStart ).Length();// get the distance. + } + + // ALERT( at_console, "check %d %d %f\n", tr.fStartSolid, tr.fAllSolid, tr.flFraction ); + if (tr.fStartSolid || tr.flFraction < 1.0) + { + if ( pTarget && (pTarget == gpGlobals->trace_ent) ) + return LOCALMOVE_VALID; + return LOCALMOVE_INVALID; + } + + return LOCALMOVE_VALID; +} + + +BOOL CMFlyingMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ) +{ + return CMBaseMonster::FTriangulate( vecStart, vecEnd, flDist, pTargetEnt, pApex ); +} + + +Activity CMFlyingMonster :: GetStoppedActivity( void ) +{ + if ( pev->movetype != MOVETYPE_FLY ) // UNDONE: Ground idle here, IDLE may be something else + return ACT_IDLE; + + return ACT_HOVER; +} + + +void CMFlyingMonster :: Stop( void ) +{ + Activity stopped = GetStoppedActivity(); + if ( m_IdealActivity != stopped ) + { + m_flightSpeed = 0; + m_IdealActivity = stopped; + } + pev->angles.z = 0; + pev->angles.x = 0; + m_vecTravel = g_vecZero; +} + + +float CMFlyingMonster :: ChangeYaw( int speed ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + { + float diff = FlYawDiff(); + float target = 0; + + if ( m_IdealActivity != GetStoppedActivity() ) + { + if ( diff < -20 ) + target = 90; + else if ( diff > 20 ) + target = -90; + } + pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime ); + } + return CMBaseMonster::ChangeYaw( speed ); +} + + +void CMFlyingMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->movetype = MOVETYPE_STEP; + ClearBits( pev->flags, FL_ONGROUND ); + pev->angles.z = 0; + pev->angles.x = 0; + CMBaseMonster::Killed( pevAttacker, iGib ); +} + + +void CMFlyingMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case FLYING_AE_FLAP: + m_flightSpeed = 400; + break; + + case FLYING_AE_FLAPSOUND: + if ( m_pFlapSound ) + EMIT_SOUND( edict(), CHAN_BODY, m_pFlapSound, 1, ATTN_NORM ); + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + + +void CMFlyingMonster :: Move( float flInterval ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + m_flGroundSpeed = m_flightSpeed; + CMBaseMonster::Move( flInterval ); +} + + +BOOL CMFlyingMonster:: ShouldAdvanceRoute( float flWaypointDist ) +{ + // Get true 3D distance to the goal so we actually reach the correct height + if ( m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL ) + flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length(); + + if ( flWaypointDist <= 64 + (m_flGroundSpeed * gpGlobals->frametime) ) + return TRUE; + + return FALSE; +} + + +void CMFlyingMonster::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) +{ + if ( pev->movetype == MOVETYPE_FLY ) + { + if ( gpGlobals->time - m_stopTime > 1.0 ) + { + if ( m_IdealActivity != m_movementActivity ) + { + m_IdealActivity = m_movementActivity; + m_flGroundSpeed = m_flightSpeed = 200; + } + } + Vector vecMove = pev->origin + (( vecDir + (m_vecTravel * m_momentum) ).Normalize() * (m_flGroundSpeed * flInterval)); + + if ( m_IdealActivity != m_movementActivity ) + { + m_flightSpeed = UTIL_Approach( 100, m_flightSpeed, 75 * gpGlobals->frametime ); + if ( m_flightSpeed < 100 ) + m_stopTime = gpGlobals->time; + } + else + m_flightSpeed = UTIL_Approach( 20, m_flightSpeed, 300 * gpGlobals->frametime ); + + if ( CheckLocalMove ( pev->origin, vecMove, pTargetEnt, NULL ) ) + { + m_vecTravel = (vecMove - pev->origin); + m_vecTravel = m_vecTravel.Normalize(); + UTIL_MoveToOrigin(ENT(pev), vecMove, (m_flGroundSpeed * flInterval), MOVE_STRAFE); + } + else + { + m_IdealActivity = GetStoppedActivity(); + m_stopTime = gpGlobals->time; + m_vecTravel = g_vecZero; + } + } + else + CMBaseMonster::MoveExecute( pTargetEnt, vecDir, flInterval ); +} + + +float CMFlyingMonster::CeilingZ( const Vector &position ) +{ + TraceResult tr; + + Vector minUp = position; + Vector maxUp = position; + maxUp.z += 4096.0; + + UTIL_TraceLine(position, maxUp, ignore_monsters, NULL, &tr); + if (tr.flFraction != 1.0) + maxUp.z = tr.vecEndPos.z; + + if ((pev->flags) & FL_SWIM) + { + return UTIL_WaterLevel( position, minUp.z, maxUp.z ); + } + return maxUp.z; +} + +BOOL CMFlyingMonster::ProbeZ( const Vector &position, const Vector &probe, float *pFraction) +{ + int conPosition = UTIL_PointContents(position); + if ( (((pev->flags) & FL_SWIM) == FL_SWIM) ^ (conPosition == CONTENTS_WATER)) + { + // SWIMING & !WATER + // or FLYING & WATER + // + *pFraction = 0.0; + return TRUE; // We hit a water boundary because we are where we don't belong. + } + int conProbe = UTIL_PointContents(probe); + if (conProbe == conPosition) + { + // The probe is either entirely inside the water (for fish) or entirely + // outside the water (for birds). + // + *pFraction = 1.0; + return FALSE; + } + + Vector ProbeUnit = (probe-position).Normalize(); + float ProbeLength = (probe-position).Length(); + float maxProbeLength = ProbeLength; + float minProbeLength = 0; + + float diff = maxProbeLength - minProbeLength; + while (diff > 1.0) + { + float midProbeLength = minProbeLength + diff/2.0; + Vector midProbeVec = midProbeLength * ProbeUnit; + if (UTIL_PointContents(position+midProbeVec) == conPosition) + { + minProbeLength = midProbeLength; + } + else + { + maxProbeLength = midProbeLength; + } + diff = maxProbeLength - minProbeLength; + } + *pFraction = minProbeLength/ProbeLength; + + return TRUE; +} + +float CMFlyingMonster::FloorZ( const Vector &position ) +{ + TraceResult tr; + + Vector down = position; + down.z -= 2048; + + UTIL_TraceLine( position, down, ignore_monsters, NULL, &tr ); + + if ( tr.flFraction != 1.0 ) + return tr.vecEndPos.z; + + return down.z; +} + diff --git a/src/dlls/func_break.h b/src/dlls/func_break.h index 4694991..225e39f 100644 --- a/src/dlls/func_break.h +++ b/src/dlls/func_break.h @@ -1,70 +1,70 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef FUNC_BREAK_H -#define FUNC_BREAK_H - -typedef enum { expRandom, expDirected} Explosions; -typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials; - -#define NUM_SHARDS 6 // this many shards spawned when breakable objects break; - -class CMBreakable : public CMBaseDelay -{ -public: - // basic functions - void Spawn( void ); - void Precache( void ); - void KeyValue( KeyValueData* pkvd); - void EXPORT BreakTouch( CMBaseEntity *pOther ); - void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ); - void DamageSound( void ); - - // breakables use an overridden takedamage - virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); - // To spark when hit - void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); - - BOOL IsBreakable( void ); - BOOL SparkWhenHit( void ); - - int DamageDecal( int bitsDamageType ); - - void EXPORT Die( void ); - virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } - - inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; } - inline int ExplosionMagnitude( void ) { return pev->impulse; } - inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; } - - static void MaterialSoundPrecache( Materials precacheMaterial ); - static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ); - static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount ); - - static const char *pSoundsWood[]; - static const char *pSoundsFlesh[]; - static const char *pSoundsGlass[]; - static const char *pSoundsMetal[]; - static const char *pSoundsConcrete[]; - static const char *pSpawnObjects[]; - - Materials m_Material; - Explosions m_Explosion; - int m_idShard; - float m_angle; - int m_iszGibModel; - int m_iszSpawnObject; -}; - -#endif // FUNC_BREAK_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef FUNC_BREAK_H +#define FUNC_BREAK_H + +typedef enum { expRandom, expDirected} Explosions; +typedef enum { matGlass = 0, matWood, matMetal, matFlesh, matCinderBlock, matCeilingTile, matComputer, matUnbreakableGlass, matRocks, matNone, matLastMaterial } Materials; + +#define NUM_SHARDS 6 // this many shards spawned when breakable objects break; + +class CMBreakable : public CMBaseDelay +{ +public: + // basic functions + void Spawn( void ); + void Precache( void ); + void KeyValue( KeyValueData* pkvd); + void EXPORT BreakTouch( CMBaseEntity *pOther ); + void Use( CMBaseEntity *pActivator, CMBaseEntity *pCaller, USE_TYPE useType, float value ); + void DamageSound( void ); + + // breakables use an overridden takedamage + virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType ); + // To spark when hit + void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); + + BOOL IsBreakable( void ); + BOOL SparkWhenHit( void ); + + int DamageDecal( int bitsDamageType ); + + void EXPORT Die( void ); + virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } + + inline BOOL Explodable( void ) { return ExplosionMagnitude() > 0; } + inline int ExplosionMagnitude( void ) { return pev->impulse; } + inline void ExplosionSetMagnitude( int magnitude ) { pev->impulse = magnitude; } + + static void MaterialSoundPrecache( Materials precacheMaterial ); + static void MaterialSoundRandom( edict_t *pEdict, Materials soundMaterial, float volume ); + static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount ); + + static const char *pSoundsWood[]; + static const char *pSoundsFlesh[]; + static const char *pSoundsGlass[]; + static const char *pSoundsMetal[]; + static const char *pSoundsConcrete[]; + static const char *pSpawnObjects[]; + + Materials m_Material; + Explosions m_Explosion; + int m_idShard; + float m_angle; + int m_iszGibModel; + int m_iszSpawnObject; +}; + +#endif // FUNC_BREAK_H diff --git a/src/dlls/gargantua.cpp b/src/dlls/gargantua.cpp old mode 100755 new mode 100644 index 8609b17..73452cc --- a/src/dlls/gargantua.cpp +++ b/src/dlls/gargantua.cpp @@ -1,1842 +1,1842 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ - -//========================================================= -// Gargantua -//========================================================= -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "skill.h" -#include "nodes.h" -#include "monsters.h" -#include "schedule.h" -#include "customentity.h" -#include "weapons.h" -#include "effects.h" -#include "decals.h" -#include "explode.h" -#include "func_break.h" - -//========================================================= -// Gargantua Monster -//========================================================= -const float GARG_ATTACKDIST = 80.0; - -// Garg animation events -#define GARG_AE_SLASH_LEFT 1 -//#define GARG_AE_BEAM_ATTACK_RIGHT 2 // No longer used -#define GARG_AE_LEFT_FOOT 3 -#define GARG_AE_RIGHT_FOOT 4 -#define GARG_AE_STOMP 5 -#define GARG_AE_BREATHE 6 - - -// Gargantua is immune to any damage but this -#define GARG_DAMAGE (DMG_ENERGYBEAM|DMG_CRUSH|DMG_MORTAR|DMG_BLAST) -#define GARG_EYE_SPRITE_NAME "sprites/gargeye1.spr" -#define GARG_BEAM_SPRITE_NAME "sprites/xbeam3.spr" -#define GARG_BEAM_SPRITE2 "sprites/xbeam3.spr" -#define GARG_STOMP_SPRITE_NAME "sprites/gargeye1.spr" -#define GARG_STOMP_BUZZ_SOUND "weapons/mine_charge.wav" -#define GARG_FLAME_LENGTH 330 -#define GARG_GIB_MODEL "models/metalplategibs.mdl" - -#define ATTN_GARG (ATTN_NORM) - -#define STOMP_SPRITE_COUNT 10 - -int gStompSprite = 0, gGargGibModel = 0; -void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ); - -class CSmoker : public CMBaseEntity -{ -public: - void Spawn( void ); - void Think( void ); -}; - -// Spiral Effect -class CSpiral : public CMBaseEntity -{ -public: - void Spawn( void ); - void Think( void ); - int ObjectCaps( void ) { return FCAP_DONT_SAVE; } - static CSpiral *Create( const Vector &origin, float height, float radius, float duration ); -}; - -class CStomp : public CMBaseEntity -{ -public: - void Spawn( void ); - void Think( void ); - static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed, float damage ); - -private: -// UNDONE: re-use this sprite list instead of creating new ones all the time -// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ]; -}; - -CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed, float damage ) -{ - CStomp *pStomp = CreateClassPtr( (CStomp *)NULL ); - - pStomp->pev->origin = origin; - Vector dir = (end - origin); - pStomp->pev->scale = dir.Length(); - pStomp->pev->movedir = dir.Normalize(); - pStomp->pev->speed = speed; - pStomp->pev->dmg = damage; - pStomp->Spawn(); - - return pStomp; -} - -void CStomp::Spawn( void ) -{ - pev->nextthink = gpGlobals->time; - pev->classname = MAKE_STRING("garg_stomp"); - pev->dmgtime = gpGlobals->time; - - pev->framerate = 30; - pev->model = MAKE_STRING(GARG_STOMP_SPRITE_NAME); - pev->rendermode = kRenderTransTexture; - pev->renderamt = 0; - EMIT_SOUND_DYN( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND, 1, ATTN_NORM, 0, PITCH_NORM * 0.55); -} - - -#define STOMP_INTERVAL 0.025 - -void CStomp::Think( void ) -{ - TraceResult tr; - - pev->nextthink = gpGlobals->time + 0.1; - - // Do damage for this frame - Vector vecStart = pev->origin; - vecStart.z += 30; - Vector vecEnd = vecStart + (pev->movedir * pev->speed * gpGlobals->frametime); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit && tr.pHit != pev->owner ) - { - CMBaseEntity *pEntity = CMBaseEntity::Instance( tr.pHit ); - entvars_t *pevOwner = pev; - if ( pev->owner ) - pevOwner = VARS(pev->owner); - - if ( pEntity ) - pEntity->TakeDamage( pev, pevOwner, pev->dmg, DMG_SONIC ); - } - - // Accelerate the effect - pev->speed = pev->speed + (gpGlobals->frametime) * pev->framerate; - pev->framerate = pev->framerate + (gpGlobals->frametime) * 1500; - - // Move and spawn trails - while ( gpGlobals->time - pev->dmgtime > STOMP_INTERVAL ) - { - pev->origin = pev->origin + pev->movedir * pev->speed * STOMP_INTERVAL; - for ( int i = 0; i < 2; i++ ) - { - CMSprite *pSprite = CMSprite::SpriteCreate( GARG_STOMP_SPRITE_NAME, pev->origin, TRUE ); - if ( pSprite ) - { - UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,500), ignore_monsters, edict(), &tr ); - pSprite->pev->origin = tr.vecEndPos; - pSprite->pev->velocity = Vector(RANDOM_FLOAT(-200,200),RANDOM_FLOAT(-200,200),175); - // pSprite->AnimateAndDie( RANDOM_FLOAT( 8.0, 12.0 ) ); - pSprite->pev->nextthink = gpGlobals->time + 0.3; - pSprite->SetThink( &CMSprite::SUB_Remove ); - pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast ); - } - } - pev->dmgtime += STOMP_INTERVAL; - // Scale has the "life" of this effect - pev->scale -= STOMP_INTERVAL * pev->speed; - if ( pev->scale <= 0 ) - { - // Life has run out - UTIL_Remove(this->edict()); - STOP_SOUND( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND ); - } - - } -} - - -void StreakSplash( const Vector &origin, const Vector &direction, int color, int count, int speed, int velocityRange ) -{ - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); - WRITE_BYTE( TE_STREAK_SPLASH ); - WRITE_COORD( origin.x ); // origin - WRITE_COORD( origin.y ); - WRITE_COORD( origin.z ); - WRITE_COORD( direction.x ); // direction - WRITE_COORD( direction.y ); - WRITE_COORD( direction.z ); - WRITE_BYTE( color ); // Streak color 6 - WRITE_SHORT( count ); // count - WRITE_SHORT( speed ); - WRITE_SHORT( velocityRange ); // Random velocity modifier - MESSAGE_END(); -} - -const char *CMGargantua::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CMGargantua::pBeamAttackSounds[] = -{ - "garg/gar_flameoff1.wav", - "garg/gar_flameon1.wav", - "garg/gar_flamerun1.wav", -}; - - -const char *CMGargantua::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CMGargantua::pRicSounds[] = -{ -#if 0 - "weapons/ric1.wav", - "weapons/ric2.wav", - "weapons/ric3.wav", - "weapons/ric4.wav", - "weapons/ric5.wav", -#else - "debris/metal4.wav", - "debris/metal6.wav", - "weapons/ric4.wav", - "weapons/ric5.wav", -#endif -}; - -const char *CMGargantua::pFootSounds[] = -{ - "garg/gar_step1.wav", - "garg/gar_step2.wav", -}; - - -const char *CMGargantua::pIdleSounds[] = -{ - "garg/gar_idle1.wav", - "garg/gar_idle2.wav", - "garg/gar_idle3.wav", - "garg/gar_idle4.wav", - "garg/gar_idle5.wav", -}; - - -const char *CMGargantua::pAttackSounds[] = -{ - "garg/gar_attack1.wav", - "garg/gar_attack2.wav", - "garg/gar_attack3.wav", -}; - -const char *CMGargantua::pAlertSounds[] = -{ - "garg/gar_alert1.wav", - "garg/gar_alert2.wav", - "garg/gar_alert3.wav", -}; - -const char *CMGargantua::pPainSounds[] = -{ - "garg/gar_pain1.wav", - "garg/gar_pain2.wav", - "garg/gar_pain3.wav", -}; - -const char *CMGargantua::pStompSounds[] = -{ - "garg/gar_stomp1.wav", -}; - -const char *CMGargantua::pBreatheSounds[] = -{ - "garg/gar_breathe1.wav", - "garg/gar_breathe2.wav", - "garg/gar_breathe3.wav", -}; -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -#if 0 -enum -{ - SCHED_ = LAST_COMMON_SCHEDULE + 1, -}; -#endif - -enum -{ - TASK_SOUND_ATTACK = LAST_COMMON_TASK + 1, - TASK_FLAME_SWEEP, -}; - -Task_t tlGargFlame[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SOUND_ATTACK, (float)0 }, - // { TASK_PLAY_SEQUENCE, (float)ACT_SIGNAL1 }, - { TASK_SET_ACTIVITY, (float)ACT_MELEE_ATTACK2 }, - { TASK_FLAME_SWEEP, (float)4.5 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slGargFlame[] = -{ - { - tlGargFlame, - ARRAYSIZE ( tlGargFlame ), - 0, - 0, - "GargFlame" - }, -}; - - -// primary melee attack -Task_t tlGargSwipe[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_MELEE_ATTACK1, (float)0 }, -}; - -Schedule_t slGargSwipe[] = -{ - { - tlGargSwipe, - ARRAYSIZE ( tlGargSwipe ), - bits_COND_CAN_MELEE_ATTACK2, - 0, - "GargSwipe" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMGargantua ) -{ - slGargFlame, - slGargSwipe, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMGargantua, CMBaseMonster ); - - -void CMGargantua::EyeOn( int level ) -{ - m_eyeBrightness = level; -} - - -void CMGargantua::EyeOff( void ) -{ - m_eyeBrightness = 0; -} - - -void CMGargantua::EyeUpdate( void ) -{ - if ( m_pEyeGlow ) - { - m_pEyeGlow->pev->renderamt = UTIL_Approach( m_eyeBrightness, m_pEyeGlow->pev->renderamt, 26 ); - if ( m_pEyeGlow->pev->renderamt == 0 ) - m_pEyeGlow->pev->effects |= EF_NODRAW; - else - m_pEyeGlow->pev->effects &= ~EF_NODRAW; - UTIL_SetOrigin( m_pEyeGlow->pev, pev->origin ); - } -} - - -void CMGargantua::StompAttack( void ) -{ - TraceResult trace; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward; - Vector vecAim = ShootAtEnemy( vecStart ); - Vector vecEnd = (vecAim * 1024) + vecStart; - - UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); - CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.gargantuaDmgStomp ); - UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 ); - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - - UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace ); - if ( trace.flFraction < 1.0 ) - UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 ); -} - - -void CMGargantua :: FlameCreate( void ) -{ - int i; - Vector posGun, angleGun; - TraceResult trace; - - UTIL_MakeVectors( pev->angles ); - - for ( i = 0; i < 4; i++ ) - { - if ( i < 2 ) - m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 240 ); - else - m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 140 ); - if ( m_pFlame[i] ) - { - int attach = i%2; - // attachment is 0 based in GetAttachment - GetAttachment( attach+1, posGun, angleGun ); - - Vector vecEnd = (gpGlobals->v_forward * GARG_FLAME_LENGTH) + posGun; - UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); - - m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); - if ( i < 2 ) - m_pFlame[i]->SetColor( 255, 130, 90 ); - else - m_pFlame[i]->SetColor( 0, 120, 255 ); - m_pFlame[i]->SetBrightness( 190 ); - m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); - m_pFlame[i]->SetScrollRate( 20 ); - // attachment is 1 based in SetEndAttachment - m_pFlame[i]->SetEndAttachment( attach + 2 ); - } - } - EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); -} - - -void CMGargantua :: FlameControls( float angleX, float angleY ) -{ - if ( angleY < -180 ) - angleY += 360; - else if ( angleY > 180 ) - angleY -= 360; - - if ( angleY < -45 ) - angleY = -45; - else if ( angleY > 45 ) - angleY = 45; - - m_flameX = UTIL_ApproachAngle( angleX, m_flameX, 4 ); - m_flameY = UTIL_ApproachAngle( angleY, m_flameY, 8 ); - SetBoneController( 0, m_flameY ); - SetBoneController( 1, m_flameX ); -} - - -void CMGargantua :: FlameUpdate( void ) -{ - int i; - static float offset[2] = { 60, -60 }; - TraceResult trace; - Vector vecStart, angleGun; - BOOL streaks = FALSE; - - for ( i = 0; i < 2; i++ ) - { - if ( m_pFlame[i] ) - { - Vector vecAim = pev->angles; - vecAim.x += m_flameX; - vecAim.y += m_flameY; - - UTIL_MakeVectors( vecAim ); - - GetAttachment( i+1, vecStart, angleGun ); - Vector vecEnd = vecStart + (gpGlobals->v_forward * GARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right; - - UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace ); - - m_pFlame[i]->SetStartPos( trace.vecEndPos ); - m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) ); - - if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime ) - { - StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); - streaks = TRUE; - UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); - } - // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN ); - FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN ); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B - WRITE_BYTE( 2 ); // life * 10 - WRITE_COORD( 0 ); // decay - MESSAGE_END(); - } - } - if ( streaks ) - m_streakTime = gpGlobals->time; -} - - - -void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) -{ - edict_t *pEntity = NULL; - TraceResult tr; - float flAdjustedDamage; - Vector vecSpot; - - Vector vecMid = (vecStart + vecEnd) * 0.5; - - float searchRadius = (vecStart - vecMid).Length(); - - Vector vecAim = (vecEnd - vecStart).Normalize( ); - - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecMid, searchRadius )) != NULL) - { - if ( pEntity->v.takedamage != DAMAGE_NO ) - { - vecSpot = UTIL_BodyTarget( pEntity, g_vecZero ); //pEntity->BodyTarget( vecMid ); - - float dist = DotProduct( vecAim, vecSpot - vecMid ); - if (dist > searchRadius) - dist = searchRadius; - else if (dist < -searchRadius) - dist = searchRadius; - - Vector vecSrc = vecMid + dist * vecAim; - - UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pev), &tr ); - - if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) - {// the explosion can 'see' this entity, so hurt them! - // decrease damage for an ent that's farther from the flame. - dist = ( vecSrc - tr.vecEndPos ).Length(); - - if (dist > 64) - { - flAdjustedDamage = flDamage - (dist - 64) * 0.4; - if (flAdjustedDamage <= 0) - continue; - } - else - { - flAdjustedDamage = flDamage; - } - - // ALERT( at_console, "hit %s\n", STRING( pEntity->v.classname ) ); - if (tr.flFraction != 1.0) - { - ClearMultiDamage(); - if ( UTIL_IsPlayer( pEntity ) ) - UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType ); - else if ( pEntity->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType ); - } - ApplyMultiDamage( pevInflictor, pevAttacker ); - } - else - { - if ( UTIL_IsPlayer( pEntity ) ) - UTIL_TakeDamage( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); - else if ( pEntity->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); - } - } - } - } - } -} - - -void CMGargantua :: FlameDestroy( void ) -{ - int i; - - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); - for ( i = 0; i < 4; i++ ) - { - if ( m_pFlame[i] ) - { - UTIL_Remove( m_pFlame[i]->edict() ); - m_pFlame[i] = NULL; - } - } -} - - -void CMGargantua :: PrescheduleThink( void ) -{ - if ( !HasConditions( bits_COND_SEE_ENEMY ) ) - { - m_seeTime = gpGlobals->time + 5; - EyeOff(); - } - else - EyeOn( 200 ); - - EyeUpdate(); -} - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMGargantua :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMGargantua :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 60; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 180; - break; - case ACT_WALK: - case ACT_RUN: - ys = 60; - break; - - default: - ys = 60; - break; - } - - pev->yaw_speed = ys; -} - - -//========================================================= -// Spawn -//========================================================= -void CMGargantua :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/garg.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = gSkillData.gargantuaHealth; - //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file - m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE ); - m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); - m_pEyeGlow->SetAttachment( edict(), 1 ); - EyeOff(); - m_seeTime = gpGlobals->time + 5; - m_flameTime = gpGlobals->time + 2; - - pev->classname = MAKE_STRING( "monster_gargantua" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Gargantua" ); - } -} - - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMGargantua :: Precache() -{ - int i; - - PRECACHE_MODEL("models/garg.mdl"); - PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); - PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); - PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); - gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); - gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); - PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pBeamAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) - PRECACHE_SOUND((char *)pRicSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ ) - PRECACHE_SOUND((char *)pFootSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ ) - PRECACHE_SOUND((char *)pStompSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ ) - PRECACHE_SOUND((char *)pBreatheSounds[i]); -} - - -void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - ALERT( at_aiconsole, "CMGargantua::TraceAttack\n"); - - if ( !IsAlive() ) - { - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); - return; - } - - // UNDONE: Hit group specific damage? - if ( bitsDamageType & (GARG_DAMAGE|DMG_BLAST) ) - { - if ( m_painSoundTime < gpGlobals->time ) - { - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); - m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 ); - } - } - - bitsDamageType &= GARG_DAMAGE; - - if ( bitsDamageType != 0 ) - { - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,100) < 20) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) ); - pev->dmgtime = gpGlobals->time; -// if ( RANDOM_LONG(0,100) < 25 ) -// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM ); - } - flDamage *= (1.01f - gSkillData.gargantuaArmor); // Again, for mods (see below) - } - - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); - -} - - - -int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - if ( IsAlive() ) - { - if ( !(bitsDamageType & GARG_DAMAGE) ) - flDamage *= (1.01f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua. - - // Always set - SetConditions( bits_COND_LIGHT_DAMAGE ); - } - - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - - -void CMGargantua::DeathEffect( void ) -{ - int i; - UTIL_MakeVectors(pev->angles); - Vector deathPos = pev->origin + gpGlobals->v_up * 100; - - // Create a spiral of streaks - CSpiral::Create( deathPos, (pev->absmax.z - pev->absmin.z) * 0.6, 125, 1.5 ); - - Vector position = pev->origin; - position.z += 32; - for ( i = 0; i < 7; i+=2 ) - { - SpawnExplosion( position, 70, (i * 0.3), 60 + (i*20), pev->owner ); - position.z += 15; - } - - CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL ); - UTIL_SetOrigin( pSmoker->pev, pev->origin ); - pSmoker->Spawn(); - pSmoker->pev->health = 1; // 1 smoke balls - pSmoker->pev->scale = 46; // 4.6X normal size - pSmoker->pev->dmg = 0; // 0 radial distribution - pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds -} - - -void CMGargantua::Killed( entvars_t *pevAttacker, int iGib ) -{ - EyeOff(); - UTIL_Remove( m_pEyeGlow->edict() ); - m_pEyeGlow = NULL; - CMBaseMonster::Killed( pevAttacker, GIB_NEVER ); -} - -//========================================================= -// CheckMeleeAttack1 -// Garg swipe attack -// -//========================================================= -BOOL CMGargantua::CheckMeleeAttack1( float flDot, float flDist ) -{ -// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); - - if (flDot >= 0.7) - { - if (flDist <= GARG_ATTACKDIST) - return TRUE; - } - return FALSE; -} - - -// Flame thrower madness! -BOOL CMGargantua::CheckMeleeAttack2( float flDot, float flDist ) -{ -// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); - - if ( gpGlobals->time > m_flameTime ) - { - if (flDot >= 0.8 && flDist > GARG_ATTACKDIST) - { - if ( flDist <= GARG_FLAME_LENGTH ) - return TRUE; - } - } - return FALSE; -} - - -//========================================================= -// CheckRangeAttack1 -// flDot is the cos of the angle of the cone within which -// the attack can occur. -//========================================================= -// -// Stomp attack -// -//========================================================= -BOOL CMGargantua::CheckRangeAttack1( float flDot, float flDist ) -{ - if ( gpGlobals->time > m_seeTime ) - { - if (flDot >= 0.7 && flDist > GARG_ATTACKDIST) - { - return TRUE; - } - } - return FALSE; -} - - - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch( pEvent->event ) - { - case GARG_AE_SLASH_LEFT: - { - // HACKHACK!!! - edict_t *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, gSkillData.gargantuaDmgSlash, DMG_SLASH ); - if (pHurt) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.x = -30; // pitch - pHurt->v.punchangle.y = -30; // yaw - pHurt->v.punchangle.z = 30; // roll - //UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 100; - } - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); - } - else // Play a random attack miss sound - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); - - Vector forward; - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - } - break; - - case GARG_AE_RIGHT_FOOT: - case GARG_AE_LEFT_FOOT: - UTIL_ScreenShake( pev->origin, 4.0, 3.0, 1.0, 750 ); - EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - break; - - case GARG_AE_STOMP: - StompAttack(); - m_seeTime = gpGlobals->time + 12; - break; - - case GARG_AE_BREATHE: - EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - break; - - default: - CMBaseMonster::HandleAnimEvent(pEvent); - break; - } -} - - -//========================================================= -// CheckTraceHullAttack - expects a length to trace, amount -// of damage to do, and damage type. Returns a pointer to -// the damaged entity in case the monster wishes to do -// other stuff to the victim (punchangle, etc) -// Used for many contact-range melee attacks. Bites, claws, etc. - -// Overridden for Gargantua because his swing starts lower as -// a percentage of his height (otherwise he swings over the -// players head) -//========================================================= -edict_t *CMGargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType) -{ - TraceResult tr; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin; - vecStart.z += 64; - Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit ) - { - if ( iDamage > 0 ) - { - if ( UTIL_IsPlayer( tr.pHit ) ) - UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType ); - else if ( tr.pHit->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); - pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); - } - } - - return tr.pHit; - } - - return NULL; -} - - -Schedule_t *CMGargantua::GetScheduleOfType( int Type ) -{ - // HACKHACK - turn off the flames if they are on and garg goes scripted / dead - if ( FlameIsOn() ) - FlameDestroy(); - - switch( Type ) - { - case SCHED_MELEE_ATTACK2: - return slGargFlame; - case SCHED_MELEE_ATTACK1: - return slGargSwipe; - break; - } - - return CMBaseMonster::GetScheduleOfType( Type ); -} - - -void CMGargantua::StartTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_FLAME_SWEEP: - FlameCreate(); - m_flWaitFinished = gpGlobals->time + pTask->flData; - m_flameTime = gpGlobals->time + 6; - m_flameX = 0; - m_flameY = 0; - break; - - case TASK_SOUND_ATTACK: - if ( RANDOM_LONG(0,100) < 30 ) - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); - TaskComplete(); - break; - - case TASK_DIE: - m_flWaitFinished = gpGlobals->time + 1.6; - DeathEffect(); - // FALL THROUGH - default: - CMBaseMonster::StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CMGargantua::RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_DIE: - if ( gpGlobals->time > m_flWaitFinished ) - { - pev->renderfx = kRenderFxExplode; - pev->rendercolor.x = 255; - pev->rendercolor.y = 0; - pev->rendercolor.z = 0; - StopAnimation(); - pev->nextthink = gpGlobals->time + 0.15; - SetThink( &CMGargantua::SUB_Remove ); - int i; - int parts = MODEL_FRAMES( gGargGibModel ); - for ( i = 0; i < 10; i++ ) - { - CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); - - pGib->Spawn( GARG_GIB_MODEL ); - - int bodyPart = 0; - if ( parts > 1 ) - bodyPart = RANDOM_LONG( 0, pev->body-1 ); - - pGib->pev->body = bodyPart; - pGib->m_bloodColor = BLOOD_COLOR_YELLOW; - pGib->m_material = matNone; - pGib->pev->origin = pev->origin; - pGib->pev->velocity = UTIL_RandomBloodVector() * RANDOM_FLOAT( 300, 500 ); - pGib->pev->nextthink = gpGlobals->time + 1.25; - pGib->SetThink( &CMGib::SUB_FadeOut ); - } - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_BREAKMODEL); - - // position - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - - // size - WRITE_COORD( 200 ); - WRITE_COORD( 200 ); - WRITE_COORD( 128 ); - - // velocity - WRITE_COORD( 0 ); - WRITE_COORD( 0 ); - WRITE_COORD( 0 ); - - // randomization - WRITE_BYTE( 200 ); - - // Model - WRITE_SHORT( gGargGibModel ); //model id# - - // # of shards - WRITE_BYTE( 50 ); - - // duration - WRITE_BYTE( 20 );// 3.0 seconds - - // flags - - WRITE_BYTE( BREAK_FLESH ); - MESSAGE_END(); - - return; - } - else - CMBaseMonster::RunTask(pTask); - break; - - case TASK_FLAME_SWEEP: - if ( gpGlobals->time > m_flWaitFinished ) - { - FlameDestroy(); - TaskComplete(); - FlameControls( 0, 0 ); - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - } - else - { - BOOL cancel = FALSE; - - Vector angles = g_vecZero; - - FlameUpdate(); - CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() ); - if ( pEnemy ) - { - Vector org = pev->origin; - org.z += 64; - Vector dir = pEnemy->BodyTarget(org) - org; - angles = UTIL_VecToAngles( dir ); - angles.x = -angles.x; - angles.y -= pev->angles.y; - if ( dir.Length() > 400 ) - cancel = TRUE; - } - if ( fabs(angles.y) > 60 ) - cancel = TRUE; - - if ( cancel ) - { - m_flWaitFinished -= 0.5; - m_flameTime -= 0.5; - } - // FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) ); - FlameControls( angles.x, angles.y ); - } - break; - - default: - CMBaseMonster::RunTask( pTask ); - break; - } -} - -void CSmoker::Spawn( void ) -{ - pev->movetype = MOVETYPE_NONE; - pev->nextthink = gpGlobals->time; - pev->solid = SOLID_NOT; - UTIL_SetSize(pev, g_vecZero, g_vecZero ); - pev->effects |= EF_NODRAW; - pev->angles = g_vecZero; -} - - -void CSmoker::Think( void ) -{ - // lots of smoke - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg )); - WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg )); - WRITE_COORD( pev->origin.z); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) ); - WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate - MESSAGE_END(); - - pev->health--; - if ( pev->health > 0 ) - pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2); - else - UTIL_Remove( this->edict() ); -} - - -void CSpiral::Spawn( void ) -{ - pev->movetype = MOVETYPE_NONE; - pev->nextthink = gpGlobals->time; - pev->solid = SOLID_NOT; - UTIL_SetSize(pev, g_vecZero, g_vecZero ); - pev->effects |= EF_NODRAW; - pev->angles = g_vecZero; -} - - -CSpiral *CSpiral::Create( const Vector &origin, float height, float radius, float duration ) -{ - if ( duration <= 0 ) - return NULL; - - CSpiral *pSpiral = CreateClassPtr( (CSpiral *)NULL ); - pSpiral->Spawn(); - pSpiral->pev->dmgtime = pSpiral->pev->nextthink; - pSpiral->pev->origin = origin; - pSpiral->pev->scale = radius; - pSpiral->pev->dmg = height; - pSpiral->pev->speed = duration; - pSpiral->pev->health = 0; - pSpiral->pev->angles = g_vecZero; - - return pSpiral; -} - -#define SPIRAL_INTERVAL 0.1 //025 - -void CSpiral::Think( void ) -{ - float time = gpGlobals->time - pev->dmgtime; - - while ( time > SPIRAL_INTERVAL ) - { - Vector position = pev->origin; - Vector direction = Vector(0,0,1); - - float fraction = 1.0 / pev->speed; - - float radius = (pev->scale * pev->health) * fraction; - - position.z += (pev->health * pev->dmg) * fraction; - pev->angles.y = (pev->health * 360 * 8) * fraction; - UTIL_MakeVectors( pev->angles ); - position = position + gpGlobals->v_forward * radius; - direction = (direction + gpGlobals->v_forward).Normalize(); - - StreakSplash( position, Vector(0,0,1), RANDOM_LONG(8,11), 20, RANDOM_LONG(50,150), 400 ); - - // Jeez, how many counters should this take ? :) - pev->dmgtime += SPIRAL_INTERVAL; - pev->health += SPIRAL_INTERVAL; - time -= SPIRAL_INTERVAL; - } - - pev->nextthink = gpGlobals->time; - - if ( pev->health >= pev->speed ) - UTIL_Remove( this->edict() ); -} - - -void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ) -{ - KeyValueData kvd; - char buf[128]; - - center.x += RANDOM_FLOAT( -randomRange, randomRange ); - center.y += RANDOM_FLOAT( -randomRange, randomRange ); - - /* - CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL ); - sprintf( buf, "%3d", magnitude ); - kvd.szKeyName = "iMagnitude"; - kvd.szValue = buf; - pExplosion->KeyValue( &kvd ); - pExplosion->pev->spawnflags |= SF_ENVEXPLOSION_NODAMAGE; - - pExplosion->Spawn(); - pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); - pExplosion->pev->nextthink = gpGlobals->time + time; - */ - - // explode.h - ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time ); -} - -/* - * The Sven Co-op's monster code was recreated from scratch. - * They do not contain their unique new attacks... YET. -Giegue - * */ - -//========================================================= -// Baby Gargantua -//========================================================= -const float BABYGARG_ATTACKDIST = 65.0; -#define BABYGARG_FLAME_LENGTH 180 - - -const char *CMBabyGargantua::pBeamAttackSounds[] = -{ - "babygarg/gar_flameoff1.wav", - "babygarg/gar_flameon1.wav", - "babygarg/gar_flamerun1.wav", -}; - -const char *CMBabyGargantua::pFootSounds[] = -{ - "babygarg/gar_step1.wav", - "babygarg/gar_step2.wav", -}; - -const char *CMBabyGargantua::pIdleSounds[] = -{ - "babygarg/gar_idle1.wav", - "babygarg/gar_idle2.wav", - "babygarg/gar_idle3.wav", - "babygarg/gar_idle4.wav", - "babygarg/gar_idle5.wav", -}; - -const char *CMBabyGargantua::pAttackSounds[] = -{ - "babygarg/gar_attack1.wav", - "babygarg/gar_attack2.wav", - "babygarg/gar_attack3.wav", -}; - -const char *CMBabyGargantua::pAlertSounds[] = -{ - "babygarg/gar_alert1.wav", - "babygarg/gar_alert2.wav", - "babygarg/gar_alert3.wav", -}; - -const char *CMBabyGargantua::pPainSounds[] = -{ - "babygarg/gar_pain1.wav", - "babygarg/gar_pain2.wav", - "babygarg/gar_pain3.wav", -}; - -const char *CMBabyGargantua::pStompSounds[] = -{ - "babygarg/gar_stomp1.wav", -}; - -const char *CMBabyGargantua::pBreatheSounds[] = -{ - "babygarg/gar_breathe1.wav", - "babygarg/gar_breathe2.wav", - "babygarg/gar_breathe3.wav", -}; - -const char *CMBabyGargantua::pDieSounds[] = -{ - "babygarg/gar_die1.wav", - "babygarg/gar_die2.wav", -}; - -//========================================================= -// Spawn -//========================================================= -void CMBabyGargantua::Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/babygarg.mdl"); - UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = gSkillData.babygargHealth; - //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file - m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - // Sven Co-op uses a modified gargeye1.spr for the eye and stomp effects. - // To economize precache count, we are going to recycle the normal garg's sprites. - - m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE ); - m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); - m_pEyeGlow->SetAttachment( edict(), 1 ); - EyeOff(); - m_seeTime = gpGlobals->time + 5; - m_flameTime = gpGlobals->time + 2; - - pev->classname = MAKE_STRING( "monster_babygarg" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Baby Gargantua" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMBabyGargantua::Precache() -{ - int i; - - PRECACHE_MODEL("models/babygarg.mdl"); - PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); - PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); - PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); - gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); - gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); - PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pBeamAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) - PRECACHE_SOUND((char *)pRicSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ ) - PRECACHE_SOUND((char *)pFootSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ ) - PRECACHE_SOUND((char *)pStompSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ ) - PRECACHE_SOUND((char *)pBreatheSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) - PRECACHE_SOUND((char *)pDieSounds[i]); -} - -void CMBabyGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - ALERT( at_aiconsole, "CMBabyGargantua::TraceAttack\n"); - - if ( !IsAlive() ) - { - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); - return; - } - - if ( m_painSoundTime < gpGlobals->time ) - { - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); - m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 ); - } - - // Override Gargantua's specific damage. Baby Garg has no protection from those. - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - -int CMBabyGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - if ( IsAlive() ) - { - // Always set - SetConditions( bits_COND_LIGHT_DAMAGE ); - } - - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMBabyGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch( pEvent->event ) - { - case GARG_AE_SLASH_LEFT: - { - // HACKHACK!!! - edict_t *pHurt = BabyGargCheckTraceHullAttack( BABYGARG_ATTACKDIST + 10.0, gSkillData.babygargDmgSlash, DMG_SLASH ); - if (pHurt) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - // Slightly lower numbers for babygarg (-20%) - pHurt->v.punchangle.x = -24; // pitch - pHurt->v.punchangle.y = -24; // yaw - pHurt->v.punchangle.z = 24; // roll - //UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 80; - } - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); - } - else // Play a random attack miss sound - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); - - Vector forward; - UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); - } - break; - - case GARG_AE_RIGHT_FOOT: - case GARG_AE_LEFT_FOOT: - // babygarg does not shake the screen - EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - break; - - case GARG_AE_STOMP: - StompAttack(); - m_seeTime = gpGlobals->time + 12; - break; - - case GARG_AE_BREATHE: - EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - break; - - default: - CMBaseMonster::HandleAnimEvent(pEvent); - break; - } -} - -//========================================================= -// CheckMeleeAttack1 -// Garg swipe attack -//========================================================= -BOOL CMBabyGargantua::CheckMeleeAttack1( float flDot, float flDist ) -{ -// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); - - if (flDot >= 0.7) - { - if (flDist <= BABYGARG_ATTACKDIST) - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckMeleeAttack2 -// Flame thrower madness! -//========================================================= -BOOL CMBabyGargantua::CheckMeleeAttack2( float flDot, float flDist ) -{ -// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); - - if ( gpGlobals->time > m_flameTime ) - { - if (flDot >= 0.8 && flDist > BABYGARG_ATTACKDIST) - { - if ( flDist <= BABYGARG_FLAME_LENGTH ) - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 -// Stomp attack -//========================================================= -BOOL CMBabyGargantua::CheckRangeAttack1( float flDot, float flDist ) -{ - if ( gpGlobals->time > m_seeTime ) - { - if (flDot >= 0.7 && flDist > BABYGARG_ATTACKDIST) - { - return TRUE; - } - } - return FALSE; -} - -void CMBabyGargantua::StartTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_FLAME_SWEEP: - FlameCreate(); - m_flWaitFinished = gpGlobals->time + pTask->flData; - m_flameTime = gpGlobals->time + 6; - m_flameX = 0; - m_flameY = 0; - break; - - case TASK_SOUND_ATTACK: - if ( RANDOM_LONG(0,100) < 30 ) - EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); - TaskComplete(); - break; - - case TASK_DIE: - // no death effect for babygarg, but give it a sound - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pDieSounds[ RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); - default: - CMBaseMonster::StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CMBabyGargantua::RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - // babygarg does not explode upon death - case TASK_FLAME_SWEEP: - if ( gpGlobals->time > m_flWaitFinished ) - { - FlameDestroy(); - TaskComplete(); - FlameControls( 0, 0 ); - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - } - else - { - BOOL cancel = FALSE; - - Vector angles = g_vecZero; - - FlameUpdate(); - CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() ); - if ( pEnemy ) - { - Vector org = pev->origin; - org.z += 64; - Vector dir = pEnemy->BodyTarget(org) - org; - angles = UTIL_VecToAngles( dir ); - angles.x = -angles.x; - angles.y -= pev->angles.y; - if ( dir.Length() > 400 ) - cancel = TRUE; - } - if ( fabs(angles.y) > 60 ) - cancel = TRUE; - - if ( cancel ) - { - m_flWaitFinished -= 0.5; - m_flameTime -= 0.5; - } - // FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) ); - FlameControls( angles.x, angles.y ); - } - break; - - default: - CMBaseMonster::RunTask( pTask ); - break; - } -} - -void CMBabyGargantua::StompAttack( void ) -{ - TraceResult trace; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward; - Vector vecAim = ShootAtEnemy( vecStart ); - Vector vecEnd = (vecAim * 1024) + vecStart; - - UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); - CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.babygargDmgStomp ); - UTIL_ScreenShake( pev->origin, 9.6, 80.0, 1.8, 800 ); // -20% "power" to the babygarg's stomp - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); - - UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace ); - if ( trace.flFraction < 1.0 ) - UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 ); -} - -void CMBabyGargantua::FlameCreate( void ) -{ - int i; - Vector posGun, angleGun; - TraceResult trace; - - UTIL_MakeVectors( pev->angles ); - - for ( i = 0; i < 4; i++ ) - { - if ( i < 2 ) - m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 120 ); - else - m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 70 ); - if ( m_pFlame[i] ) - { - int attach = i%2; - // attachment is 0 based in GetAttachment - GetAttachment( attach+1, posGun, angleGun ); - - Vector vecEnd = (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH) + posGun; - UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); - - m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); - if ( i < 2 ) - m_pFlame[i]->SetColor( 255, 130, 90 ); - else - m_pFlame[i]->SetColor( 0, 120, 255 ); - m_pFlame[i]->SetBrightness( 190 ); - m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); - m_pFlame[i]->SetScrollRate( 20 ); - // attachment is 1 based in SetEndAttachment - m_pFlame[i]->SetEndAttachment( attach + 2 ); - } - } - EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); -} - -void CMBabyGargantua::FlameUpdate( void ) -{ - int i; - static float offset[2] = { 60, -60 }; - TraceResult trace; - Vector vecStart, angleGun; - BOOL streaks = FALSE; - - for ( i = 0; i < 2; i++ ) - { - if ( m_pFlame[i] ) - { - Vector vecAim = pev->angles; - vecAim.x += m_flameX; - vecAim.y += m_flameY; - - UTIL_MakeVectors( vecAim ); - - GetAttachment( i+1, vecStart, angleGun ); - Vector vecEnd = vecStart + (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right; - - UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace ); - - m_pFlame[i]->SetStartPos( trace.vecEndPos ); - m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) ); - - if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime ) - { - StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); - streaks = TRUE; - UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); - } - // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN ); - FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN ); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B - WRITE_BYTE( 2 ); // life * 10 - WRITE_COORD( 0 ); // decay - MESSAGE_END(); - } - } - if ( streaks ) - m_streakTime = gpGlobals->time; -} - -void CMBabyGargantua::FlameDestroy( void ) -{ - int i; - - EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // sound must stop. - for ( i = 0; i < 4; i++ ) - { - if ( m_pFlame[i] ) - { - UTIL_Remove( m_pFlame[i]->edict() ); - m_pFlame[i] = NULL; - } - } -} - -//========================================================= -// CheckTraceHullAttack - expects a length to trace, amount -// of damage to do, and damage type. Returns a pointer to -// the damaged entity in case the monster wishes to do -// other stuff to the victim (punchangle, etc) -// Used for many contact-range melee attacks. Bites, claws, etc. - -// Overridden for Gargantua because his swing starts lower as -// a percentage of his height (otherwise he swings over the -// players head) - -// Also overriden for Baby Gargantua to prevent players from -// dodging the swing attacks by crouching. -//========================================================= -edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType) -{ - TraceResult tr; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin; - vecStart.z += 32; - Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit ) - { - if ( iDamage > 0 ) - { - if ( UTIL_IsPlayer( tr.pHit ) ) - UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType ); - else if ( tr.pHit->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); - pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); - } - } - - return tr.pHit; - } - - return NULL; -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ + +//========================================================= +// Gargantua +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "skill.h" +#include "nodes.h" +#include "monsters.h" +#include "schedule.h" +#include "customentity.h" +#include "weapons.h" +#include "effects.h" +#include "decals.h" +#include "explode.h" +#include "func_break.h" + +//========================================================= +// Gargantua Monster +//========================================================= +const float GARG_ATTACKDIST = 80.0; + +// Garg animation events +#define GARG_AE_SLASH_LEFT 1 +//#define GARG_AE_BEAM_ATTACK_RIGHT 2 // No longer used +#define GARG_AE_LEFT_FOOT 3 +#define GARG_AE_RIGHT_FOOT 4 +#define GARG_AE_STOMP 5 +#define GARG_AE_BREATHE 6 + + +// Gargantua is immune to any damage but this +#define GARG_DAMAGE (DMG_ENERGYBEAM|DMG_CRUSH|DMG_MORTAR|DMG_BLAST) +#define GARG_EYE_SPRITE_NAME "sprites/gargeye1.spr" +#define GARG_BEAM_SPRITE_NAME "sprites/xbeam3.spr" +#define GARG_BEAM_SPRITE2 "sprites/xbeam3.spr" +#define GARG_STOMP_SPRITE_NAME "sprites/gargeye1.spr" +#define GARG_STOMP_BUZZ_SOUND "weapons/mine_charge.wav" +#define GARG_FLAME_LENGTH 330 +#define GARG_GIB_MODEL "models/metalplategibs.mdl" + +#define ATTN_GARG (ATTN_NORM) + +#define STOMP_SPRITE_COUNT 10 + +int gStompSprite = 0, gGargGibModel = 0; +void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ); + +class CSmoker : public CMBaseEntity +{ +public: + void Spawn( void ); + void Think( void ); +}; + +// Spiral Effect +class CSpiral : public CMBaseEntity +{ +public: + void Spawn( void ); + void Think( void ); + int ObjectCaps( void ) { return FCAP_DONT_SAVE; } + static CSpiral *Create( const Vector &origin, float height, float radius, float duration ); +}; + +class CStomp : public CMBaseEntity +{ +public: + void Spawn( void ); + void Think( void ); + static CStomp *StompCreate( const Vector &origin, const Vector &end, float speed, float damage ); + +private: +// UNDONE: re-use this sprite list instead of creating new ones all the time +// CSprite *m_pSprites[ STOMP_SPRITE_COUNT ]; +}; + +CStomp *CStomp::StompCreate( const Vector &origin, const Vector &end, float speed, float damage ) +{ + CStomp *pStomp = CreateClassPtr( (CStomp *)NULL ); + + pStomp->pev->origin = origin; + Vector dir = (end - origin); + pStomp->pev->scale = dir.Length(); + pStomp->pev->movedir = dir.Normalize(); + pStomp->pev->speed = speed; + pStomp->pev->dmg = damage; + pStomp->Spawn(); + + return pStomp; +} + +void CStomp::Spawn( void ) +{ + pev->nextthink = gpGlobals->time; + pev->classname = MAKE_STRING("garg_stomp"); + pev->dmgtime = gpGlobals->time; + + pev->framerate = 30; + pev->model = MAKE_STRING(GARG_STOMP_SPRITE_NAME); + pev->rendermode = kRenderTransTexture; + pev->renderamt = 0; + EMIT_SOUND_DYN( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND, 1, ATTN_NORM, 0, PITCH_NORM * 0.55); +} + + +#define STOMP_INTERVAL 0.025 + +void CStomp::Think( void ) +{ + TraceResult tr; + + pev->nextthink = gpGlobals->time + 0.1; + + // Do damage for this frame + Vector vecStart = pev->origin; + vecStart.z += 30; + Vector vecEnd = vecStart + (pev->movedir * pev->speed * gpGlobals->frametime); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit && tr.pHit != pev->owner ) + { + CMBaseEntity *pEntity = CMBaseEntity::Instance( tr.pHit ); + entvars_t *pevOwner = pev; + if ( pev->owner ) + pevOwner = VARS(pev->owner); + + if ( pEntity ) + pEntity->TakeDamage( pev, pevOwner, pev->dmg, DMG_SONIC ); + } + + // Accelerate the effect + pev->speed = pev->speed + (gpGlobals->frametime) * pev->framerate; + pev->framerate = pev->framerate + (gpGlobals->frametime) * 1500; + + // Move and spawn trails + while ( gpGlobals->time - pev->dmgtime > STOMP_INTERVAL ) + { + pev->origin = pev->origin + pev->movedir * pev->speed * STOMP_INTERVAL; + for ( int i = 0; i < 2; i++ ) + { + CMSprite *pSprite = CMSprite::SpriteCreate( GARG_STOMP_SPRITE_NAME, pev->origin, TRUE ); + if ( pSprite ) + { + UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,500), ignore_monsters, edict(), &tr ); + pSprite->pev->origin = tr.vecEndPos; + pSprite->pev->velocity = Vector(RANDOM_FLOAT(-200,200),RANDOM_FLOAT(-200,200),175); + // pSprite->AnimateAndDie( RANDOM_FLOAT( 8.0, 12.0 ) ); + pSprite->pev->nextthink = gpGlobals->time + 0.3; + pSprite->SetThink( &CMSprite::SUB_Remove ); + pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxFadeFast ); + } + } + pev->dmgtime += STOMP_INTERVAL; + // Scale has the "life" of this effect + pev->scale -= STOMP_INTERVAL * pev->speed; + if ( pev->scale <= 0 ) + { + // Life has run out + UTIL_Remove(this->edict()); + STOP_SOUND( edict(), CHAN_BODY, GARG_STOMP_BUZZ_SOUND ); + } + + } +} + + +void StreakSplash( const Vector &origin, const Vector &direction, int color, int count, int speed, int velocityRange ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); + WRITE_BYTE( TE_STREAK_SPLASH ); + WRITE_COORD( origin.x ); // origin + WRITE_COORD( origin.y ); + WRITE_COORD( origin.z ); + WRITE_COORD( direction.x ); // direction + WRITE_COORD( direction.y ); + WRITE_COORD( direction.z ); + WRITE_BYTE( color ); // Streak color 6 + WRITE_SHORT( count ); // count + WRITE_SHORT( speed ); + WRITE_SHORT( velocityRange ); // Random velocity modifier + MESSAGE_END(); +} + +const char *CMGargantua::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CMGargantua::pBeamAttackSounds[] = +{ + "garg/gar_flameoff1.wav", + "garg/gar_flameon1.wav", + "garg/gar_flamerun1.wav", +}; + + +const char *CMGargantua::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CMGargantua::pRicSounds[] = +{ +#if 0 + "weapons/ric1.wav", + "weapons/ric2.wav", + "weapons/ric3.wav", + "weapons/ric4.wav", + "weapons/ric5.wav", +#else + "debris/metal4.wav", + "debris/metal6.wav", + "weapons/ric4.wav", + "weapons/ric5.wav", +#endif +}; + +const char *CMGargantua::pFootSounds[] = +{ + "garg/gar_step1.wav", + "garg/gar_step2.wav", +}; + + +const char *CMGargantua::pIdleSounds[] = +{ + "garg/gar_idle1.wav", + "garg/gar_idle2.wav", + "garg/gar_idle3.wav", + "garg/gar_idle4.wav", + "garg/gar_idle5.wav", +}; + + +const char *CMGargantua::pAttackSounds[] = +{ + "garg/gar_attack1.wav", + "garg/gar_attack2.wav", + "garg/gar_attack3.wav", +}; + +const char *CMGargantua::pAlertSounds[] = +{ + "garg/gar_alert1.wav", + "garg/gar_alert2.wav", + "garg/gar_alert3.wav", +}; + +const char *CMGargantua::pPainSounds[] = +{ + "garg/gar_pain1.wav", + "garg/gar_pain2.wav", + "garg/gar_pain3.wav", +}; + +const char *CMGargantua::pStompSounds[] = +{ + "garg/gar_stomp1.wav", +}; + +const char *CMGargantua::pBreatheSounds[] = +{ + "garg/gar_breathe1.wav", + "garg/gar_breathe2.wav", + "garg/gar_breathe3.wav", +}; +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +#if 0 +enum +{ + SCHED_ = LAST_COMMON_SCHEDULE + 1, +}; +#endif + +enum +{ + TASK_SOUND_ATTACK = LAST_COMMON_TASK + 1, + TASK_FLAME_SWEEP, +}; + +Task_t tlGargFlame[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SOUND_ATTACK, (float)0 }, + // { TASK_PLAY_SEQUENCE, (float)ACT_SIGNAL1 }, + { TASK_SET_ACTIVITY, (float)ACT_MELEE_ATTACK2 }, + { TASK_FLAME_SWEEP, (float)4.5 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slGargFlame[] = +{ + { + tlGargFlame, + ARRAYSIZE ( tlGargFlame ), + 0, + 0, + "GargFlame" + }, +}; + + +// primary melee attack +Task_t tlGargSwipe[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_MELEE_ATTACK1, (float)0 }, +}; + +Schedule_t slGargSwipe[] = +{ + { + tlGargSwipe, + ARRAYSIZE ( tlGargSwipe ), + bits_COND_CAN_MELEE_ATTACK2, + 0, + "GargSwipe" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMGargantua ) +{ + slGargFlame, + slGargSwipe, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMGargantua, CMBaseMonster ); + + +void CMGargantua::EyeOn( int level ) +{ + m_eyeBrightness = level; +} + + +void CMGargantua::EyeOff( void ) +{ + m_eyeBrightness = 0; +} + + +void CMGargantua::EyeUpdate( void ) +{ + if ( m_pEyeGlow ) + { + m_pEyeGlow->pev->renderamt = UTIL_Approach( m_eyeBrightness, m_pEyeGlow->pev->renderamt, 26 ); + if ( m_pEyeGlow->pev->renderamt == 0 ) + m_pEyeGlow->pev->effects |= EF_NODRAW; + else + m_pEyeGlow->pev->effects &= ~EF_NODRAW; + UTIL_SetOrigin( m_pEyeGlow->pev, pev->origin ); + } +} + + +void CMGargantua::StompAttack( void ) +{ + TraceResult trace; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward; + Vector vecAim = ShootAtEnemy( vecStart ); + Vector vecEnd = (vecAim * 1024) + vecStart; + + UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); + CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.gargantuaDmgStomp ); + UTIL_ScreenShake( pev->origin, 12.0, 100.0, 2.0, 1000 ); + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + + UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace ); + if ( trace.flFraction < 1.0 ) + UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 ); +} + + +void CMGargantua :: FlameCreate( void ) +{ + int i; + Vector posGun, angleGun; + TraceResult trace; + + UTIL_MakeVectors( pev->angles ); + + for ( i = 0; i < 4; i++ ) + { + if ( i < 2 ) + m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 240 ); + else + m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 140 ); + if ( m_pFlame[i] ) + { + int attach = i%2; + // attachment is 0 based in GetAttachment + GetAttachment( attach+1, posGun, angleGun ); + + Vector vecEnd = (gpGlobals->v_forward * GARG_FLAME_LENGTH) + posGun; + UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); + + m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); + if ( i < 2 ) + m_pFlame[i]->SetColor( 255, 130, 90 ); + else + m_pFlame[i]->SetColor( 0, 120, 255 ); + m_pFlame[i]->SetBrightness( 190 ); + m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); + m_pFlame[i]->SetScrollRate( 20 ); + // attachment is 1 based in SetEndAttachment + m_pFlame[i]->SetEndAttachment( attach + 2 ); + } + } + EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); +} + + +void CMGargantua :: FlameControls( float angleX, float angleY ) +{ + if ( angleY < -180 ) + angleY += 360; + else if ( angleY > 180 ) + angleY -= 360; + + if ( angleY < -45 ) + angleY = -45; + else if ( angleY > 45 ) + angleY = 45; + + m_flameX = UTIL_ApproachAngle( angleX, m_flameX, 4 ); + m_flameY = UTIL_ApproachAngle( angleY, m_flameY, 8 ); + SetBoneController( 0, m_flameY ); + SetBoneController( 1, m_flameX ); +} + + +void CMGargantua :: FlameUpdate( void ) +{ + int i; + static float offset[2] = { 60, -60 }; + TraceResult trace; + Vector vecStart, angleGun; + BOOL streaks = FALSE; + + for ( i = 0; i < 2; i++ ) + { + if ( m_pFlame[i] ) + { + Vector vecAim = pev->angles; + vecAim.x += m_flameX; + vecAim.y += m_flameY; + + UTIL_MakeVectors( vecAim ); + + GetAttachment( i+1, vecStart, angleGun ); + Vector vecEnd = vecStart + (gpGlobals->v_forward * GARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right; + + UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace ); + + m_pFlame[i]->SetStartPos( trace.vecEndPos ); + m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) ); + + if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime ) + { + StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); + streaks = TRUE; + UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); + } + // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN ); + FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.gargantuaDmgFire, Classify(), DMG_BURN ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment + WRITE_COORD( vecStart.x ); // origin + WRITE_COORD( vecStart.y ); + WRITE_COORD( vecStart.z ); + WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 255 ); // G + WRITE_BYTE( 255 ); // B + WRITE_BYTE( 2 ); // life * 10 + WRITE_COORD( 0 ); // decay + MESSAGE_END(); + } + } + if ( streaks ) + m_streakTime = gpGlobals->time; +} + + + +void CMGargantua :: FlameDamage( Vector vecStart, Vector vecEnd, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType ) +{ + edict_t *pEntity = NULL; + TraceResult tr; + float flAdjustedDamage; + Vector vecSpot; + + Vector vecMid = (vecStart + vecEnd) * 0.5; + + float searchRadius = (vecStart - vecMid).Length(); + + Vector vecAim = (vecEnd - vecStart).Normalize( ); + + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecMid, searchRadius )) != NULL) + { + if ( pEntity->v.takedamage != DAMAGE_NO ) + { + vecSpot = UTIL_BodyTarget( pEntity, g_vecZero ); //pEntity->BodyTarget( vecMid ); + + float dist = DotProduct( vecAim, vecSpot - vecMid ); + if (dist > searchRadius) + dist = searchRadius; + else if (dist < -searchRadius) + dist = searchRadius; + + Vector vecSrc = vecMid + dist * vecAim; + + UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pev), &tr ); + + if ( tr.flFraction == 1.0 || tr.pHit == pEntity ) + {// the explosion can 'see' this entity, so hurt them! + // decrease damage for an ent that's farther from the flame. + dist = ( vecSrc - tr.vecEndPos ).Length(); + + if (dist > 64) + { + flAdjustedDamage = flDamage - (dist - 64) * 0.4; + if (flAdjustedDamage <= 0) + continue; + } + else + { + flAdjustedDamage = flDamage; + } + + // ALERT( at_console, "hit %s\n", STRING( pEntity->v.classname ) ); + if (tr.flFraction != 1.0) + { + ClearMultiDamage(); + if ( UTIL_IsPlayer( pEntity ) ) + UTIL_TraceAttack( pEntity, pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType ); + else if ( pEntity->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize(), &tr, bitsDamageType ); + } + ApplyMultiDamage( pevInflictor, pevAttacker ); + } + else + { + if ( UTIL_IsPlayer( pEntity ) ) + UTIL_TakeDamage( pEntity, pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); + else if ( pEntity->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TakeDamage( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType ); + } + } + } + } + } +} + + +void CMGargantua :: FlameDestroy( void ) +{ + int i; + + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); + for ( i = 0; i < 4; i++ ) + { + if ( m_pFlame[i] ) + { + UTIL_Remove( m_pFlame[i]->edict() ); + m_pFlame[i] = NULL; + } + } +} + + +void CMGargantua :: PrescheduleThink( void ) +{ + if ( !HasConditions( bits_COND_SEE_ENEMY ) ) + { + m_seeTime = gpGlobals->time + 5; + EyeOff(); + } + else + EyeOn( 200 ); + + EyeUpdate(); +} + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMGargantua :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMGargantua :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 60; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 180; + break; + case ACT_WALK: + case ACT_RUN: + ys = 60; + break; + + default: + ys = 60; + break; + } + + pev->yaw_speed = ys; +} + + +//========================================================= +// Spawn +//========================================================= +void CMGargantua :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/garg.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = gSkillData.gargantuaHealth; + //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file + m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE ); + m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( edict(), 1 ); + EyeOff(); + m_seeTime = gpGlobals->time + 5; + m_flameTime = gpGlobals->time + 2; + + pev->classname = MAKE_STRING( "monster_gargantua" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Gargantua" ); + } +} + + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMGargantua :: Precache() +{ + int i; + + PRECACHE_MODEL("models/garg.mdl"); + PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); + PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); + PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); + gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); + gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); + PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pBeamAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) + PRECACHE_SOUND((char *)pRicSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ ) + PRECACHE_SOUND((char *)pFootSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ ) + PRECACHE_SOUND((char *)pStompSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ ) + PRECACHE_SOUND((char *)pBreatheSounds[i]); +} + + +void CMGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + ALERT( at_aiconsole, "CMGargantua::TraceAttack\n"); + + if ( !IsAlive() ) + { + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); + return; + } + + // UNDONE: Hit group specific damage? + if ( bitsDamageType & (GARG_DAMAGE|DMG_BLAST) ) + { + if ( m_painSoundTime < gpGlobals->time ) + { + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); + m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 ); + } + } + + bitsDamageType &= GARG_DAMAGE; + + if ( bitsDamageType != 0 ) + { + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,100) < 20) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) ); + pev->dmgtime = gpGlobals->time; +// if ( RANDOM_LONG(0,100) < 25 ) +// EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, pRicSounds[ RANDOM_LONG(0,ARRAYSIZE(pRicSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM ); + } + flDamage *= (1.01f - gSkillData.gargantuaArmor); // Again, for mods (see below) + } + + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); + +} + + + +int CMGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + if ( IsAlive() ) + { + if ( !(bitsDamageType & GARG_DAMAGE) ) + flDamage *= (1.01f - gSkillData.gargantuaArmor); // This is for mods that don't use explosives of any kind or do not work with the gargantua. + + // Always set + SetConditions( bits_COND_LIGHT_DAMAGE ); + } + + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + + +void CMGargantua::DeathEffect( void ) +{ + int i; + UTIL_MakeVectors(pev->angles); + Vector deathPos = pev->origin + gpGlobals->v_up * 100; + + // Create a spiral of streaks + CSpiral::Create( deathPos, (pev->absmax.z - pev->absmin.z) * 0.6, 125, 1.5 ); + + Vector position = pev->origin; + position.z += 32; + for ( i = 0; i < 7; i+=2 ) + { + SpawnExplosion( position, 70, (i * 0.3), 60 + (i*20), pev->owner ); + position.z += 15; + } + + CMBaseEntity *pSmoker = CreateClassPtr((CSmoker *)NULL); // CMBaseEntity::Create( "env_smoker", pev->origin, g_vecZero, NULL ); + UTIL_SetOrigin( pSmoker->pev, pev->origin ); + pSmoker->Spawn(); + pSmoker->pev->health = 1; // 1 smoke balls + pSmoker->pev->scale = 46; // 4.6X normal size + pSmoker->pev->dmg = 0; // 0 radial distribution + pSmoker->pev->nextthink = gpGlobals->time + 2.5; // Start in 2.5 seconds +} + + +void CMGargantua::Killed( entvars_t *pevAttacker, int iGib ) +{ + EyeOff(); + UTIL_Remove( m_pEyeGlow->edict() ); + m_pEyeGlow = NULL; + CMBaseMonster::Killed( pevAttacker, GIB_NEVER ); +} + +//========================================================= +// CheckMeleeAttack1 +// Garg swipe attack +// +//========================================================= +BOOL CMGargantua::CheckMeleeAttack1( float flDot, float flDist ) +{ +// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); + + if (flDot >= 0.7) + { + if (flDist <= GARG_ATTACKDIST) + return TRUE; + } + return FALSE; +} + + +// Flame thrower madness! +BOOL CMGargantua::CheckMeleeAttack2( float flDot, float flDist ) +{ +// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); + + if ( gpGlobals->time > m_flameTime ) + { + if (flDot >= 0.8 && flDist > GARG_ATTACKDIST) + { + if ( flDist <= GARG_FLAME_LENGTH ) + return TRUE; + } + } + return FALSE; +} + + +//========================================================= +// CheckRangeAttack1 +// flDot is the cos of the angle of the cone within which +// the attack can occur. +//========================================================= +// +// Stomp attack +// +//========================================================= +BOOL CMGargantua::CheckRangeAttack1( float flDot, float flDist ) +{ + if ( gpGlobals->time > m_seeTime ) + { + if (flDot >= 0.7 && flDist > GARG_ATTACKDIST) + { + return TRUE; + } + } + return FALSE; +} + + + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch( pEvent->event ) + { + case GARG_AE_SLASH_LEFT: + { + // HACKHACK!!! + edict_t *pHurt = GargantuaCheckTraceHullAttack( GARG_ATTACKDIST + 10.0, gSkillData.gargantuaDmgSlash, DMG_SLASH ); + if (pHurt) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.x = -30; // pitch + pHurt->v.punchangle.y = -30; // yaw + pHurt->v.punchangle.z = 30; // roll + //UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 100; + } + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); + } + else // Play a random attack miss sound + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); + + Vector forward; + UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); + } + break; + + case GARG_AE_RIGHT_FOOT: + case GARG_AE_LEFT_FOOT: + UTIL_ScreenShake( pev->origin, 4.0, 3.0, 1.0, 750 ); + EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + break; + + case GARG_AE_STOMP: + StompAttack(); + m_seeTime = gpGlobals->time + 12; + break; + + case GARG_AE_BREATHE: + EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + break; + + default: + CMBaseMonster::HandleAnimEvent(pEvent); + break; + } +} + + +//========================================================= +// CheckTraceHullAttack - expects a length to trace, amount +// of damage to do, and damage type. Returns a pointer to +// the damaged entity in case the monster wishes to do +// other stuff to the victim (punchangle, etc) +// Used for many contact-range melee attacks. Bites, claws, etc. + +// Overridden for Gargantua because his swing starts lower as +// a percentage of his height (otherwise he swings over the +// players head) +//========================================================= +edict_t *CMGargantua::GargantuaCheckTraceHullAttack(float flDist, int iDamage, int iDmgType) +{ + TraceResult tr; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin; + vecStart.z += 64; + Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit ) + { + if ( iDamage > 0 ) + { + if ( UTIL_IsPlayer( tr.pHit ) ) + UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType ); + else if ( tr.pHit->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); + pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); + } + } + + return tr.pHit; + } + + return NULL; +} + + +Schedule_t *CMGargantua::GetScheduleOfType( int Type ) +{ + // HACKHACK - turn off the flames if they are on and garg goes scripted / dead + if ( FlameIsOn() ) + FlameDestroy(); + + switch( Type ) + { + case SCHED_MELEE_ATTACK2: + return slGargFlame; + case SCHED_MELEE_ATTACK1: + return slGargSwipe; + break; + } + + return CMBaseMonster::GetScheduleOfType( Type ); +} + + +void CMGargantua::StartTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_FLAME_SWEEP: + FlameCreate(); + m_flWaitFinished = gpGlobals->time + pTask->flData; + m_flameTime = gpGlobals->time + 6; + m_flameX = 0; + m_flameY = 0; + break; + + case TASK_SOUND_ATTACK: + if ( RANDOM_LONG(0,100) < 30 ) + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); + TaskComplete(); + break; + + case TASK_DIE: + m_flWaitFinished = gpGlobals->time + 1.6; + DeathEffect(); + // FALL THROUGH + default: + CMBaseMonster::StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CMGargantua::RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_DIE: + if ( gpGlobals->time > m_flWaitFinished ) + { + pev->renderfx = kRenderFxExplode; + pev->rendercolor.x = 255; + pev->rendercolor.y = 0; + pev->rendercolor.z = 0; + StopAnimation(); + pev->nextthink = gpGlobals->time + 0.15; + SetThink( &CMGargantua::SUB_Remove ); + int i; + int parts = MODEL_FRAMES( gGargGibModel ); + for ( i = 0; i < 10; i++ ) + { + CMGib *pGib = CreateClassPtr( (CMGib *)NULL ); + + pGib->Spawn( GARG_GIB_MODEL ); + + int bodyPart = 0; + if ( parts > 1 ) + bodyPart = RANDOM_LONG( 0, pev->body-1 ); + + pGib->pev->body = bodyPart; + pGib->m_bloodColor = BLOOD_COLOR_YELLOW; + pGib->m_material = matNone; + pGib->pev->origin = pev->origin; + pGib->pev->velocity = UTIL_RandomBloodVector() * RANDOM_FLOAT( 300, 500 ); + pGib->pev->nextthink = gpGlobals->time + 1.25; + pGib->SetThink( &CMGib::SUB_FadeOut ); + } + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BREAKMODEL); + + // position + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + + // size + WRITE_COORD( 200 ); + WRITE_COORD( 200 ); + WRITE_COORD( 128 ); + + // velocity + WRITE_COORD( 0 ); + WRITE_COORD( 0 ); + WRITE_COORD( 0 ); + + // randomization + WRITE_BYTE( 200 ); + + // Model + WRITE_SHORT( gGargGibModel ); //model id# + + // # of shards + WRITE_BYTE( 50 ); + + // duration + WRITE_BYTE( 20 );// 3.0 seconds + + // flags + + WRITE_BYTE( BREAK_FLESH ); + MESSAGE_END(); + + return; + } + else + CMBaseMonster::RunTask(pTask); + break; + + case TASK_FLAME_SWEEP: + if ( gpGlobals->time > m_flWaitFinished ) + { + FlameDestroy(); + TaskComplete(); + FlameControls( 0, 0 ); + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + } + else + { + BOOL cancel = FALSE; + + Vector angles = g_vecZero; + + FlameUpdate(); + CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() ); + if ( pEnemy ) + { + Vector org = pev->origin; + org.z += 64; + Vector dir = pEnemy->BodyTarget(org) - org; + angles = UTIL_VecToAngles( dir ); + angles.x = -angles.x; + angles.y -= pev->angles.y; + if ( dir.Length() > 400 ) + cancel = TRUE; + } + if ( fabs(angles.y) > 60 ) + cancel = TRUE; + + if ( cancel ) + { + m_flWaitFinished -= 0.5; + m_flameTime -= 0.5; + } + // FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) ); + FlameControls( angles.x, angles.y ); + } + break; + + default: + CMBaseMonster::RunTask( pTask ); + break; + } +} + +void CSmoker::Spawn( void ) +{ + pev->movetype = MOVETYPE_NONE; + pev->nextthink = gpGlobals->time; + pev->solid = SOLID_NOT; + UTIL_SetSize(pev, g_vecZero, g_vecZero ); + pev->effects |= EF_NODRAW; + pev->angles = g_vecZero; +} + + +void CSmoker::Think( void ) +{ + // lots of smoke + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( pev->origin.x + RANDOM_FLOAT( -pev->dmg, pev->dmg )); + WRITE_COORD( pev->origin.y + RANDOM_FLOAT( -pev->dmg, pev->dmg )); + WRITE_COORD( pev->origin.z); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( RANDOM_LONG(pev->scale, pev->scale * 1.1) ); + WRITE_BYTE( RANDOM_LONG(8,14) ); // framerate + MESSAGE_END(); + + pev->health--; + if ( pev->health > 0 ) + pev->nextthink = gpGlobals->time + RANDOM_FLOAT(0.1, 0.2); + else + UTIL_Remove( this->edict() ); +} + + +void CSpiral::Spawn( void ) +{ + pev->movetype = MOVETYPE_NONE; + pev->nextthink = gpGlobals->time; + pev->solid = SOLID_NOT; + UTIL_SetSize(pev, g_vecZero, g_vecZero ); + pev->effects |= EF_NODRAW; + pev->angles = g_vecZero; +} + + +CSpiral *CSpiral::Create( const Vector &origin, float height, float radius, float duration ) +{ + if ( duration <= 0 ) + return NULL; + + CSpiral *pSpiral = CreateClassPtr( (CSpiral *)NULL ); + pSpiral->Spawn(); + pSpiral->pev->dmgtime = pSpiral->pev->nextthink; + pSpiral->pev->origin = origin; + pSpiral->pev->scale = radius; + pSpiral->pev->dmg = height; + pSpiral->pev->speed = duration; + pSpiral->pev->health = 0; + pSpiral->pev->angles = g_vecZero; + + return pSpiral; +} + +#define SPIRAL_INTERVAL 0.1 //025 + +void CSpiral::Think( void ) +{ + float time = gpGlobals->time - pev->dmgtime; + + while ( time > SPIRAL_INTERVAL ) + { + Vector position = pev->origin; + Vector direction = Vector(0,0,1); + + float fraction = 1.0 / pev->speed; + + float radius = (pev->scale * pev->health) * fraction; + + position.z += (pev->health * pev->dmg) * fraction; + pev->angles.y = (pev->health * 360 * 8) * fraction; + UTIL_MakeVectors( pev->angles ); + position = position + gpGlobals->v_forward * radius; + direction = (direction + gpGlobals->v_forward).Normalize(); + + StreakSplash( position, Vector(0,0,1), RANDOM_LONG(8,11), 20, RANDOM_LONG(50,150), 400 ); + + // Jeez, how many counters should this take ? :) + pev->dmgtime += SPIRAL_INTERVAL; + pev->health += SPIRAL_INTERVAL; + time -= SPIRAL_INTERVAL; + } + + pev->nextthink = gpGlobals->time; + + if ( pev->health >= pev->speed ) + UTIL_Remove( this->edict() ); +} + + +void SpawnExplosion( Vector center, float randomRange, float time, int magnitude, edict_t *owner ) +{ + /* no need for this + KeyValueData kvd; + char buf[128]; + + CMBaseEntity *pExplosion = CreateClassPtr((CEnvExplosion *)NULL); // CMBaseEntity::Create( "env_explosion", center, g_vecZero, NULL ); + sprintf( buf, "%3d", magnitude ); + kvd.szKeyName = "iMagnitude"; + kvd.szValue = buf; + pExplosion->KeyValue( &kvd ); + pExplosion->pev->spawnflags |= SF_ENVEXPLOSION_NODAMAGE; + + pExplosion->Spawn(); + pExplosion->SetThink( &CMBaseEntity::SUB_CallUseToggle ); + pExplosion->pev->nextthink = gpGlobals->time + time; + */ + + center.x += RANDOM_FLOAT( -randomRange, randomRange ); + center.y += RANDOM_FLOAT( -randomRange, randomRange ); + + // explode.h + ExplosionCreate( center, g_vecZero, owner, magnitude, SF_ENVEXPLOSION_NODAMAGE, time ); +} + +/* + * The Sven Co-op's monster code was recreated from scratch. + * They do not contain their unique new attacks... YET. -Giegue + * */ + +//========================================================= +// Baby Gargantua +//========================================================= +const float BABYGARG_ATTACKDIST = 65.0; +#define BABYGARG_FLAME_LENGTH 180 + + +const char *CMBabyGargantua::pBeamAttackSounds[] = +{ + "babygarg/gar_flameoff1.wav", + "babygarg/gar_flameon1.wav", + "babygarg/gar_flamerun1.wav", +}; + +const char *CMBabyGargantua::pFootSounds[] = +{ + "babygarg/gar_step1.wav", + "babygarg/gar_step2.wav", +}; + +const char *CMBabyGargantua::pIdleSounds[] = +{ + "babygarg/gar_idle1.wav", + "babygarg/gar_idle2.wav", + "babygarg/gar_idle3.wav", + "babygarg/gar_idle4.wav", + "babygarg/gar_idle5.wav", +}; + +const char *CMBabyGargantua::pAttackSounds[] = +{ + "babygarg/gar_attack1.wav", + "babygarg/gar_attack2.wav", + "babygarg/gar_attack3.wav", +}; + +const char *CMBabyGargantua::pAlertSounds[] = +{ + "babygarg/gar_alert1.wav", + "babygarg/gar_alert2.wav", + "babygarg/gar_alert3.wav", +}; + +const char *CMBabyGargantua::pPainSounds[] = +{ + "babygarg/gar_pain1.wav", + "babygarg/gar_pain2.wav", + "babygarg/gar_pain3.wav", +}; + +const char *CMBabyGargantua::pStompSounds[] = +{ + "babygarg/gar_stomp1.wav", +}; + +const char *CMBabyGargantua::pBreatheSounds[] = +{ + "babygarg/gar_breathe1.wav", + "babygarg/gar_breathe2.wav", + "babygarg/gar_breathe3.wav", +}; + +const char *CMBabyGargantua::pDieSounds[] = +{ + "babygarg/gar_die1.wav", + "babygarg/gar_die2.wav", +}; + +//========================================================= +// Spawn +//========================================================= +void CMBabyGargantua::Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/babygarg.mdl"); + UTIL_SetSize( pev, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = gSkillData.babygargHealth; + //pev->view_ofs = Vector ( 0, 0, 96 );// taken from mdl file + m_flFieldOfView = -0.2;// width of forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + // Sven Co-op uses a modified gargeye1.spr for the eye and stomp effects. + // To economize precache count, we are going to recycle the normal garg's sprites. + + m_pEyeGlow = CMSprite::SpriteCreate( GARG_EYE_SPRITE_NAME, pev->origin, FALSE ); + m_pEyeGlow->SetTransparency( kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( edict(), 1 ); + EyeOff(); + m_seeTime = gpGlobals->time + 5; + m_flameTime = gpGlobals->time + 2; + + pev->classname = MAKE_STRING( "monster_babygarg" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Baby Gargantua" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMBabyGargantua::Precache() +{ + int i; + + PRECACHE_MODEL("models/babygarg.mdl"); + PRECACHE_MODEL( GARG_EYE_SPRITE_NAME ); + PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); + PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); + gStompSprite = PRECACHE_MODEL( GARG_STOMP_SPRITE_NAME ); + gGargGibModel = PRECACHE_MODEL( GARG_GIB_MODEL ); + PRECACHE_SOUND( GARG_STOMP_BUZZ_SOUND ); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pBeamAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pBeamAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pRicSounds ); i++ ) + PRECACHE_SOUND((char *)pRicSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pFootSounds ); i++ ) + PRECACHE_SOUND((char *)pFootSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pStompSounds ); i++ ) + PRECACHE_SOUND((char *)pStompSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pBreatheSounds ); i++ ) + PRECACHE_SOUND((char *)pBreatheSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pDieSounds ); i++ ) + PRECACHE_SOUND((char *)pDieSounds[i]); +} + +void CMBabyGargantua::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + ALERT( at_aiconsole, "CMBabyGargantua::TraceAttack\n"); + + if ( !IsAlive() ) + { + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); + return; + } + + if ( m_painSoundTime < gpGlobals->time ) + { + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); + m_painSoundTime = gpGlobals->time + RANDOM_FLOAT( 2.5, 4 ); + } + + // Override Gargantua's specific damage. Baby Garg has no protection from those. + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + +int CMBabyGargantua::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + if ( IsAlive() ) + { + // Always set + SetConditions( bits_COND_LIGHT_DAMAGE ); + } + + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMBabyGargantua::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch( pEvent->event ) + { + case GARG_AE_SLASH_LEFT: + { + // HACKHACK!!! + edict_t *pHurt = BabyGargCheckTraceHullAttack( BABYGARG_ATTACKDIST + 10.0, gSkillData.babygargDmgSlash, DMG_SLASH ); + if (pHurt) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + // Slightly lower numbers for babygarg (-20%) + pHurt->v.punchangle.x = -24; // pitch + pHurt->v.punchangle.y = -24; // yaw + pHurt->v.punchangle.z = 24; // roll + //UTIL_MakeVectors(pev->angles); // called by CheckTraceHullAttack + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 80; + } + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); + } + else // Play a random attack miss sound + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 50 + RANDOM_LONG(0,15) ); + + Vector forward; + UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL ); + } + break; + + case GARG_AE_RIGHT_FOOT: + case GARG_AE_LEFT_FOOT: + // babygarg does not shake the screen + EMIT_SOUND_DYN ( edict(), CHAN_BODY, pFootSounds[ RANDOM_LONG(0,ARRAYSIZE(pFootSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + break; + + case GARG_AE_STOMP: + StompAttack(); + m_seeTime = gpGlobals->time + 12; + break; + + case GARG_AE_BREATHE: + EMIT_SOUND_DYN ( edict(), CHAN_VOICE, pBreatheSounds[ RANDOM_LONG(0,ARRAYSIZE(pBreatheSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + break; + + default: + CMBaseMonster::HandleAnimEvent(pEvent); + break; + } +} + +//========================================================= +// CheckMeleeAttack1 +// Garg swipe attack +//========================================================= +BOOL CMBabyGargantua::CheckMeleeAttack1( float flDot, float flDist ) +{ +// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); + + if (flDot >= 0.7) + { + if (flDist <= BABYGARG_ATTACKDIST) + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckMeleeAttack2 +// Flame thrower madness! +//========================================================= +BOOL CMBabyGargantua::CheckMeleeAttack2( float flDot, float flDist ) +{ +// ALERT(at_aiconsole, "CheckMelee(%f, %f)\n", flDot, flDist); + + if ( gpGlobals->time > m_flameTime ) + { + if (flDot >= 0.8 && flDist > BABYGARG_ATTACKDIST) + { + if ( flDist <= BABYGARG_FLAME_LENGTH ) + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 +// Stomp attack +//========================================================= +BOOL CMBabyGargantua::CheckRangeAttack1( float flDot, float flDist ) +{ + if ( gpGlobals->time > m_seeTime ) + { + if (flDot >= 0.7 && flDist > BABYGARG_ATTACKDIST) + { + return TRUE; + } + } + return FALSE; +} + +void CMBabyGargantua::StartTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_FLAME_SWEEP: + FlameCreate(); + m_flWaitFinished = gpGlobals->time + pTask->flData; + m_flameTime = gpGlobals->time + 6; + m_flameX = 0; + m_flameY = 0; + break; + + case TASK_SOUND_ATTACK: + if ( RANDOM_LONG(0,100) < 30 ) + EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); + TaskComplete(); + break; + + case TASK_DIE: + // no death effect for babygarg, but give it a sound + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pDieSounds[ RANDOM_LONG(0,ARRAYSIZE(pDieSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM ); + default: + CMBaseMonster::StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CMBabyGargantua::RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + // babygarg does not explode upon death + case TASK_FLAME_SWEEP: + if ( gpGlobals->time > m_flWaitFinished ) + { + FlameDestroy(); + TaskComplete(); + FlameControls( 0, 0 ); + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + } + else + { + BOOL cancel = FALSE; + + Vector angles = g_vecZero; + + FlameUpdate(); + CMBaseEntity *pEnemy = CMBaseEntity::Instance( m_hEnemy.Get() ); + if ( pEnemy ) + { + Vector org = pev->origin; + org.z += 64; + Vector dir = pEnemy->BodyTarget(org) - org; + angles = UTIL_VecToAngles( dir ); + angles.x = -angles.x; + angles.y -= pev->angles.y; + if ( dir.Length() > 400 ) + cancel = TRUE; + } + if ( fabs(angles.y) > 60 ) + cancel = TRUE; + + if ( cancel ) + { + m_flWaitFinished -= 0.5; + m_flameTime -= 0.5; + } + // FlameControls( angles.x + 2 * sin(gpGlobals->time*8), angles.y + 28 * sin(gpGlobals->time*8.5) ); + FlameControls( angles.x, angles.y ); + } + break; + + default: + CMBaseMonster::RunTask( pTask ); + break; + } +} + +void CMBabyGargantua::StompAttack( void ) +{ + TraceResult trace; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin + Vector(0,0,60) + 35 * gpGlobals->v_forward; + Vector vecAim = ShootAtEnemy( vecStart ); + Vector vecEnd = (vecAim * 1024) + vecStart; + + UTIL_TraceLine( vecStart, vecEnd, ignore_monsters, edict(), &trace ); + CStomp::StompCreate( vecStart, trace.vecEndPos, 0, gSkillData.babygargDmgStomp ); + UTIL_ScreenShake( pev->origin, 9.6, 80.0, 1.8, 800 ); // -20% "power" to the babygarg's stomp + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pStompSounds[ RANDOM_LONG(0,ARRAYSIZE(pStompSounds)-1) ], 1.0, ATTN_GARG, 0, PITCH_NORM + RANDOM_LONG(-10,10) ); + + UTIL_TraceLine( pev->origin, pev->origin - Vector(0,0,20), ignore_monsters, edict(), &trace ); + if ( trace.flFraction < 1.0 ) + UTIL_DecalTrace( &trace, DECAL_GARGSTOMP1 ); +} + +void CMBabyGargantua::FlameCreate( void ) +{ + int i; + Vector posGun, angleGun; + TraceResult trace; + + UTIL_MakeVectors( pev->angles ); + + for ( i = 0; i < 4; i++ ) + { + if ( i < 2 ) + m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, 120 ); + else + m_pFlame[i] = CMBeam::BeamCreate( GARG_BEAM_SPRITE2, 70 ); + if ( m_pFlame[i] ) + { + int attach = i%2; + // attachment is 0 based in GetAttachment + GetAttachment( attach+1, posGun, angleGun ); + + Vector vecEnd = (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH) + posGun; + UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); + + m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); + if ( i < 2 ) + m_pFlame[i]->SetColor( 255, 130, 90 ); + else + m_pFlame[i]->SetColor( 0, 120, 255 ); + m_pFlame[i]->SetBrightness( 190 ); + m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); + m_pFlame[i]->SetScrollRate( 20 ); + // attachment is 1 based in SetEndAttachment + m_pFlame[i]->SetEndAttachment( attach + 2 ); + } + } + EMIT_SOUND_DYN ( edict(), CHAN_BODY, pBeamAttackSounds[ 1 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 2 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); +} + +void CMBabyGargantua::FlameUpdate( void ) +{ + int i; + static float offset[2] = { 60, -60 }; + TraceResult trace; + Vector vecStart, angleGun; + BOOL streaks = FALSE; + + for ( i = 0; i < 2; i++ ) + { + if ( m_pFlame[i] ) + { + Vector vecAim = pev->angles; + vecAim.x += m_flameX; + vecAim.y += m_flameY; + + UTIL_MakeVectors( vecAim ); + + GetAttachment( i+1, vecStart, angleGun ); + Vector vecEnd = vecStart + (gpGlobals->v_forward * BABYGARG_FLAME_LENGTH); // - offset[i] * gpGlobals->v_right; + + UTIL_TraceLine( vecStart, vecEnd, dont_ignore_monsters, edict(), &trace ); + + m_pFlame[i]->SetStartPos( trace.vecEndPos ); + m_pFlame[i+2]->SetStartPos( (vecStart * 0.6) + (trace.vecEndPos * 0.4) ); + + if ( trace.flFraction != 1.0 && gpGlobals->time > m_streakTime ) + { + StreakSplash( trace.vecEndPos, trace.vecPlaneNormal, 6, 20, 50, 400 ); + streaks = TRUE; + UTIL_DecalTrace( &trace, DECAL_SMALLSCORCH1 + RANDOM_LONG(0,2) ); + } + // RadiusDamage( trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN ); + FlameDamage( vecStart, trace.vecEndPos, pev, pev, gSkillData.babygargDmgFire, Classify(), DMG_BURN ); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex( ) + 0x1000 * (i + 2) ); // entity, attachment + WRITE_COORD( vecStart.x ); // origin + WRITE_COORD( vecStart.y ); + WRITE_COORD( vecStart.z ); + WRITE_COORD( RANDOM_FLOAT( 32, 48 ) ); // radius + WRITE_BYTE( 255 ); // R + WRITE_BYTE( 255 ); // G + WRITE_BYTE( 255 ); // B + WRITE_BYTE( 2 ); // life * 10 + WRITE_COORD( 0 ); // decay + MESSAGE_END(); + } + } + if ( streaks ) + m_streakTime = gpGlobals->time; +} + +void CMBabyGargantua::FlameDestroy( void ) +{ + int i; + + EMIT_SOUND_DYN ( edict(), CHAN_WEAPON, pBeamAttackSounds[ 0 ], 1.0, ATTN_NORM, 0, PITCH_NORM ); // sound must stop. + for ( i = 0; i < 4; i++ ) + { + if ( m_pFlame[i] ) + { + UTIL_Remove( m_pFlame[i]->edict() ); + m_pFlame[i] = NULL; + } + } +} + +//========================================================= +// CheckTraceHullAttack - expects a length to trace, amount +// of damage to do, and damage type. Returns a pointer to +// the damaged entity in case the monster wishes to do +// other stuff to the victim (punchangle, etc) +// Used for many contact-range melee attacks. Bites, claws, etc. + +// Overridden for Gargantua because his swing starts lower as +// a percentage of his height (otherwise he swings over the +// players head) + +// Also overriden for Baby Gargantua to prevent players from +// dodging the swing attacks by crouching. +//========================================================= +edict_t *CMBabyGargantua::BabyGargCheckTraceHullAttack(float flDist, int iDamage, int iDmgType) +{ + TraceResult tr; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin; + vecStart.z += 32; + Vector vecEnd = vecStart + (gpGlobals->v_forward * flDist) - (gpGlobals->v_up * flDist * 0.3); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit ) + { + if ( iDamage > 0 ) + { + if ( UTIL_IsPlayer( tr.pHit ) ) + UTIL_TakeDamage( tr.pHit, pev, pev, iDamage, iDmgType ); + else if ( tr.pHit->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(tr.pHit)); + pMonster->TakeDamage( pev, pev, iDamage, iDmgType ); + } + } + + return tr.pHit; + } + + return NULL; +} diff --git a/src/dlls/ggrenade.cpp b/src/dlls/ggrenade.cpp index 9b1ea51..f0dc470 100644 --- a/src/dlls/ggrenade.cpp +++ b/src/dlls/ggrenade.cpp @@ -1,466 +1,466 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== generic grenade.cpp ======================================================== - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "decals.h" -#include "explode.h" - - -//===================grenade - -// Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges -#define SF_DETONATE 0x0001 - -// -// Grenade Explode -// -void CMGrenade::Explode( Vector vecSrc, Vector vecAim ) -{ - TraceResult tr; - UTIL_TraceLine ( pev->origin, pev->origin + Vector ( 0, 0, -32 ), ignore_monsters, ENT(pev), & tr); - - Explode( &tr, DMG_BLAST ); -} - -// UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution. -void CMGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) -{ - float flRndSound;// sound randomizer - - pev->model = iStringNull;//invisible - pev->solid = SOLID_NOT;// intangible - - pev->takedamage = DAMAGE_NO; - - // Pull out of the wall a bit - if ( pTrace->flFraction != 1.0 ) - { - pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6); - } - - int iContents = UTIL_PointContents ( pev->origin ); - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound - WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - if (iContents != CONTENTS_WATER) - { - WRITE_SHORT( g_sModelIndexFireball ); - } - else - { - WRITE_SHORT( g_sModelIndexWExplosion ); - } - WRITE_BYTE( (pev->dmg - 50) * .60 ); // scale * 10 - WRITE_BYTE( 15 ); // framerate - WRITE_BYTE( TE_EXPLFLAG_NONE ); - MESSAGE_END(); - - entvars_t *pevOwner; - if ( pev->owner ) - pevOwner = VARS( pev->owner ); - else - pevOwner = NULL; - - pev->owner = NULL; // can't traceline attack owner if this is set - - RadiusDamage ( pev, pevOwner, pev->dmg, CLASS_NONE, bitsDamageType ); - - if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) - { - UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); - } - else - { - UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); - } - - flRndSound = RANDOM_FLOAT( 0 , 1 ); - - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break; - } - - pev->effects |= EF_NODRAW; - SetThink( &CMGrenade::Smoke ); - pev->velocity = g_vecZero; - pev->nextthink = gpGlobals->time + 0.3; - -/*jlb - if (iContents != CONTENTS_WATER) - { - int sparkCount = RANDOM_LONG(0,3); - for ( int i = 0; i < sparkCount; i++ ) - Create( "spark_shower", pev->origin, pTrace->vecPlaneNormal, NULL ); - } -jlb*/ -} - - -void CMGrenade::Smoke( void ) -{ - if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) - { - UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); - } - else - { - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( (pev->dmg - 50) * 0.80 ); // scale * 10 - WRITE_BYTE( 12 ); // framerate - MESSAGE_END(); - } - UTIL_Remove( this->edict() ); -} - -void CMGrenade::Killed( entvars_t *pevAttacker, int iGib ) -{ - Detonate( ); -} - - -// Timed grenade, this think is called when time runs out. -void CMGrenade::DetonateUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - SetThink( &CMGrenade::Detonate ); - pev->nextthink = gpGlobals->time; -} - -void CMGrenade::PreDetonate( void ) -{ - SetThink( &CMGrenade::Detonate ); - pev->nextthink = gpGlobals->time + 1; -} - - -void CMGrenade::Detonate( void ) -{ - TraceResult tr; - Vector vecSpot;// trace starts here! - - vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); - UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); - - Explode( &tr, DMG_BLAST ); -} - - -// -// Contact grenade, explode when it touches something -// -void CMGrenade::ExplodeTouch( edict_t *pOther ) -{ - TraceResult tr; - Vector vecSpot;// trace starts here! - - pev->enemy = pOther; - - vecSpot = pev->origin - pev->velocity.Normalize() * 32; - UTIL_TraceLine( vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr ); - - Explode( &tr, DMG_BLAST ); -} - - -void CMGrenade::DangerSoundThink( void ) -{ - if (!IsInWorld()) - { - UTIL_Remove( this->edict() ); - return; - } - - pev->nextthink = gpGlobals->time + 0.2; - - if (pev->waterlevel != 0) - { - pev->velocity = pev->velocity * 0.5; - } -} - - -void CMGrenade::BounceTouch( edict_t *pOther ) -{ - // don't hit the guy that launched this grenade - if ( pOther == pev->owner ) - return; - - // only do damage if we're moving fairly fast - if (m_flNextAttack < gpGlobals->time && pev->velocity.Length() > 100) - { - entvars_t *pevOwner = VARS( pev->owner ); - if (pevOwner) - { - TraceResult tr = UTIL_GetGlobalTrace( ); - ClearMultiDamage( ); - - if (UTIL_IsPlayer(pOther)) - UTIL_TraceAttack(pOther, pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB ); - } - - ApplyMultiDamage( pev, pevOwner); - } - m_flNextAttack = gpGlobals->time + 1.0; // debounce - } - - Vector vecTestVelocity; - // pev->avelocity = Vector (300, 300, 300); - - // this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical - // or thrown very far tend to slow down too quickly for me to always catch just by testing velocity. - // trimming the Z velocity a bit seems to help quite a bit. - vecTestVelocity = pev->velocity; - vecTestVelocity.z *= 0.45; - - if ( !m_fRegisteredSound && vecTestVelocity.Length() <= 60 ) - { - //ALERT( at_console, "Grenade Registered!: %f\n", vecTestVelocity.Length() ); - - // grenade is moving really slow. It's probably very close to where it will ultimately stop moving. - // go ahead and emit the danger sound. - - // register a radius louder than the explosion, so we make sure everyone gets out of the way - m_fRegisteredSound = TRUE; - } - - if (pev->flags & FL_ONGROUND) - { - // add a bit of static friction - pev->velocity = pev->velocity * 0.8; - - pev->sequence = RANDOM_LONG( 1, 1 ); - } - else - { - // play bounce sound - BounceSound(); - } - pev->framerate = pev->velocity.Length() / 200.0; - if (pev->framerate > 1.0) - pev->framerate = 1; - else if (pev->framerate < 0.5) - pev->framerate = 0; - -} - - - -void CMGrenade::SlideTouch( edict_t *pOther ) -{ - // don't hit the guy that launched this grenade - if ( pOther == pev->owner ) - return; - - // pev->avelocity = Vector (300, 300, 300); - - if (pev->flags & FL_ONGROUND) - { - // add a bit of static friction - pev->velocity = pev->velocity * 0.95; - - if (pev->velocity.x != 0 || pev->velocity.y != 0) - { - // maintain sliding sound - } - } - else - { - BounceSound(); - } -} - -void CMGrenade :: BounceSound( void ) -{ - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit2.wav", 0.25, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit3.wav", 0.25, ATTN_NORM); break; - } -} - -void CMGrenade :: TumbleThink( void ) -{ - if (!IsInWorld()) - { - UTIL_Remove( this->edict() ); - return; - } - - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - if (pev->dmgtime <= gpGlobals->time) - { - SetThink( &CMGrenade::Detonate ); - } - if (pev->waterlevel != 0) - { - pev->velocity = pev->velocity * 0.5; - pev->framerate = 0.2; - } -} - - -void CMGrenade:: Spawn( void ) -{ - pev->movetype = MOVETYPE_BOUNCE; - pev->classname = MAKE_STRING( "grenade" ); - - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "models/grenade.mdl"); - UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - - pev->dmg = 100; - m_fRegisteredSound = FALSE; -} - - -CMGrenade *CMGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) -{ - CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); - - if (pGrenade == NULL) // no free monster edicts left? - return NULL; - - pGrenade->Spawn(); - // contact grenades arc lower - pGrenade->pev->gravity = 0.5;// lower gravity since grenade is aerodynamic and engine doesn't know it. - UTIL_SetOrigin( pGrenade->pev, vecStart ); - pGrenade->pev->velocity = vecVelocity; - pGrenade->pev->angles = UTIL_VecToAngles (pGrenade->pev->velocity); - pGrenade->pev->owner = ENT(pevOwner); - - // make monsters afaid of it while in the air - pGrenade->SetThink( &CMGrenade::DangerSoundThink ); - pGrenade->pev->nextthink = gpGlobals->time; - - // Tumble in air - pGrenade->pev->avelocity.x = RANDOM_FLOAT ( -100, -500 ); - - // Explode on contact - pGrenade->SetTouch( &CMGrenade::ExplodeTouch ); - - pGrenade->pev->dmg = gSkillData.monDmgM203Grenade; - - return pGrenade; -} - - -CMGrenade * CMGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ) -{ - CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); - - if (pGrenade == NULL) // no free monster edicts left? - return NULL; - - pGrenade->Spawn(); - UTIL_SetOrigin( pGrenade->pev, vecStart ); - pGrenade->pev->velocity = vecVelocity; - pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); - pGrenade->pev->owner = ENT(pevOwner); - - pGrenade->SetTouch( &CMGrenade::BounceTouch ); // Bounce if touched - - // Take one second off of the desired detonation time and set the think to PreDetonate. PreDetonate - // will insert a DANGER sound into the world sound list and delay detonation for one second so that - // the grenade explodes after the exact amount of time specified in the call to ShootTimed(). - - pGrenade->pev->dmgtime = gpGlobals->time + time; - pGrenade->SetThink( &CMGrenade::TumbleThink ); - pGrenade->pev->nextthink = gpGlobals->time + 0.1; - if (time < 0.1) - { - pGrenade->pev->nextthink = gpGlobals->time; - pGrenade->pev->velocity = Vector( 0, 0, 0 ); - } - - pGrenade->pev->sequence = RANDOM_LONG( 3, 6 ); - pGrenade->pev->framerate = 1.0; - - // Tumble through the air - // pGrenade->pev->avelocity.x = -400; - - pGrenade->pev->gravity = 0.5; - pGrenade->pev->friction = 0.8; - - SET_MODEL(ENT(pGrenade->pev), "models/w_grenade.mdl"); - pGrenade->pev->dmg = 100; - - return pGrenade; -} - - -CMGrenade * CMGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) -{ - CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); - - if (pGrenade == NULL) // no free monster edicts left? - return NULL; - - pGrenade->pev->movetype = MOVETYPE_BOUNCE; - pGrenade->pev->classname = MAKE_STRING( "grenade" ); - - pGrenade->pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pGrenade->pev), "models/grenade.mdl"); // Change this to satchel charge model - - UTIL_SetSize(pGrenade->pev, Vector( 0, 0, 0), Vector(0, 0, 0)); - - pGrenade->pev->dmg = 200; - UTIL_SetOrigin( pGrenade->pev, vecStart ); - pGrenade->pev->velocity = vecVelocity; - pGrenade->pev->angles = g_vecZero; - pGrenade->pev->owner = ENT(pevOwner); - - // Detonate in "time" seconds - pGrenade->SetThink( &CMGrenade::SUB_DoNothing ); - pGrenade->SetUse( &CMGrenade::DetonateUse ); - pGrenade->SetTouch( &CMGrenade::SlideTouch ); - pGrenade->pev->spawnflags = SF_DETONATE; - - pGrenade->pev->friction = 0.9; - - return pGrenade; -} - -//======================end grenade - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== generic grenade.cpp ======================================================== + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "decals.h" +#include "explode.h" + + +//===================grenade + +// Grenades flagged with this will be triggered when the owner calls detonateSatchelCharges +#define SF_DETONATE 0x0001 + +// +// Grenade Explode +// +void CMGrenade::Explode( Vector vecSrc, Vector vecAim ) +{ + TraceResult tr; + UTIL_TraceLine ( pev->origin, pev->origin + Vector ( 0, 0, -32 ), ignore_monsters, ENT(pev), & tr); + + Explode( &tr, DMG_BLAST ); +} + +// UNDONE: temporary scorching for PreAlpha - find a less sleazy permenant solution. +void CMGrenade::Explode( TraceResult *pTrace, int bitsDamageType ) +{ + float flRndSound;// sound randomizer + + pev->model = iStringNull;//invisible + pev->solid = SOLID_NOT;// intangible + + pev->takedamage = DAMAGE_NO; + + // Pull out of the wall a bit + if ( pTrace->flFraction != 1.0 ) + { + pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6); + } + + int iContents = UTIL_PointContents ( pev->origin ); + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound + WRITE_COORD( pev->origin.x ); // Send to PAS because of the sound + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + if (iContents != CONTENTS_WATER) + { + WRITE_SHORT( g_sModelIndexFireball ); + } + else + { + WRITE_SHORT( g_sModelIndexWExplosion ); + } + WRITE_BYTE( (pev->dmg - 50) * .60 ); // scale * 10 + WRITE_BYTE( 15 ); // framerate + WRITE_BYTE( TE_EXPLFLAG_NONE ); + MESSAGE_END(); + + entvars_t *pevOwner; + if ( pev->owner ) + pevOwner = VARS( pev->owner ); + else + pevOwner = NULL; + + pev->owner = NULL; // can't traceline attack owner if this is set + + RadiusDamage ( pev, pevOwner, pev->dmg, CLASS_NONE, bitsDamageType ); + + if ( RANDOM_FLOAT( 0 , 1 ) < 0.5 ) + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); + } + else + { + UTIL_DecalTrace( pTrace, DECAL_SCORCH2 ); + } + + flRndSound = RANDOM_FLOAT( 0 , 1 ); + + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris1.wav", 0.55, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris2.wav", 0.55, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/debris3.wav", 0.55, ATTN_NORM); break; + } + + pev->effects |= EF_NODRAW; + SetThink( &CMGrenade::Smoke ); + pev->velocity = g_vecZero; + pev->nextthink = gpGlobals->time + 0.3; + +/*jlb + if (iContents != CONTENTS_WATER) + { + int sparkCount = RANDOM_LONG(0,3); + for ( int i = 0; i < sparkCount; i++ ) + Create( "spark_shower", pev->origin, pTrace->vecPlaneNormal, NULL ); + } +jlb*/ +} + + +void CMGrenade::Smoke( void ) +{ + if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) + { + UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); + } + else + { + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( (pev->dmg - 50) * 0.80 ); // scale * 10 + WRITE_BYTE( 12 ); // framerate + MESSAGE_END(); + } + UTIL_Remove( this->edict() ); +} + +void CMGrenade::Killed( entvars_t *pevAttacker, int iGib ) +{ + Detonate( ); +} + + +// Timed grenade, this think is called when time runs out. +void CMGrenade::DetonateUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + SetThink( &CMGrenade::Detonate ); + pev->nextthink = gpGlobals->time; +} + +void CMGrenade::PreDetonate( void ) +{ + SetThink( &CMGrenade::Detonate ); + pev->nextthink = gpGlobals->time + 1; +} + + +void CMGrenade::Detonate( void ) +{ + TraceResult tr; + Vector vecSpot;// trace starts here! + + vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); + UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); + + Explode( &tr, DMG_BLAST ); +} + + +// +// Contact grenade, explode when it touches something +// +void CMGrenade::ExplodeTouch( edict_t *pOther ) +{ + TraceResult tr; + Vector vecSpot;// trace starts here! + + pev->enemy = pOther; + + vecSpot = pev->origin - pev->velocity.Normalize() * 32; + UTIL_TraceLine( vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr ); + + Explode( &tr, DMG_BLAST ); +} + + +void CMGrenade::DangerSoundThink( void ) +{ + if (!IsInWorld()) + { + UTIL_Remove( this->edict() ); + return; + } + + pev->nextthink = gpGlobals->time + 0.2; + + if (pev->waterlevel != 0) + { + pev->velocity = pev->velocity * 0.5; + } +} + + +void CMGrenade::BounceTouch( edict_t *pOther ) +{ + // don't hit the guy that launched this grenade + if ( pOther == pev->owner ) + return; + + // only do damage if we're moving fairly fast + if (m_flNextAttack < gpGlobals->time && pev->velocity.Length() > 100) + { + entvars_t *pevOwner = VARS( pev->owner ); + if (pevOwner) + { + TraceResult tr = UTIL_GetGlobalTrace( ); + ClearMultiDamage( ); + + if (UTIL_IsPlayer(pOther)) + UTIL_TraceAttack(pOther, pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TraceAttack(pevOwner, 1, gpGlobals->v_forward, &tr, DMG_CLUB ); + } + + ApplyMultiDamage( pev, pevOwner); + } + m_flNextAttack = gpGlobals->time + 1.0; // debounce + } + + Vector vecTestVelocity; + // pev->avelocity = Vector (300, 300, 300); + + // this is my heuristic for modulating the grenade velocity because grenades dropped purely vertical + // or thrown very far tend to slow down too quickly for me to always catch just by testing velocity. + // trimming the Z velocity a bit seems to help quite a bit. + vecTestVelocity = pev->velocity; + vecTestVelocity.z *= 0.45; + + if ( !m_fRegisteredSound && vecTestVelocity.Length() <= 60 ) + { + //ALERT( at_console, "Grenade Registered!: %f\n", vecTestVelocity.Length() ); + + // grenade is moving really slow. It's probably very close to where it will ultimately stop moving. + // go ahead and emit the danger sound. + + // register a radius louder than the explosion, so we make sure everyone gets out of the way + m_fRegisteredSound = TRUE; + } + + if (pev->flags & FL_ONGROUND) + { + // add a bit of static friction + pev->velocity = pev->velocity * 0.8; + + pev->sequence = RANDOM_LONG( 1, 1 ); + } + else + { + // play bounce sound + BounceSound(); + } + pev->framerate = pev->velocity.Length() / 200.0; + if (pev->framerate > 1.0) + pev->framerate = 1; + else if (pev->framerate < 0.5) + pev->framerate = 0; + +} + + + +void CMGrenade::SlideTouch( edict_t *pOther ) +{ + // don't hit the guy that launched this grenade + if ( pOther == pev->owner ) + return; + + // pev->avelocity = Vector (300, 300, 300); + + if (pev->flags & FL_ONGROUND) + { + // add a bit of static friction + pev->velocity = pev->velocity * 0.95; + + if (pev->velocity.x != 0 || pev->velocity.y != 0) + { + // maintain sliding sound + } + } + else + { + BounceSound(); + } +} + +void CMGrenade :: BounceSound( void ) +{ + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit1.wav", 0.25, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit2.wav", 0.25, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/grenade_hit3.wav", 0.25, ATTN_NORM); break; + } +} + +void CMGrenade :: TumbleThink( void ) +{ + if (!IsInWorld()) + { + UTIL_Remove( this->edict() ); + return; + } + + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + if (pev->dmgtime <= gpGlobals->time) + { + SetThink( &CMGrenade::Detonate ); + } + if (pev->waterlevel != 0) + { + pev->velocity = pev->velocity * 0.5; + pev->framerate = 0.2; + } +} + + +void CMGrenade:: Spawn( void ) +{ + pev->movetype = MOVETYPE_BOUNCE; + pev->classname = MAKE_STRING( "grenade" ); + + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/grenade.mdl"); + UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + + pev->dmg = 100; + m_fRegisteredSound = FALSE; +} + + +CMGrenade *CMGrenade::ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) +{ + CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); + + if (pGrenade == NULL) // no free monster edicts left? + return NULL; + + pGrenade->Spawn(); + // contact grenades arc lower + pGrenade->pev->gravity = 0.5;// lower gravity since grenade is aerodynamic and engine doesn't know it. + UTIL_SetOrigin( pGrenade->pev, vecStart ); + pGrenade->pev->velocity = vecVelocity; + pGrenade->pev->angles = UTIL_VecToAngles (pGrenade->pev->velocity); + pGrenade->pev->owner = ENT(pevOwner); + + // make monsters afaid of it while in the air + pGrenade->SetThink( &CMGrenade::DangerSoundThink ); + pGrenade->pev->nextthink = gpGlobals->time; + + // Tumble in air + pGrenade->pev->avelocity.x = RANDOM_FLOAT ( -100, -500 ); + + // Explode on contact + pGrenade->SetTouch( &CMGrenade::ExplodeTouch ); + + pGrenade->pev->dmg = gSkillData.monDmgM203Grenade; + + return pGrenade; +} + + +CMGrenade * CMGrenade:: ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ) +{ + CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); + + if (pGrenade == NULL) // no free monster edicts left? + return NULL; + + pGrenade->Spawn(); + UTIL_SetOrigin( pGrenade->pev, vecStart ); + pGrenade->pev->velocity = vecVelocity; + pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); + pGrenade->pev->owner = ENT(pevOwner); + + pGrenade->SetTouch( &CMGrenade::BounceTouch ); // Bounce if touched + + // Take one second off of the desired detonation time and set the think to PreDetonate. PreDetonate + // will insert a DANGER sound into the world sound list and delay detonation for one second so that + // the grenade explodes after the exact amount of time specified in the call to ShootTimed(). + + pGrenade->pev->dmgtime = gpGlobals->time + time; + pGrenade->SetThink( &CMGrenade::TumbleThink ); + pGrenade->pev->nextthink = gpGlobals->time + 0.1; + if (time < 0.1) + { + pGrenade->pev->nextthink = gpGlobals->time; + pGrenade->pev->velocity = Vector( 0, 0, 0 ); + } + + pGrenade->pev->sequence = RANDOM_LONG( 3, 6 ); + pGrenade->pev->framerate = 1.0; + + // Tumble through the air + // pGrenade->pev->avelocity.x = -400; + + pGrenade->pev->gravity = 0.5; + pGrenade->pev->friction = 0.8; + + SET_MODEL(ENT(pGrenade->pev), "models/w_grenade.mdl"); + pGrenade->pev->dmg = 100; + + return pGrenade; +} + + +CMGrenade * CMGrenade :: ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) +{ + CMGrenade *pGrenade = CreateClassPtr( (CMGrenade *)NULL ); + + if (pGrenade == NULL) // no free monster edicts left? + return NULL; + + pGrenade->pev->movetype = MOVETYPE_BOUNCE; + pGrenade->pev->classname = MAKE_STRING( "grenade" ); + + pGrenade->pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pGrenade->pev), "models/grenade.mdl"); // Change this to satchel charge model + + UTIL_SetSize(pGrenade->pev, Vector( 0, 0, 0), Vector(0, 0, 0)); + + pGrenade->pev->dmg = 200; + UTIL_SetOrigin( pGrenade->pev, vecStart ); + pGrenade->pev->velocity = vecVelocity; + pGrenade->pev->angles = g_vecZero; + pGrenade->pev->owner = ENT(pevOwner); + + // Detonate in "time" seconds + pGrenade->SetThink( &CMGrenade::SUB_DoNothing ); + pGrenade->SetUse( &CMGrenade::DetonateUse ); + pGrenade->SetTouch( &CMGrenade::SlideTouch ); + pGrenade->pev->spawnflags = SF_DETONATE; + + pGrenade->pev->friction = 0.9; + + return pGrenade; +} + +//======================end grenade + diff --git a/src/dlls/gonome.cpp b/src/dlls/gonome.cpp index 8dbd73f..8075178 100644 --- a/src/dlls/gonome.cpp +++ b/src/dlls/gonome.cpp @@ -1,881 +1,881 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - -//========================================================= -// Gonome.cpp -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "decals.h" -#include "nodes.h" - -#define GONOME_MELEE_ATTACK_RADIUS 70 - -enum -{ - TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1 -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= - -#define GONOME_AE_SLASH_RIGHT ( 1 ) -#define GONOME_AE_SLASH_LEFT ( 2 ) -#define GONOME_AE_SPIT ( 3 ) -#define GONOME_AE_THROW ( 4 ) - -#define GONOME_AE_BITE1 ( 19 ) -#define GONOME_AE_BITE2 ( 20 ) -#define GONOME_AE_BITE3 ( 21 ) -#define GONOME_AE_BITE4 ( 22 ) - -#define GONOME_SCRIPT_EVENT_SOUND ( 1011 ) - -void CGonomeGuts :: Spawn( void ) -{ - pev->movetype = MOVETYPE_FLY; - pev->classname = MAKE_STRING( "gonomeguts" ); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - - SET_MODEL( ENT( pev ), "sprites/bigspit.spr" ); - pev->frame = 0; - pev->scale = 0.5; - pev->rendercolor.x = 255; - - UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); - - m_maxFrame = (float)MODEL_FRAMES( pev->modelindex ) - 1; -} - -void CGonomeGuts :: Animate( void ) -{ - pev->nextthink = gpGlobals->time + 0.1; - - if ( pev->frame++ ) - { - if ( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } -} - -edict_t *CGonomeGuts :: Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) -{ - CGonomeGuts *pSpit = CreateClassPtr( (CGonomeGuts *)NULL ); - - if (pSpit == NULL) - return NULL; - - pSpit->Spawn(); - - UTIL_SetOrigin( pSpit->pev, vecStart ); - pSpit->pev->velocity = vecVelocity; - pSpit->pev->owner = ENT(pevOwner); - - pSpit->SetThink ( &CGonomeGuts::Animate ); - pSpit->pev->nextthink = gpGlobals->time + 0.1; - pSpit->SetTouch ( &CGonomeGuts::GutsTouch ); - - return pSpit->edict(); -} - -void CGonomeGuts :: GutsTouch( edict_t *pOther ) -{ - TraceResult tr; - int iPitch; - - // splat sound - iPitch = RANDOM_FLOAT( 90, 110 ); - - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); - - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); - break; - } - - if( !pOther->v.takedamage ) - { - // make a splat on the wall - UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); - UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); - UTIL_BloodDrips( tr.vecEndPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); - } - else - { - if (UTIL_IsPlayer(pOther)) - UTIL_TakeDamage( pOther, pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage ( pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC ); - } - } - - SetThink( &CGonomeGuts::SUB_Remove ); - pev->nextthink = gpGlobals->time; -} - - -const char* CMGonome::pPainSounds[] = { - "gonome/gonome_pain1.wav", - "gonome/gonome_pain2.wav", - "gonome/gonome_pain3.wav", - "gonome/gonome_pain4.wav" -}; - -const char* CMGonome::pIdleSounds[] = { - "gonome/gonome_idle1.wav", - "gonome/gonome_idle2.wav", - "gonome/gonome_idle3.wav" -}; - -const char* CMGonome::pDeathSounds[] = { - "gonome/gonome_death2.wav", - "gonome/gonome_death3.wav", - "gonome/gonome_death4.wav" -}; - -const char* CMGonome::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char* CMGonome::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - - -void CMGonome::Killed(entvars_t *pevAttacker, int iGib) -{ - ClearGuts(); - UnlockPlayer(); - CMBaseMonster::Killed(pevAttacker, iGib); -} - -void CMGonome::UnlockPlayer() -{ - if (m_fPlayerLocked) - { - edict_t *player = 0; - if (m_lockedPlayer != 0 && UTIL_IsPlayer(m_lockedPlayer)) - player = m_lockedPlayer; - else // if ehandle is empty for some reason just unlock the first player - player = UTIL_FindEntityByClassname(0, "player"); - - if (player) - player->v.flags &= ~FL_FROZEN; - - m_lockedPlayer = 0; - m_fPlayerLocked = FALSE; - } -} - -CGonomeGuts* CMGonome::GetGonomeGuts(entvars_t *pevOwner, const Vector &pos) -{ - if (m_pGonomeGuts) - return m_pGonomeGuts; - edict_t *pEdict = CGonomeGuts::Shoot( pevOwner, g_vecZero, g_vecZero ); - CGonomeGuts *pGuts = GetClassPtr((CGonomeGuts*)VARS(pEdict)); - pGuts->Spawn(); - - UTIL_SetOrigin( pGuts->pev, pos ); - - m_pGonomeGuts = pGuts; - return m_pGonomeGuts; -} - -void CMGonome::ClearGuts() -{ - if (m_pGonomeGuts) - { - UTIL_Remove( m_pGonomeGuts->edict() ); - m_pGonomeGuts = 0; - } -} - -void CMGonome::PainSound( void ) -{ - int pitch = 95 + RANDOM_LONG( 0, 9 ); - - if( RANDOM_LONG( 0, 5 ) < 2 ) - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1.0, ATTN_NORM, 0, pitch ); -} - -void CMGonome::DeathSound( void ) -{ - int pitch = 95 + RANDOM_LONG( 0, 9 ); - - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1.0, ATTN_NORM, 0, pitch ); -} - -void CMGonome::IdleSound( void ) -{ - int pitch = 95 + RANDOM_LONG( 0, 9 ); - - // Play a random idle sound - EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1.0, ATTN_NORM, 0, pitch ); -} - -void CMGonome::AlertSound( void ) -{ - const int iPitch = RANDOM_LONG(0, 9) + 95; - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM, 0, iPitch); -} - -void CMGonome::SetActivity( Activity NewActivity ) -{ - Activity OldActivity = m_Activity; - int iSequence = ACTIVITY_NOT_AVAILABLE; - - if (NewActivity != ACT_RANGE_ATTACK1) - { - ClearGuts(); - } - if (NewActivity == ACT_MELEE_ATTACK1 && m_hEnemy != 0) - { - // special melee animations - if ((pev->origin - m_hEnemy->v.origin).Length2D() >= 48 ) - { - m_meleeAttack2 = false; - iSequence = LookupSequence("attack1"); - } - else - { - m_meleeAttack2 = true; - iSequence = LookupSequence("attack2"); - } - } - else - { - UnlockPlayer(); - - if (NewActivity == ACT_RUN && m_hEnemy != 0) - { - // special run animations - if ((pev->origin - m_hEnemy->v.origin).Length2D() <= 512 ) - { - iSequence = LookupSequence("runshort"); - } - else - { - iSequence = LookupSequence("runlong"); - } - } - else - { - iSequence = LookupActivity(NewActivity); - } - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // In case someone calls this with something other than the ideal activity - m_IdealActivity = m_Activity; - - // Set to the desired anim, or default anim if the desired is not present - if( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if( pev->sequence != iSequence || !m_fSequenceLoops ) - { - // don't reset frame between walk and run - if( !( OldActivity == ACT_WALK || OldActivity == ACT_RUN ) || !( NewActivity == ACT_WALK || NewActivity == ACT_RUN ) ) - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo(); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT( at_aiconsole, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMGonome::Classify(void) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// TakeDamage - overridden for gonome so we can keep track -// of how much time has passed since it was last injured -//========================================================= -int CMGonome::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - // Take 15% damage from bullets - if( bitsDamageType == DMG_BULLET ) - { - Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; - vecDir = vecDir.Normalize(); - float flForce = DamageForce( flDamage ); - pev->velocity = pev->velocity + vecDir * flForce; - flDamage *= 0.15; - } - - // HACK HACK -- until we fix this. - if( IsAlive() ) - PainSound(); - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMGonome::CheckRangeAttack1(float flDot, float flDist) -{ - if (flDist < 256) - return FALSE; - - if (IsMoving() && flDist >= 512) - { - // squid will far too far behind if he stops running to spit at this distance from the enemy. - return FALSE; - } - - if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextThrowTime) - { - if (m_hEnemy != 0) - { - if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256) - { - // don't try to spit at someone up really high or down really low. - return FALSE; - } - } - - if (IsMoving()) - { - // don't spit again for a long time, resume chasing enemy. - m_flNextThrowTime = gpGlobals->time + 5; - } - else - { - // not moving, so spit again pretty soon. - m_flNextThrowTime = gpGlobals->time + 0.5; - } - - return TRUE; - } - - return FALSE; -} - -//========================================================= -// CheckMeleeAttack2 - both gonome's melee attacks are ACT_MELEE_ATTACK1 -//========================================================= -BOOL CMGonome::CheckMeleeAttack2(float flDot, float flDist) -{ - return FALSE; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMGonome::SetYawSpeed( void ) -{ - pev->yaw_speed = 120; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMGonome::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch (pEvent->event) - { - case GONOME_SCRIPT_EVENT_SOUND: - if (m_Activity != ACT_MELEE_ATTACK1) - EMIT_SOUND(ENT(pev), CHAN_BODY, pEvent->options, 1, ATTN_NORM); - break; - case GONOME_AE_SPIT: - { - Vector vecArmPos, vecArmAng; - GetAttachment(0, vecArmPos, vecArmAng); - - if (GetGonomeGuts(pev, vecArmPos)) - { - m_pGonomeGuts->pev->skin = entindex(); - m_pGonomeGuts->pev->body = 1; - m_pGonomeGuts->pev->aiment = ENT(pev); - m_pGonomeGuts->pev->movetype = MOVETYPE_FOLLOW; - } - UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); - } - break; - case GONOME_AE_THROW: - { - UTIL_MakeVectors(pev->angles); - Vector vecArmPos, vecArmAng; - GetAttachment(0, vecArmPos, vecArmAng); - - if (GetGonomeGuts(pev, vecArmPos)) - { - Vector vecSpitDir; - - Vector vecEnemyPosition; - if (m_hEnemy != 0) - vecEnemyPosition = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs); - else - vecEnemyPosition = m_vecEnemyLKP; - vecSpitDir = (vecEnemyPosition - vecArmPos).Normalize(); - - vecSpitDir.x += RANDOM_FLOAT(-0.05, 0.05); - vecSpitDir.y += RANDOM_FLOAT(-0.05, 0.05); - vecSpitDir.z += RANDOM_FLOAT(-0.05, 0); - - m_pGonomeGuts->pev->body = 0; - m_pGonomeGuts->pev->skin = 0; - m_pGonomeGuts->pev->owner = ENT( pev ); - m_pGonomeGuts->pev->aiment = 0; - m_pGonomeGuts->pev->movetype = MOVETYPE_FLY; - m_pGonomeGuts->pev->velocity = vecSpitDir * 900; - m_pGonomeGuts->SetThink( &CGonomeGuts::Animate ); - m_pGonomeGuts->pev->nextthink = gpGlobals->time + 0.1; - UTIL_SetOrigin(m_pGonomeGuts->pev, vecArmPos); - - m_pGonomeGuts = 0; - } - UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); - } - break; - - case GONOME_AE_SLASH_LEFT: - { - edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.z = 9; - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 25; - } - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); - } - else - { - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); - } - } - break; - - case GONOME_AE_SLASH_RIGHT: - { - edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.z = -9; - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -25; - } - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); - } - else - { - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); - } - } - break; - - case GONOME_AE_BITE1: - case GONOME_AE_BITE2: - case GONOME_AE_BITE3: - case GONOME_AE_BITE4: - { - int iPitch; - edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneBite, DMG_SLASH); - - if (pHurt) - { - // croonchy bite sound - iPitch = RANDOM_FLOAT(90, 110); - switch (RANDOM_LONG(0, 1)) - { - case 0: - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch); - break; - case 1: - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch); - break; - } - - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - if (pEvent->event == GONOME_AE_BITE4) - { - pHurt->v.punchangle.x = 15; - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 75; - } - else - { - pHurt->v.punchangle.x = 9; - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 25; - } - } - // lock player - if (pEvent->event == GONOME_AE_BITE4) - { - UnlockPlayer(); - } - else if (UTIL_IsPlayer( pHurt ) && UTIL_IsAlive( pHurt )) - { - if (!m_fPlayerLocked) - { - edict_t *player = pHurt; - player->v.flags |= FL_FROZEN; - m_lockedPlayer = player; - m_fPlayerLocked = TRUE; - } - } - } - } - break; - - default: - CMBaseMonster::HandleAnimEvent(pEvent); - } -} - -#define GONOME_FLINCH_DELAY 2 - -int CMGonome::IgnoreConditions( void ) -{ - int iIgnore = CMBaseMonster::IgnoreConditions(); - - if( m_Activity == ACT_MELEE_ATTACK1 ) - { - if( m_flNextFlinch >= gpGlobals->time ) - iIgnore |= ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); - } - - if( ( m_Activity == ACT_SMALL_FLINCH ) || ( m_Activity == ACT_BIG_FLINCH ) ) - { - if( m_flNextFlinch < gpGlobals->time ) - m_flNextFlinch = gpGlobals->time + GONOME_FLINCH_DELAY; - } - - return iIgnore; -} - -//========================================================= -// Spawn -//========================================================= -void CMGonome::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/gonome.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.gonomeHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_flNextThrowTime = gpGlobals->time; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_gonome" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Gonome" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMGonome::Precache() -{ - PRECACHE_MODEL("models/gonome.mdl"); - - PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - PRECACHE_SOUND("gonome/gonome_eat.wav"); - PRECACHE_SOUND("gonome/gonome_jumpattack.wav"); - PRECACHE_SOUND("gonome/gonome_melee1.wav"); - PRECACHE_SOUND("gonome/gonome_melee2.wav"); - - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pDeathSounds); - - PRECACHE_SOUND("gonome/gonome_run.wav"); - - PRECACHE_SOUND("bullchicken/bc_acid1.wav"); - - PRECACHE_SOUND("bullchicken/bc_bite2.wav"); - PRECACHE_SOUND("bullchicken/bc_bite3.wav"); - - PRECACHE_SOUND("bullchicken/bc_spithit1.wav"); - PRECACHE_SOUND("bullchicken/bc_spithit2.wav"); -} - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMGonome::GetSchedule( void ) -{ - switch( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { - // dead enemy - if( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster::GetSchedule(); - } - - if( HasConditions( bits_COND_NEW_ENEMY ) ) - { - return GetScheduleOfType( SCHED_WAKE_ANGRY ); - } - - if( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); - } - - if( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); - } - - if( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) - { - return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); - } - - return GetScheduleOfType( SCHED_CHASE_ENEMY ); - break; - } - default: - break; - } - - return CMBaseMonster::GetSchedule(); -} - -// primary range attack -Task_t tlGonomeRangeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slGonomeRangeAttack1[] = -{ - { - tlGonomeRangeAttack1, - ARRAYSIZE( tlGonomeRangeAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "Gonome Range Attack1" - }, -}; - -// Chase enemy schedule -Task_t tlGonomeChaseEnemy1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation. - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slGonomeChaseEnemy[] = -{ - { - tlGonomeChaseEnemy1, - ARRAYSIZE( tlGonomeChaseEnemy1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_SMELL_FOOD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_TASK_FAILED, - 0, - "Gonome Chase Enemy" - }, -}; - -// victory dance (eating body) -Task_t tlGonomeVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.1 }, - { TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE } -}; - -Schedule_t slGonomeVictoryDance[] = -{ - { - tlGonomeVictoryDance, - ARRAYSIZE( tlGonomeVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "GonomeVictoryDance" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMGonome ) -{ - slGonomeRangeAttack1, - slGonomeChaseEnemy, - slGonomeVictoryDance, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMGonome, CMBaseMonster ) - -Schedule_t* CMGonome::GetScheduleOfType(int Type) -{ - switch ( Type ) - { - case SCHED_RANGE_ATTACK1: - return &slGonomeRangeAttack1[0]; - break; - case SCHED_CHASE_ENEMY: - return &slGonomeChaseEnemy[0]; - break; - case SCHED_VICTORY_DANCE: - return &slGonomeVictoryDance[0]; - break; - default: - break; - } - return CMBaseMonster::GetScheduleOfType(Type); -} - -void CMGonome::RunTask(Task_t *pTask) -{ - // HACK to stop Gonome from playing attack sound twice - if (pTask->iTask == TASK_MELEE_ATTACK1) - { - if (!m_playedAttackSound) - { - const char* sample = NULL; - if (m_meleeAttack2) - { - sample = "gonome/gonome_melee2.wav"; - } - else - { - sample = "gonome/gonome_melee1.wav"; - } - EMIT_SOUND(ENT(pev), CHAN_BODY, sample, 1, ATTN_NORM); - m_playedAttackSound = true; - } - } - else - { - m_playedAttackSound = false; - } - CMBaseMonster::RunTask(pTask); -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. -//========================================================= -void CMGonome::StartTask(Task_t *pTask) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch (pTask->iTask) - { - case TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE: - { - UTIL_MakeVectors( pev->angles ); - if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 40, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else - { - ALERT( at_aiconsole, "GonomeGetPathToEnemyCorpse failed!!\n" ); - TaskFail(); - } - } - break; - default: - CMBaseMonster::StartTask(pTask); - break; - - } -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + +//========================================================= +// Gonome.cpp +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "decals.h" +#include "nodes.h" + +#define GONOME_MELEE_ATTACK_RADIUS 70 + +enum +{ + TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1 +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= + +#define GONOME_AE_SLASH_RIGHT ( 1 ) +#define GONOME_AE_SLASH_LEFT ( 2 ) +#define GONOME_AE_SPIT ( 3 ) +#define GONOME_AE_THROW ( 4 ) + +#define GONOME_AE_BITE1 ( 19 ) +#define GONOME_AE_BITE2 ( 20 ) +#define GONOME_AE_BITE3 ( 21 ) +#define GONOME_AE_BITE4 ( 22 ) + +#define GONOME_SCRIPT_EVENT_SOUND ( 1011 ) + +void CGonomeGuts :: Spawn( void ) +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING( "gonomeguts" ); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL( ENT( pev ), "sprites/bigspit.spr" ); + pev->frame = 0; + pev->scale = 0.5; + pev->rendercolor.x = 255; + + UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); + + m_maxFrame = (float)MODEL_FRAMES( pev->modelindex ) - 1; +} + +void CGonomeGuts :: Animate( void ) +{ + pev->nextthink = gpGlobals->time + 0.1; + + if ( pev->frame++ ) + { + if ( pev->frame > m_maxFrame ) + { + pev->frame = 0; + } + } +} + +edict_t *CGonomeGuts :: Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ) +{ + CGonomeGuts *pSpit = CreateClassPtr( (CGonomeGuts *)NULL ); + + if (pSpit == NULL) + return NULL; + + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->owner = ENT(pevOwner); + + pSpit->SetThink ( &CGonomeGuts::Animate ); + pSpit->pev->nextthink = gpGlobals->time + 0.1; + pSpit->SetTouch ( &CGonomeGuts::GutsTouch ); + + return pSpit->edict(); +} + +void CGonomeGuts :: GutsTouch( edict_t *pOther ) +{ + TraceResult tr; + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT( 90, 110 ); + + EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, "bullchicken/bc_acid1.wav", 1, ATTN_NORM, 0, iPitch ); + + switch( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit1.wav", 1, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_spithit2.wav", 1, ATTN_NORM, 0, iPitch ); + break; + } + + if( !pOther->v.takedamage ) + { + // make a splat on the wall + UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); + UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_RED ); + UTIL_BloodDrips( tr.vecEndPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); + } + else + { + if (UTIL_IsPlayer(pOther)) + UTIL_TakeDamage( pOther, pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage ( pev, pev, gSkillData.gonomeDmgGuts, DMG_GENERIC ); + } + } + + SetThink( &CGonomeGuts::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + + +const char* CMGonome::pPainSounds[] = { + "gonome/gonome_pain1.wav", + "gonome/gonome_pain2.wav", + "gonome/gonome_pain3.wav", + "gonome/gonome_pain4.wav" +}; + +const char* CMGonome::pIdleSounds[] = { + "gonome/gonome_idle1.wav", + "gonome/gonome_idle2.wav", + "gonome/gonome_idle3.wav" +}; + +const char* CMGonome::pDeathSounds[] = { + "gonome/gonome_death2.wav", + "gonome/gonome_death3.wav", + "gonome/gonome_death4.wav" +}; + +const char* CMGonome::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char* CMGonome::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + + +void CMGonome::Killed(entvars_t *pevAttacker, int iGib) +{ + ClearGuts(); + UnlockPlayer(); + CMBaseMonster::Killed(pevAttacker, iGib); +} + +void CMGonome::UnlockPlayer() +{ + if (m_fPlayerLocked) + { + edict_t *player = 0; + if (m_lockedPlayer != 0 && UTIL_IsPlayer(m_lockedPlayer)) + player = m_lockedPlayer; + else // if ehandle is empty for some reason just unlock the first player + player = UTIL_FindEntityByClassname(0, "player"); + + if (player) + player->v.flags &= ~FL_FROZEN; + + m_lockedPlayer = 0; + m_fPlayerLocked = FALSE; + } +} + +CGonomeGuts* CMGonome::GetGonomeGuts(entvars_t *pevOwner, const Vector &pos) +{ + if (m_pGonomeGuts) + return m_pGonomeGuts; + edict_t *pEdict = CGonomeGuts::Shoot( pevOwner, g_vecZero, g_vecZero ); + CGonomeGuts *pGuts = GetClassPtr((CGonomeGuts*)VARS(pEdict)); + pGuts->Spawn(); + + UTIL_SetOrigin( pGuts->pev, pos ); + + m_pGonomeGuts = pGuts; + return m_pGonomeGuts; +} + +void CMGonome::ClearGuts() +{ + if (m_pGonomeGuts) + { + UTIL_Remove( m_pGonomeGuts->edict() ); + m_pGonomeGuts = 0; + } +} + +void CMGonome::PainSound( void ) +{ + int pitch = 95 + RANDOM_LONG( 0, 9 ); + + if( RANDOM_LONG( 0, 5 ) < 2 ) + EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1.0, ATTN_NORM, 0, pitch ); +} + +void CMGonome::DeathSound( void ) +{ + int pitch = 95 + RANDOM_LONG( 0, 9 ); + + EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1.0, ATTN_NORM, 0, pitch ); +} + +void CMGonome::IdleSound( void ) +{ + int pitch = 95 + RANDOM_LONG( 0, 9 ); + + // Play a random idle sound + EMIT_SOUND_DYN( ENT( pev ), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1.0, ATTN_NORM, 0, pitch ); +} + +void CMGonome::AlertSound( void ) +{ + const int iPitch = RANDOM_LONG(0, 9) + 95; + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM, 0, iPitch); +} + +void CMGonome::SetActivity( Activity NewActivity ) +{ + Activity OldActivity = m_Activity; + int iSequence = ACTIVITY_NOT_AVAILABLE; + + if (NewActivity != ACT_RANGE_ATTACK1) + { + ClearGuts(); + } + if (NewActivity == ACT_MELEE_ATTACK1 && m_hEnemy != 0) + { + // special melee animations + if ((pev->origin - m_hEnemy->v.origin).Length2D() >= 48 ) + { + m_meleeAttack2 = false; + iSequence = LookupSequence("attack1"); + } + else + { + m_meleeAttack2 = true; + iSequence = LookupSequence("attack2"); + } + } + else + { + UnlockPlayer(); + + if (NewActivity == ACT_RUN && m_hEnemy != 0) + { + // special run animations + if ((pev->origin - m_hEnemy->v.origin).Length2D() <= 512 ) + { + iSequence = LookupSequence("runshort"); + } + else + { + iSequence = LookupSequence("runlong"); + } + } + else + { + iSequence = LookupActivity(NewActivity); + } + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // In case someone calls this with something other than the ideal activity + m_IdealActivity = m_Activity; + + // Set to the desired anim, or default anim if the desired is not present + if( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if( pev->sequence != iSequence || !m_fSequenceLoops ) + { + // don't reset frame between walk and run + if( !( OldActivity == ACT_WALK || OldActivity == ACT_RUN ) || !( NewActivity == ACT_WALK || NewActivity == ACT_RUN ) ) + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo(); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT( at_aiconsole, "%s has no sequence for act:%d\n", STRING( pev->classname ), NewActivity ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMGonome::Classify(void) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// TakeDamage - overridden for gonome so we can keep track +// of how much time has passed since it was last injured +//========================================================= +int CMGonome::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + // Take 15% damage from bullets + if( bitsDamageType == DMG_BULLET ) + { + Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; + vecDir = vecDir.Normalize(); + float flForce = DamageForce( flDamage ); + pev->velocity = pev->velocity + vecDir * flForce; + flDamage *= 0.15; + } + + // HACK HACK -- until we fix this. + if( IsAlive() ) + PainSound(); + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMGonome::CheckRangeAttack1(float flDot, float flDist) +{ + if (flDist < 256) + return FALSE; + + if (IsMoving() && flDist >= 512) + { + // squid will far too far behind if he stops running to spit at this distance from the enemy. + return FALSE; + } + + if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextThrowTime) + { + if (m_hEnemy != 0) + { + if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256) + { + // don't try to spit at someone up really high or down really low. + return FALSE; + } + } + + if (IsMoving()) + { + // don't spit again for a long time, resume chasing enemy. + m_flNextThrowTime = gpGlobals->time + 5; + } + else + { + // not moving, so spit again pretty soon. + m_flNextThrowTime = gpGlobals->time + 0.5; + } + + return TRUE; + } + + return FALSE; +} + +//========================================================= +// CheckMeleeAttack2 - both gonome's melee attacks are ACT_MELEE_ATTACK1 +//========================================================= +BOOL CMGonome::CheckMeleeAttack2(float flDot, float flDist) +{ + return FALSE; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMGonome::SetYawSpeed( void ) +{ + pev->yaw_speed = 120; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMGonome::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case GONOME_SCRIPT_EVENT_SOUND: + if (m_Activity != ACT_MELEE_ATTACK1) + EMIT_SOUND(ENT(pev), CHAN_BODY, pEvent->options, 1, ATTN_NORM); + break; + case GONOME_AE_SPIT: + { + Vector vecArmPos, vecArmAng; + GetAttachment(0, vecArmPos, vecArmAng); + + if (GetGonomeGuts(pev, vecArmPos)) + { + m_pGonomeGuts->pev->skin = entindex(); + m_pGonomeGuts->pev->body = 1; + m_pGonomeGuts->pev->aiment = ENT(pev); + m_pGonomeGuts->pev->movetype = MOVETYPE_FOLLOW; + } + UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); + } + break; + case GONOME_AE_THROW: + { + UTIL_MakeVectors(pev->angles); + Vector vecArmPos, vecArmAng; + GetAttachment(0, vecArmPos, vecArmAng); + + if (GetGonomeGuts(pev, vecArmPos)) + { + Vector vecSpitDir; + + Vector vecEnemyPosition; + if (m_hEnemy != 0) + vecEnemyPosition = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs); + else + vecEnemyPosition = m_vecEnemyLKP; + vecSpitDir = (vecEnemyPosition - vecArmPos).Normalize(); + + vecSpitDir.x += RANDOM_FLOAT(-0.05, 0.05); + vecSpitDir.y += RANDOM_FLOAT(-0.05, 0.05); + vecSpitDir.z += RANDOM_FLOAT(-0.05, 0); + + m_pGonomeGuts->pev->body = 0; + m_pGonomeGuts->pev->skin = 0; + m_pGonomeGuts->pev->owner = ENT( pev ); + m_pGonomeGuts->pev->aiment = 0; + m_pGonomeGuts->pev->movetype = MOVETYPE_FLY; + m_pGonomeGuts->pev->velocity = vecSpitDir * 900; + m_pGonomeGuts->SetThink( &CGonomeGuts::Animate ); + m_pGonomeGuts->pev->nextthink = gpGlobals->time + 0.1; + UTIL_SetOrigin(m_pGonomeGuts->pev, vecArmPos); + + m_pGonomeGuts = 0; + } + UTIL_BloodDrips( vecArmPos, UTIL_RandomBloodVector(), BLOOD_COLOR_RED, 35 ); + } + break; + + case GONOME_AE_SLASH_LEFT: + { + edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.z = 9; + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 25; + } + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); + } + else + { + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); + } + } + break; + + case GONOME_AE_SLASH_RIGHT: + { + edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneSlash, DMG_SLASH); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.z = -9; + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -25; + } + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackHitSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); + } + else + { + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackMissSounds), 1, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5)); + } + } + break; + + case GONOME_AE_BITE1: + case GONOME_AE_BITE2: + case GONOME_AE_BITE3: + case GONOME_AE_BITE4: + { + int iPitch; + edict_t *pHurt = CheckTraceHullAttack(GONOME_MELEE_ATTACK_RADIUS, gSkillData.gonomeDmgOneBite, DMG_SLASH); + + if (pHurt) + { + // croonchy bite sound + iPitch = RANDOM_FLOAT(90, 110); + switch (RANDOM_LONG(0, 1)) + { + case 0: + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 1, ATTN_NORM, 0, iPitch); + break; + case 1: + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 1, ATTN_NORM, 0, iPitch); + break; + } + + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + if (pEvent->event == GONOME_AE_BITE4) + { + pHurt->v.punchangle.x = 15; + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 75; + } + else + { + pHurt->v.punchangle.x = 9; + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_forward * 25; + } + } + // lock player + if (pEvent->event == GONOME_AE_BITE4) + { + UnlockPlayer(); + } + else if (UTIL_IsPlayer( pHurt ) && UTIL_IsAlive( pHurt )) + { + if (!m_fPlayerLocked) + { + edict_t *player = pHurt; + player->v.flags |= FL_FROZEN; + m_lockedPlayer = player; + m_fPlayerLocked = TRUE; + } + } + } + } + break; + + default: + CMBaseMonster::HandleAnimEvent(pEvent); + } +} + +#define GONOME_FLINCH_DELAY 2 + +int CMGonome::IgnoreConditions( void ) +{ + int iIgnore = CMBaseMonster::IgnoreConditions(); + + if( m_Activity == ACT_MELEE_ATTACK1 ) + { + if( m_flNextFlinch >= gpGlobals->time ) + iIgnore |= ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); + } + + if( ( m_Activity == ACT_SMALL_FLINCH ) || ( m_Activity == ACT_BIG_FLINCH ) ) + { + if( m_flNextFlinch < gpGlobals->time ) + m_flNextFlinch = gpGlobals->time + GONOME_FLINCH_DELAY; + } + + return iIgnore; +} + +//========================================================= +// Spawn +//========================================================= +void CMGonome::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/gonome.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.gonomeHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextThrowTime = gpGlobals->time; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_gonome" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Gonome" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMGonome::Precache() +{ + PRECACHE_MODEL("models/gonome.mdl"); + + PRECACHE_MODEL("sprites/bigspit.spr");// spit projectile. + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + PRECACHE_SOUND("gonome/gonome_eat.wav"); + PRECACHE_SOUND("gonome/gonome_jumpattack.wav"); + PRECACHE_SOUND("gonome/gonome_melee1.wav"); + PRECACHE_SOUND("gonome/gonome_melee2.wav"); + + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + + PRECACHE_SOUND("gonome/gonome_run.wav"); + + PRECACHE_SOUND("bullchicken/bc_acid1.wav"); + + PRECACHE_SOUND("bullchicken/bc_bite2.wav"); + PRECACHE_SOUND("bullchicken/bc_bite3.wav"); + + PRECACHE_SOUND("bullchicken/bc_spithit1.wav"); + PRECACHE_SOUND("bullchicken/bc_spithit2.wav"); +} + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMGonome::GetSchedule( void ) +{ + switch( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { + // dead enemy + if( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster::GetSchedule(); + } + + if( HasConditions( bits_COND_NEW_ENEMY ) ) + { + return GetScheduleOfType( SCHED_WAKE_ANGRY ); + } + + if( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + + if( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); + } + + if( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) + { + return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); + } + + return GetScheduleOfType( SCHED_CHASE_ENEMY ); + break; + } + default: + break; + } + + return CMBaseMonster::GetSchedule(); +} + +// primary range attack +Task_t tlGonomeRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slGonomeRangeAttack1[] = +{ + { + tlGonomeRangeAttack1, + ARRAYSIZE( tlGonomeRangeAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "Gonome Range Attack1" + }, +}; + +// Chase enemy schedule +Task_t tlGonomeChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty squid oscillation. + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slGonomeChaseEnemy[] = +{ + { + tlGonomeChaseEnemy1, + ARRAYSIZE( tlGonomeChaseEnemy1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_SMELL_FOOD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED, + 0, + "Gonome Chase Enemy" + }, +}; + +// victory dance (eating body) +Task_t tlGonomeVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.1 }, + { TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE } +}; + +Schedule_t slGonomeVictoryDance[] = +{ + { + tlGonomeVictoryDance, + ARRAYSIZE( tlGonomeVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "GonomeVictoryDance" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMGonome ) +{ + slGonomeRangeAttack1, + slGonomeChaseEnemy, + slGonomeVictoryDance, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMGonome, CMBaseMonster ) + +Schedule_t* CMGonome::GetScheduleOfType(int Type) +{ + switch ( Type ) + { + case SCHED_RANGE_ATTACK1: + return &slGonomeRangeAttack1[0]; + break; + case SCHED_CHASE_ENEMY: + return &slGonomeChaseEnemy[0]; + break; + case SCHED_VICTORY_DANCE: + return &slGonomeVictoryDance[0]; + break; + default: + break; + } + return CMBaseMonster::GetScheduleOfType(Type); +} + +void CMGonome::RunTask(Task_t *pTask) +{ + // HACK to stop Gonome from playing attack sound twice + if (pTask->iTask == TASK_MELEE_ATTACK1) + { + if (!m_playedAttackSound) + { + const char* sample = NULL; + if (m_meleeAttack2) + { + sample = "gonome/gonome_melee2.wav"; + } + else + { + sample = "gonome/gonome_melee1.wav"; + } + EMIT_SOUND(ENT(pev), CHAN_BODY, sample, 1, ATTN_NORM); + m_playedAttackSound = true; + } + } + else + { + m_playedAttackSound = false; + } + CMBaseMonster::RunTask(pTask); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. +//========================================================= +void CMGonome::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch (pTask->iTask) + { + case TASK_GONOME_GET_PATH_TO_ENEMY_CORPSE: + { + UTIL_MakeVectors( pev->angles ); + if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 40, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT( at_aiconsole, "GonomeGetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + } + break; + default: + CMBaseMonster::StartTask(pTask); + break; + + } +} diff --git a/src/dlls/h_ai.cpp b/src/dlls/h_ai.cpp index 425f0e1..c7d7da3 100644 --- a/src/dlls/h_ai.cpp +++ b/src/dlls/h_ai.cpp @@ -1,199 +1,199 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - - h_ai.cpp - halflife specific ai code - -*/ - - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" - -#define NUM_LATERAL_CHECKS 13 // how many checks are made on each side of a monster looking for lateral cover -#define NUM_LATERAL_LOS_CHECKS 6 // how many checks are made on each side of a monster looking for lateral cover - -//float flRandom = RANDOM_FLOAT(0,1); - -DLL_GLOBAL BOOL g_fDrawLines = FALSE; - -//========================================================= -// -// AI UTILITY FUNCTIONS -// -// !!!UNDONE - move CBaseMonster functions to monsters.cpp -//========================================================= - -//========================================================= -// FBoxVisible - a more accurate ( and slower ) version -// of FVisible. -// -// !!!UNDONE - make this CBaseMonster? -//========================================================= -BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize ) -{ - // don't look through water - if ((pevLooker->waterlevel != 3 && pevTarget->waterlevel == 3) - || (pevLooker->waterlevel == 3 && pevTarget->waterlevel == 0)) - return FALSE; - - TraceResult tr; - Vector vecLookerOrigin = pevLooker->origin + pevLooker->view_ofs;//look through the monster's 'eyes' - for (int i = 0; i < 5; i++) - { - Vector vecTarget = pevTarget->origin; - vecTarget.x += RANDOM_FLOAT( pevTarget->mins.x + flSize, pevTarget->maxs.x - flSize); - vecTarget.y += RANDOM_FLOAT( pevTarget->mins.y + flSize, pevTarget->maxs.y - flSize); - vecTarget.z += RANDOM_FLOAT( pevTarget->mins.z + flSize, pevTarget->maxs.z - flSize); - - UTIL_TraceLine(vecLookerOrigin, vecTarget, ignore_monsters, ignore_glass, ENT(pevLooker)/*pentIgnore*/, &tr); - - if (tr.flFraction == 1.0) - { - vecTargetOrigin = vecTarget; - return TRUE;// line of sight is valid. - } - } - return FALSE;// Line of sight is not established -} - -// -// VecCheckToss - returns the velocity at which an object should be lobbed from vecspot1 to land near vecspot2. -// returns g_vecZero if toss is not feasible. -// -Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj ) -{ - TraceResult tr; - Vector vecMidPoint;// halfway point between Spot1 and Spot2 - Vector vecApex;// highest point - Vector vecScale; - Vector vecGrenadeVel; - Vector vecTemp; - float flGravity = g_psv_gravity->value * flGravityAdj; - - if (vecSpot2.z - vecSpot1.z > 500) - { - // to high, fail - return g_vecZero; - } - - UTIL_MakeVectors (pev->angles); - - // toss a little bit to the left or right, not right down on the enemy's bean (head). - vecSpot2 = vecSpot2 + gpGlobals->v_right * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); - vecSpot2 = vecSpot2 + gpGlobals->v_forward * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); - - // calculate the midpoint and apex of the 'triangle' - // UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT - - // How much time does it take to get there? - - // get a rough idea of how high it can be thrown - vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, ENT(pev), &tr); - vecMidPoint = tr.vecEndPos; - // (subtract 15 so the grenade doesn't hit the ceiling) - vecMidPoint.z -= 15; - - if (vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z) - { - // to not enough space, fail - return g_vecZero; - } - - // How high should the grenade travel to reach the apex - float distance1 = (vecMidPoint.z - vecSpot1.z); - float distance2 = (vecMidPoint.z - vecSpot2.z); - - // How long will it take for the grenade to travel this distance - float time1 = sqrt( distance1 / (0.5 * flGravity) ); - float time2 = sqrt( distance2 / (0.5 * flGravity) ); - - if (time1 < 0.1) - { - // too close - return g_vecZero; - } - - // how hard to throw sideways to get there in time. - vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2); - // how hard upwards to reach the apex at the right time. - vecGrenadeVel.z = flGravity * time1; - - // find the apex - vecApex = vecSpot1 + vecGrenadeVel * time1; - vecApex.z = vecMidPoint.z; - - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - // UNDONE: either ignore monsters or change it to not care if we hit our enemy - UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - return vecGrenadeVel; -} - - -// -// VecCheckThrow - returns the velocity vector at which an object should be thrown from vecspot1 to hit vecspot2. -// returns g_vecZero if throw is not feasible. -// -Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj ) -{ - float flGravity = g_psv_gravity->value * flGravityAdj; - - Vector vecGrenadeVel = (vecSpot2 - vecSpot1); - - // throw at a constant time - float time = vecGrenadeVel.Length( ) / flSpeed; - vecGrenadeVel = vecGrenadeVel * (1.0 / time); - - // adjust upward toss to compensate for gravity loss - vecGrenadeVel.z += flGravity * time * 0.5; - - Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; - vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5); - - TraceResult tr; - UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); - if (tr.flFraction != 1.0) - { - // fail! - return g_vecZero; - } - - return vecGrenadeVel; -} - - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + + h_ai.cpp - halflife specific ai code + +*/ + + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" + +#define NUM_LATERAL_CHECKS 13 // how many checks are made on each side of a monster looking for lateral cover +#define NUM_LATERAL_LOS_CHECKS 6 // how many checks are made on each side of a monster looking for lateral cover + +//float flRandom = RANDOM_FLOAT(0,1); + +DLL_GLOBAL BOOL g_fDrawLines = FALSE; + +//========================================================= +// +// AI UTILITY FUNCTIONS +// +// !!!UNDONE - move CBaseMonster functions to monsters.cpp +//========================================================= + +//========================================================= +// FBoxVisible - a more accurate ( and slower ) version +// of FVisible. +// +// !!!UNDONE - make this CBaseMonster? +//========================================================= +BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize ) +{ + // don't look through water + if ((pevLooker->waterlevel != 3 && pevTarget->waterlevel == 3) + || (pevLooker->waterlevel == 3 && pevTarget->waterlevel == 0)) + return FALSE; + + TraceResult tr; + Vector vecLookerOrigin = pevLooker->origin + pevLooker->view_ofs;//look through the monster's 'eyes' + for (int i = 0; i < 5; i++) + { + Vector vecTarget = pevTarget->origin; + vecTarget.x += RANDOM_FLOAT( pevTarget->mins.x + flSize, pevTarget->maxs.x - flSize); + vecTarget.y += RANDOM_FLOAT( pevTarget->mins.y + flSize, pevTarget->maxs.y - flSize); + vecTarget.z += RANDOM_FLOAT( pevTarget->mins.z + flSize, pevTarget->maxs.z - flSize); + + UTIL_TraceLine(vecLookerOrigin, vecTarget, ignore_monsters, ignore_glass, ENT(pevLooker)/*pentIgnore*/, &tr); + + if (tr.flFraction == 1.0) + { + vecTargetOrigin = vecTarget; + return TRUE;// line of sight is valid. + } + } + return FALSE;// Line of sight is not established +} + +// +// VecCheckToss - returns the velocity at which an object should be lobbed from vecspot1 to land near vecspot2. +// returns g_vecZero if toss is not feasible. +// +Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj ) +{ + TraceResult tr; + Vector vecMidPoint;// halfway point between Spot1 and Spot2 + Vector vecApex;// highest point + Vector vecScale; + Vector vecGrenadeVel; + Vector vecTemp; + float flGravity = g_psv_gravity->value * flGravityAdj; + + if (vecSpot2.z - vecSpot1.z > 500) + { + // to high, fail + return g_vecZero; + } + + UTIL_MakeVectors (pev->angles); + + // toss a little bit to the left or right, not right down on the enemy's bean (head). + vecSpot2 = vecSpot2 + gpGlobals->v_right * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); + vecSpot2 = vecSpot2 + gpGlobals->v_forward * ( RANDOM_FLOAT(-8,8) + RANDOM_FLOAT(-16,16) ); + + // calculate the midpoint and apex of the 'triangle' + // UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT + + // How much time does it take to get there? + + // get a rough idea of how high it can be thrown + vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, ENT(pev), &tr); + vecMidPoint = tr.vecEndPos; + // (subtract 15 so the grenade doesn't hit the ceiling) + vecMidPoint.z -= 15; + + if (vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z) + { + // to not enough space, fail + return g_vecZero; + } + + // How high should the grenade travel to reach the apex + float distance1 = (vecMidPoint.z - vecSpot1.z); + float distance2 = (vecMidPoint.z - vecSpot2.z); + + // How long will it take for the grenade to travel this distance + float time1 = sqrt( distance1 / (0.5 * flGravity) ); + float time2 = sqrt( distance2 / (0.5 * flGravity) ); + + if (time1 < 0.1) + { + // too close + return g_vecZero; + } + + // how hard to throw sideways to get there in time. + vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2); + // how hard upwards to reach the apex at the right time. + vecGrenadeVel.z = flGravity * time1; + + // find the apex + vecApex = vecSpot1 + vecGrenadeVel * time1; + vecApex.z = vecMidPoint.z; + + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + // UNDONE: either ignore monsters or change it to not care if we hit our enemy + UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + return vecGrenadeVel; +} + + +// +// VecCheckThrow - returns the velocity vector at which an object should be thrown from vecspot1 to hit vecspot2. +// returns g_vecZero if throw is not feasible. +// +Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj ) +{ + float flGravity = g_psv_gravity->value * flGravityAdj; + + Vector vecGrenadeVel = (vecSpot2 - vecSpot1); + + // throw at a constant time + float time = vecGrenadeVel.Length( ) / flSpeed; + vecGrenadeVel = vecGrenadeVel * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecGrenadeVel.z += flGravity * time * 0.5; + + Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5; + vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5); + + TraceResult tr; + UTIL_TraceLine(vecSpot1, vecApex, dont_ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + UTIL_TraceLine(vecSpot2, vecApex, ignore_monsters, ENT(pev), &tr); + if (tr.flFraction != 1.0) + { + // fail! + return g_vecZero; + } + + return vecGrenadeVel; +} + + diff --git a/src/dlls/h_export.cpp b/src/dlls/h_export.cpp index 920fb75..9904641 100644 --- a/src/dlls/h_export.cpp +++ b/src/dlls/h_export.cpp @@ -1,50 +1,50 @@ -// -// Monster Mod is a modification based on Botman's original "Monster" plugin. -// The "forgotten" modification was made by Rick90. -// This is an attempt to recreate the plugin so it does not become lost again. -// -// Recreated by Giegue. -// -// h_export.cpp -// - -/*** -* -* Copyright (c) 1999, 2000 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. -* -****/ -/* - -===== h_export.cpp ======================================================== - - Entity classes exported by Halflife. - -*/ - -#include "extdll.h" -#include "h_export.h" - -// From SDK dlls/h_export.cpp: - -//! Holds engine functionality callbacks -enginefuncs_t g_engfuncs; -globalvars_t *gpGlobals; - -// Receive engine function table from engine. -// This appears to be the _first_ DLL routine called by the engine, so we -// do some setup operations here. -void WINAPI GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) -{ - memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); - gpGlobals = pGlobals; -} - +// +// Monster Mod is a modification based on Botman's original "Monster" plugin. +// The "forgotten" modification was made by Rick90. +// This is an attempt to recreate the plugin so it does not become lost again. +// +// Recreated by Giegue. +// +// h_export.cpp +// + +/*** +* +* Copyright (c) 1999, 2000 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. +* +****/ +/* + +===== h_export.cpp ======================================================== + + Entity classes exported by Halflife. + +*/ + +#include "extdll.h" +#include "h_export.h" + +// From SDK dlls/h_export.cpp: + +//! Holds engine functionality callbacks +enginefuncs_t g_engfuncs; +globalvars_t *gpGlobals; + +// Receive engine function table from engine. +// This appears to be the _first_ DLL routine called by the engine, so we +// do some setup operations here. +void WINAPI GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals ) +{ + memcpy(&g_engfuncs, pengfuncsFromEngine, sizeof(enginefuncs_t)); + gpGlobals = pGlobals; +} + diff --git a/src/dlls/hassassin.cpp b/src/dlls/hassassin.cpp index 91de38f..ae567ef 100644 --- a/src/dlls/hassassin.cpp +++ b/src/dlls/hassassin.cpp @@ -1,921 +1,921 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -//========================================================= -// hassassin - Human assassin, fast and stealthy -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "weapons.h" - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_ASSASSIN_EXPOSED = LAST_COMMON_SCHEDULE + 1,// cover was blown. - SCHED_ASSASSIN_JUMP, // fly through the air - SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot - SCHED_ASSASSIN_JUMP_LAND, // hit and run away -}; - -//========================================================= -// monster-specific tasks -//========================================================= - -enum -{ - TASK_ASSASSIN_FALL_TO_GROUND = LAST_COMMON_TASK + 1, // falling and waiting to hit ground -}; - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define ASSASSIN_AE_SHOOT1 1 -#define ASSASSIN_AE_TOSS1 2 -#define ASSASSIN_AE_JUMP 3 - - -#define bits_MEMORY_BADJUMP (bits_MEMORY_CUSTOM1) - - -//========================================================= -// DieSound -//========================================================= -void CMHAssassin :: DeathSound ( void ) -{ -} - -//========================================================= -// IdleSound -//========================================================= -void CMHAssassin :: IdleSound ( void ) -{ -} - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. -//========================================================= -int CMHAssassin :: ISoundMask ( void) -{ - return 0; -} - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMHAssassin :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_HUMAN_MILITARY; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMHAssassin :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 360; - break; - default: - ys = 360; - break; - } - - pev->yaw_speed = ys; -} - - -//========================================================= -// Shoot -//========================================================= -void CMHAssassin :: Shoot ( void ) -{ - if (m_hEnemy == NULL) - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - if (m_flLastShot + 2 < gpGlobals->time) - { - m_flDiviation = 0.10; - } - else - { - m_flDiviation -= 0.01; - if (m_flDiviation < 0.02) - m_flDiviation = 0.02; - } - m_flLastShot = gpGlobals->time; - - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( pev->origin + gpGlobals->v_up * 32 + gpGlobals->v_forward * 12, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL); - FireBullets(1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, BULLET_MONSTER_9MM ); // shoot +-8 degrees - - switch(RANDOM_LONG(0,1)) - { - case 0: - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); - break; - case 1: - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); - break; - } - - pev->effects |= EF_MUZZLEFLASH; - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); - - m_cAmmoLoaded--; -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMHAssassin :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case ASSASSIN_AE_SHOOT1: - Shoot( ); - break; - case ASSASSIN_AE_TOSS1: - { - UTIL_MakeVectors( pev->angles ); - CMGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 2.0 ); - - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - m_fThrowGrenade = FALSE; - // !!!LATER - when in a group, only try to throw grenade if ordered. - } - break; - case ASSASSIN_AE_JUMP: - { - // ALERT( at_console, "jumping"); - UTIL_MakeAimVectors( pev->angles ); - pev->movetype = MOVETYPE_TOSS; - pev->flags &= ~FL_ONGROUND; - pev->velocity = m_vecJumpVelocity; - m_flNextJump = gpGlobals->time + 3.0; - } - return; - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMHAssassin :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/hassassin.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->effects = 0; - pev->health = gSkillData.hassassinHealth; - m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP; - pev->friction = 1; - - m_HackedGunPos = Vector( 0, 24, 48 ); - - m_iTargetRanderamt = 20; - pev->renderamt = 20; - pev->rendermode = kRenderTransTexture; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_human_assassin" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Female Assassin" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMHAssassin :: Precache() -{ - PRECACHE_MODEL("models/hassassin.mdl"); - - PRECACHE_SOUND("weapons/pl_gun1.wav"); - PRECACHE_SOUND("weapons/pl_gun2.wav"); - - PRECACHE_SOUND("debris/beamstart1.wav"); - - m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell -} - - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// Fail Schedule -//========================================================= -Task_t tlAssassinFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - // { TASK_WAIT_PVS, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, -}; - -Schedule_t slAssassinFail[] = -{ - { - tlAssassinFail, - ARRAYSIZE ( tlAssassinFail ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND, - 0, - "AssassinFail" - }, -}; - - -//========================================================= -// Enemy exposed Agrunt's cover -//========================================================= -Task_t tlAssassinExposed[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slAssassinExposed[] = -{ - { - tlAssassinExposed, - ARRAYSIZE ( tlAssassinExposed ), - bits_COND_CAN_MELEE_ATTACK1, - 0, - "AssassinExposed", - }, -}; - - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAssassinTakeCoverFromEnemy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAssassinTakeCoverFromEnemy[] = -{ - { - tlAssassinTakeCoverFromEnemy, - ARRAYSIZE ( tlAssassinTakeCoverFromEnemy ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND, - 0, - "AssassinTakeCoverFromEnemy" - }, -}; - - -//========================================================= -// Take cover from enemy! Tries lateral cover before node -// cover! -//========================================================= -Task_t tlAssassinTakeCoverFromEnemy2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK2 }, - { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, -}; - -Schedule_t slAssassinTakeCoverFromEnemy2[] = -{ - { - tlAssassinTakeCoverFromEnemy2, - ARRAYSIZE ( tlAssassinTakeCoverFromEnemy2 ), - bits_COND_NEW_ENEMY | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_HEAR_SOUND, - 0, - "AssassinTakeCoverFromEnemy2" - }, -}; - - -//========================================================= -// hide from the loudest sound source -//========================================================= -Task_t tlAssassinTakeCoverFromBestSound[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slAssassinTakeCoverFromBestSound[] = -{ - { - tlAssassinTakeCoverFromBestSound, - ARRAYSIZE ( tlAssassinTakeCoverFromBestSound ), - bits_COND_NEW_ENEMY, - 0, - "AssassinTakeCoverFromBestSound" - }, -}; - - - - - -//========================================================= -// AlertIdle Schedules -//========================================================= -Task_t tlAssassinHide[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, -}; - -Schedule_t slAssassinHide[] = -{ - { - tlAssassinHide, - ARRAYSIZE ( tlAssassinHide ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_ENEMY | - bits_COND_SEE_FEAR | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_HEAR_SOUND, - 0, - "AssassinHide" - }, -}; - - - -//========================================================= -// HUNT Schedules -//========================================================= -Task_t tlAssassinHunt[] = -{ - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slAssassinHunt[] = -{ - { - tlAssassinHunt, - ARRAYSIZE ( tlAssassinHunt ), - bits_COND_NEW_ENEMY | - // bits_COND_SEE_ENEMY | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_HEAR_SOUND, - 0, - "AssassinHunt" - }, -}; - - -//========================================================= -// Jumping Schedules -//========================================================= -Task_t tlAssassinJump[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, - { TASK_SET_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_ATTACK }, -}; - -Schedule_t slAssassinJump[] = -{ - { - tlAssassinJump, - ARRAYSIZE ( tlAssassinJump ), - 0, - 0, - "AssassinJump" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlAssassinJumpAttack[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_LAND }, - // { TASK_SET_ACTIVITY, (float)ACT_FLY }, - { TASK_ASSASSIN_FALL_TO_GROUND, (float)0 }, -}; - - -Schedule_t slAssassinJumpAttack[] = -{ - { - tlAssassinJumpAttack, - ARRAYSIZE ( tlAssassinJumpAttack ), - 0, - 0, - "AssassinJumpAttack" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlAssassinJumpLand[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_EXPOSED }, - // { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_REMEMBER, (float)bits_MEMORY_BADJUMP }, - { TASK_FIND_NODE_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_FORGET, (float)bits_MEMORY_BADJUMP }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, -}; - -Schedule_t slAssassinJumpLand[] = -{ - { - tlAssassinJumpLand, - ARRAYSIZE ( tlAssassinJumpLand ), - 0, - 0, - "AssassinJumpLand" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMHAssassin ) -{ - slAssassinFail, - slAssassinExposed, - slAssassinTakeCoverFromEnemy, - slAssassinTakeCoverFromEnemy2, - slAssassinTakeCoverFromBestSound, - slAssassinHide, - slAssassinHunt, - slAssassinJump, - slAssassinJumpAttack, - slAssassinJumpLand, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMHAssassin, CMBaseMonster ); - - -//========================================================= -// CheckMeleeAttack1 - jump like crazy if the enemy gets too close. -//========================================================= -BOOL CMHAssassin :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if ( m_flNextJump < gpGlobals->time && (flDist <= 128 || HasMemory( bits_MEMORY_BADJUMP )) && m_hEnemy != NULL ) - { - TraceResult tr; - - Vector vecDest = pev->origin + Vector( RANDOM_FLOAT( -64, 64), RANDOM_FLOAT( -64, 64 ), 160 ); - - UTIL_TraceHull( pev->origin + Vector( 0, 0, 36 ), vecDest + Vector( 0, 0, 36 ), dont_ignore_monsters, human_hull, ENT(pev), &tr); - - if ( tr.fStartSolid || tr.flFraction < 1.0) - { - return FALSE; - } - - float flGravity = g_psv_gravity->value; - - float time = sqrt( 160 / (0.5 * flGravity)); - float speed = flGravity * time / 160; - m_vecJumpVelocity = (vecDest - pev->origin) * speed; - - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 - drop a cap in their ass -// -//========================================================= -BOOL CMHAssassin :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) - { - TraceResult tr; - - Vector vecSrc = GetGunPosition(); - - // verify that a bullet fired from the gun will hit the enemy before the world. - UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), dont_ignore_monsters, ENT(pev), &tr); - - if ( tr.flFraction == 1 || tr.pHit == m_hEnemy ) - { - return TRUE; - } - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close. -//========================================================= -BOOL CMHAssassin :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - m_fThrowGrenade = FALSE; - if ( !FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) ) - { - // don't throw grenades at anything that isn't on the ground! - return FALSE; - } - - // don't get grenade happy unless the player starts to piss you off - if ( m_iFrustration <= 2) - return FALSE; - - if ( m_flNextGrenadeCheck < gpGlobals->time && !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 512 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) - { - Vector vecToss = VecCheckThrow( pev, GetGunPosition( ), UTIL_BodyTarget(m_hEnemy, g_vecZero), flDist, 0.5 ); // use dist as speed to get there in 1 second - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - - return TRUE; - } - } - - return FALSE; -} - - -//========================================================= -// RunAI -//========================================================= -void CMHAssassin :: RunAI( void ) -{ - CMBaseMonster :: RunAI(); - - // always visible if moving - // always visible is not on hard - if (m_hEnemy == NULL || pev->deadflag != DEAD_NO || m_Activity == ACT_RUN || m_Activity == ACT_WALK || !(pev->flags & FL_ONGROUND)) - m_iTargetRanderamt = 255; - - if (pev->renderamt > m_iTargetRanderamt) - { - if (pev->renderamt == 255) - { - EMIT_SOUND (ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 0.2, ATTN_NORM ); - } - - pev->renderamt = max( pev->renderamt - 50, m_iTargetRanderamt ); - pev->rendermode = kRenderTransTexture; - } - else if (pev->renderamt < m_iTargetRanderamt) - { - pev->renderamt = min( pev->renderamt + 50, m_iTargetRanderamt ); - if (pev->renderamt == 255) - pev->rendermode = kRenderNormal; - } - - if (m_Activity == ACT_RUN || m_Activity == ACT_WALK) - { - static int iStep = 0; - iStep = ! iStep; - if (iStep) - { - switch( RANDOM_LONG( 0, 3 ) ) - { - case 0: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 0.5, ATTN_NORM); break; - case 1: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 0.5, ATTN_NORM); break; - case 2: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 0.5, ATTN_NORM); break; - case 3: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 0.5, ATTN_NORM); break; - } - } - } -} - - -//========================================================= -// StartTask -//========================================================= -void CMHAssassin :: StartTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK2: - if (!m_fThrowGrenade) - { - TaskComplete( ); - } - else - { - CMBaseMonster :: StartTask ( pTask ); - } - break; - case TASK_ASSASSIN_FALL_TO_GROUND: - break; - default: - CMBaseMonster :: StartTask ( pTask ); - break; - } -} - - -//========================================================= -// RunTask -//========================================================= -void CMHAssassin :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_ASSASSIN_FALL_TO_GROUND: - MakeIdealYaw( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if (m_fSequenceFinished) - { - if (pev->velocity.z > 0) - { - pev->sequence = LookupSequence( "fly_up" ); - } - else if (HasConditions ( bits_COND_SEE_ENEMY )) - { - pev->sequence = LookupSequence( "fly_attack" ); - pev->frame = 0; - } - else - { - pev->sequence = LookupSequence( "fly_down" ); - pev->frame = 0; - } - - ResetSequenceInfo( ); - SetYawSpeed(); - } - if (pev->flags & FL_ONGROUND) - { - // ALERT( at_console, "on ground\n"); - TaskComplete( ); - } - break; - default: - CMBaseMonster :: RunTask ( pTask ); - break; - } -} - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMHAssassin :: GetSchedule ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_IDLE: - case MONSTERSTATE_ALERT: - { - } - break; - - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - // flying? - if ( pev->movetype == MOVETYPE_TOSS) - { - if (pev->flags & FL_ONGROUND) - { - // ALERT( at_console, "landed\n"); - // just landed - pev->movetype = MOVETYPE_STEP; - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_LAND ); - } - else - { - // ALERT( at_console, "jump\n"); - // jump or jump/shoot - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP ); - else - return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_ATTACK ); - } - } - - if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) - { - m_iFrustration++; - } - if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) - { - m_iFrustration++; - } - - // jump player! - if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - // ALERT( at_console, "melee attack 1\n"); - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } - - // throw grenade - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) - { - // ALERT( at_console, "range attack 2\n"); - return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ); - } - - // spotted - if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) - { - // ALERT( at_console, "exposed\n"); - m_iFrustration++; - return GetScheduleOfType ( SCHED_ASSASSIN_EXPOSED ); - } - - // can attack - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - // ALERT( at_console, "range attack 1\n"); - m_iFrustration = 0; - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - - if ( HasConditions ( bits_COND_SEE_ENEMY ) ) - { - // ALERT( at_console, "face\n"); - return GetScheduleOfType ( SCHED_COMBAT_FACE ); - } - - // new enemy - if ( HasConditions ( bits_COND_NEW_ENEMY ) ) - { - // ALERT( at_console, "take cover\n"); - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - - // ALERT( at_console, "stand\n"); - return GetScheduleOfType ( SCHED_ALERT_STAND ); - } - break; - } - - return CMBaseMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CMHAssassin :: GetScheduleOfType ( int Type ) -{ - // ALERT( at_console, "%d\n", m_iFrustration ); - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - if (pev->health > 30) - return slAssassinTakeCoverFromEnemy; - else - return slAssassinTakeCoverFromEnemy2; - case SCHED_TAKE_COVER_FROM_BEST_SOUND: - return slAssassinTakeCoverFromBestSound; - case SCHED_ASSASSIN_EXPOSED: - return slAssassinExposed; - case SCHED_FAIL: - if (m_MonsterState == MONSTERSTATE_COMBAT) - return slAssassinFail; - break; - case SCHED_ALERT_STAND: - if (m_MonsterState == MONSTERSTATE_COMBAT) - return slAssassinHide; - break; - case SCHED_CHASE_ENEMY: - return slAssassinHunt; - case SCHED_MELEE_ATTACK1: - if (pev->flags & FL_ONGROUND) - { - if (m_flNextJump > gpGlobals->time) - { - // can't jump yet, go ahead and fail - return slAssassinFail; - } - else - { - return slAssassinJump; - } - } - else - { - return slAssassinJumpAttack; - } - case SCHED_ASSASSIN_JUMP: - case SCHED_ASSASSIN_JUMP_ATTACK: - return slAssassinJumpAttack; - case SCHED_ASSASSIN_JUMP_LAND: - return slAssassinJumpLand; - } - - return CMBaseMonster :: GetScheduleOfType( Type ); -} - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +//========================================================= +// hassassin - Human assassin, fast and stealthy +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "weapons.h" + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_ASSASSIN_EXPOSED = LAST_COMMON_SCHEDULE + 1,// cover was blown. + SCHED_ASSASSIN_JUMP, // fly through the air + SCHED_ASSASSIN_JUMP_ATTACK, // fly through the air and shoot + SCHED_ASSASSIN_JUMP_LAND, // hit and run away +}; + +//========================================================= +// monster-specific tasks +//========================================================= + +enum +{ + TASK_ASSASSIN_FALL_TO_GROUND = LAST_COMMON_TASK + 1, // falling and waiting to hit ground +}; + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ASSASSIN_AE_SHOOT1 1 +#define ASSASSIN_AE_TOSS1 2 +#define ASSASSIN_AE_JUMP 3 + + +#define bits_MEMORY_BADJUMP (bits_MEMORY_CUSTOM1) + + +//========================================================= +// DieSound +//========================================================= +void CMHAssassin :: DeathSound ( void ) +{ +} + +//========================================================= +// IdleSound +//========================================================= +void CMHAssassin :: IdleSound ( void ) +{ +} + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. +//========================================================= +int CMHAssassin :: ISoundMask ( void) +{ + return 0; +} + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMHAssassin :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_HUMAN_MILITARY; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMHAssassin :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 360; + break; + default: + ys = 360; + break; + } + + pev->yaw_speed = ys; +} + + +//========================================================= +// Shoot +//========================================================= +void CMHAssassin :: Shoot ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + if (m_flLastShot + 2 < gpGlobals->time) + { + m_flDiviation = 0.10; + } + else + { + m_flDiviation -= 0.01; + if (m_flDiviation < 0.02) + m_flDiviation = 0.02; + } + m_flLastShot = gpGlobals->time; + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( pev->origin + gpGlobals->v_up * 32 + gpGlobals->v_forward * 12, vecShellVelocity, pev->angles.y, m_iShell, TE_BOUNCE_SHELL); + FireBullets(1, vecShootOrigin, vecShootDir, Vector( m_flDiviation, m_flDiviation, m_flDiviation ), 2048, BULLET_MONSTER_9MM ); // shoot +-8 degrees + + switch(RANDOM_LONG(0,1)) + { + case 0: + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun1.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); + break; + case 1: + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/pl_gun2.wav", RANDOM_FLOAT(0.6, 0.8), ATTN_NORM); + break; + } + + pev->effects |= EF_MUZZLEFLASH; + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); + + m_cAmmoLoaded--; +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMHAssassin :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case ASSASSIN_AE_SHOOT1: + Shoot( ); + break; + case ASSASSIN_AE_TOSS1: + { + UTIL_MakeVectors( pev->angles ); + CMGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 2.0 ); + + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + m_fThrowGrenade = FALSE; + // !!!LATER - when in a group, only try to throw grenade if ordered. + } + break; + case ASSASSIN_AE_JUMP: + { + // ALERT( at_console, "jumping"); + UTIL_MakeAimVectors( pev->angles ); + pev->movetype = MOVETYPE_TOSS; + pev->flags &= ~FL_ONGROUND; + pev->velocity = m_vecJumpVelocity; + m_flNextJump = gpGlobals->time + 3.0; + } + return; + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMHAssassin :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/hassassin.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->effects = 0; + pev->health = gSkillData.hassassinHealth; + m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP; + pev->friction = 1; + + m_HackedGunPos = Vector( 0, 24, 48 ); + + m_iTargetRanderamt = 20; + pev->renderamt = 20; + pev->rendermode = kRenderTransTexture; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_human_assassin" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Female Assassin" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMHAssassin :: Precache() +{ + PRECACHE_MODEL("models/hassassin.mdl"); + + PRECACHE_SOUND("weapons/pl_gun1.wav"); + PRECACHE_SOUND("weapons/pl_gun2.wav"); + + PRECACHE_SOUND("debris/beamstart1.wav"); + + m_iShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell +} + + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// Fail Schedule +//========================================================= +Task_t tlAssassinFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + // { TASK_WAIT_PVS, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, +}; + +Schedule_t slAssassinFail[] = +{ + { + tlAssassinFail, + ARRAYSIZE ( tlAssassinFail ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND, + 0, + "AssassinFail" + }, +}; + + +//========================================================= +// Enemy exposed Agrunt's cover +//========================================================= +Task_t tlAssassinExposed[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slAssassinExposed[] = +{ + { + tlAssassinExposed, + ARRAYSIZE ( tlAssassinExposed ), + bits_COND_CAN_MELEE_ATTACK1, + 0, + "AssassinExposed", + }, +}; + + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAssassinTakeCoverFromEnemy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAssassinTakeCoverFromEnemy[] = +{ + { + tlAssassinTakeCoverFromEnemy, + ARRAYSIZE ( tlAssassinTakeCoverFromEnemy ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND, + 0, + "AssassinTakeCoverFromEnemy" + }, +}; + + +//========================================================= +// Take cover from enemy! Tries lateral cover before node +// cover! +//========================================================= +Task_t tlAssassinTakeCoverFromEnemy2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK2 }, + { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, +}; + +Schedule_t slAssassinTakeCoverFromEnemy2[] = +{ + { + tlAssassinTakeCoverFromEnemy2, + ARRAYSIZE ( tlAssassinTakeCoverFromEnemy2 ), + bits_COND_NEW_ENEMY | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_HEAR_SOUND, + 0, + "AssassinTakeCoverFromEnemy2" + }, +}; + + +//========================================================= +// hide from the loudest sound source +//========================================================= +Task_t tlAssassinTakeCoverFromBestSound[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slAssassinTakeCoverFromBestSound[] = +{ + { + tlAssassinTakeCoverFromBestSound, + ARRAYSIZE ( tlAssassinTakeCoverFromBestSound ), + bits_COND_NEW_ENEMY, + 0, + "AssassinTakeCoverFromBestSound" + }, +}; + + + + + +//========================================================= +// AlertIdle Schedules +//========================================================= +Task_t tlAssassinHide[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_SET_SCHEDULE, (float)SCHED_CHASE_ENEMY }, +}; + +Schedule_t slAssassinHide[] = +{ + { + tlAssassinHide, + ARRAYSIZE ( tlAssassinHide ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_ENEMY | + bits_COND_SEE_FEAR | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_HEAR_SOUND, + 0, + "AssassinHide" + }, +}; + + + +//========================================================= +// HUNT Schedules +//========================================================= +Task_t tlAssassinHunt[] = +{ + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slAssassinHunt[] = +{ + { + tlAssassinHunt, + ARRAYSIZE ( tlAssassinHunt ), + bits_COND_NEW_ENEMY | + // bits_COND_SEE_ENEMY | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_HEAR_SOUND, + 0, + "AssassinHunt" + }, +}; + + +//========================================================= +// Jumping Schedules +//========================================================= +Task_t tlAssassinJump[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, + { TASK_SET_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_ATTACK }, +}; + +Schedule_t slAssassinJump[] = +{ + { + tlAssassinJump, + ARRAYSIZE ( tlAssassinJump ), + 0, + 0, + "AssassinJump" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlAssassinJumpAttack[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_JUMP_LAND }, + // { TASK_SET_ACTIVITY, (float)ACT_FLY }, + { TASK_ASSASSIN_FALL_TO_GROUND, (float)0 }, +}; + + +Schedule_t slAssassinJumpAttack[] = +{ + { + tlAssassinJumpAttack, + ARRAYSIZE ( tlAssassinJumpAttack ), + 0, + 0, + "AssassinJumpAttack" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlAssassinJumpLand[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_ASSASSIN_EXPOSED }, + // { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MELEE_ATTACK1 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_REMEMBER, (float)bits_MEMORY_BADJUMP }, + { TASK_FIND_NODE_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_FORGET, (float)bits_MEMORY_BADJUMP }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 }, +}; + +Schedule_t slAssassinJumpLand[] = +{ + { + tlAssassinJumpLand, + ARRAYSIZE ( tlAssassinJumpLand ), + 0, + 0, + "AssassinJumpLand" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMHAssassin ) +{ + slAssassinFail, + slAssassinExposed, + slAssassinTakeCoverFromEnemy, + slAssassinTakeCoverFromEnemy2, + slAssassinTakeCoverFromBestSound, + slAssassinHide, + slAssassinHunt, + slAssassinJump, + slAssassinJumpAttack, + slAssassinJumpLand, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMHAssassin, CMBaseMonster ); + + +//========================================================= +// CheckMeleeAttack1 - jump like crazy if the enemy gets too close. +//========================================================= +BOOL CMHAssassin :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if ( m_flNextJump < gpGlobals->time && (flDist <= 128 || HasMemory( bits_MEMORY_BADJUMP )) && m_hEnemy != NULL ) + { + TraceResult tr; + + Vector vecDest = pev->origin + Vector( RANDOM_FLOAT( -64, 64), RANDOM_FLOAT( -64, 64 ), 160 ); + + UTIL_TraceHull( pev->origin + Vector( 0, 0, 36 ), vecDest + Vector( 0, 0, 36 ), dont_ignore_monsters, human_hull, ENT(pev), &tr); + + if ( tr.fStartSolid || tr.flFraction < 1.0) + { + return FALSE; + } + + float flGravity = g_psv_gravity->value; + + float time = sqrt( 160 / (0.5 * flGravity)); + float speed = flGravity * time / 160; + m_vecJumpVelocity = (vecDest - pev->origin) * speed; + + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 - drop a cap in their ass +// +//========================================================= +BOOL CMHAssassin :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist > 64 && flDist <= 2048 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) + { + TraceResult tr; + + Vector vecSrc = GetGunPosition(); + + // verify that a bullet fired from the gun will hit the enemy before the world. + UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), dont_ignore_monsters, ENT(pev), &tr); + + if ( tr.flFraction == 1 || tr.pHit == m_hEnemy ) + { + return TRUE; + } + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 - toss grenade is enemy gets in the way and is too close. +//========================================================= +BOOL CMHAssassin :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + m_fThrowGrenade = FALSE; + if ( !FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) ) + { + // don't throw grenades at anything that isn't on the ground! + return FALSE; + } + + // don't get grenade happy unless the player starts to piss you off + if ( m_iFrustration <= 2) + return FALSE; + + if ( m_flNextGrenadeCheck < gpGlobals->time && !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 512 /* && flDot >= 0.5 */ /* && NoFriendlyFire() */ ) + { + Vector vecToss = VecCheckThrow( pev, GetGunPosition( ), UTIL_BodyTarget(m_hEnemy, g_vecZero), flDist, 0.5 ); // use dist as speed to get there in 1 second + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + + return TRUE; + } + } + + return FALSE; +} + + +//========================================================= +// RunAI +//========================================================= +void CMHAssassin :: RunAI( void ) +{ + CMBaseMonster :: RunAI(); + + // always visible if moving + // always visible is not on hard + if (m_hEnemy == NULL || pev->deadflag != DEAD_NO || m_Activity == ACT_RUN || m_Activity == ACT_WALK || !(pev->flags & FL_ONGROUND)) + m_iTargetRanderamt = 255; + + if (pev->renderamt > m_iTargetRanderamt) + { + if (pev->renderamt == 255) + { + EMIT_SOUND (ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 0.2, ATTN_NORM ); + } + + pev->renderamt = max( pev->renderamt - 50, m_iTargetRanderamt ); + pev->rendermode = kRenderTransTexture; + } + else if (pev->renderamt < m_iTargetRanderamt) + { + pev->renderamt = min( pev->renderamt + 50, m_iTargetRanderamt ); + if (pev->renderamt == 255) + pev->rendermode = kRenderNormal; + } + + if (m_Activity == ACT_RUN || m_Activity == ACT_WALK) + { + static int iStep = 0; + iStep = ! iStep; + if (iStep) + { + switch( RANDOM_LONG( 0, 3 ) ) + { + case 0: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step1.wav", 0.5, ATTN_NORM); break; + case 1: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step3.wav", 0.5, ATTN_NORM); break; + case 2: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step2.wav", 0.5, ATTN_NORM); break; + case 3: EMIT_SOUND( ENT(pev), CHAN_BODY, "player/pl_step4.wav", 0.5, ATTN_NORM); break; + } + } + } +} + + +//========================================================= +// StartTask +//========================================================= +void CMHAssassin :: StartTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK2: + if (!m_fThrowGrenade) + { + TaskComplete( ); + } + else + { + CMBaseMonster :: StartTask ( pTask ); + } + break; + case TASK_ASSASSIN_FALL_TO_GROUND: + break; + default: + CMBaseMonster :: StartTask ( pTask ); + break; + } +} + + +//========================================================= +// RunTask +//========================================================= +void CMHAssassin :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_ASSASSIN_FALL_TO_GROUND: + MakeIdealYaw( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if (m_fSequenceFinished) + { + if (pev->velocity.z > 0) + { + pev->sequence = LookupSequence( "fly_up" ); + } + else if (HasConditions ( bits_COND_SEE_ENEMY )) + { + pev->sequence = LookupSequence( "fly_attack" ); + pev->frame = 0; + } + else + { + pev->sequence = LookupSequence( "fly_down" ); + pev->frame = 0; + } + + ResetSequenceInfo( ); + SetYawSpeed(); + } + if (pev->flags & FL_ONGROUND) + { + // ALERT( at_console, "on ground\n"); + TaskComplete( ); + } + break; + default: + CMBaseMonster :: RunTask ( pTask ); + break; + } +} + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMHAssassin :: GetSchedule ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_IDLE: + case MONSTERSTATE_ALERT: + { + } + break; + + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + // flying? + if ( pev->movetype == MOVETYPE_TOSS) + { + if (pev->flags & FL_ONGROUND) + { + // ALERT( at_console, "landed\n"); + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_LAND ); + } + else + { + // ALERT( at_console, "jump\n"); + // jump or jump/shoot + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP ); + else + return GetScheduleOfType ( SCHED_ASSASSIN_JUMP_ATTACK ); + } + } + + if ( HasConditions ( bits_COND_LIGHT_DAMAGE ) ) + { + m_iFrustration++; + } + if ( HasConditions ( bits_COND_HEAVY_DAMAGE ) ) + { + m_iFrustration++; + } + + // jump player! + if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + // ALERT( at_console, "melee attack 1\n"); + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } + + // throw grenade + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) + { + // ALERT( at_console, "range attack 2\n"); + return GetScheduleOfType ( SCHED_RANGE_ATTACK2 ); + } + + // spotted + if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) + { + // ALERT( at_console, "exposed\n"); + m_iFrustration++; + return GetScheduleOfType ( SCHED_ASSASSIN_EXPOSED ); + } + + // can attack + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + // ALERT( at_console, "range attack 1\n"); + m_iFrustration = 0; + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + + if ( HasConditions ( bits_COND_SEE_ENEMY ) ) + { + // ALERT( at_console, "face\n"); + return GetScheduleOfType ( SCHED_COMBAT_FACE ); + } + + // new enemy + if ( HasConditions ( bits_COND_NEW_ENEMY ) ) + { + // ALERT( at_console, "take cover\n"); + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + + // ALERT( at_console, "stand\n"); + return GetScheduleOfType ( SCHED_ALERT_STAND ); + } + break; + } + + return CMBaseMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CMHAssassin :: GetScheduleOfType ( int Type ) +{ + // ALERT( at_console, "%d\n", m_iFrustration ); + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + if (pev->health > 30) + return slAssassinTakeCoverFromEnemy; + else + return slAssassinTakeCoverFromEnemy2; + case SCHED_TAKE_COVER_FROM_BEST_SOUND: + return slAssassinTakeCoverFromBestSound; + case SCHED_ASSASSIN_EXPOSED: + return slAssassinExposed; + case SCHED_FAIL: + if (m_MonsterState == MONSTERSTATE_COMBAT) + return slAssassinFail; + break; + case SCHED_ALERT_STAND: + if (m_MonsterState == MONSTERSTATE_COMBAT) + return slAssassinHide; + break; + case SCHED_CHASE_ENEMY: + return slAssassinHunt; + case SCHED_MELEE_ATTACK1: + if (pev->flags & FL_ONGROUND) + { + if (m_flNextJump > gpGlobals->time) + { + // can't jump yet, go ahead and fail + return slAssassinFail; + } + else + { + return slAssassinJump; + } + } + else + { + return slAssassinJumpAttack; + } + case SCHED_ASSASSIN_JUMP: + case SCHED_ASSASSIN_JUMP_ATTACK: + return slAssassinJumpAttack; + case SCHED_ASSASSIN_JUMP_LAND: + return slAssassinJumpLand; + } + + return CMBaseMonster :: GetScheduleOfType( Type ); +} + +#endif diff --git a/src/dlls/headcrab.cpp b/src/dlls/headcrab.cpp index 46ce5a2..4fd5116 100644 --- a/src/dlls/headcrab.cpp +++ b/src/dlls/headcrab.cpp @@ -1,518 +1,518 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// headcrab.cpp - tiny, jumpy alien parasite -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HC_AE_JUMPATTACK ( 2 ) - -Task_t tlHCRangeAttack1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_WAIT_RANDOM, (float)0.5 }, -}; - -Schedule_t slHCRangeAttack1[] = -{ - { - tlHCRangeAttack1, - ARRAYSIZE ( tlHCRangeAttack1 ), - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "HCRangeAttack1" - }, -}; - -Task_t tlHCRangeAttack1Fast[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slHCRangeAttack1Fast[] = -{ - { - tlHCRangeAttack1Fast, - ARRAYSIZE ( tlHCRangeAttack1Fast ), - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "HCRAFast" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMHeadCrab ) -{ - slHCRangeAttack1, - slHCRangeAttack1Fast, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMHeadCrab, CMBaseMonster ); - -const char *CMHeadCrab::pIdleSounds[] = -{ - "headcrab/hc_idle1.wav", - "headcrab/hc_idle2.wav", - "headcrab/hc_idle3.wav", -}; -const char *CMHeadCrab::pAlertSounds[] = -{ - "headcrab/hc_alert1.wav", -}; -const char *CMHeadCrab::pPainSounds[] = -{ - "headcrab/hc_pain1.wav", - "headcrab/hc_pain2.wav", - "headcrab/hc_pain3.wav", -}; -const char *CMHeadCrab::pAttackSounds[] = -{ - "headcrab/hc_attack1.wav", - "headcrab/hc_attack2.wav", - "headcrab/hc_attack3.wav", -}; - -const char *CMHeadCrab::pDeathSounds[] = -{ - "headcrab/hc_die1.wav", - "headcrab/hc_die2.wav", -}; - -const char *CMHeadCrab::pBiteSounds[] = -{ - "headcrab/hc_headbite.wav", -}; - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMHeadCrab :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_PREY; -} - -//========================================================= -// Center - returns the real center of the headcrab. The -// bounding box is much larger than the actual creature so -// this is needed for targeting -//========================================================= -Vector CMHeadCrab :: Center ( void ) -{ - return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 ); -} - - -Vector CMHeadCrab :: BodyTarget( const Vector &posSrc ) -{ - return Center( ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMHeadCrab :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 30; - break; - case ACT_RUN: - case ACT_WALK: - ys = 20; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 60; - break; - case ACT_RANGE_ATTACK1: - ys = 30; - break; - default: - ys = 30; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case HC_AE_JUMPATTACK: - { - ClearBits( pev->flags, FL_ONGROUND ); - - UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground - UTIL_MakeVectors ( pev->angles ); - - Vector vecJumpDir; - if (m_hEnemy != NULL) - { - float gravity = g_psv_gravity->value; - if (gravity <= 1) - gravity = 1; - - // How fast does the headcrab need to travel to reach that height given gravity? - float height = (m_hEnemy->v.origin.z + m_hEnemy->v.view_ofs.z - pev->origin.z); - if (height < 16) - height = 16; - float speed = sqrt( 2 * gravity * height ); - float time = speed / gravity; - - // Scale the sideways velocity to get there at the right time - vecJumpDir = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs - pev->origin); - vecJumpDir = vecJumpDir * ( 1.0 / time ); - - // Speed to offset gravity at the desired height - vecJumpDir.z = speed; - - // Don't jump too far/fast - float distance = vecJumpDir.Length(); - - if (distance > 650) - { - vecJumpDir = vecJumpDir * ( 650.0 / distance ); - } - } - else - { - // jump hop, don't care where - vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350; - } - - int iSound = RANDOM_LONG(0,2); - if ( iSound != 0 ) - EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); - - pev->velocity = vecJumpDir; - m_flNextAttack = gpGlobals->time + 2; - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMHeadCrab :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/headcrab.mdl"); - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.headcrabHealth; - pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin. - pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_headcrab" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Head Crab" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMHeadCrab :: Precache() -{ - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pAlertSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pAttackSounds); - PRECACHE_SOUND_ARRAY(pDeathSounds); - PRECACHE_SOUND_ARRAY(pBiteSounds); - - PRECACHE_MODEL("models/headcrab.mdl"); -} - - -//========================================================= -// RunTask -//========================================================= -void CMHeadCrab :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - case TASK_RANGE_ATTACK2: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - SetTouch( NULL ); - m_IdealActivity = ACT_IDLE; - } - break; - } - default: - { - CMBaseMonster :: RunTask(pTask); - } - } -} - -//========================================================= -// LeapTouch - this is the headcrab's touch function when it -// is in the air -//========================================================= -void CMHeadCrab :: LeapTouch ( edict_t *pOther ) -{ - if ( !pOther->v.takedamage ) - { - return; - } - - // Don't hit if back on ground - if ( !FBitSet( pev->flags, FL_ONGROUND ) ) - { - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); - - if (UTIL_IsPlayer(pOther)) - UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); - } - } - - SetTouch( NULL ); -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CMHeadCrab :: PrescheduleThink ( void ) -{ - // make the crab coo a little bit in combat state - if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 ) - { - IdleSound(); - } -} - -void CMHeadCrab :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_RANGE_ATTACK1: - { - EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); - m_IdealActivity = ACT_RANGE_ATTACK1; - SetTouch ( &CMHeadCrab::LeapTouch ); - break; - } - default: - { - CMBaseMonster :: StartTask( pTask ); - } - } -} - - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 -//========================================================= -BOOL CMHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - return FALSE; - // BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now. -#if 0 - if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 ) - { - return TRUE; - } - return FALSE; -#endif -} - -int CMHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Don't take any acid damage -- BigMomma's mortar is acid - if ( bitsDamageType & DMG_ACID ) - flDamage = 0; - - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// IdleSound -//========================================================= -#define CRAB_ATTN_IDLE (float)1.5 -void CMHeadCrab :: IdleSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// AlertSound -//========================================================= -void CMHeadCrab :: AlertSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// AlertSound -//========================================================= -void CMHeadCrab :: PainSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -//========================================================= -// DeathSound -//========================================================= -void CMHeadCrab :: DeathSound ( void ) -{ - EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); -} - -Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_RANGE_ATTACK1: - { - return &slHCRangeAttack1[ 0 ]; - } - break; - } - - return CMBaseMonster::GetScheduleOfType( Type ); -} - - - -void CMBabyCrab :: Spawn( void ) -{ - CMHeadCrab::Spawn(); - SET_MODEL(ENT(pev), "models/baby_headcrab.mdl"); - pev->rendermode = kRenderTransTexture; - pev->renderamt = 192; - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - - pev->health = gSkillData.headcrabHealth * 0.25; // less health than full grown -} - -void CMBabyCrab :: Precache( void ) -{ - PRECACHE_MODEL( "models/baby_headcrab.mdl" ); - CMHeadCrab::Precache(); -} - - -void CMBabyCrab :: SetYawSpeed ( void ) -{ - pev->yaw_speed = 120; -} - - -BOOL CMBabyCrab :: CheckRangeAttack1( float flDot, float flDist ) -{ - if ( pev->flags & FL_ONGROUND ) - { - if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) ) - return TRUE; - - // A little less accurate, but jump from closer - if ( flDist <= 180 && flDot >= 0.55 ) - return TRUE; - } - - return FALSE; -} - - -Schedule_t* CMBabyCrab :: GetScheduleOfType ( int Type ) -{ - switch( Type ) - { - case SCHED_FAIL: // If you fail, try to jump! - if ( m_hEnemy != NULL ) - return slHCRangeAttack1Fast; - break; - - case SCHED_RANGE_ATTACK1: - { - return slHCRangeAttack1Fast; - } - break; - } - - return CMHeadCrab::GetScheduleOfType( Type ); -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// headcrab.cpp - tiny, jumpy alien parasite +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HC_AE_JUMPATTACK ( 2 ) + +Task_t tlHCRangeAttack1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_WAIT_RANDOM, (float)0.5 }, +}; + +Schedule_t slHCRangeAttack1[] = +{ + { + tlHCRangeAttack1, + ARRAYSIZE ( tlHCRangeAttack1 ), + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "HCRangeAttack1" + }, +}; + +Task_t tlHCRangeAttack1Fast[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slHCRangeAttack1Fast[] = +{ + { + tlHCRangeAttack1Fast, + ARRAYSIZE ( tlHCRangeAttack1Fast ), + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "HCRAFast" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMHeadCrab ) +{ + slHCRangeAttack1, + slHCRangeAttack1Fast, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMHeadCrab, CMBaseMonster ); + +const char *CMHeadCrab::pIdleSounds[] = +{ + "headcrab/hc_idle1.wav", + "headcrab/hc_idle2.wav", + "headcrab/hc_idle3.wav", +}; +const char *CMHeadCrab::pAlertSounds[] = +{ + "headcrab/hc_alert1.wav", +}; +const char *CMHeadCrab::pPainSounds[] = +{ + "headcrab/hc_pain1.wav", + "headcrab/hc_pain2.wav", + "headcrab/hc_pain3.wav", +}; +const char *CMHeadCrab::pAttackSounds[] = +{ + "headcrab/hc_attack1.wav", + "headcrab/hc_attack2.wav", + "headcrab/hc_attack3.wav", +}; + +const char *CMHeadCrab::pDeathSounds[] = +{ + "headcrab/hc_die1.wav", + "headcrab/hc_die2.wav", +}; + +const char *CMHeadCrab::pBiteSounds[] = +{ + "headcrab/hc_headbite.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMHeadCrab :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_PREY; +} + +//========================================================= +// Center - returns the real center of the headcrab. The +// bounding box is much larger than the actual creature so +// this is needed for targeting +//========================================================= +Vector CMHeadCrab :: Center ( void ) +{ + return Vector( pev->origin.x, pev->origin.y, pev->origin.z + 6 ); +} + + +Vector CMHeadCrab :: BodyTarget( const Vector &posSrc ) +{ + return Center( ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMHeadCrab :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 30; + break; + case ACT_RUN: + case ACT_WALK: + ys = 20; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 60; + break; + case ACT_RANGE_ATTACK1: + ys = 30; + break; + default: + ys = 30; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMHeadCrab :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case HC_AE_JUMPATTACK: + { + ClearBits( pev->flags, FL_ONGROUND ); + + UTIL_SetOrigin (pev, pev->origin + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground + UTIL_MakeVectors ( pev->angles ); + + Vector vecJumpDir; + if (m_hEnemy != NULL) + { + float gravity = g_psv_gravity->value; + if (gravity <= 1) + gravity = 1; + + // How fast does the headcrab need to travel to reach that height given gravity? + float height = (m_hEnemy->v.origin.z + m_hEnemy->v.view_ofs.z - pev->origin.z); + if (height < 16) + height = 16; + float speed = sqrt( 2 * gravity * height ); + float time = speed / gravity; + + // Scale the sideways velocity to get there at the right time + vecJumpDir = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs - pev->origin); + vecJumpDir = vecJumpDir * ( 1.0 / time ); + + // Speed to offset gravity at the desired height + vecJumpDir.z = speed; + + // Don't jump too far/fast + float distance = vecJumpDir.Length(); + + if (distance > 650) + { + vecJumpDir = vecJumpDir * ( 650.0 / distance ); + } + } + else + { + // jump hop, don't care where + vecJumpDir = Vector( gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_up.z ) * 350; + } + + int iSound = RANDOM_LONG(0,2); + if ( iSound != 0 ) + EMIT_SOUND_DYN( edict(), CHAN_VOICE, pAttackSounds[iSound], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); + + pev->velocity = vecJumpDir; + m_flNextAttack = gpGlobals->time + 2; + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMHeadCrab :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/headcrab.mdl"); + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.headcrabHealth; + pev->view_ofs = Vector ( 0, 0, 20 );// position of the eyes relative to monster's origin. + pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_headcrab" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Head Crab" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMHeadCrab :: Precache() +{ + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pAlertSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pAttackSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + PRECACHE_SOUND_ARRAY(pBiteSounds); + + PRECACHE_MODEL("models/headcrab.mdl"); +} + + +//========================================================= +// RunTask +//========================================================= +void CMHeadCrab :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + case TASK_RANGE_ATTACK2: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + SetTouch( NULL ); + m_IdealActivity = ACT_IDLE; + } + break; + } + default: + { + CMBaseMonster :: RunTask(pTask); + } + } +} + +//========================================================= +// LeapTouch - this is the headcrab's touch function when it +// is in the air +//========================================================= +void CMHeadCrab :: LeapTouch ( edict_t *pOther ) +{ + if ( !pOther->v.takedamage ) + { + return; + } + + // Don't hit if back on ground + if ( !FBitSet( pev->flags, FL_ONGROUND ) ) + { + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); + + if (UTIL_IsPlayer(pOther)) + UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); + } + } + + SetTouch( NULL ); +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CMHeadCrab :: PrescheduleThink ( void ) +{ + // make the crab coo a little bit in combat state + if ( m_MonsterState == MONSTERSTATE_COMBAT && RANDOM_FLOAT( 0, 5 ) < 0.1 ) + { + IdleSound(); + } +} + +void CMHeadCrab :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_RANGE_ATTACK1: + { + EMIT_SOUND_DYN( edict(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); + m_IdealActivity = ACT_RANGE_ATTACK1; + SetTouch ( &CMHeadCrab::LeapTouch ); + break; + } + default: + { + CMBaseMonster :: StartTask( pTask ); + } + } +} + + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMHeadCrab :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist <= 256 && flDot >= 0.65 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 +//========================================================= +BOOL CMHeadCrab :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + return FALSE; + // BUGBUG: Why is this code here? There is no ACT_RANGE_ATTACK2 animation. I've disabled it for now. +#if 0 + if ( FBitSet( pev->flags, FL_ONGROUND ) && flDist > 64 && flDist <= 256 && flDot >= 0.5 ) + { + return TRUE; + } + return FALSE; +#endif +} + +int CMHeadCrab :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Don't take any acid damage -- BigMomma's mortar is acid + if ( bitsDamageType & DMG_ACID ) + flDamage = 0; + + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// IdleSound +//========================================================= +#define CRAB_ATTN_IDLE (float)1.5 +void CMHeadCrab :: IdleSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// AlertSound +//========================================================= +void CMHeadCrab :: AlertSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// AlertSound +//========================================================= +void CMHeadCrab :: PainSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +//========================================================= +// DeathSound +//========================================================= +void CMHeadCrab :: DeathSound ( void ) +{ + EMIT_SOUND_DYN( edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() ); +} + +Schedule_t* CMHeadCrab :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_RANGE_ATTACK1: + { + return &slHCRangeAttack1[ 0 ]; + } + break; + } + + return CMBaseMonster::GetScheduleOfType( Type ); +} + + + +void CMBabyCrab :: Spawn( void ) +{ + CMHeadCrab::Spawn(); + SET_MODEL(ENT(pev), "models/baby_headcrab.mdl"); + pev->rendermode = kRenderTransTexture; + pev->renderamt = 192; + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + + pev->health = gSkillData.headcrabHealth * 0.25; // less health than full grown +} + +void CMBabyCrab :: Precache( void ) +{ + PRECACHE_MODEL( "models/baby_headcrab.mdl" ); + CMHeadCrab::Precache(); +} + + +void CMBabyCrab :: SetYawSpeed ( void ) +{ + pev->yaw_speed = 120; +} + + +BOOL CMBabyCrab :: CheckRangeAttack1( float flDot, float flDist ) +{ + if ( pev->flags & FL_ONGROUND ) + { + if ( pev->groundentity && (pev->groundentity->v.flags & (FL_CLIENT|FL_MONSTER)) ) + return TRUE; + + // A little less accurate, but jump from closer + if ( flDist <= 180 && flDot >= 0.55 ) + return TRUE; + } + + return FALSE; +} + + +Schedule_t* CMBabyCrab :: GetScheduleOfType ( int Type ) +{ + switch( Type ) + { + case SCHED_FAIL: // If you fail, try to jump! + if ( m_hEnemy != NULL ) + return slHCRangeAttack1Fast; + break; + + case SCHED_RANGE_ATTACK1: + { + return slHCRangeAttack1Fast; + } + break; + } + + return CMHeadCrab::GetScheduleOfType( Type ); +} diff --git a/src/dlls/hgrunt.cpp b/src/dlls/hgrunt.cpp index 0378c7c..532a496 100644 --- a/src/dlls/hgrunt.cpp +++ b/src/dlls/hgrunt.cpp @@ -1,2121 +1,2121 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// hgrunt -//========================================================= - -//========================================================= -// Hit groups! -//========================================================= -/* - - 1 - Head - 2 - Stomach - 3 - Gun - -*/ - - -#include "extdll.h" -#include "plane.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "weapons.h" -#include "cmtalkmonster.h" -#include "effects.h" -#include "customentity.h" - -int g_fGruntQuestion; // true if an idle grunt asked a question. Cleared when someone answers. - - -//========================================================= -// monster-specific DEFINE's -//========================================================= -#define GRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! -#define GRUNT_VOL 0.35 // volume of grunt sounds -#define GRUNT_ATTN ATTN_NORM // attenutation of grunt sentences -#define HGRUNT_LIMP_HEALTH 20 -#define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. -#define HGRUNT_NUM_HEADS 2 // how many grunt heads are there? -#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill -#define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences - -#define HGRUNT_9MMAR ( 1 << 0) -#define HGRUNT_HANDGRENADE ( 1 << 1) -#define HGRUNT_GRENADELAUNCHER ( 1 << 2) -#define HGRUNT_SHOTGUN ( 1 << 3) - -#define HEAD_GROUP 1 -#define HEAD_GRUNT 0 -#define HEAD_COMMANDER 1 -#define HEAD_SHOTGUN 2 -#define HEAD_M203 3 -#define GUN_GROUP 2 -#define GUN_MP5 0 -#define GUN_SHOTGUN 1 -#define GUN_NONE 2 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HGRUNT_AE_RELOAD ( 2 ) -#define HGRUNT_AE_KICK ( 3 ) -#define HGRUNT_AE_BURST1 ( 4 ) -#define HGRUNT_AE_BURST2 ( 5 ) -#define HGRUNT_AE_BURST3 ( 6 ) -#define HGRUNT_AE_GREN_TOSS ( 7 ) -#define HGRUNT_AE_GREN_LAUNCH ( 8 ) -#define HGRUNT_AE_GREN_DROP ( 9 ) -#define HGRUNT_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad. -#define HGRUNT_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5. - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_GRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, - SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). - SCHED_GRUNT_COVER_AND_RELOAD, - SCHED_GRUNT_SWEEP, - SCHED_GRUNT_FOUND_ENEMY, - SCHED_GRUNT_REPEL, - SCHED_GRUNT_REPEL_ATTACK, - SCHED_GRUNT_REPEL_LAND, - SCHED_GRUNT_WAIT_FACE_ENEMY, - SCHED_GRUNT_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. - SCHED_GRUNT_ELOF_FAIL, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_GRUNT_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, - TASK_GRUNT_SPEAK_SENTENCE, - TASK_GRUNT_CHECK_FIRE, -}; - -//========================================================= -// monster-specific conditions -//========================================================= -#define bits_COND_GRUNT_NOFIRE ( bits_COND_SPECIAL1 ) - -const char *CMHGrunt::pGruntSentences[] = -{ - "HG_GREN", // grenade scared grunt - "HG_ALERT", // sees player - "HG_MONSTER", // sees monster - "HG_COVER", // running to cover - "HG_THROW", // about to throw grenade - "HG_CHARGE", // running out to get the enemy - "HG_TAUNT", // say rude things -}; - -enum -{ - HGRUNT_SENT_NONE = -1, - HGRUNT_SENT_GREN = 0, - HGRUNT_SENT_ALERT, - HGRUNT_SENT_MONSTER, - HGRUNT_SENT_COVER, - HGRUNT_SENT_THROW, - HGRUNT_SENT_CHARGE, - HGRUNT_SENT_TAUNT, -} HGRUNT_SENTENCE_TYPES; - -//========================================================= -// Speak Sentence - say your cued up sentence. -// -// Some grunt sentences (take cover and charge) rely on actually -// being able to execute the intended action. It's really lame -// when a grunt says 'COVER ME' and then doesn't move. The problem -// is that the sentences were played when the decision to TRY -// to move to cover was made. Now the sentence is played after -// we know for sure that there is a valid path. The schedule -// may still fail but in most cases, well after the grunt has -// started moving. -//========================================================= -void CMHGrunt :: SpeakSentence( void ) -{ - if ( m_iSentence == HGRUNT_SENT_NONE ) - { - // no sentence cued up. - return; - } - - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } -} - -//========================================================= -// IRelationship - overridden because Alien Grunts are -// Human Grunt's nemesis. -//========================================================= -int CMHGrunt::IRelationship ( CMBaseEntity *pTarget ) -{ - // on single player, forcing R_NM makes sense. - // on multiplayer, a custom classification will cause misbehaviour. - /* - if (( strcmp(STRING(pTarget->pev->model), "models/agrunt.mdl") == 0 ) || - ( strcmp(STRING(pTarget->pev->model), "models/garg.mdl") == 0 )) - { - return R_NM; - } - */ - return CMBaseMonster::IRelationship( pTarget ); -} - -//========================================================= -// GibMonster - make gun fly through the air. -//========================================================= -void CMHGrunt :: GibMonster ( void ) -{ - Vector vecGunPos; - Vector vecGunAngles; - - CMBaseMonster :: GibMonster(); -} - -//========================================================= -// ISoundMask - Overidden for human grunts because they -// hear the DANGER sound that is made by hand grenades and -// other dangerous items. -//========================================================= -int CMHGrunt :: ISoundMask ( void ) -{ - return 0; -} - -//========================================================= -// someone else is talking - don't speak -//========================================================= -BOOL CMHGrunt :: FOkToSpeak( void ) -{ -// if someone else is talking, don't speak - if (gpGlobals->time <= CMTalkMonster::g_talkWaitTime) - return FALSE; - - if ( pev->spawnflags & SF_MONSTER_GAG ) - { - if ( m_MonsterState != MONSTERSTATE_COMBAT ) - { - // no talking outside of combat if gagged. - return FALSE; - } - } - - // if player is not in pvs, don't speak -// if (FNullEnt(FIND_CLIENT_IN_PVS(edict()))) -// return FALSE; - - return TRUE; -} - -//========================================================= -//========================================================= -void CMHGrunt :: JustSpoke( void ) -{ - CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(1.5, 2.0); - m_iSentence = HGRUNT_SENT_NONE; -} - -//========================================================= -// PrescheduleThink - this function runs after conditions -// are collected and before scheduling code is run. -//========================================================= -void CMHGrunt :: PrescheduleThink ( void ) -{ - return; -} - -//========================================================= -// FCanCheckAttacks - this is overridden for human grunts -// because they can throw/shoot grenades when they can't see their -// target and the base class doesn't check attacks if the monster -// cannot see its enemy. -// -// !!!BUGBUG - this gets called before a 3-round burst is fired -// which means that a friendly can still be hit with up to 2 rounds. -// ALSO, grenades will not be tossed if there is a friendly in front, -// this is a bad bug. Friendly machine gun fire avoidance -// will unecessarily prevent the throwing of a grenade as well. -//========================================================= -BOOL CMHGrunt :: FCanCheckAttacks ( void ) -{ - if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) - { - return TRUE; - } - else - { - return FALSE; - } -} - - -//========================================================= -// CheckMeleeAttack1 -//========================================================= -BOOL CMHGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - if (m_hEnemy != NULL) - { - if (UTIL_IsPlayer(m_hEnemy)) - { - if ( flDist <= 64 && flDot >= 0.7) - { - return TRUE; - } - } - else if (m_hEnemy->v.euser4 != NULL) - { - edict_t *pEdict = m_hEnemy; - CMBaseMonster *pEnemy = GetClassPtr((CMBaseMonster *)VARS(pEdict)); - - if ( flDist <= 64 && flDot >= 0.7 && - pEnemy->Classify() != CLASS_ALIEN_BIOWEAPON && - pEnemy->Classify() != CLASS_PLAYER_BIOWEAPON ) - { - return TRUE; - } - } - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 - overridden for HGrunt, cause -// FCanCheckAttacks() doesn't disqualify all attacks based -// on whether or not the enemy is occluded because unlike -// the base class, the HGrunt can attack when the enemy is -// occluded (throw grenade over wall, etc). We must -// disqualify the machine gun attack if the enemy is occluded. -//========================================================= -BOOL CMHGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 ) - { - TraceResult tr; - - if ( !UTIL_IsPlayer(m_hEnemy) && flDist <= 64 ) - { - // kick nonclients, but don't shoot at them. - return FALSE; - } - - Vector vecSrc = GetGunPosition(); - - // verify that a bullet fired from the gun will hit the enemy before the world. - UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr); - - if ( tr.flFraction == 1.0 ) - { - return TRUE; - } - } - - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 - this checks the Grunt's grenade -// attack. -//========================================================= -BOOL CMHGrunt :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - if (! FBitSet(pev->weapons, (HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER))) - { - return FALSE; - } - - // if the grunt isn't moving, it's ok to check. - if ( m_flGroundSpeed != 0 ) - { - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - // assume things haven't changed too much since last time - if (gpGlobals->time < m_flNextGrenadeCheck ) - { - return m_fThrowGrenade; - } - - if ( !FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) && m_hEnemy->v.waterlevel == 0 && m_vecEnemyLKP.z > pev->absmax.z ) - { - //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to - // be grenaded. - // don't throw grenades at anything that isn't on the ground! - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - Vector vecTarget; - - if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) - { - // find feet - if (RANDOM_LONG(0,1)) - { - // magically know where they are - vecTarget = Vector( m_hEnemy->v.origin.x, m_hEnemy->v.origin.y, m_hEnemy->v.absmin.z ); - } - else - { - // toss it to where you last saw them - vecTarget = m_vecEnemyLKP; - } - // vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); - // estimate position - // vecTarget = vecTarget + m_hEnemy->pev->velocity * 2; - } - else - { - // find target - // vecTarget = m_hEnemy->BodyTarget( pev->origin ); - vecTarget = m_vecEnemyLKP + (UTIL_BodyTarget(m_hEnemy, pev->origin ) - m_hEnemy->v.origin); - // estimate position - if (HasConditions( bits_COND_SEE_ENEMY)) - vecTarget = vecTarget + ((vecTarget - pev->origin).Length() / gSkillData.hgruntGrenadeSpeed) * m_hEnemy->v.velocity; - } - - if ( ( vecTarget - pev->origin ).Length2D() <= 256 ) - { - // crap, I don't want to blow myself up - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - m_fThrowGrenade = FALSE; - return m_fThrowGrenade; - } - - if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) - { - Vector vecToss = VecCheckToss( pev, GetGunPosition(), vecTarget, 0.5 ); - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time; // 1/3 second. - } - else - { - // don't throw - m_fThrowGrenade = FALSE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - } - } - else - { - Vector vecToss = VecCheckThrow( pev, GetGunPosition(), vecTarget, gSkillData.hgruntGrenadeSpeed, 0.5 ); - - if ( vecToss != g_vecZero ) - { - m_vecTossVelocity = vecToss; - - // throw a hand grenade - m_fThrowGrenade = TRUE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 0.3; // 1/3 second. - } - else - { - // don't throw - m_fThrowGrenade = FALSE; - // don't check again for a while. - m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. - } - } - - - - return m_fThrowGrenade; -} - - -//========================================================= -// TraceAttack - make sure we're not taking it in the helmet -//========================================================= -void CMHGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - // check for helmet shot - if (ptr->iHitgroup == 11) - { - // make sure we're wearing one - if (GetBodygroup( 1 ) == HEAD_GRUNT && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB))) - { - // absorb damage - flDamage -= 20; - if (flDamage <= 0) - { - UTIL_Ricochet( ptr->vecEndPos, 1.0 ); - flDamage = 0.01; - } - } - // it's head shot anyways - ptr->iHitgroup = HITGROUP_HEAD; - } - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -//========================================================= -// TakeDamage - overridden for the grunt because the grunt -// needs to forget that he is in cover if he's hurt. (Obviously -// not in a safe place anymore). -//========================================================= -int CMHGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - Forget( bits_MEMORY_INCOVER ); - - return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMHGrunt :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 150; - break; - case ACT_RUN: - ys = 150; - break; - case ACT_WALK: - ys = 180; - break; - case ACT_RANGE_ATTACK1: - ys = 120; - break; - case ACT_RANGE_ATTACK2: - ys = 120; - break; - case ACT_MELEE_ATTACK1: - ys = 120; - break; - case ACT_MELEE_ATTACK2: - ys = 120; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 180; - break; - case ACT_GLIDE: - case ACT_FLY: - ys = 30; - break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -void CMHGrunt :: IdleSound( void ) -{ - if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1))) - { - if (!g_fGruntQuestion) - { - // ask question or make statement - switch (RANDOM_LONG(0,2)) - { - case 0: // check in - SENTENCEG_PlayRndSz(ENT(pev), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fGruntQuestion = 1; - break; - case 1: // question - SENTENCEG_PlayRndSz(ENT(pev), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fGruntQuestion = 2; - break; - case 2: // statement - SENTENCEG_PlayRndSz(ENT(pev), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - } - else - { - switch (g_fGruntQuestion) - { - case 1: // check in - SENTENCEG_PlayRndSz(ENT(pev), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - case 2: // question - SENTENCEG_PlayRndSz(ENT(pev), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - g_fGruntQuestion = 0; - } - JustSpoke(); - } -} - -//========================================================= -// CheckAmmo - overridden for the grunt because he actually -// uses ammo! (base class doesn't) -//========================================================= -void CMHGrunt :: CheckAmmo ( void ) -{ - if ( m_cAmmoLoaded <= 0 ) - { - SetConditions(bits_COND_NO_AMMO_LOADED); - } -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMHGrunt :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_HUMAN_MILITARY; -} - -//========================================================= -//========================================================= -edict_t *CMHGrunt :: Kick( void ) -{ - TraceResult tr; - - UTIL_MakeVectors( pev->angles ); - Vector vecStart = pev->origin; - vecStart.z += pev->size.z * 0.5; - Vector vecEnd = vecStart + (gpGlobals->v_forward * 70); - - UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); - - if ( tr.pHit ) - return tr.pHit; - - return NULL; -} - -//========================================================= -// GetGunPosition return the end of the barrel -//========================================================= - -Vector CMHGrunt :: GetGunPosition( ) -{ - if (m_fStanding ) - { - return pev->origin + Vector( 0, 0, 60 ); - } - else - { - return pev->origin + Vector( 0, 0, 48 ); - } -} - -//========================================================= -// Shoot -//========================================================= -void CMHGrunt :: Shoot ( void ) -{ - if (m_hEnemy == NULL) - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_MP5 ); // shoot +-5 degrees - - pev->effects |= EF_MUZZLEFLASH; - - // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. - // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue - //m_cAmmoLoaded--;// take away a bullet! - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); -} - -//========================================================= -// Shoot -//========================================================= -void CMHGrunt :: Shotgun ( void ) -{ - if (m_hEnemy == NULL) - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - - UTIL_MakeVectors ( pev->angles ); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL); - FireBullets(gSkillData.hgruntShotgunPellets, vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0 ); // shoot +-7.5 degrees - - pev->effects |= EF_MUZZLEFLASH; - - // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. - // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue - //m_cAmmoLoaded--;// take away a bullet! - - Vector angDir = UTIL_VecToAngles( vecShootDir ); - SetBlending( 0, angDir.x ); -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - Vector vecShootDir; - Vector vecShootOrigin; - - switch( pEvent->event ) - { - case HGRUNT_AE_DROP_GUN: - { - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment( 0, vecGunPos, vecGunAngles ); - - // switch to body group with no gun. - SetBodygroup( GUN_GROUP, GUN_NONE ); - } - break; - - case HGRUNT_AE_RELOAD: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_reload1.wav", 1, ATTN_NORM ); - m_cAmmoLoaded = m_cClipSize; - ClearConditions(bits_COND_NO_AMMO_LOADED); - break; - - case HGRUNT_AE_GREN_TOSS: - { - UTIL_MakeVectors( pev->angles ); - // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); - CMGrenade::ShootTimed( pev, GetGunPosition(), m_vecTossVelocity, 3.5 ); - - m_fThrowGrenade = FALSE; - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - // !!!LATER - when in a group, only try to throw grenade if ordered. - } - break; - - case HGRUNT_AE_GREN_LAUNCH: - { - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM); - CMGrenade::ShootContact( pev, GetGunPosition(), m_vecTossVelocity ); - m_fThrowGrenade = FALSE; - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - } - break; - - case HGRUNT_AE_GREN_DROP: - { - UTIL_MakeVectors( pev->angles ); - CMGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 17 - gpGlobals->v_right * 27 + gpGlobals->v_up * 6, g_vecZero, 3 ); - } - break; - - case HGRUNT_AE_BURST1: - { - if ( FBitSet( pev->weapons, HGRUNT_9MMAR )) - { - Shoot(); - - // the first round of the three round burst plays the sound and puts a sound in the world sound list. - if ( RANDOM_LONG(0,1) ) - { - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM ); - } - else - { - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM ); - } - } - else - { - Shotgun( ); - - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sbarrel1.wav", 1, ATTN_NORM ); - } - } - break; - - case HGRUNT_AE_BURST2: - case HGRUNT_AE_BURST3: - Shoot(); - break; - - case HGRUNT_AE_KICK: - { - edict_t *pHurt = Kick(); - - if ( pHurt ) - { - // SOUND HERE! - UTIL_MakeVectors( pev->angles ); - pHurt->v.punchangle.x = 15; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; - if (UTIL_IsPlayer(pHurt)) - UTIL_TakeDamage( pHurt, pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); - else if (pHurt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - pMonster->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); - } - } - } - break; - - case HGRUNT_AE_CAUGHT_ENEMY: - { - if ( FOkToSpeak() ) - { - SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - - } - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMHGrunt :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/hgrunt.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->effects = 0; - pev->health = gSkillData.hgruntHealth; - m_flFieldOfView = VIEW_FIELD_FULL; // indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_flNextGrenadeCheck = gpGlobals->time + 1; - m_flNextPainTime = gpGlobals->time; - m_iSentence = HGRUNT_SENT_NONE; - - m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - -//jlb m_fEnemyEluded = FALSE; - m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. - - m_HackedGunPos = Vector ( 0, 0, 55 ); - - if (pev->weapons == 0) - { - // initialize to original values - switch(RANDOM_LONG(0, 2)) - { - case 0: - pev->weapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE; - break; - case 1: - pev->weapons = HGRUNT_SHOTGUN; - break; - case 2: - pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER; - break; - } - } - - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - SetBodygroup( GUN_GROUP, GUN_SHOTGUN ); - m_cClipSize = 8; - } - else - { - m_cClipSize = GRUNT_CLIP_SIZE; - } - m_cAmmoLoaded = m_cClipSize; - - if (RANDOM_LONG( 0, 99 ) < 80) - pev->skin = 0; // light skin - else - pev->skin = 1; // dark skin - - if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) - { - SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN); - } - else if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) - { - SetBodygroup( HEAD_GROUP, HEAD_M203 ); - pev->skin = 1; // alway dark skin - } - - CMTalkMonster::g_talkWaitTime = 0; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_human_grunt" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Human Grunt" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMHGrunt :: Precache() -{ - PRECACHE_MODEL("models/hgrunt.mdl"); - - PRECACHE_SOUND( "hgrunt/gr_mgun1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_mgun2.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_die1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_die2.wav" ); - PRECACHE_SOUND( "hgrunt/gr_die3.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_pain1.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain2.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain3.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain4.wav" ); - PRECACHE_SOUND( "hgrunt/gr_pain5.wav" ); - - PRECACHE_SOUND( "hgrunt/gr_reload1.wav" ); - - PRECACHE_SOUND( "weapons/glauncher.wav" ); - - PRECACHE_SOUND( "weapons/sbarrel1.wav" ); - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - // get voice pitch - if (RANDOM_LONG(0,1)) - m_voicePitch = 109 + RANDOM_LONG(0,7); - else - m_voicePitch = 100; - - m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell - m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl"); -} - -//========================================================= -// start task -//========================================================= -void CMHGrunt :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_GRUNT_CHECK_FIRE: - TaskComplete(); - break; - - case TASK_GRUNT_SPEAK_SENTENCE: - SpeakSentence(); - TaskComplete(); - break; - - case TASK_WALK_PATH: - case TASK_RUN_PATH: - // grunt no longer assumes he is covered if he moves - Forget( bits_MEMORY_INCOVER ); - CMBaseMonster ::StartTask( pTask ); - break; - - case TASK_RELOAD: - m_IdealActivity = ACT_RELOAD; - break; - - case TASK_GRUNT_FACE_TOSS_DIR: - break; - - case TASK_FACE_IDEAL: - case TASK_FACE_ENEMY: - CMBaseMonster :: StartTask( pTask ); - if (pev->movetype == MOVETYPE_FLY) - { - m_IdealActivity = ACT_GLIDE; - } - break; - - default: - CMBaseMonster :: StartTask( pTask ); - break; - } -} - -//========================================================= -// RunTask -//========================================================= -void CMHGrunt :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_GRUNT_FACE_TOSS_DIR: - { - // project a point along the toss vector and turn to face that point. - MakeIdealYaw( pev->origin + m_vecTossVelocity * 64 ); - ChangeYaw( pev->yaw_speed ); - - if ( FacingIdeal() ) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - break; - } - default: - { - CMBaseMonster :: RunTask( pTask ); - break; - } - } -} - -//========================================================= -// PainSound -//========================================================= -void CMHGrunt :: PainSound ( void ) -{ - if ( gpGlobals->time > m_flNextPainTime ) - { -#if 0 - if ( RANDOM_LONG(0,99) < 5 ) - { - // pain sentences are rare - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); - JustSpoke(); - return; - } - } -#endif - switch ( RANDOM_LONG(0,6) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain3.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain4.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain5.wav", 1, ATTN_NORM ); - break; - case 3: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain1.wav", 1, ATTN_NORM ); - break; - case 4: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain2.wav", 1, ATTN_NORM ); - break; - } - - m_flNextPainTime = gpGlobals->time + 1; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CMHGrunt :: DeathSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE ); - break; - } -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// GruntFail -//========================================================= -Task_t tlGruntFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slGruntFail[] = -{ - { - tlGruntFail, - ARRAYSIZE ( tlGruntFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2, - 0, - "Grunt Fail" - }, -}; - -//========================================================= -// Grunt Combat Fail -//========================================================= -Task_t tlGruntCombatFail[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_WAIT_PVS, (float)0 }, -}; - -Schedule_t slGruntCombatFail[] = -{ - { - tlGruntCombatFail, - ARRAYSIZE ( tlGruntCombatFail ), - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2, - 0, - "Grunt Combat Fail" - }, -}; - -//========================================================= -// Victory dance! -//========================================================= -Task_t tlGruntVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1.5 }, - { TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, -}; - -Schedule_t slGruntVictoryDance[] = -{ - { - tlGruntVictoryDance, - ARRAYSIZE ( tlGruntVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "GruntVictoryDance" - }, -}; - -//========================================================= -// Establish line of fire - move to a position that allows -// the grunt to attack. -//========================================================= -Task_t tlGruntEstablishLineOfFire[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_ELOF_FAIL }, - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_GRUNT_SPEAK_SENTENCE,(float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slGruntEstablishLineOfFire[] = -{ - { - tlGruntEstablishLineOfFire, - ARRAYSIZE ( tlGruntEstablishLineOfFire ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_HEAR_SOUND, - 0, - "GruntEstablishLineOfFire" - }, -}; - -//========================================================= -// GruntFoundEnemy - grunt established sight with an enemy -// that was hiding from the squad. -//========================================================= -Task_t tlGruntFoundEnemy[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_SIGNAL1 }, -}; - -Schedule_t slGruntFoundEnemy[] = -{ - { - tlGruntFoundEnemy, - ARRAYSIZE ( tlGruntFoundEnemy ), - bits_COND_HEAR_SOUND, - 0, - "GruntFoundEnemy" - }, -}; - -//========================================================= -// GruntCombatFace Schedule -//========================================================= -Task_t tlGruntCombatFace1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_WAIT, (float)1.5 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_SWEEP }, -}; - -Schedule_t slGruntCombatFace[] = -{ - { - tlGruntCombatFace1, - ARRAYSIZE ( tlGruntCombatFace1 ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2, - 0, - "Combat Face" - }, -}; - -//========================================================= -// Suppressing fire - don't stop shooting until the clip is -// empty or grunt gets hurt. -//========================================================= -Task_t tlGruntSignalSuppress[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_SIGNAL2 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntSignalSuppress[] = -{ - { - tlGruntSignalSuppress, - ARRAYSIZE ( tlGruntSignalSuppress ), - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - 0, - "SignalSuppress" - }, -}; - -Task_t tlGruntSuppress[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntSuppress[] = -{ - { - tlGruntSuppress, - ARRAYSIZE ( tlGruntSuppress ), - bits_COND_ENEMY_DEAD | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - 0, - "Suppress" - }, -}; - - -//========================================================= -// grunt wait in cover - we don't allow danger or the ability -// to attack to break a grunt's run to cover schedule, but -// when a grunt is in cover, we do want them to attack if they can. -//========================================================= -Task_t tlGruntWaitInCover[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_FACE_ENEMY, (float)1 }, -}; - -Schedule_t slGruntWaitInCover[] = -{ - { - tlGruntWaitInCover, - ARRAYSIZE ( tlGruntWaitInCover ), - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2, - 0, - "GruntWaitInCover" - }, -}; - -//========================================================= -// run to cover. -// !!!BUGBUG - set a decent fail schedule here. -//========================================================= -Task_t tlGruntTakeCover1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_TAKECOVER_FAILED }, - { TASK_WAIT, (float)0.2 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_GRUNT_SPEAK_SENTENCE, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, -}; - -Schedule_t slGruntTakeCover[] = -{ - { - tlGruntTakeCover1, - ARRAYSIZE ( tlGruntTakeCover1 ), - 0, - 0, - "TakeCover" - }, -}; - -//========================================================= -// drop grenade then run to cover. -//========================================================= -Task_t tlGruntGrenadeCover1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)99 }, - { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, - { TASK_PLAY_SEQUENCE, (float)ACT_SPECIAL_ATTACK1 }, - { TASK_CLEAR_MOVE_WAIT, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, -}; - -Schedule_t slGruntGrenadeCover[] = -{ - { - tlGruntGrenadeCover1, - ARRAYSIZE ( tlGruntGrenadeCover1 ), - 0, - 0, - "GrenadeCover" - }, -}; - - -//========================================================= -// drop grenade then run to cover. -//========================================================= -Task_t tlGruntTossGrenadeCover1[] = -{ - { TASK_FACE_ENEMY, (float)0 }, - { TASK_RANGE_ATTACK2, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slGruntTossGrenadeCover[] = -{ - { - tlGruntTossGrenadeCover1, - ARRAYSIZE ( tlGruntTossGrenadeCover1 ), - 0, - 0, - "TossGrenadeCover" - }, -}; - -//========================================================= -// hide from the loudest sound source (to run from grenade) -//========================================================= -Task_t tlGruntTakeCoverFromBestSound[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_COWER },// duck and cover if cannot move from explosion - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_TURN_LEFT, (float)179 }, -}; - -Schedule_t slGruntTakeCoverFromBestSound[] = -{ - { - tlGruntTakeCoverFromBestSound, - ARRAYSIZE ( tlGruntTakeCoverFromBestSound ), - 0, - 0, - "GruntTakeCoverFromBestSound" - }, -}; - -//========================================================= -// Grunt reload schedule -//========================================================= -Task_t tlGruntHideReload[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, -}; - -Schedule_t slGruntHideReload[] = -{ - { - tlGruntHideReload, - ARRAYSIZE ( tlGruntHideReload ), - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "GruntHideReload" - } -}; - -//========================================================= -// Do a turning sweep of the area -//========================================================= -Task_t tlGruntSweep[] = -{ - { TASK_TURN_LEFT, (float)179 }, - { TASK_WAIT, (float)1 }, - { TASK_TURN_LEFT, (float)179 }, - { TASK_WAIT, (float)1 }, -}; - -Schedule_t slGruntSweep[] = -{ - { - tlGruntSweep, - ARRAYSIZE ( tlGruntSweep ), - - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_RANGE_ATTACK2 | - bits_COND_HEAR_SOUND, - 0, - "Grunt Sweep" - }, -}; - -//========================================================= -// primary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack1A[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntRangeAttack1A[] = -{ - { - tlGruntRangeAttack1A, - ARRAYSIZE ( tlGruntRangeAttack1A ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_HEAR_SOUND | - bits_COND_GRUNT_NOFIRE | - bits_COND_NO_AMMO_LOADED, - 0, - "Range Attack1A" - }, -}; - - -//========================================================= -// primary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack1B[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_GRUNT_CHECK_FIRE, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slGruntRangeAttack1B[] = -{ - { - tlGruntRangeAttack1B, - ARRAYSIZE ( tlGruntRangeAttack1B ), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED | - bits_COND_GRUNT_NOFIRE | - bits_COND_HEAR_SOUND, - 0, - "Range Attack1B" - }, -}; - -//========================================================= -// secondary range attack. Overriden because base class stops attacking when the enemy is occluded. -// grunt's grenade toss requires the enemy be occluded. -//========================================================= -Task_t tlGruntRangeAttack2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_GRUNT_FACE_TOSS_DIR, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_RANGE_ATTACK2 }, - { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY },// don't run immediately after throwing grenade. -}; - -Schedule_t slGruntRangeAttack2[] = -{ - { - tlGruntRangeAttack2, - ARRAYSIZE ( tlGruntRangeAttack2 ), - 0, - 0, - "RangeAttack2" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlGruntRepel[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_GLIDE }, -}; - -Schedule_t slGruntRepel[] = -{ - { - tlGruntRepel, - ARRAYSIZE ( tlGruntRepel ), - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "Repel" - }, -}; - - -//========================================================= -// repel -//========================================================= -Task_t tlGruntRepelAttack[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_FLY }, -}; - -Schedule_t slGruntRepelAttack[] = -{ - { - tlGruntRepelAttack, - ARRAYSIZE ( tlGruntRepelAttack ), - bits_COND_ENEMY_OCCLUDED, - 0, - "Repel Attack" - }, -}; - -//========================================================= -// repel land -//========================================================= -Task_t tlGruntRepelLand[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_LAND }, - { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slGruntRepelLand[] = -{ - { - tlGruntRepelLand, - ARRAYSIZE ( tlGruntRepelLand ), - bits_COND_SEE_ENEMY | - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "Repel Land" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMHGrunt ) -{ - slGruntFail, - slGruntCombatFail, - slGruntVictoryDance, - slGruntEstablishLineOfFire, - slGruntFoundEnemy, - slGruntCombatFace, - slGruntSignalSuppress, - slGruntSuppress, - slGruntWaitInCover, - slGruntTakeCover, - slGruntGrenadeCover, - slGruntTossGrenadeCover, - slGruntTakeCoverFromBestSound, - slGruntHideReload, - slGruntSweep, - slGruntRangeAttack1A, - slGruntRangeAttack1B, - slGruntRangeAttack2, - slGruntRepel, - slGruntRepelAttack, - slGruntRepelLand, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMHGrunt, CMBaseMonster ); - -//========================================================= -// SetActivity -//========================================================= -void CMHGrunt :: SetActivity ( Activity NewActivity ) -{ - int iSequence = ACTIVITY_NOT_AVAILABLE; - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - switch ( NewActivity) - { - case ACT_RANGE_ATTACK1: - // grunt is either shooting standing or shooting crouched - if (FBitSet( pev->weapons, HGRUNT_9MMAR)) - { - if ( m_fStanding ) - { - // get aimable sequence - iSequence = LookupSequence( "standing_mp5" ); - } - else - { - // get crouching shoot - iSequence = LookupSequence( "crouching_mp5" ); - } - } - else - { - if ( m_fStanding ) - { - // get aimable sequence - iSequence = LookupSequence( "standing_shotgun" ); - } - else - { - // get crouching shoot - iSequence = LookupSequence( "crouching_shotgun" ); - } - } - break; - case ACT_RANGE_ATTACK2: - // grunt is going to a secondary long range attack. This may be a thrown - // grenade or fired grenade, we must determine which and pick proper sequence - if ( pev->weapons & HGRUNT_HANDGRENADE ) - { - // get toss anim - iSequence = LookupSequence( "throwgrenade" ); - } - else - { - // get launch anim - iSequence = LookupSequence( "launchgrenade" ); - } - break; - case ACT_RUN: - if ( pev->health <= HGRUNT_LIMP_HEALTH ) - { - // limp! - iSequence = LookupActivity ( ACT_RUN_HURT ); - } - else - { - iSequence = LookupActivity ( NewActivity ); - } - break; - case ACT_WALK: - if ( pev->health <= HGRUNT_LIMP_HEALTH ) - { - // limp! - iSequence = LookupActivity ( ACT_WALK_HURT ); - } - else - { - iSequence = LookupActivity ( NewActivity ); - } - break; - case ACT_IDLE: - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - { - NewActivity = ACT_IDLE_ANGRY; - } - iSequence = LookupActivity ( NewActivity ); - break; - default: - iSequence = LookupActivity ( NewActivity ); - break; - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( pev->sequence != iSequence || !m_fSequenceLoops ) - { - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo( ); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } -} - -//========================================================= -// Get Schedule! -//========================================================= -Schedule_t *CMHGrunt :: GetSchedule( void ) -{ - - // clear old sentence - m_iSentence = HGRUNT_SENT_NONE; - - // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. - if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE ) - { - if (pev->flags & FL_ONGROUND) - { - // just landed - pev->movetype = MOVETYPE_STEP; - return GetScheduleOfType ( SCHED_GRUNT_REPEL_LAND ); - } - else - { - // repel down a rope, - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - return GetScheduleOfType ( SCHED_GRUNT_REPEL_ATTACK ); - else - return GetScheduleOfType ( SCHED_GRUNT_REPEL ); - } - } - - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - -// new enemy - if ( HasConditions(bits_COND_NEW_ENEMY) ) - { - { - { - //!!!KELLY - the leader of a squad of grunts has just seen the player or a - // monster and has made it the squad's enemy. You - // can check pev->flags for FL_CLIENT to determine whether this is the player - // or a monster. He's going to immediately start - // firing, though. If you'd like, we can make an alternate "first sight" - // schedule where the leader plays a handsign anim - // that gives us enough time to hear a short sentence or spoken command - // before he starts pluggin away. - if (FOkToSpeak())// && RANDOM_LONG(0,1)) - { - if ((m_hEnemy != NULL) && UTIL_IsPlayer(m_hEnemy)) - // player - SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); -/*jlb - else if ((m_hEnemy != NULL) && - (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && - (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && - (m_hEnemy->Classify() != CLASS_MACHINE)) - // monster - SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); -jlb*/ - JustSpoke(); - } - - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_GRUNT_SUPPRESS ); - } - else - { - return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); - } - } - } - } -// no ammo - else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) - { - //!!!KELLY - this individual just realized he's out of bullet ammo. - // He's going to try to find cover to run to and reload, but rarely, if - // none is available, he'll drop and reload in the open here. - return GetScheduleOfType ( SCHED_GRUNT_COVER_AND_RELOAD ); - } - -// damaged just a little - else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) - { - // if hurt: - // 90% chance of taking cover - // 10% chance of flinch. - int iPercent = RANDOM_LONG(0,99); - - if ( iPercent <= 90 && m_hEnemy != NULL ) - { - // only try to take cover if we actually have an enemy! - - //!!!KELLY - this grunt was hit and is going to run to cover. - if (FOkToSpeak()) // && RANDOM_LONG(0,1)) - { - //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - m_iSentence = HGRUNT_SENT_COVER; - //JustSpoke(); - } - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - else - { - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - } -// can kick - else if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); - } -// can grenade launch - - else if ( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER) && HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) - { - // shoot a grenade if you can - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } -// can shoot - else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) - { - // throw a grenade if can and no engage slots are available - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } - else - { - // hide! - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - } -// can't see enemy - else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) - { - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) ) - { - //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); - } - else - { - //!!!KELLY - grunt is going to stay put for a couple seconds to see if - // the enemy wanders back out into the open, or approaches the - // grunt's covered position. Good place for a taunt, I guess? - if (FOkToSpeak() && RANDOM_LONG(0,1)) - { - SENTENCEG_PlayRndSz( ENT(pev), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType( SCHED_STANDOFF ); - } - } - - if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); - } - } - } - - // no special cases here, call the base class - return CMBaseMonster :: GetSchedule(); -} - -//========================================================= -//========================================================= -Schedule_t* CMHGrunt :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - { - { - if ( RANDOM_LONG(0,1) ) - { - return &slGruntTakeCover[ 0 ]; - } - else - { - return &slGruntGrenadeCover[ 0 ]; - } - } - } - case SCHED_TAKE_COVER_FROM_BEST_SOUND: - { - return &slGruntTakeCoverFromBestSound[ 0 ]; - } - case SCHED_GRUNT_TAKECOVER_FAILED: - { - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); - } - - return GetScheduleOfType ( SCHED_FAIL ); - } - break; - case SCHED_GRUNT_ELOF_FAIL: - { - // human grunt is unable to move to a position that allows him to attack the enemy. - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - break; - case SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE: - { - return &slGruntEstablishLineOfFire[ 0 ]; - } - break; - case SCHED_RANGE_ATTACK1: - { - // randomly stand or crouch - if (RANDOM_LONG(0,9) == 0) - m_fStanding = RANDOM_LONG(0,1); - - if (m_fStanding) - return &slGruntRangeAttack1B[ 0 ]; - else - return &slGruntRangeAttack1A[ 0 ]; - } - case SCHED_RANGE_ATTACK2: - { - return &slGruntRangeAttack2[ 0 ]; - } - case SCHED_COMBAT_FACE: - { - return &slGruntCombatFace[ 0 ]; - } - case SCHED_GRUNT_WAIT_FACE_ENEMY: - { - return &slGruntWaitInCover[ 0 ]; - } - case SCHED_GRUNT_SWEEP: - { - return &slGruntSweep[ 0 ]; - } - case SCHED_GRUNT_COVER_AND_RELOAD: - { - return &slGruntHideReload[ 0 ]; - } - case SCHED_GRUNT_FOUND_ENEMY: - { - return &slGruntFoundEnemy[ 0 ]; - } - case SCHED_VICTORY_DANCE: - { - return &slGruntVictoryDance[ 0 ]; - } - case SCHED_GRUNT_SUPPRESS: - { - if ( UTIL_IsPlayer(m_hEnemy) && m_fFirstEncounter ) - { - m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy - return &slGruntSignalSuppress[ 0 ]; - } - else - { - return &slGruntSuppress[ 0 ]; - } - } - case SCHED_FAIL: - { - if ( m_hEnemy != NULL ) - { - // grunt has an enemy, so pick a different default fail schedule most likely to help recover. - return &slGruntCombatFail[ 0 ]; - } - - return &slGruntFail[ 0 ]; - } - case SCHED_GRUNT_REPEL: - { - if (pev->velocity.z > -128) - pev->velocity.z -= 32; - return &slGruntRepel[ 0 ]; - } - case SCHED_GRUNT_REPEL_ATTACK: - { - if (pev->velocity.z > -128) - pev->velocity.z -= 32; - return &slGruntRepelAttack[ 0 ]; - } - case SCHED_GRUNT_REPEL_LAND: - { - return &slGruntRepelLand[ 0 ]; - } - default: - { - return CMBaseMonster :: GetScheduleOfType ( Type ); - } - } -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// hgrunt +//========================================================= + +//========================================================= +// Hit groups! +//========================================================= +/* + + 1 - Head + 2 - Stomach + 3 - Gun + +*/ + + +#include "extdll.h" +#include "plane.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "weapons.h" +#include "cmtalkmonster.h" +#include "effects.h" +#include "customentity.h" + +int g_fGruntQuestion; // true if an idle grunt asked a question. Cleared when someone answers. + + +//========================================================= +// monster-specific DEFINE's +//========================================================= +#define GRUNT_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! +#define GRUNT_VOL 0.35 // volume of grunt sounds +#define GRUNT_ATTN ATTN_NORM // attenutation of grunt sentences +#define HGRUNT_LIMP_HEALTH 20 +#define HGRUNT_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. +#define HGRUNT_NUM_HEADS 2 // how many grunt heads are there? +#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill +#define HGRUNT_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences + +#define HGRUNT_9MMAR ( 1 << 0) +#define HGRUNT_HANDGRENADE ( 1 << 1) +#define HGRUNT_GRENADELAUNCHER ( 1 << 2) +#define HGRUNT_SHOTGUN ( 1 << 3) + +#define HEAD_GROUP 1 +#define HEAD_GRUNT 0 +#define HEAD_COMMANDER 1 +#define HEAD_SHOTGUN 2 +#define HEAD_M203 3 +#define GUN_GROUP 2 +#define GUN_MP5 0 +#define GUN_SHOTGUN 1 +#define GUN_NONE 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HGRUNT_AE_RELOAD ( 2 ) +#define HGRUNT_AE_KICK ( 3 ) +#define HGRUNT_AE_BURST1 ( 4 ) +#define HGRUNT_AE_BURST2 ( 5 ) +#define HGRUNT_AE_BURST3 ( 6 ) +#define HGRUNT_AE_GREN_TOSS ( 7 ) +#define HGRUNT_AE_GREN_LAUNCH ( 8 ) +#define HGRUNT_AE_GREN_DROP ( 9 ) +#define HGRUNT_AE_CAUGHT_ENEMY ( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad. +#define HGRUNT_AE_DROP_GUN ( 11) // grunt (probably dead) is dropping his mp5. + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_GRUNT_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). + SCHED_GRUNT_COVER_AND_RELOAD, + SCHED_GRUNT_SWEEP, + SCHED_GRUNT_FOUND_ENEMY, + SCHED_GRUNT_REPEL, + SCHED_GRUNT_REPEL_ATTACK, + SCHED_GRUNT_REPEL_LAND, + SCHED_GRUNT_WAIT_FACE_ENEMY, + SCHED_GRUNT_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. + SCHED_GRUNT_ELOF_FAIL, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_GRUNT_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, + TASK_GRUNT_SPEAK_SENTENCE, + TASK_GRUNT_CHECK_FIRE, +}; + +//========================================================= +// monster-specific conditions +//========================================================= +#define bits_COND_GRUNT_NOFIRE ( bits_COND_SPECIAL1 ) + +const char *CMHGrunt::pGruntSentences[] = +{ + "HG_GREN", // grenade scared grunt + "HG_ALERT", // sees player + "HG_MONSTER", // sees monster + "HG_COVER", // running to cover + "HG_THROW", // about to throw grenade + "HG_CHARGE", // running out to get the enemy + "HG_TAUNT", // say rude things +}; + +enum +{ + HGRUNT_SENT_NONE = -1, + HGRUNT_SENT_GREN = 0, + HGRUNT_SENT_ALERT, + HGRUNT_SENT_MONSTER, + HGRUNT_SENT_COVER, + HGRUNT_SENT_THROW, + HGRUNT_SENT_CHARGE, + HGRUNT_SENT_TAUNT, +} HGRUNT_SENTENCE_TYPES; + +//========================================================= +// Speak Sentence - say your cued up sentence. +// +// Some grunt sentences (take cover and charge) rely on actually +// being able to execute the intended action. It's really lame +// when a grunt says 'COVER ME' and then doesn't move. The problem +// is that the sentences were played when the decision to TRY +// to move to cover was made. Now the sentence is played after +// we know for sure that there is a valid path. The schedule +// may still fail but in most cases, well after the grunt has +// started moving. +//========================================================= +void CMHGrunt :: SpeakSentence( void ) +{ + if ( m_iSentence == HGRUNT_SENT_NONE ) + { + // no sentence cued up. + return; + } + + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } +} + +//========================================================= +// IRelationship - overridden because Alien Grunts are +// Human Grunt's nemesis. +//========================================================= +int CMHGrunt::IRelationship ( CMBaseEntity *pTarget ) +{ + // on single player, forcing R_NM makes sense. + // on multiplayer, a custom classification will cause misbehaviour. + /* + if (( strcmp(STRING(pTarget->pev->model), "models/agrunt.mdl") == 0 ) || + ( strcmp(STRING(pTarget->pev->model), "models/garg.mdl") == 0 )) + { + return R_NM; + } + */ + return CMBaseMonster::IRelationship( pTarget ); +} + +//========================================================= +// GibMonster - make gun fly through the air. +//========================================================= +void CMHGrunt :: GibMonster ( void ) +{ + Vector vecGunPos; + Vector vecGunAngles; + + CMBaseMonster :: GibMonster(); +} + +//========================================================= +// ISoundMask - Overidden for human grunts because they +// hear the DANGER sound that is made by hand grenades and +// other dangerous items. +//========================================================= +int CMHGrunt :: ISoundMask ( void ) +{ + return 0; +} + +//========================================================= +// someone else is talking - don't speak +//========================================================= +BOOL CMHGrunt :: FOkToSpeak( void ) +{ +// if someone else is talking, don't speak + if (gpGlobals->time <= CMTalkMonster::g_talkWaitTime) + return FALSE; + + if ( pev->spawnflags & SF_MONSTER_GAG ) + { + if ( m_MonsterState != MONSTERSTATE_COMBAT ) + { + // no talking outside of combat if gagged. + return FALSE; + } + } + + // if player is not in pvs, don't speak +// if (FNullEnt(FIND_CLIENT_IN_PVS(edict()))) +// return FALSE; + + return TRUE; +} + +//========================================================= +//========================================================= +void CMHGrunt :: JustSpoke( void ) +{ + CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(1.5, 2.0); + m_iSentence = HGRUNT_SENT_NONE; +} + +//========================================================= +// PrescheduleThink - this function runs after conditions +// are collected and before scheduling code is run. +//========================================================= +void CMHGrunt :: PrescheduleThink ( void ) +{ + return; +} + +//========================================================= +// FCanCheckAttacks - this is overridden for human grunts +// because they can throw/shoot grenades when they can't see their +// target and the base class doesn't check attacks if the monster +// cannot see its enemy. +// +// !!!BUGBUG - this gets called before a 3-round burst is fired +// which means that a friendly can still be hit with up to 2 rounds. +// ALSO, grenades will not be tossed if there is a friendly in front, +// this is a bad bug. Friendly machine gun fire avoidance +// will unecessarily prevent the throwing of a grenade as well. +//========================================================= +BOOL CMHGrunt :: FCanCheckAttacks ( void ) +{ + if ( !HasConditions( bits_COND_ENEMY_TOOFAR ) ) + { + return TRUE; + } + else + { + return FALSE; + } +} + + +//========================================================= +// CheckMeleeAttack1 +//========================================================= +BOOL CMHGrunt :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + if (m_hEnemy != NULL) + { + if (UTIL_IsPlayer(m_hEnemy)) + { + if ( flDist <= 64 && flDot >= 0.7) + { + return TRUE; + } + } + else if (m_hEnemy->v.euser4 != NULL) + { + edict_t *pEdict = m_hEnemy; + CMBaseMonster *pEnemy = GetClassPtr((CMBaseMonster *)VARS(pEdict)); + + if ( flDist <= 64 && flDot >= 0.7 && + pEnemy->Classify() != CLASS_ALIEN_BIOWEAPON && + pEnemy->Classify() != CLASS_PLAYER_BIOWEAPON ) + { + return TRUE; + } + } + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 - overridden for HGrunt, cause +// FCanCheckAttacks() doesn't disqualify all attacks based +// on whether or not the enemy is occluded because unlike +// the base class, the HGrunt can attack when the enemy is +// occluded (throw grenade over wall, etc). We must +// disqualify the machine gun attack if the enemy is occluded. +//========================================================= +BOOL CMHGrunt :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 ) + { + TraceResult tr; + + if ( !UTIL_IsPlayer(m_hEnemy) && flDist <= 64 ) + { + // kick nonclients, but don't shoot at them. + return FALSE; + } + + Vector vecSrc = GetGunPosition(); + + // verify that a bullet fired from the gun will hit the enemy before the world. + UTIL_TraceLine( vecSrc, UTIL_BodyTarget(m_hEnemy, vecSrc), ignore_monsters, ignore_glass, ENT(pev), &tr); + + if ( tr.flFraction == 1.0 ) + { + return TRUE; + } + } + + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 - this checks the Grunt's grenade +// attack. +//========================================================= +BOOL CMHGrunt :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + if (! FBitSet(pev->weapons, (HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER))) + { + return FALSE; + } + + // if the grunt isn't moving, it's ok to check. + if ( m_flGroundSpeed != 0 ) + { + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + // assume things haven't changed too much since last time + if (gpGlobals->time < m_flNextGrenadeCheck ) + { + return m_fThrowGrenade; + } + + if ( !FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) && m_hEnemy->v.waterlevel == 0 && m_vecEnemyLKP.z > pev->absmax.z ) + { + //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to + // be grenaded. + // don't throw grenades at anything that isn't on the ground! + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + Vector vecTarget; + + if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) + { + // find feet + if (RANDOM_LONG(0,1)) + { + // magically know where they are + vecTarget = Vector( m_hEnemy->v.origin.x, m_hEnemy->v.origin.y, m_hEnemy->v.absmin.z ); + } + else + { + // toss it to where you last saw them + vecTarget = m_vecEnemyLKP; + } + // vecTarget = m_vecEnemyLKP + (m_hEnemy->BodyTarget( pev->origin ) - m_hEnemy->pev->origin); + // estimate position + // vecTarget = vecTarget + m_hEnemy->pev->velocity * 2; + } + else + { + // find target + // vecTarget = m_hEnemy->BodyTarget( pev->origin ); + vecTarget = m_vecEnemyLKP + (UTIL_BodyTarget(m_hEnemy, pev->origin ) - m_hEnemy->v.origin); + // estimate position + if (HasConditions( bits_COND_SEE_ENEMY)) + vecTarget = vecTarget + ((vecTarget - pev->origin).Length() / gSkillData.hgruntGrenadeSpeed) * m_hEnemy->v.velocity; + } + + if ( ( vecTarget - pev->origin ).Length2D() <= 256 ) + { + // crap, I don't want to blow myself up + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + m_fThrowGrenade = FALSE; + return m_fThrowGrenade; + } + + if (FBitSet( pev->weapons, HGRUNT_HANDGRENADE)) + { + Vector vecToss = VecCheckToss( pev, GetGunPosition(), vecTarget, 0.5 ); + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time; // 1/3 second. + } + else + { + // don't throw + m_fThrowGrenade = FALSE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + } + } + else + { + Vector vecToss = VecCheckThrow( pev, GetGunPosition(), vecTarget, gSkillData.hgruntGrenadeSpeed, 0.5 ); + + if ( vecToss != g_vecZero ) + { + m_vecTossVelocity = vecToss; + + // throw a hand grenade + m_fThrowGrenade = TRUE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 0.3; // 1/3 second. + } + else + { + // don't throw + m_fThrowGrenade = FALSE; + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->time + 1; // one full second. + } + } + + + + return m_fThrowGrenade; +} + + +//========================================================= +// TraceAttack - make sure we're not taking it in the helmet +//========================================================= +void CMHGrunt :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + // check for helmet shot + if (ptr->iHitgroup == 11) + { + // make sure we're wearing one + if (GetBodygroup( 1 ) == HEAD_GRUNT && (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB))) + { + // absorb damage + flDamage -= 20; + if (flDamage <= 0) + { + UTIL_Ricochet( ptr->vecEndPos, 1.0 ); + flDamage = 0.01; + } + } + // it's head shot anyways + ptr->iHitgroup = HITGROUP_HEAD; + } + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +//========================================================= +// TakeDamage - overridden for the grunt because the grunt +// needs to forget that he is in cover if he's hurt. (Obviously +// not in a safe place anymore). +//========================================================= +int CMHGrunt :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + Forget( bits_MEMORY_INCOVER ); + + return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMHGrunt :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 150; + break; + case ACT_RUN: + ys = 150; + break; + case ACT_WALK: + ys = 180; + break; + case ACT_RANGE_ATTACK1: + ys = 120; + break; + case ACT_RANGE_ATTACK2: + ys = 120; + break; + case ACT_MELEE_ATTACK1: + ys = 120; + break; + case ACT_MELEE_ATTACK2: + ys = 120; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 180; + break; + case ACT_GLIDE: + case ACT_FLY: + ys = 30; + break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +void CMHGrunt :: IdleSound( void ) +{ + if (FOkToSpeak() && (g_fGruntQuestion || RANDOM_LONG(0,1))) + { + if (!g_fGruntQuestion) + { + // ask question or make statement + switch (RANDOM_LONG(0,2)) + { + case 0: // check in + SENTENCEG_PlayRndSz(ENT(pev), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fGruntQuestion = 1; + break; + case 1: // question + SENTENCEG_PlayRndSz(ENT(pev), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fGruntQuestion = 2; + break; + case 2: // statement + SENTENCEG_PlayRndSz(ENT(pev), "HG_IDLE", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + } + else + { + switch (g_fGruntQuestion) + { + case 1: // check in + SENTENCEG_PlayRndSz(ENT(pev), "HG_CLEAR", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + case 2: // question + SENTENCEG_PlayRndSz(ENT(pev), "HG_ANSWER", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + g_fGruntQuestion = 0; + } + JustSpoke(); + } +} + +//========================================================= +// CheckAmmo - overridden for the grunt because he actually +// uses ammo! (base class doesn't) +//========================================================= +void CMHGrunt :: CheckAmmo ( void ) +{ + if ( m_cAmmoLoaded <= 0 ) + { + SetConditions(bits_COND_NO_AMMO_LOADED); + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMHGrunt :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_HUMAN_MILITARY; +} + +//========================================================= +//========================================================= +edict_t *CMHGrunt :: Kick( void ) +{ + TraceResult tr; + + UTIL_MakeVectors( pev->angles ); + Vector vecStart = pev->origin; + vecStart.z += pev->size.z * 0.5; + Vector vecEnd = vecStart + (gpGlobals->v_forward * 70); + + UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr ); + + if ( tr.pHit ) + return tr.pHit; + + return NULL; +} + +//========================================================= +// GetGunPosition return the end of the barrel +//========================================================= + +Vector CMHGrunt :: GetGunPosition( ) +{ + if (m_fStanding ) + { + return pev->origin + Vector( 0, 0, 60 ); + } + else + { + return pev->origin + Vector( 0, 0, 48 ); + } +} + +//========================================================= +// Shoot +//========================================================= +void CMHGrunt :: Shoot ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_10DEGREES, 2048, BULLET_MONSTER_MP5 ); // shoot +-5 degrees + + pev->effects |= EF_MUZZLEFLASH; + + // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. + // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue + //m_cAmmoLoaded--;// take away a bullet! + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); +} + +//========================================================= +// Shoot +//========================================================= +void CMHGrunt :: Shotgun ( void ) +{ + if (m_hEnemy == NULL) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + + UTIL_MakeVectors ( pev->angles ); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40,90) + gpGlobals->v_up * RANDOM_FLOAT(75,200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass ( vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iShotgunShell, TE_BOUNCE_SHOTSHELL); + FireBullets(gSkillData.hgruntShotgunPellets, vecShootOrigin, vecShootDir, VECTOR_CONE_15DEGREES, 2048, BULLET_PLAYER_BUCKSHOT, 0 ); // shoot +-7.5 degrees + + pev->effects |= EF_MUZZLEFLASH; + + // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. + // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue + //m_cAmmoLoaded--;// take away a bullet! + + Vector angDir = UTIL_VecToAngles( vecShootDir ); + SetBlending( 0, angDir.x ); +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMHGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + Vector vecShootDir; + Vector vecShootOrigin; + + switch( pEvent->event ) + { + case HGRUNT_AE_DROP_GUN: + { + Vector vecGunPos; + Vector vecGunAngles; + + GetAttachment( 0, vecGunPos, vecGunAngles ); + + // switch to body group with no gun. + SetBodygroup( GUN_GROUP, GUN_NONE ); + } + break; + + case HGRUNT_AE_RELOAD: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_reload1.wav", 1, ATTN_NORM ); + m_cAmmoLoaded = m_cClipSize; + ClearConditions(bits_COND_NO_AMMO_LOADED); + break; + + case HGRUNT_AE_GREN_TOSS: + { + UTIL_MakeVectors( pev->angles ); + // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); + CMGrenade::ShootTimed( pev, GetGunPosition(), m_vecTossVelocity, 3.5 ); + + m_fThrowGrenade = FALSE; + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + // !!!LATER - when in a group, only try to throw grenade if ordered. + } + break; + + case HGRUNT_AE_GREN_LAUNCH: + { + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/glauncher.wav", 0.8, ATTN_NORM); + CMGrenade::ShootContact( pev, GetGunPosition(), m_vecTossVelocity ); + m_fThrowGrenade = FALSE; + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + } + break; + + case HGRUNT_AE_GREN_DROP: + { + UTIL_MakeVectors( pev->angles ); + CMGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 17 - gpGlobals->v_right * 27 + gpGlobals->v_up * 6, g_vecZero, 3 ); + } + break; + + case HGRUNT_AE_BURST1: + { + if ( FBitSet( pev->weapons, HGRUNT_9MMAR )) + { + Shoot(); + + // the first round of the three round burst plays the sound and puts a sound in the world sound list. + if ( RANDOM_LONG(0,1) ) + { + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM ); + } + else + { + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM ); + } + } + else + { + Shotgun( ); + + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sbarrel1.wav", 1, ATTN_NORM ); + } + } + break; + + case HGRUNT_AE_BURST2: + case HGRUNT_AE_BURST3: + Shoot(); + break; + + case HGRUNT_AE_KICK: + { + edict_t *pHurt = Kick(); + + if ( pHurt ) + { + // SOUND HERE! + UTIL_MakeVectors( pev->angles ); + pHurt->v.punchangle.x = 15; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; + if (UTIL_IsPlayer(pHurt)) + UTIL_TakeDamage( pHurt, pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); + else if (pHurt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + pMonster->TakeDamage( pev, pev, gSkillData.hgruntDmgKick, DMG_CLUB ); + } + } + } + break; + + case HGRUNT_AE_CAUGHT_ENEMY: + { + if ( FOkToSpeak() ) + { + SENTENCEG_PlayRndSz(ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + + } + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMHGrunt :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/hgrunt.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->effects = 0; + pev->health = gSkillData.hgruntHealth; + m_flFieldOfView = VIEW_FIELD_FULL; // indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_flNextGrenadeCheck = gpGlobals->time + 1; + m_flNextPainTime = gpGlobals->time; + m_iSentence = HGRUNT_SENT_NONE; + + m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + +//jlb m_fEnemyEluded = FALSE; + m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. + + m_HackedGunPos = Vector ( 0, 0, 55 ); + + if (pev->weapons == 0) + { + // initialize to original values + switch(RANDOM_LONG(0, 2)) + { + case 0: + pev->weapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE; + break; + case 1: + pev->weapons = HGRUNT_SHOTGUN; + break; + case 2: + pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER; + break; + } + } + + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + SetBodygroup( GUN_GROUP, GUN_SHOTGUN ); + m_cClipSize = 8; + } + else + { + m_cClipSize = GRUNT_CLIP_SIZE; + } + m_cAmmoLoaded = m_cClipSize; + + if (RANDOM_LONG( 0, 99 ) < 80) + pev->skin = 0; // light skin + else + pev->skin = 1; // dark skin + + if (FBitSet( pev->weapons, HGRUNT_SHOTGUN )) + { + SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN); + } + else if (FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER )) + { + SetBodygroup( HEAD_GROUP, HEAD_M203 ); + pev->skin = 1; // alway dark skin + } + + CMTalkMonster::g_talkWaitTime = 0; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_human_grunt" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Human Grunt" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMHGrunt :: Precache() +{ + PRECACHE_MODEL("models/hgrunt.mdl"); + + PRECACHE_SOUND( "hgrunt/gr_mgun1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_mgun2.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_die1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_die2.wav" ); + PRECACHE_SOUND( "hgrunt/gr_die3.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_pain1.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain2.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain3.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain4.wav" ); + PRECACHE_SOUND( "hgrunt/gr_pain5.wav" ); + + PRECACHE_SOUND( "hgrunt/gr_reload1.wav" ); + + PRECACHE_SOUND( "weapons/glauncher.wav" ); + + PRECACHE_SOUND( "weapons/sbarrel1.wav" ); + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + // get voice pitch + if (RANDOM_LONG(0,1)) + m_voicePitch = 109 + RANDOM_LONG(0,7); + else + m_voicePitch = 100; + + m_iBrassShell = PRECACHE_MODEL ("models/shell.mdl");// brass shell + m_iShotgunShell = PRECACHE_MODEL ("models/shotgunshell.mdl"); +} + +//========================================================= +// start task +//========================================================= +void CMHGrunt :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_GRUNT_CHECK_FIRE: + TaskComplete(); + break; + + case TASK_GRUNT_SPEAK_SENTENCE: + SpeakSentence(); + TaskComplete(); + break; + + case TASK_WALK_PATH: + case TASK_RUN_PATH: + // grunt no longer assumes he is covered if he moves + Forget( bits_MEMORY_INCOVER ); + CMBaseMonster ::StartTask( pTask ); + break; + + case TASK_RELOAD: + m_IdealActivity = ACT_RELOAD; + break; + + case TASK_GRUNT_FACE_TOSS_DIR: + break; + + case TASK_FACE_IDEAL: + case TASK_FACE_ENEMY: + CMBaseMonster :: StartTask( pTask ); + if (pev->movetype == MOVETYPE_FLY) + { + m_IdealActivity = ACT_GLIDE; + } + break; + + default: + CMBaseMonster :: StartTask( pTask ); + break; + } +} + +//========================================================= +// RunTask +//========================================================= +void CMHGrunt :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_GRUNT_FACE_TOSS_DIR: + { + // project a point along the toss vector and turn to face that point. + MakeIdealYaw( pev->origin + m_vecTossVelocity * 64 ); + ChangeYaw( pev->yaw_speed ); + + if ( FacingIdeal() ) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + break; + } + default: + { + CMBaseMonster :: RunTask( pTask ); + break; + } + } +} + +//========================================================= +// PainSound +//========================================================= +void CMHGrunt :: PainSound ( void ) +{ + if ( gpGlobals->time > m_flNextPainTime ) + { +#if 0 + if ( RANDOM_LONG(0,99) < 5 ) + { + // pain sentences are rare + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); + JustSpoke(); + return; + } + } +#endif + switch ( RANDOM_LONG(0,6) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain3.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain4.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain5.wav", 1, ATTN_NORM ); + break; + case 3: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain1.wav", 1, ATTN_NORM ); + break; + case 4: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_pain2.wav", 1, ATTN_NORM ); + break; + } + + m_flNextPainTime = gpGlobals->time + 1; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CMHGrunt :: DeathSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die1.wav", 1, ATTN_IDLE ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die2.wav", 1, ATTN_IDLE ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "hgrunt/gr_die3.wav", 1, ATTN_IDLE ); + break; + } +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GruntFail +//========================================================= +Task_t tlGruntFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slGruntFail[] = +{ + { + tlGruntFail, + ARRAYSIZE ( tlGruntFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2, + 0, + "Grunt Fail" + }, +}; + +//========================================================= +// Grunt Combat Fail +//========================================================= +Task_t tlGruntCombatFail[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_WAIT_PVS, (float)0 }, +}; + +Schedule_t slGruntCombatFail[] = +{ + { + tlGruntCombatFail, + ARRAYSIZE ( tlGruntCombatFail ), + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2, + 0, + "Grunt Combat Fail" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlGruntVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, +}; + +Schedule_t slGruntVictoryDance[] = +{ + { + tlGruntVictoryDance, + ARRAYSIZE ( tlGruntVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "GruntVictoryDance" + }, +}; + +//========================================================= +// Establish line of fire - move to a position that allows +// the grunt to attack. +//========================================================= +Task_t tlGruntEstablishLineOfFire[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_ELOF_FAIL }, + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_GRUNT_SPEAK_SENTENCE,(float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slGruntEstablishLineOfFire[] = +{ + { + tlGruntEstablishLineOfFire, + ARRAYSIZE ( tlGruntEstablishLineOfFire ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_HEAR_SOUND, + 0, + "GruntEstablishLineOfFire" + }, +}; + +//========================================================= +// GruntFoundEnemy - grunt established sight with an enemy +// that was hiding from the squad. +//========================================================= +Task_t tlGruntFoundEnemy[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_SIGNAL1 }, +}; + +Schedule_t slGruntFoundEnemy[] = +{ + { + tlGruntFoundEnemy, + ARRAYSIZE ( tlGruntFoundEnemy ), + bits_COND_HEAR_SOUND, + 0, + "GruntFoundEnemy" + }, +}; + +//========================================================= +// GruntCombatFace Schedule +//========================================================= +Task_t tlGruntCombatFace1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_WAIT, (float)1.5 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_SWEEP }, +}; + +Schedule_t slGruntCombatFace[] = +{ + { + tlGruntCombatFace1, + ARRAYSIZE ( tlGruntCombatFace1 ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2, + 0, + "Combat Face" + }, +}; + +//========================================================= +// Suppressing fire - don't stop shooting until the clip is +// empty or grunt gets hurt. +//========================================================= +Task_t tlGruntSignalSuppress[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_SIGNAL2 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntSignalSuppress[] = +{ + { + tlGruntSignalSuppress, + ARRAYSIZE ( tlGruntSignalSuppress ), + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + 0, + "SignalSuppress" + }, +}; + +Task_t tlGruntSuppress[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntSuppress[] = +{ + { + tlGruntSuppress, + ARRAYSIZE ( tlGruntSuppress ), + bits_COND_ENEMY_DEAD | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + 0, + "Suppress" + }, +}; + + +//========================================================= +// grunt wait in cover - we don't allow danger or the ability +// to attack to break a grunt's run to cover schedule, but +// when a grunt is in cover, we do want them to attack if they can. +//========================================================= +Task_t tlGruntWaitInCover[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_FACE_ENEMY, (float)1 }, +}; + +Schedule_t slGruntWaitInCover[] = +{ + { + tlGruntWaitInCover, + ARRAYSIZE ( tlGruntWaitInCover ), + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2, + 0, + "GruntWaitInCover" + }, +}; + +//========================================================= +// run to cover. +// !!!BUGBUG - set a decent fail schedule here. +//========================================================= +Task_t tlGruntTakeCover1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_GRUNT_TAKECOVER_FAILED }, + { TASK_WAIT, (float)0.2 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_GRUNT_SPEAK_SENTENCE, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, +}; + +Schedule_t slGruntTakeCover[] = +{ + { + tlGruntTakeCover1, + ARRAYSIZE ( tlGruntTakeCover1 ), + 0, + 0, + "TakeCover" + }, +}; + +//========================================================= +// drop grenade then run to cover. +//========================================================= +Task_t tlGruntGrenadeCover1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)99 }, + { TASK_FIND_FAR_NODE_COVER_FROM_ENEMY, (float)384 }, + { TASK_PLAY_SEQUENCE, (float)ACT_SPECIAL_ATTACK1 }, + { TASK_CLEAR_MOVE_WAIT, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY }, +}; + +Schedule_t slGruntGrenadeCover[] = +{ + { + tlGruntGrenadeCover1, + ARRAYSIZE ( tlGruntGrenadeCover1 ), + 0, + 0, + "GrenadeCover" + }, +}; + + +//========================================================= +// drop grenade then run to cover. +//========================================================= +Task_t tlGruntTossGrenadeCover1[] = +{ + { TASK_FACE_ENEMY, (float)0 }, + { TASK_RANGE_ATTACK2, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slGruntTossGrenadeCover[] = +{ + { + tlGruntTossGrenadeCover1, + ARRAYSIZE ( tlGruntTossGrenadeCover1 ), + 0, + 0, + "TossGrenadeCover" + }, +}; + +//========================================================= +// hide from the loudest sound source (to run from grenade) +//========================================================= +Task_t tlGruntTakeCoverFromBestSound[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_COWER },// duck and cover if cannot move from explosion + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_BEST_SOUND, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_TURN_LEFT, (float)179 }, +}; + +Schedule_t slGruntTakeCoverFromBestSound[] = +{ + { + tlGruntTakeCoverFromBestSound, + ARRAYSIZE ( tlGruntTakeCoverFromBestSound ), + 0, + 0, + "GruntTakeCoverFromBestSound" + }, +}; + +//========================================================= +// Grunt reload schedule +//========================================================= +Task_t tlGruntHideReload[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, +}; + +Schedule_t slGruntHideReload[] = +{ + { + tlGruntHideReload, + ARRAYSIZE ( tlGruntHideReload ), + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "GruntHideReload" + } +}; + +//========================================================= +// Do a turning sweep of the area +//========================================================= +Task_t tlGruntSweep[] = +{ + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, + { TASK_TURN_LEFT, (float)179 }, + { TASK_WAIT, (float)1 }, +}; + +Schedule_t slGruntSweep[] = +{ + { + tlGruntSweep, + ARRAYSIZE ( tlGruntSweep ), + + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_RANGE_ATTACK2 | + bits_COND_HEAR_SOUND, + 0, + "Grunt Sweep" + }, +}; + +//========================================================= +// primary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack1A[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntRangeAttack1A[] = +{ + { + tlGruntRangeAttack1A, + ARRAYSIZE ( tlGruntRangeAttack1A ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_HEAR_SOUND | + bits_COND_GRUNT_NOFIRE | + bits_COND_NO_AMMO_LOADED, + 0, + "Range Attack1A" + }, +}; + + +//========================================================= +// primary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack1B[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY,(float)ACT_IDLE_ANGRY }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_GRUNT_CHECK_FIRE, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slGruntRangeAttack1B[] = +{ + { + tlGruntRangeAttack1B, + ARRAYSIZE ( tlGruntRangeAttack1B ), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED | + bits_COND_GRUNT_NOFIRE | + bits_COND_HEAR_SOUND, + 0, + "Range Attack1B" + }, +}; + +//========================================================= +// secondary range attack. Overriden because base class stops attacking when the enemy is occluded. +// grunt's grenade toss requires the enemy be occluded. +//========================================================= +Task_t tlGruntRangeAttack2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_GRUNT_FACE_TOSS_DIR, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_RANGE_ATTACK2 }, + { TASK_SET_SCHEDULE, (float)SCHED_GRUNT_WAIT_FACE_ENEMY },// don't run immediately after throwing grenade. +}; + +Schedule_t slGruntRangeAttack2[] = +{ + { + tlGruntRangeAttack2, + ARRAYSIZE ( tlGruntRangeAttack2 ), + 0, + 0, + "RangeAttack2" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlGruntRepel[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_GLIDE }, +}; + +Schedule_t slGruntRepel[] = +{ + { + tlGruntRepel, + ARRAYSIZE ( tlGruntRepel ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "Repel" + }, +}; + + +//========================================================= +// repel +//========================================================= +Task_t tlGruntRepelAttack[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_FLY }, +}; + +Schedule_t slGruntRepelAttack[] = +{ + { + tlGruntRepelAttack, + ARRAYSIZE ( tlGruntRepelAttack ), + bits_COND_ENEMY_OCCLUDED, + 0, + "Repel Attack" + }, +}; + +//========================================================= +// repel land +//========================================================= +Task_t tlGruntRepelLand[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_LAND }, + { TASK_GET_PATH_TO_LASTPOSITION,(float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slGruntRepelLand[] = +{ + { + tlGruntRepelLand, + ARRAYSIZE ( tlGruntRepelLand ), + bits_COND_SEE_ENEMY | + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "Repel Land" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMHGrunt ) +{ + slGruntFail, + slGruntCombatFail, + slGruntVictoryDance, + slGruntEstablishLineOfFire, + slGruntFoundEnemy, + slGruntCombatFace, + slGruntSignalSuppress, + slGruntSuppress, + slGruntWaitInCover, + slGruntTakeCover, + slGruntGrenadeCover, + slGruntTossGrenadeCover, + slGruntTakeCoverFromBestSound, + slGruntHideReload, + slGruntSweep, + slGruntRangeAttack1A, + slGruntRangeAttack1B, + slGruntRangeAttack2, + slGruntRepel, + slGruntRepelAttack, + slGruntRepelLand, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMHGrunt, CMBaseMonster ); + +//========================================================= +// SetActivity +//========================================================= +void CMHGrunt :: SetActivity ( Activity NewActivity ) +{ + int iSequence = ACTIVITY_NOT_AVAILABLE; + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + switch ( NewActivity) + { + case ACT_RANGE_ATTACK1: + // grunt is either shooting standing or shooting crouched + if (FBitSet( pev->weapons, HGRUNT_9MMAR)) + { + if ( m_fStanding ) + { + // get aimable sequence + iSequence = LookupSequence( "standing_mp5" ); + } + else + { + // get crouching shoot + iSequence = LookupSequence( "crouching_mp5" ); + } + } + else + { + if ( m_fStanding ) + { + // get aimable sequence + iSequence = LookupSequence( "standing_shotgun" ); + } + else + { + // get crouching shoot + iSequence = LookupSequence( "crouching_shotgun" ); + } + } + break; + case ACT_RANGE_ATTACK2: + // grunt is going to a secondary long range attack. This may be a thrown + // grenade or fired grenade, we must determine which and pick proper sequence + if ( pev->weapons & HGRUNT_HANDGRENADE ) + { + // get toss anim + iSequence = LookupSequence( "throwgrenade" ); + } + else + { + // get launch anim + iSequence = LookupSequence( "launchgrenade" ); + } + break; + case ACT_RUN: + if ( pev->health <= HGRUNT_LIMP_HEALTH ) + { + // limp! + iSequence = LookupActivity ( ACT_RUN_HURT ); + } + else + { + iSequence = LookupActivity ( NewActivity ); + } + break; + case ACT_WALK: + if ( pev->health <= HGRUNT_LIMP_HEALTH ) + { + // limp! + iSequence = LookupActivity ( ACT_WALK_HURT ); + } + else + { + iSequence = LookupActivity ( NewActivity ); + } + break; + case ACT_IDLE: + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + { + NewActivity = ACT_IDLE_ANGRY; + } + iSequence = LookupActivity ( NewActivity ); + break; + default: + iSequence = LookupActivity ( NewActivity ); + break; + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( pev->sequence != iSequence || !m_fSequenceLoops ) + { + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo( ); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT ( at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } +} + +//========================================================= +// Get Schedule! +//========================================================= +Schedule_t *CMHGrunt :: GetSchedule( void ) +{ + + // clear old sentence + m_iSentence = HGRUNT_SENT_NONE; + + // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. + if ( pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE ) + { + if (pev->flags & FL_ONGROUND) + { + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType ( SCHED_GRUNT_REPEL_LAND ); + } + else + { + // repel down a rope, + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + return GetScheduleOfType ( SCHED_GRUNT_REPEL_ATTACK ); + else + return GetScheduleOfType ( SCHED_GRUNT_REPEL ); + } + } + + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + +// new enemy + if ( HasConditions(bits_COND_NEW_ENEMY) ) + { + { + { + //!!!KELLY - the leader of a squad of grunts has just seen the player or a + // monster and has made it the squad's enemy. You + // can check pev->flags for FL_CLIENT to determine whether this is the player + // or a monster. He's going to immediately start + // firing, though. If you'd like, we can make an alternate "first sight" + // schedule where the leader plays a handsign anim + // that gives us enough time to hear a short sentence or spoken command + // before he starts pluggin away. + if (FOkToSpeak())// && RANDOM_LONG(0,1)) + { + if ((m_hEnemy != NULL) && UTIL_IsPlayer(m_hEnemy)) + // player + SENTENCEG_PlayRndSz( ENT(pev), "HG_ALERT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); +/*jlb + else if ((m_hEnemy != NULL) && + (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && + (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && + (m_hEnemy->Classify() != CLASS_MACHINE)) + // monster + SENTENCEG_PlayRndSz( ENT(pev), "HG_MONST", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); +jlb*/ + JustSpoke(); + } + + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_GRUNT_SUPPRESS ); + } + else + { + return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } + } + } +// no ammo + else if ( HasConditions ( bits_COND_NO_AMMO_LOADED ) ) + { + //!!!KELLY - this individual just realized he's out of bullet ammo. + // He's going to try to find cover to run to and reload, but rarely, if + // none is available, he'll drop and reload in the open here. + return GetScheduleOfType ( SCHED_GRUNT_COVER_AND_RELOAD ); + } + +// damaged just a little + else if ( HasConditions( bits_COND_LIGHT_DAMAGE ) ) + { + // if hurt: + // 90% chance of taking cover + // 10% chance of flinch. + int iPercent = RANDOM_LONG(0,99); + + if ( iPercent <= 90 && m_hEnemy != NULL ) + { + // only try to take cover if we actually have an enemy! + + //!!!KELLY - this grunt was hit and is going to run to cover. + if (FOkToSpeak()) // && RANDOM_LONG(0,1)) + { + //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + m_iSentence = HGRUNT_SENT_COVER; + //JustSpoke(); + } + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + else + { + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + } +// can kick + else if ( HasConditions ( bits_COND_CAN_MELEE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); + } +// can grenade launch + + else if ( FBitSet( pev->weapons, HGRUNT_GRENADELAUNCHER) && HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) + { + // shoot a grenade if you can + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } +// can shoot + else if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + if ( HasConditions ( bits_COND_CAN_RANGE_ATTACK2 ) ) + { + // throw a grenade if can and no engage slots are available + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + else + { + // hide! + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + } +// can't see enemy + else if ( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) + { + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) ) + { + //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_THROW", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); + } + else + { + //!!!KELLY - grunt is going to stay put for a couple seconds to see if + // the enemy wanders back out into the open, or approaches the + // grunt's covered position. Good place for a taunt, I guess? + if (FOkToSpeak() && RANDOM_LONG(0,1)) + { + SENTENCEG_PlayRndSz( ENT(pev), "HG_TAUNT", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType( SCHED_STANDOFF ); + } + } + + if ( HasConditions( bits_COND_SEE_ENEMY ) && !HasConditions ( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE ); + } + } + } + + // no special cases here, call the base class + return CMBaseMonster :: GetSchedule(); +} + +//========================================================= +//========================================================= +Schedule_t* CMHGrunt :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + { + { + if ( RANDOM_LONG(0,1) ) + { + return &slGruntTakeCover[ 0 ]; + } + else + { + return &slGruntGrenadeCover[ 0 ]; + } + } + } + case SCHED_TAKE_COVER_FROM_BEST_SOUND: + { + return &slGruntTakeCoverFromBestSound[ 0 ]; + } + case SCHED_GRUNT_TAKECOVER_FAILED: + { + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); + } + + return GetScheduleOfType ( SCHED_FAIL ); + } + break; + case SCHED_GRUNT_ELOF_FAIL: + { + // human grunt is unable to move to a position that allows him to attack the enemy. + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + break; + case SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE: + { + return &slGruntEstablishLineOfFire[ 0 ]; + } + break; + case SCHED_RANGE_ATTACK1: + { + // randomly stand or crouch + if (RANDOM_LONG(0,9) == 0) + m_fStanding = RANDOM_LONG(0,1); + + if (m_fStanding) + return &slGruntRangeAttack1B[ 0 ]; + else + return &slGruntRangeAttack1A[ 0 ]; + } + case SCHED_RANGE_ATTACK2: + { + return &slGruntRangeAttack2[ 0 ]; + } + case SCHED_COMBAT_FACE: + { + return &slGruntCombatFace[ 0 ]; + } + case SCHED_GRUNT_WAIT_FACE_ENEMY: + { + return &slGruntWaitInCover[ 0 ]; + } + case SCHED_GRUNT_SWEEP: + { + return &slGruntSweep[ 0 ]; + } + case SCHED_GRUNT_COVER_AND_RELOAD: + { + return &slGruntHideReload[ 0 ]; + } + case SCHED_GRUNT_FOUND_ENEMY: + { + return &slGruntFoundEnemy[ 0 ]; + } + case SCHED_VICTORY_DANCE: + { + return &slGruntVictoryDance[ 0 ]; + } + case SCHED_GRUNT_SUPPRESS: + { + if ( UTIL_IsPlayer(m_hEnemy) && m_fFirstEncounter ) + { + m_fFirstEncounter = FALSE;// after first encounter, leader won't issue handsigns anymore when he has a new enemy + return &slGruntSignalSuppress[ 0 ]; + } + else + { + return &slGruntSuppress[ 0 ]; + } + } + case SCHED_FAIL: + { + if ( m_hEnemy != NULL ) + { + // grunt has an enemy, so pick a different default fail schedule most likely to help recover. + return &slGruntCombatFail[ 0 ]; + } + + return &slGruntFail[ 0 ]; + } + case SCHED_GRUNT_REPEL: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slGruntRepel[ 0 ]; + } + case SCHED_GRUNT_REPEL_ATTACK: + { + if (pev->velocity.z > -128) + pev->velocity.z -= 32; + return &slGruntRepelAttack[ 0 ]; + } + case SCHED_GRUNT_REPEL_LAND: + { + return &slGruntRepelLand[ 0 ]; + } + default: + { + return CMBaseMonster :: GetScheduleOfType ( Type ); + } + } +} diff --git a/src/dlls/hornet.cpp b/src/dlls/hornet.cpp index 960e4f5..db6a52e 100644 --- a/src/dlls/hornet.cpp +++ b/src/dlls/hornet.cpp @@ -1,384 +1,384 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -//========================================================= -// Hornets -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "hornet.h" - - -int iHornetTrail; -int iHornetPuff; - -//========================================================= -// don't let hornets gib, ever. -//========================================================= -int CMHornet :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // filter these bits a little. - bitsDamageType &= ~ ( DMG_ALWAYSGIB ); - bitsDamageType |= DMG_NEVERGIB; - - return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -//========================================================= -//========================================================= -void CMHornet :: Spawn( void ) -{ - Precache(); - - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - pev->takedamage = DAMAGE_YES; - pev->flags |= FL_MONSTER; // I have a bad feeling about this - pev->health = 1;// weak! - - // hornets don't live as long in multiplayer - m_flStopAttack = gpGlobals->time + 3.5; - - m_flFieldOfView = 0.9; // +- 25 degrees - - if ( RANDOM_LONG ( 1, 5 ) <= 2 ) - { - m_iHornetType = HORNET_TYPE_RED; - m_flFlySpeed = HORNET_RED_SPEED; - } - else - { - m_iHornetType = HORNET_TYPE_ORANGE; - m_flFlySpeed = HORNET_ORANGE_SPEED; - } - - SET_MODEL(ENT( pev ), "models/hornet.mdl"); - UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) ); - - SetTouch( &CMHornet::DieTouch ); - SetThink( &CMHornet::StartTrack ); - - edict_t *pSoundEnt = pev->owner; - if ( !pSoundEnt ) - pSoundEnt = edict(); - - // no real owner, or owner isn't a client. - pev->dmg = gSkillData.monDmgHornet; - - pev->nextthink = gpGlobals->time + 0.1; - ResetSequenceInfo( ); - - pev->classname = MAKE_STRING( "hornet" ); -} - - -void CMHornet :: Precache() -{ - PRECACHE_MODEL("models/hornet.mdl"); - - PRECACHE_SOUND( "agrunt/ag_fire1.wav" ); - PRECACHE_SOUND( "agrunt/ag_fire2.wav" ); - PRECACHE_SOUND( "agrunt/ag_fire3.wav" ); - - PRECACHE_SOUND( "hornet/ag_buzz1.wav" ); - PRECACHE_SOUND( "hornet/ag_buzz2.wav" ); - PRECACHE_SOUND( "hornet/ag_buzz3.wav" ); - - PRECACHE_SOUND( "hornet/ag_hornethit1.wav" ); - PRECACHE_SOUND( "hornet/ag_hornethit2.wav" ); - PRECACHE_SOUND( "hornet/ag_hornethit3.wav" ); - - iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" ); - iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr"); -} - -//========================================================= -// hornets will never get mad at each other, no matter who the owner is. -//========================================================= -int CMHornet::IRelationship ( CMBaseEntity *pTarget ) -{ - if ( pTarget->pev->modelindex == pev->modelindex ) - { - return R_NO; - } - - return CMBaseMonster :: IRelationship( pTarget ); -} - -//========================================================= -// ID's Hornet as their owner -//========================================================= -int CMHornet::Classify ( void ) -{ - /* - if ( pev->owner && pev->owner->v.flags & FL_CLIENT) - { - return CLASS_PLAYER_BIOWEAPON; - } - - return CLASS_ALIEN_BIOWEAPON; - */ - - // Ensure classify is consistent with the owner, in the event - // it's classification was overriden. - if ( pev->owner == NULL ) - return CLASS_ALIEN_BIOWEAPON; - - // Ain't this going to make the hornets code "slow"? - CMBaseMonster *pOwner = GetClassPtr((CMBaseMonster *)VARS(pev->owner)); - return pOwner->Classify(); -} - -//========================================================= -// StartTrack - starts a hornet out tracking its target -//========================================================= -void CMHornet :: StartTrack ( void ) -{ - IgniteTrail(); - - SetTouch( &CMHornet::TrackTouch ); - SetThink( &CMHornet::TrackTarget ); - - pev->nextthink = gpGlobals->time + 0.1; -} - -//========================================================= -// StartDart - starts a hornet out just flying straight. -//========================================================= -void CMHornet :: StartDart ( void ) -{ - IgniteTrail(); - - SetTouch( &CMHornet::DartTouch ); - - SetThink( &CMHornet::SUB_Remove ); - pev->nextthink = gpGlobals->time + 4; -} - -void CMHornet::IgniteTrail( void ) -{ -/* - - ted's suggested trail colors: - -r161 -g25 -b97 - -r173 -g39 -b14 - -old colors - case HORNET_TYPE_RED: - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 128 ); // r, g, b - WRITE_BYTE( 0 ); // r, g, b - break; - case HORNET_TYPE_ORANGE: - WRITE_BYTE( 0 ); // r, g, b - WRITE_BYTE( 100 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - break; - -*/ - - // trail - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT( entindex() ); // entity - WRITE_SHORT( iHornetTrail ); // model - WRITE_BYTE( 10 ); // life - WRITE_BYTE( 2 ); // width - - switch ( m_iHornetType ) - { - case HORNET_TYPE_RED: - WRITE_BYTE( 179 ); // r, g, b - WRITE_BYTE( 39 ); // r, g, b - WRITE_BYTE( 14 ); // r, g, b - break; - case HORNET_TYPE_ORANGE: - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 128 ); // r, g, b - WRITE_BYTE( 0 ); // r, g, b - break; - } - - WRITE_BYTE( 128 ); // brightness - - MESSAGE_END(); -} - -//========================================================= -// Hornet is flying, gently tracking target -//========================================================= -void CMHornet :: TrackTarget ( void ) -{ - Vector vecFlightDir; - Vector vecDirToEnemy; - float flDelta; - - StudioFrameAdvance( ); - - if (gpGlobals->time > m_flStopAttack) - { - SetTouch( NULL ); - SetThink( &CMHornet::SUB_Remove ); - pev->nextthink = gpGlobals->time + 0.1; - return; - } - - // UNDONE: The player pointer should come back after returning from another level - if ( m_hEnemy == NULL ) - {// enemy is dead. - Look( 512 ); - m_hEnemy = BestVisibleEnemy( ); - } - - if ( m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, ENT(pev) )) - { - m_vecEnemyLKP = UTIL_BodyTarget( m_hEnemy, pev->origin ); - } - else - { - m_vecEnemyLKP = m_vecEnemyLKP + pev->velocity * m_flFlySpeed * 0.1; - } - - vecDirToEnemy = ( m_vecEnemyLKP - pev->origin ).Normalize(); - - if (pev->velocity.Length() < 0.1) - vecFlightDir = vecDirToEnemy; - else - vecFlightDir = pev->velocity.Normalize(); - - // measure how far the turn is, the wider the turn, the slow we'll go this time. - flDelta = DotProduct ( vecFlightDir, vecDirToEnemy ); - - if ( flDelta < 0.5 ) - {// hafta turn wide again. play sound - switch (RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; - case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; - case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; - } - } - - if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED ) - {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far. - flDelta = 0.25; - } - - pev->velocity = ( vecFlightDir + vecDirToEnemy).Normalize(); - - if ( pev->owner && (pev->owner->v.flags & FL_MONSTER) ) - { - // random pattern only applies to hornets fired by monsters, not players. - - pev->velocity.x += RANDOM_FLOAT ( -0.10, 0.10 );// scramble the flight dir a bit. - pev->velocity.y += RANDOM_FLOAT ( -0.10, 0.10 ); - pev->velocity.z += RANDOM_FLOAT ( -0.10, 0.10 ); - } - - switch ( m_iHornetType ) - { - case HORNET_TYPE_RED: - pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn ) - pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 ); - break; - case HORNET_TYPE_ORANGE: - pev->velocity = pev->velocity * m_flFlySpeed;// do not have to slow down to turn. - pev->nextthink = gpGlobals->time + 0.1;// fixed think time - break; - } - - pev->angles = UTIL_VecToAngles (pev->velocity); - - pev->solid = SOLID_BBOX; -} - -//========================================================= -// Tracking Hornet hit something -//========================================================= -void CMHornet :: TrackTouch ( edict_t *pOther ) -{ - if ( (pOther == pev->owner) || pOther->v.modelindex == pev->modelindex ) - {// bumped into the guy that shot it. - pev->solid = SOLID_NOT; - return; - } - - // is this NOT a player and IS a monster? - if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL)) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - - if ( IRelationship( pMonster ) <= R_NO ) - { - // hit something we don't want to hurt, so turn around. - - pev->velocity = pev->velocity.Normalize(); - - pev->velocity.x *= -1; - pev->velocity.y *= -1; - - pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit. - pev->velocity = pev->velocity * m_flFlySpeed; - - return; - } - } - - DieTouch( pOther ); -} - -void CMHornet::DartTouch( edict_t *pOther ) -{ - DieTouch( pOther ); -} - -void CMHornet::DieTouch ( edict_t *pOther ) -{ - if ( pOther && pOther->v.takedamage ) - {// do the damage - - switch (RANDOM_LONG(0,2)) - {// buzz when you plug someone - case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit3.wav", 1, ATTN_NORM); break; - } - - if (UTIL_IsPlayer(pOther)) - UTIL_TakeDamage( pOther, pev, VARS( pev->owner ), pev->dmg, DMG_BULLET ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET ); - } - } - - pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid - pev->solid = SOLID_NOT; - - SetThink ( &CMHornet::SUB_Remove ); - pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish! -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +//========================================================= +// Hornets +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "hornet.h" + + +int iHornetTrail; +int iHornetPuff; + +//========================================================= +// don't let hornets gib, ever. +//========================================================= +int CMHornet :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // filter these bits a little. + bitsDamageType &= ~ ( DMG_ALWAYSGIB ); + bitsDamageType |= DMG_NEVERGIB; + + return CMBaseMonster :: TakeDamage ( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +//========================================================= +//========================================================= +void CMHornet :: Spawn( void ) +{ + Precache(); + + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + pev->takedamage = DAMAGE_YES; + pev->flags |= FL_MONSTER; // I have a bad feeling about this + pev->health = 1;// weak! + + // hornets don't live as long in multiplayer + m_flStopAttack = gpGlobals->time + 3.5; + + m_flFieldOfView = 0.9; // +- 25 degrees + + if ( RANDOM_LONG ( 1, 5 ) <= 2 ) + { + m_iHornetType = HORNET_TYPE_RED; + m_flFlySpeed = HORNET_RED_SPEED; + } + else + { + m_iHornetType = HORNET_TYPE_ORANGE; + m_flFlySpeed = HORNET_ORANGE_SPEED; + } + + SET_MODEL(ENT( pev ), "models/hornet.mdl"); + UTIL_SetSize( pev, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) ); + + SetTouch( &CMHornet::DieTouch ); + SetThink( &CMHornet::StartTrack ); + + edict_t *pSoundEnt = pev->owner; + if ( !pSoundEnt ) + pSoundEnt = edict(); + + // no real owner, or owner isn't a client. + pev->dmg = gSkillData.monDmgHornet; + + pev->nextthink = gpGlobals->time + 0.1; + ResetSequenceInfo( ); + + pev->classname = MAKE_STRING( "hornet" ); +} + + +void CMHornet :: Precache() +{ + PRECACHE_MODEL("models/hornet.mdl"); + + PRECACHE_SOUND( "agrunt/ag_fire1.wav" ); + PRECACHE_SOUND( "agrunt/ag_fire2.wav" ); + PRECACHE_SOUND( "agrunt/ag_fire3.wav" ); + + PRECACHE_SOUND( "hornet/ag_buzz1.wav" ); + PRECACHE_SOUND( "hornet/ag_buzz2.wav" ); + PRECACHE_SOUND( "hornet/ag_buzz3.wav" ); + + PRECACHE_SOUND( "hornet/ag_hornethit1.wav" ); + PRECACHE_SOUND( "hornet/ag_hornethit2.wav" ); + PRECACHE_SOUND( "hornet/ag_hornethit3.wav" ); + + iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" ); + iHornetTrail = PRECACHE_MODEL("sprites/laserbeam.spr"); +} + +//========================================================= +// hornets will never get mad at each other, no matter who the owner is. +//========================================================= +int CMHornet::IRelationship ( CMBaseEntity *pTarget ) +{ + if ( pTarget->pev->modelindex == pev->modelindex ) + { + return R_NO; + } + + return CMBaseMonster :: IRelationship( pTarget ); +} + +//========================================================= +// ID's Hornet as their owner +//========================================================= +int CMHornet::Classify ( void ) +{ + /* + if ( pev->owner && pev->owner->v.flags & FL_CLIENT) + { + return CLASS_PLAYER_BIOWEAPON; + } + + return CLASS_ALIEN_BIOWEAPON; + */ + + // Ensure classify is consistent with the owner, in the event + // it's classification was overriden. + if ( pev->owner == NULL ) + return CLASS_ALIEN_BIOWEAPON; + + // Ain't this going to make the hornets code "slow"? + CMBaseMonster *pOwner = GetClassPtr((CMBaseMonster *)VARS(pev->owner)); + return pOwner->Classify(); +} + +//========================================================= +// StartTrack - starts a hornet out tracking its target +//========================================================= +void CMHornet :: StartTrack ( void ) +{ + IgniteTrail(); + + SetTouch( &CMHornet::TrackTouch ); + SetThink( &CMHornet::TrackTarget ); + + pev->nextthink = gpGlobals->time + 0.1; +} + +//========================================================= +// StartDart - starts a hornet out just flying straight. +//========================================================= +void CMHornet :: StartDart ( void ) +{ + IgniteTrail(); + + SetTouch( &CMHornet::DartTouch ); + + SetThink( &CMHornet::SUB_Remove ); + pev->nextthink = gpGlobals->time + 4; +} + +void CMHornet::IgniteTrail( void ) +{ +/* + + ted's suggested trail colors: + +r161 +g25 +b97 + +r173 +g39 +b14 + +old colors + case HORNET_TYPE_RED: + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 128 ); // r, g, b + WRITE_BYTE( 0 ); // r, g, b + break; + case HORNET_TYPE_ORANGE: + WRITE_BYTE( 0 ); // r, g, b + WRITE_BYTE( 100 ); // r, g, b + WRITE_BYTE( 255 ); // r, g, b + break; + +*/ + + // trail + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT( entindex() ); // entity + WRITE_SHORT( iHornetTrail ); // model + WRITE_BYTE( 10 ); // life + WRITE_BYTE( 2 ); // width + + switch ( m_iHornetType ) + { + case HORNET_TYPE_RED: + WRITE_BYTE( 179 ); // r, g, b + WRITE_BYTE( 39 ); // r, g, b + WRITE_BYTE( 14 ); // r, g, b + break; + case HORNET_TYPE_ORANGE: + WRITE_BYTE( 255 ); // r, g, b + WRITE_BYTE( 128 ); // r, g, b + WRITE_BYTE( 0 ); // r, g, b + break; + } + + WRITE_BYTE( 128 ); // brightness + + MESSAGE_END(); +} + +//========================================================= +// Hornet is flying, gently tracking target +//========================================================= +void CMHornet :: TrackTarget ( void ) +{ + Vector vecFlightDir; + Vector vecDirToEnemy; + float flDelta; + + StudioFrameAdvance( ); + + if (gpGlobals->time > m_flStopAttack) + { + SetTouch( NULL ); + SetThink( &CMHornet::SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.1; + return; + } + + // UNDONE: The player pointer should come back after returning from another level + if ( m_hEnemy == NULL ) + {// enemy is dead. + Look( 512 ); + m_hEnemy = BestVisibleEnemy( ); + } + + if ( m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, ENT(pev) )) + { + m_vecEnemyLKP = UTIL_BodyTarget( m_hEnemy, pev->origin ); + } + else + { + m_vecEnemyLKP = m_vecEnemyLKP + pev->velocity * m_flFlySpeed * 0.1; + } + + vecDirToEnemy = ( m_vecEnemyLKP - pev->origin ).Normalize(); + + if (pev->velocity.Length() < 0.1) + vecFlightDir = vecDirToEnemy; + else + vecFlightDir = pev->velocity.Normalize(); + + // measure how far the turn is, the wider the turn, the slow we'll go this time. + flDelta = DotProduct ( vecFlightDir, vecDirToEnemy ); + + if ( flDelta < 0.5 ) + {// hafta turn wide again. play sound + switch (RANDOM_LONG(0,2)) + { + case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; + case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; + case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; + } + } + + if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED ) + {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far. + flDelta = 0.25; + } + + pev->velocity = ( vecFlightDir + vecDirToEnemy).Normalize(); + + if ( pev->owner && (pev->owner->v.flags & FL_MONSTER) ) + { + // random pattern only applies to hornets fired by monsters, not players. + + pev->velocity.x += RANDOM_FLOAT ( -0.10, 0.10 );// scramble the flight dir a bit. + pev->velocity.y += RANDOM_FLOAT ( -0.10, 0.10 ); + pev->velocity.z += RANDOM_FLOAT ( -0.10, 0.10 ); + } + + switch ( m_iHornetType ) + { + case HORNET_TYPE_RED: + pev->velocity = pev->velocity * ( m_flFlySpeed * flDelta );// scale the dir by the ( speed * width of turn ) + pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 ); + break; + case HORNET_TYPE_ORANGE: + pev->velocity = pev->velocity * m_flFlySpeed;// do not have to slow down to turn. + pev->nextthink = gpGlobals->time + 0.1;// fixed think time + break; + } + + pev->angles = UTIL_VecToAngles (pev->velocity); + + pev->solid = SOLID_BBOX; +} + +//========================================================= +// Tracking Hornet hit something +//========================================================= +void CMHornet :: TrackTouch ( edict_t *pOther ) +{ + if ( (pOther == pev->owner) || pOther->v.modelindex == pev->modelindex ) + {// bumped into the guy that shot it. + pev->solid = SOLID_NOT; + return; + } + + // is this NOT a player and IS a monster? + if (!UTIL_IsPlayer(pOther) && (pOther->v.euser4 != NULL)) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + + if ( IRelationship( pMonster ) <= R_NO ) + { + // hit something we don't want to hurt, so turn around. + + pev->velocity = pev->velocity.Normalize(); + + pev->velocity.x *= -1; + pev->velocity.y *= -1; + + pev->origin = pev->origin + pev->velocity * 4; // bounce the hornet off a bit. + pev->velocity = pev->velocity * m_flFlySpeed; + + return; + } + } + + DieTouch( pOther ); +} + +void CMHornet::DartTouch( edict_t *pOther ) +{ + DieTouch( pOther ); +} + +void CMHornet::DieTouch ( edict_t *pOther ) +{ + if ( pOther && pOther->v.takedamage ) + {// do the damage + + switch (RANDOM_LONG(0,2)) + {// buzz when you plug someone + case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit1.wav", 1, ATTN_NORM); break; + case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit2.wav", 1, ATTN_NORM); break; + case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_hornethit3.wav", 1, ATTN_NORM); break; + } + + if (UTIL_IsPlayer(pOther)) + UTIL_TakeDamage( pOther, pev, VARS( pev->owner ), pev->dmg, DMG_BULLET ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage( pev, VARS( pev->owner ), pev->dmg, DMG_BULLET ); + } + } + + pev->modelindex = 0;// so will disappear for the 0.1 secs we wait until NEXTTHINK gets rid + pev->solid = SOLID_NOT; + + SetThink ( &CMHornet::SUB_Remove ); + pev->nextthink = gpGlobals->time + 1;// stick around long enough for the sound to finish! +} + diff --git a/src/dlls/hornet.h b/src/dlls/hornet.h index 074f946..cce7880 100644 --- a/src/dlls/hornet.h +++ b/src/dlls/hornet.h @@ -1,55 +1,55 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -//========================================================= -// Hornets -//========================================================= - -//========================================================= -// Hornet Defines -//========================================================= -#define HORNET_TYPE_RED 0 -#define HORNET_TYPE_ORANGE 1 -#define HORNET_RED_SPEED (float)600 -#define HORNET_ORANGE_SPEED (float)800 -#define HORNET_BUZZ_VOLUME (float)0.8 - -extern int iHornetPuff; - -//========================================================= -// Hornet - this is the projectile that the Alien Grunt fires. -//========================================================= -class CMHornet : public CMBaseMonster -{ -public: - void Spawn( void ); - void Precache( void ); - int Classify ( void ); - int IRelationship ( CMBaseEntity *pTarget ); - - void IgniteTrail( void ); - void EXPORT StartTrack ( void ); - void EXPORT StartDart ( void ); - void EXPORT TrackTarget ( void ); - void EXPORT TrackTouch ( edict_t *pOther ); - void EXPORT DartTouch( edict_t *pOther ); - void EXPORT DieTouch ( edict_t *pOther ); - - int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); - - float m_flStopAttack; - int m_iHornetType; - float m_flFlySpeed; -}; - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +//========================================================= +// Hornets +//========================================================= + +//========================================================= +// Hornet Defines +//========================================================= +#define HORNET_TYPE_RED 0 +#define HORNET_TYPE_ORANGE 1 +#define HORNET_RED_SPEED (float)600 +#define HORNET_ORANGE_SPEED (float)800 +#define HORNET_BUZZ_VOLUME (float)0.8 + +extern int iHornetPuff; + +//========================================================= +// Hornet - this is the projectile that the Alien Grunt fires. +//========================================================= +class CMHornet : public CMBaseMonster +{ +public: + void Spawn( void ); + void Precache( void ); + int Classify ( void ); + int IRelationship ( CMBaseEntity *pTarget ); + + void IgniteTrail( void ); + void EXPORT StartTrack ( void ); + void EXPORT StartDart ( void ); + void EXPORT TrackTarget ( void ); + void EXPORT TrackTouch ( edict_t *pOther ); + void EXPORT DartTouch( edict_t *pOther ); + void EXPORT DieTouch ( edict_t *pOther ); + + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); + + float m_flStopAttack; + int m_iHornetType; + float m_flFlySpeed; +}; + diff --git a/src/dlls/houndeye.cpp b/src/dlls/houndeye.cpp index 5f1c4aa..65bae96 100644 --- a/src/dlls/houndeye.cpp +++ b/src/dlls/houndeye.cpp @@ -1,1112 +1,1112 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Houndeye - spooky sonic dog. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "nodes.h" - -extern CGraph WorldGraph; - -// houndeye does 20 points of damage spread over a sphere 384 units in diameter, and each additional -// squad member increases the BASE damage by 110%, per the spec. -#define HOUNDEYE_MAX_SQUAD_SIZE 4 -#define HOUNDEYE_MAX_ATTACK_RADIUS 384 -#define HOUNDEYE_SQUAD_BONUS (float)1.1 - -#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye - -#define HOUNDEYE_SOUND_STARTLE_VOLUME 128 // how loud a sound has to be to badly scare a sleeping houndeye - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_HOUND_CLOSE_EYE = LAST_COMMON_TASK + 1, - TASK_HOUND_OPEN_EYE, - TASK_HOUND_THREAT_DISPLAY, - TASK_HOUND_FALL_ASLEEP, - TASK_HOUND_WAKE_UP, - TASK_HOUND_HOP_BACK -}; - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_HOUND_AGITATED = LAST_COMMON_SCHEDULE + 1, - SCHED_HOUND_HOP_RETREAT, - SCHED_HOUND_FAIL, -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define HOUND_AE_WARN 1 -#define HOUND_AE_STARTATTACK 2 -#define HOUND_AE_THUMP 3 -#define HOUND_AE_ANGERSOUND1 4 -#define HOUND_AE_ANGERSOUND2 5 -#define HOUND_AE_HOPBACK 6 -#define HOUND_AE_CLOSE_EYE 7 - - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMHoundeye :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// FValidateHintType -//========================================================= -BOOL CMHoundeye :: FValidateHintType ( short sHint ) -{ - int i; - - static short sHoundHints[] = - { - HINT_WORLD_MACHINERY, - HINT_WORLD_BLINKING_LIGHT, - HINT_WORLD_HUMAN_BLOOD, - HINT_WORLD_ALIEN_BLOOD, - }; - - for ( i = 0 ; i < ARRAYSIZE ( sHoundHints ) ; i++ ) - { - if ( sHoundHints[ i ] == sHint ) - { - return TRUE; - } - } - - ALERT ( at_aiconsole, "Couldn't validate hint type" ); - return FALSE; -} - - -//========================================================= -// FCanActiveIdle -//========================================================= -BOOL CMHoundeye :: FCanActiveIdle ( void ) -{ - return TRUE; -} - - -//========================================================= -// CheckRangeAttack1 - overridden for houndeyes so that they -// try to get within half of their max attack radius before -// attacking, so as to increase their chances of doing damage. -//========================================================= -BOOL CMHoundeye :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( flDist <= ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ) && flDot >= 0.3 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMHoundeye :: SetYawSpeed ( void ) -{ - int ys; - - ys = 90; - - switch ( m_Activity ) - { - case ACT_CROUCHIDLE://sleeping! - ys = 0; - break; - case ACT_IDLE: - ys = 60; - break; - case ACT_WALK: - ys = 90; - break; - case ACT_RUN: - ys = 90; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// SetActivity -//========================================================= -void CMHoundeye :: SetActivity ( Activity NewActivity ) -{ - int iSequence; - - if ( NewActivity == m_Activity ) - return; - - if ( m_MonsterState == MONSTERSTATE_COMBAT && NewActivity == ACT_IDLE && RANDOM_LONG(0,1) ) - { - // play pissed idle. - iSequence = LookupSequence( "madidle" ); - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // In case someone calls this with something other than the ideal activity - m_IdealActivity = m_Activity; - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - pev->sequence = iSequence; // Set to the reset anim (if it's there) - pev->frame = 0; // FIX: frame counter shouldn't be reset when its the same activity as before - ResetSequenceInfo(); - SetYawSpeed(); - } - } - else - { - CMBaseMonster :: SetActivity ( NewActivity ); - } -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMHoundeye :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch ( pEvent->event ) - { - case HOUND_AE_WARN: - // do stuff for this event. - WarnSound(); - break; - - case HOUND_AE_STARTATTACK: - WarmUpSound(); - break; - - case HOUND_AE_HOPBACK: - { - float flGravity = g_psv_gravity->value; - - pev->flags &= ~FL_ONGROUND; - - pev->velocity = gpGlobals->v_forward * -200; - pev->velocity.z += (0.6 * flGravity) * 0.5; - - break; - } - - case HOUND_AE_THUMP: - // emit the shockwaves - SonicAttack(); - break; - - case HOUND_AE_ANGERSOUND1: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM); - break; - - case HOUND_AE_ANGERSOUND2: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain1.wav", 1, ATTN_NORM); - break; - - case HOUND_AE_CLOSE_EYE: - if ( !m_fDontBlink ) - { - pev->skin = HOUNDEYE_EYE_FRAMES - 1; - } - break; - - default: - CMBaseMonster :: HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMHoundeye :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/houndeye.mdl"); - UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_YELLOW; - pev->effects = 0; - pev->health = gSkillData.houndeyeHealth; - pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_fAsleep = FALSE; // everyone spawns awake - m_fDontBlink = FALSE; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_houndeye" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Houndeye" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMHoundeye :: Precache() -{ - PRECACHE_MODEL("models/houndeye.mdl"); - - PRECACHE_SOUND("houndeye/he_alert1.wav"); - PRECACHE_SOUND("houndeye/he_alert2.wav"); - PRECACHE_SOUND("houndeye/he_alert3.wav"); - - PRECACHE_SOUND("houndeye/he_die1.wav"); - PRECACHE_SOUND("houndeye/he_die2.wav"); - PRECACHE_SOUND("houndeye/he_die3.wav"); - - PRECACHE_SOUND("houndeye/he_idle1.wav"); - PRECACHE_SOUND("houndeye/he_idle2.wav"); - PRECACHE_SOUND("houndeye/he_idle3.wav"); - - PRECACHE_SOUND("houndeye/he_hunt1.wav"); - PRECACHE_SOUND("houndeye/he_hunt2.wav"); - PRECACHE_SOUND("houndeye/he_hunt3.wav"); - - PRECACHE_SOUND("houndeye/he_pain1.wav"); - PRECACHE_SOUND("houndeye/he_pain3.wav"); - PRECACHE_SOUND("houndeye/he_pain4.wav"); - PRECACHE_SOUND("houndeye/he_pain5.wav"); - - PRECACHE_SOUND("houndeye/he_attack1.wav"); - PRECACHE_SOUND("houndeye/he_attack3.wav"); - - PRECACHE_SOUND("houndeye/he_blast1.wav"); - PRECACHE_SOUND("houndeye/he_blast2.wav"); - PRECACHE_SOUND("houndeye/he_blast3.wav"); - - m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); -} - -//========================================================= -// IdleSound -//========================================================= -void CMHoundeye :: IdleSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// IdleSound -//========================================================= -void CMHoundeye :: WarmUpSound ( void ) -{ - switch ( RANDOM_LONG(0,1) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack1.wav", 0.7, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack3.wav", 0.7, ATTN_NORM ); - break; - } -} - -//========================================================= -// WarnSound -//========================================================= -void CMHoundeye :: WarnSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// AlertSound -//========================================================= -void CMHoundeye :: AlertSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CMHoundeye :: DeathSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die1.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die2.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die3.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// PainSound -//========================================================= -void CMHoundeye :: PainSound ( void ) -{ - switch ( RANDOM_LONG(0,2) ) - { - case 0: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM ); - break; - case 1: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain4.wav", 1, ATTN_NORM ); - break; - case 2: - EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain5.wav", 1, ATTN_NORM ); - break; - } -} - -//========================================================= -// WriteBeamColor - writes a color vector to the network -// based on the size of the group. -//========================================================= -void CMHoundeye :: WriteBeamColor ( void ) -{ - BYTE bRed, bGreen, bBlue; - - // solo houndeye - weakest beam - bRed = 188; - bGreen = 220; - bBlue = 255; - - WRITE_BYTE( bRed ); - WRITE_BYTE( bGreen ); - WRITE_BYTE( bBlue ); -} - - -//========================================================= -// SonicAttack -//========================================================= -void CMHoundeye :: SonicAttack ( void ) -{ - float flAdjustedDamage; - float flDist; - - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast3.wav", 1, ATTN_NORM); break; - } - - // blast circles - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16 + HOUNDEYE_MAX_ATTACK_RADIUS / .2); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_BEAMCYLINDER ); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16 + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2 ) / .2); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - - edict_t *pEntity = NULL; - // iterate on all entities in the vicinity. - while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, HOUNDEYE_MAX_ATTACK_RADIUS )) != NULL) - { - if ( pEntity->v.takedamage != DAMAGE_NO ) - { - if ( strcmp(STRING(pEntity->v.model), "models/houndeye.mdl") != 0 ) - {// houndeyes don't hurt other houndeyes with their attack - - // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. - // This means that you must get out of the houndeye's attack range entirely to avoid damage. - // Calculate full damage first - - // solo - flAdjustedDamage = gSkillData.houndeyeDmgBlast; - - flDist = (pEntity->v.origin - pev->origin).Length(); - - flAdjustedDamage -= ( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ) * flAdjustedDamage; - - if ( !UTIL_FVisible( pEntity, this->edict() ) ) - { - if ( UTIL_IsPlayer(pEntity) ) - { - // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still - // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients - // so that monsters in other parts of the level don't take the damage and get pissed. - flAdjustedDamage *= 0.5; - } - else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) - { - // do not hurt nonclients through walls, but allow damage to be done to breakables - flAdjustedDamage = 0; - } - } - - //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); - - if (flAdjustedDamage > 0 ) - { - if (UTIL_IsPlayer(pEntity)) - UTIL_TakeDamage( pEntity, pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); - else if (pEntity->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); - } - } - } - } - } -} - -//========================================================= -// start task -//========================================================= -void CMHoundeye :: StartTask ( Task_t *pTask ) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch ( pTask->iTask ) - { - case TASK_HOUND_FALL_ASLEEP: - { - m_fAsleep = TRUE; // signal that hound is lying down (must stand again before doing anything else!) - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_WAKE_UP: - { - m_fAsleep = FALSE; // signal that hound is standing again - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_OPEN_EYE: - { - m_fDontBlink = FALSE; // turn blinking back on and that code will automatically open the eye - m_iTaskStatus = TASKSTATUS_COMPLETE; - break; - } - case TASK_HOUND_CLOSE_EYE: - { - pev->skin = 0; - m_fDontBlink = TRUE; // tell blink code to leave the eye alone. - break; - } - case TASK_HOUND_THREAT_DISPLAY: - { - m_IdealActivity = ACT_IDLE_ANGRY; - break; - } - case TASK_HOUND_HOP_BACK: - { - m_IdealActivity = ACT_LEAP; - break; - } - case TASK_RANGE_ATTACK1: - { - m_IdealActivity = ACT_RANGE_ATTACK1; - break; - } - case TASK_SPECIAL_ATTACK1: - { - m_IdealActivity = ACT_SPECIAL_ATTACK1; - break; - } - case TASK_GUARD: - { - m_IdealActivity = ACT_GUARD; - break; - } - default: - { - CMBaseMonster :: StartTask(pTask); - break; - } - } -} - -//========================================================= -// RunTask -//========================================================= -void CMHoundeye :: RunTask ( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_HOUND_THREAT_DISPLAY: - { - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw ( pev->yaw_speed ); - - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - - break; - } - case TASK_HOUND_CLOSE_EYE: - { - if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 ) - { - pev->skin++; - } - break; - } - case TASK_HOUND_HOP_BACK: - { - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - break; - } - case TASK_SPECIAL_ATTACK1: - { - pev->skin = RANDOM_LONG(0, HOUNDEYE_EYE_FRAMES - 1); - - MakeIdealYaw ( m_vecEnemyLKP ); - ChangeYaw ( pev->yaw_speed ); - - float life; - life = ((255 - pev->frame) / (pev->framerate * m_flFrameRate)); - if (life < 0.1) life = 0.1; - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_IMPLOSION); - WRITE_COORD( pev->origin.x); - WRITE_COORD( pev->origin.y); - WRITE_COORD( pev->origin.z + 16); - WRITE_BYTE( 50 * life + 100); // radius - WRITE_BYTE( pev->frame / 25.0 ); // count - WRITE_BYTE( life * 10 ); // life - MESSAGE_END(); - - if ( m_fSequenceFinished ) - { - SonicAttack(); - TaskComplete(); - } - - break; - } - default: - { - CMBaseMonster :: RunTask(pTask); - break; - } - } -} - -//========================================================= -// PrescheduleThink -//========================================================= -void CMHoundeye::PrescheduleThink ( void ) -{ - // if the hound is mad and is running, make hunt noises. - if ( m_MonsterState == MONSTERSTATE_COMBAT && m_Activity == ACT_RUN && RANDOM_FLOAT( 0, 1 ) < 0.2 ) - { - WarnSound(); - } - - // at random, initiate a blink if not already blinking or sleeping - if ( !m_fDontBlink ) - { - if ( ( pev->skin == 0 ) && RANDOM_LONG(0,0x7F) == 0 ) - {// start blinking! - pev->skin = HOUNDEYE_EYE_FRAMES - 1; - } - else if ( pev->skin != 0 ) - {// already blinking - pev->skin--; - } - } -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -Task_t tlHoundGuardPack[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_GUARD, (float)0 }, -}; - -Schedule_t slHoundGuardPack[] = -{ - { - tlHoundGuardPack, - ARRAYSIZE ( tlHoundGuardPack ), - bits_COND_SEE_HATE | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_PROVOKED | - bits_COND_HEAR_SOUND, - 0, - "GuardPack" - }, -}; - -// primary range attack -Task_t tlHoundYell1[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_HOUND_AGITATED }, -}; - -Task_t tlHoundYell2[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slHoundRangeAttack[] = -{ - { - tlHoundYell1, - ARRAYSIZE ( tlHoundYell1 ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundRangeAttack1" - }, - { - tlHoundYell2, - ARRAYSIZE ( tlHoundYell2 ), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundRangeAttack2" - }, -}; - -// lie down and fall asleep -Task_t tlHoundSleep[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_RANDOM, (float)5 }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, - { TASK_HOUND_FALL_ASLEEP, (float)0 }, - { TASK_WAIT_RANDOM, (float)25 }, - { TASK_HOUND_CLOSE_EYE, (float)0 }, - //{ TASK_WAIT, (float)10 }, - //{ TASK_WAIT_RANDOM, (float)10 }, -}; - -Schedule_t slHoundSleep[] = -{ - { - tlHoundSleep, - ARRAYSIZE ( tlHoundSleep ), - bits_COND_HEAR_SOUND | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY, - 0, - "Hound Sleep" - }, -}; - -// wake and stand up lazily -Task_t tlHoundWakeLazy[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_HOUND_OPEN_EYE, (float)0 }, - { TASK_WAIT_RANDOM, (float)2.5 }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, - { TASK_HOUND_WAKE_UP, (float)0 }, -}; - -Schedule_t slHoundWakeLazy[] = -{ - { - tlHoundWakeLazy, - ARRAYSIZE ( tlHoundWakeLazy ), - 0, - 0, - "WakeLazy" - }, -}; - -// wake and stand up with great urgency! -Task_t tlHoundWakeUrgent[] = -{ - { TASK_HOUND_OPEN_EYE, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_HOUND_WAKE_UP, (float)0 }, -}; - -Schedule_t slHoundWakeUrgent[] = -{ - { - tlHoundWakeUrgent, - ARRAYSIZE ( tlHoundWakeUrgent ), - 0, - 0, - "WakeUrgent" - }, -}; - - -Task_t tlHoundSpecialAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SPECIAL_ATTACK1, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_IDLE_ANGRY }, -}; - -Schedule_t slHoundSpecialAttack1[] = -{ - { - tlHoundSpecialAttack1, - ARRAYSIZE ( tlHoundSpecialAttack1 ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED, - - 0, - "Hound Special Attack1" - }, -}; - -Task_t tlHoundAgitated[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, -}; - -Schedule_t slHoundAgitated[] = -{ - { - tlHoundAgitated, - ARRAYSIZE ( tlHoundAgitated ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Hound Agitated" - }, -}; - -Task_t tlHoundHopRetreat[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_HOP_BACK, 0 }, - { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, -}; - -Schedule_t slHoundHopRetreat[] = -{ - { - tlHoundHopRetreat, - ARRAYSIZE ( tlHoundHopRetreat ), - 0, - 0, - "Hound Hop Retreat" - }, -}; - -// hound fails in combat with client in the PVS -Task_t tlHoundCombatFailPVS[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, - { TASK_WAIT_FACE_ENEMY, (float)1 }, -}; - -Schedule_t slHoundCombatFailPVS[] = -{ - { - tlHoundCombatFailPVS, - ARRAYSIZE ( tlHoundCombatFailPVS ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundCombatFailPVS" - }, -}; - -// hound fails in combat with no client in the PVS. Don't keep peeping! -Task_t tlHoundCombatFailNoPVS[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_HOUND_THREAT_DISPLAY, 0 }, - { TASK_WAIT_FACE_ENEMY, (float)2 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT_PVS, 0 }, -}; - -Schedule_t slHoundCombatFailNoPVS[] = -{ - { - tlHoundCombatFailNoPVS, - ARRAYSIZE ( tlHoundCombatFailNoPVS ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "HoundCombatFailNoPVS" - }, -}; - -DEFINE_CUSTOM_SCHEDULES( CMHoundeye ) -{ - slHoundGuardPack, - slHoundRangeAttack, - &slHoundRangeAttack[ 1 ], - slHoundSleep, - slHoundWakeLazy, - slHoundWakeUrgent, - slHoundSpecialAttack1, - slHoundAgitated, - slHoundHopRetreat, - slHoundCombatFailPVS, - slHoundCombatFailNoPVS, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMHoundeye, CMBaseMonster ); - -//========================================================= -// GetScheduleOfType -//========================================================= -Schedule_t* CMHoundeye :: GetScheduleOfType ( int Type ) -{ - if ( m_fAsleep ) - { - if ( HasConditions( bits_COND_NEW_ENEMY ) ) - { - // get up fast, to fight. - return &slHoundWakeUrgent[ 0 ]; - } - - else - { - // hound is waking up on its own - return &slHoundWakeLazy[ 0 ]; - } - } - switch ( Type ) - { - case SCHED_IDLE_STAND: - { - // we may want to sleep instead of stand! - if ( !m_fAsleep && RANDOM_LONG(0,29) < 1 ) - { - return &slHoundSleep[ 0 ]; - } - else - { - return CMBaseMonster :: GetScheduleOfType( Type ); - } - } - case SCHED_RANGE_ATTACK1: - { - return &slHoundRangeAttack[ 0 ]; - } - case SCHED_SPECIAL_ATTACK1: - { - return &slHoundSpecialAttack1[ 0 ]; - } - case SCHED_GUARD: - { - return &slHoundGuardPack[ 0 ]; - } - case SCHED_HOUND_AGITATED: - { - return &slHoundAgitated[ 0 ]; - } - case SCHED_HOUND_HOP_RETREAT: - { - return &slHoundHopRetreat[ 0 ]; - } - case SCHED_FAIL: - { - if ( m_MonsterState == MONSTERSTATE_COMBAT ) - { - if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) - { - // client in PVS - return &slHoundCombatFailPVS[ 0 ]; - } - else - { - // client has taken off! - return &slHoundCombatFailNoPVS[ 0 ]; - } - } - else - { - return CMBaseMonster :: GetScheduleOfType ( Type ); - } - } - default: - { - return CMBaseMonster :: GetScheduleOfType ( Type ); - } - } -} - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMHoundeye :: GetSchedule( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_COMBAT: - { -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - if ( RANDOM_FLOAT( 0 , 1 ) <= 0.4 ) - { - TraceResult tr; - UTIL_MakeVectors( pev->angles ); - UTIL_TraceHull( pev->origin, pev->origin + gpGlobals->v_forward * -128, dont_ignore_monsters, head_hull, ENT( pev ), &tr ); - - if ( tr.flFraction == 1.0 ) - { - // it's clear behind, so the hound will jump - return GetScheduleOfType ( SCHED_HOUND_HOP_RETREAT ); - } - } - - return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); - } - - if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) - { - return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); - } - break; - } - } - - return CMBaseMonster :: GetSchedule(); -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Houndeye - spooky sonic dog. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "nodes.h" + +extern CGraph WorldGraph; + +// houndeye does 20 points of damage spread over a sphere 384 units in diameter, and each additional +// squad member increases the BASE damage by 110%, per the spec. +#define HOUNDEYE_MAX_SQUAD_SIZE 4 +#define HOUNDEYE_MAX_ATTACK_RADIUS 384 +#define HOUNDEYE_SQUAD_BONUS (float)1.1 + +#define HOUNDEYE_EYE_FRAMES 4 // how many different switchable maps for the eye + +#define HOUNDEYE_SOUND_STARTLE_VOLUME 128 // how loud a sound has to be to badly scare a sleeping houndeye + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_HOUND_CLOSE_EYE = LAST_COMMON_TASK + 1, + TASK_HOUND_OPEN_EYE, + TASK_HOUND_THREAT_DISPLAY, + TASK_HOUND_FALL_ASLEEP, + TASK_HOUND_WAKE_UP, + TASK_HOUND_HOP_BACK +}; + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_HOUND_AGITATED = LAST_COMMON_SCHEDULE + 1, + SCHED_HOUND_HOP_RETREAT, + SCHED_HOUND_FAIL, +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define HOUND_AE_WARN 1 +#define HOUND_AE_STARTATTACK 2 +#define HOUND_AE_THUMP 3 +#define HOUND_AE_ANGERSOUND1 4 +#define HOUND_AE_ANGERSOUND2 5 +#define HOUND_AE_HOPBACK 6 +#define HOUND_AE_CLOSE_EYE 7 + + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMHoundeye :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// FValidateHintType +//========================================================= +BOOL CMHoundeye :: FValidateHintType ( short sHint ) +{ + int i; + + static short sHoundHints[] = + { + HINT_WORLD_MACHINERY, + HINT_WORLD_BLINKING_LIGHT, + HINT_WORLD_HUMAN_BLOOD, + HINT_WORLD_ALIEN_BLOOD, + }; + + for ( i = 0 ; i < ARRAYSIZE ( sHoundHints ) ; i++ ) + { + if ( sHoundHints[ i ] == sHint ) + { + return TRUE; + } + } + + ALERT ( at_aiconsole, "Couldn't validate hint type" ); + return FALSE; +} + + +//========================================================= +// FCanActiveIdle +//========================================================= +BOOL CMHoundeye :: FCanActiveIdle ( void ) +{ + return TRUE; +} + + +//========================================================= +// CheckRangeAttack1 - overridden for houndeyes so that they +// try to get within half of their max attack radius before +// attacking, so as to increase their chances of doing damage. +//========================================================= +BOOL CMHoundeye :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( flDist <= ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 ) && flDot >= 0.3 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMHoundeye :: SetYawSpeed ( void ) +{ + int ys; + + ys = 90; + + switch ( m_Activity ) + { + case ACT_CROUCHIDLE://sleeping! + ys = 0; + break; + case ACT_IDLE: + ys = 60; + break; + case ACT_WALK: + ys = 90; + break; + case ACT_RUN: + ys = 90; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// SetActivity +//========================================================= +void CMHoundeye :: SetActivity ( Activity NewActivity ) +{ + int iSequence; + + if ( NewActivity == m_Activity ) + return; + + if ( m_MonsterState == MONSTERSTATE_COMBAT && NewActivity == ACT_IDLE && RANDOM_LONG(0,1) ) + { + // play pissed idle. + iSequence = LookupSequence( "madidle" ); + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // In case someone calls this with something other than the ideal activity + m_IdealActivity = m_Activity; + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + pev->sequence = iSequence; // Set to the reset anim (if it's there) + pev->frame = 0; // FIX: frame counter shouldn't be reset when its the same activity as before + ResetSequenceInfo(); + SetYawSpeed(); + } + } + else + { + CMBaseMonster :: SetActivity ( NewActivity ); + } +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMHoundeye :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch ( pEvent->event ) + { + case HOUND_AE_WARN: + // do stuff for this event. + WarnSound(); + break; + + case HOUND_AE_STARTATTACK: + WarmUpSound(); + break; + + case HOUND_AE_HOPBACK: + { + float flGravity = g_psv_gravity->value; + + pev->flags &= ~FL_ONGROUND; + + pev->velocity = gpGlobals->v_forward * -200; + pev->velocity.z += (0.6 * flGravity) * 0.5; + + break; + } + + case HOUND_AE_THUMP: + // emit the shockwaves + SonicAttack(); + break; + + case HOUND_AE_ANGERSOUND1: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM); + break; + + case HOUND_AE_ANGERSOUND2: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "houndeye/he_pain1.wav", 1, ATTN_NORM); + break; + + case HOUND_AE_CLOSE_EYE: + if ( !m_fDontBlink ) + { + pev->skin = HOUNDEYE_EYE_FRAMES - 1; + } + break; + + default: + CMBaseMonster :: HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMHoundeye :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/houndeye.mdl"); + UTIL_SetSize(pev, Vector ( -16, -16, 0 ), Vector ( 16, 16, 36 ) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_YELLOW; + pev->effects = 0; + pev->health = gSkillData.houndeyeHealth; + pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_fAsleep = FALSE; // everyone spawns awake + m_fDontBlink = FALSE; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_houndeye" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Houndeye" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMHoundeye :: Precache() +{ + PRECACHE_MODEL("models/houndeye.mdl"); + + PRECACHE_SOUND("houndeye/he_alert1.wav"); + PRECACHE_SOUND("houndeye/he_alert2.wav"); + PRECACHE_SOUND("houndeye/he_alert3.wav"); + + PRECACHE_SOUND("houndeye/he_die1.wav"); + PRECACHE_SOUND("houndeye/he_die2.wav"); + PRECACHE_SOUND("houndeye/he_die3.wav"); + + PRECACHE_SOUND("houndeye/he_idle1.wav"); + PRECACHE_SOUND("houndeye/he_idle2.wav"); + PRECACHE_SOUND("houndeye/he_idle3.wav"); + + PRECACHE_SOUND("houndeye/he_hunt1.wav"); + PRECACHE_SOUND("houndeye/he_hunt2.wav"); + PRECACHE_SOUND("houndeye/he_hunt3.wav"); + + PRECACHE_SOUND("houndeye/he_pain1.wav"); + PRECACHE_SOUND("houndeye/he_pain3.wav"); + PRECACHE_SOUND("houndeye/he_pain4.wav"); + PRECACHE_SOUND("houndeye/he_pain5.wav"); + + PRECACHE_SOUND("houndeye/he_attack1.wav"); + PRECACHE_SOUND("houndeye/he_attack3.wav"); + + PRECACHE_SOUND("houndeye/he_blast1.wav"); + PRECACHE_SOUND("houndeye/he_blast2.wav"); + PRECACHE_SOUND("houndeye/he_blast3.wav"); + + m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); +} + +//========================================================= +// IdleSound +//========================================================= +void CMHoundeye :: IdleSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_idle3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// IdleSound +//========================================================= +void CMHoundeye :: WarmUpSound ( void ) +{ + switch ( RANDOM_LONG(0,1) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack1.wav", 0.7, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_WEAPON, "houndeye/he_attack3.wav", 0.7, ATTN_NORM ); + break; + } +} + +//========================================================= +// WarnSound +//========================================================= +void CMHoundeye :: WarnSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_hunt3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// AlertSound +//========================================================= +void CMHoundeye :: AlertSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_alert3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CMHoundeye :: DeathSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die1.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die2.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_die3.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// PainSound +//========================================================= +void CMHoundeye :: PainSound ( void ) +{ + switch ( RANDOM_LONG(0,2) ) + { + case 0: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain3.wav", 1, ATTN_NORM ); + break; + case 1: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain4.wav", 1, ATTN_NORM ); + break; + case 2: + EMIT_SOUND( ENT(pev), CHAN_VOICE, "houndeye/he_pain5.wav", 1, ATTN_NORM ); + break; + } +} + +//========================================================= +// WriteBeamColor - writes a color vector to the network +// based on the size of the group. +//========================================================= +void CMHoundeye :: WriteBeamColor ( void ) +{ + BYTE bRed, bGreen, bBlue; + + // solo houndeye - weakest beam + bRed = 188; + bGreen = 220; + bBlue = 255; + + WRITE_BYTE( bRed ); + WRITE_BYTE( bGreen ); + WRITE_BYTE( bBlue ); +} + + +//========================================================= +// SonicAttack +//========================================================= +void CMHoundeye :: SonicAttack ( void ) +{ + float flAdjustedDamage; + float flDist; + + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast1.wav", 1, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast2.wav", 1, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast3.wav", 1, ATTN_NORM); break; + } + + // blast circles + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16 + HOUNDEYE_MAX_ATTACK_RADIUS / .2); // reach damage radius over .3 seconds + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 2 ); // life + WRITE_BYTE( 16 ); // width + WRITE_BYTE( 0 ); // noise + + WriteBeamColor(); + + WRITE_BYTE( 255 ); //brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_BEAMCYLINDER ); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16 + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2 ) / .2); // reach damage radius over .3 seconds + WRITE_SHORT( m_iSpriteTexture ); + WRITE_BYTE( 0 ); // startframe + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 2 ); // life + WRITE_BYTE( 16 ); // width + WRITE_BYTE( 0 ); // noise + + WriteBeamColor(); + + WRITE_BYTE( 255 ); //brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + + edict_t *pEntity = NULL; + // iterate on all entities in the vicinity. + while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, HOUNDEYE_MAX_ATTACK_RADIUS )) != NULL) + { + if ( pEntity->v.takedamage != DAMAGE_NO ) + { + if ( strcmp(STRING(pEntity->v.model), "models/houndeye.mdl") != 0 ) + {// houndeyes don't hurt other houndeyes with their attack + + // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. + // This means that you must get out of the houndeye's attack range entirely to avoid damage. + // Calculate full damage first + + // solo + flAdjustedDamage = gSkillData.houndeyeDmgBlast; + + flDist = (pEntity->v.origin - pev->origin).Length(); + + flAdjustedDamage -= ( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ) * flAdjustedDamage; + + if ( !UTIL_FVisible( pEntity, this->edict() ) ) + { + if ( UTIL_IsPlayer(pEntity) ) + { + // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still + // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients + // so that monsters in other parts of the level don't take the damage and get pissed. + flAdjustedDamage *= 0.5; + } + else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) + { + // do not hurt nonclients through walls, but allow damage to be done to breakables + flAdjustedDamage = 0; + } + } + + //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); + + if (flAdjustedDamage > 0 ) + { + if (UTIL_IsPlayer(pEntity)) + UTIL_TakeDamage( pEntity, pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); + else if (pEntity->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); + } + } + } + } + } +} + +//========================================================= +// start task +//========================================================= +void CMHoundeye :: StartTask ( Task_t *pTask ) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch ( pTask->iTask ) + { + case TASK_HOUND_FALL_ASLEEP: + { + m_fAsleep = TRUE; // signal that hound is lying down (must stand again before doing anything else!) + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_WAKE_UP: + { + m_fAsleep = FALSE; // signal that hound is standing again + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_OPEN_EYE: + { + m_fDontBlink = FALSE; // turn blinking back on and that code will automatically open the eye + m_iTaskStatus = TASKSTATUS_COMPLETE; + break; + } + case TASK_HOUND_CLOSE_EYE: + { + pev->skin = 0; + m_fDontBlink = TRUE; // tell blink code to leave the eye alone. + break; + } + case TASK_HOUND_THREAT_DISPLAY: + { + m_IdealActivity = ACT_IDLE_ANGRY; + break; + } + case TASK_HOUND_HOP_BACK: + { + m_IdealActivity = ACT_LEAP; + break; + } + case TASK_RANGE_ATTACK1: + { + m_IdealActivity = ACT_RANGE_ATTACK1; + break; + } + case TASK_SPECIAL_ATTACK1: + { + m_IdealActivity = ACT_SPECIAL_ATTACK1; + break; + } + case TASK_GUARD: + { + m_IdealActivity = ACT_GUARD; + break; + } + default: + { + CMBaseMonster :: StartTask(pTask); + break; + } + } +} + +//========================================================= +// RunTask +//========================================================= +void CMHoundeye :: RunTask ( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_HOUND_THREAT_DISPLAY: + { + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + + break; + } + case TASK_HOUND_CLOSE_EYE: + { + if ( pev->skin < HOUNDEYE_EYE_FRAMES - 1 ) + { + pev->skin++; + } + break; + } + case TASK_HOUND_HOP_BACK: + { + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + break; + } + case TASK_SPECIAL_ATTACK1: + { + pev->skin = RANDOM_LONG(0, HOUNDEYE_EYE_FRAMES - 1); + + MakeIdealYaw ( m_vecEnemyLKP ); + ChangeYaw ( pev->yaw_speed ); + + float life; + life = ((255 - pev->frame) / (pev->framerate * m_flFrameRate)); + if (life < 0.1) life = 0.1; + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_IMPLOSION); + WRITE_COORD( pev->origin.x); + WRITE_COORD( pev->origin.y); + WRITE_COORD( pev->origin.z + 16); + WRITE_BYTE( 50 * life + 100); // radius + WRITE_BYTE( pev->frame / 25.0 ); // count + WRITE_BYTE( life * 10 ); // life + MESSAGE_END(); + + if ( m_fSequenceFinished ) + { + SonicAttack(); + TaskComplete(); + } + + break; + } + default: + { + CMBaseMonster :: RunTask(pTask); + break; + } + } +} + +//========================================================= +// PrescheduleThink +//========================================================= +void CMHoundeye::PrescheduleThink ( void ) +{ + // if the hound is mad and is running, make hunt noises. + if ( m_MonsterState == MONSTERSTATE_COMBAT && m_Activity == ACT_RUN && RANDOM_FLOAT( 0, 1 ) < 0.2 ) + { + WarnSound(); + } + + // at random, initiate a blink if not already blinking or sleeping + if ( !m_fDontBlink ) + { + if ( ( pev->skin == 0 ) && RANDOM_LONG(0,0x7F) == 0 ) + {// start blinking! + pev->skin = HOUNDEYE_EYE_FRAMES - 1; + } + else if ( pev->skin != 0 ) + {// already blinking + pev->skin--; + } + } +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +Task_t tlHoundGuardPack[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_GUARD, (float)0 }, +}; + +Schedule_t slHoundGuardPack[] = +{ + { + tlHoundGuardPack, + ARRAYSIZE ( tlHoundGuardPack ), + bits_COND_SEE_HATE | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_PROVOKED | + bits_COND_HEAR_SOUND, + 0, + "GuardPack" + }, +}; + +// primary range attack +Task_t tlHoundYell1[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_HOUND_AGITATED }, +}; + +Task_t tlHoundYell2[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slHoundRangeAttack[] = +{ + { + tlHoundYell1, + ARRAYSIZE ( tlHoundYell1 ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundRangeAttack1" + }, + { + tlHoundYell2, + ARRAYSIZE ( tlHoundYell2 ), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundRangeAttack2" + }, +}; + +// lie down and fall asleep +Task_t tlHoundSleep[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_RANDOM, (float)5 }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, + { TASK_HOUND_FALL_ASLEEP, (float)0 }, + { TASK_WAIT_RANDOM, (float)25 }, + { TASK_HOUND_CLOSE_EYE, (float)0 }, + //{ TASK_WAIT, (float)10 }, + //{ TASK_WAIT_RANDOM, (float)10 }, +}; + +Schedule_t slHoundSleep[] = +{ + { + tlHoundSleep, + ARRAYSIZE ( tlHoundSleep ), + bits_COND_HEAR_SOUND | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY, + 0, + "Hound Sleep" + }, +}; + +// wake and stand up lazily +Task_t tlHoundWakeLazy[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_HOUND_OPEN_EYE, (float)0 }, + { TASK_WAIT_RANDOM, (float)2.5 }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, + { TASK_HOUND_WAKE_UP, (float)0 }, +}; + +Schedule_t slHoundWakeLazy[] = +{ + { + tlHoundWakeLazy, + ARRAYSIZE ( tlHoundWakeLazy ), + 0, + 0, + "WakeLazy" + }, +}; + +// wake and stand up with great urgency! +Task_t tlHoundWakeUrgent[] = +{ + { TASK_HOUND_OPEN_EYE, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_HOP }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_HOUND_WAKE_UP, (float)0 }, +}; + +Schedule_t slHoundWakeUrgent[] = +{ + { + tlHoundWakeUrgent, + ARRAYSIZE ( tlHoundWakeUrgent ), + 0, + 0, + "WakeUrgent" + }, +}; + + +Task_t tlHoundSpecialAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SPECIAL_ATTACK1, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_IDLE_ANGRY }, +}; + +Schedule_t slHoundSpecialAttack1[] = +{ + { + tlHoundSpecialAttack1, + ARRAYSIZE ( tlHoundSpecialAttack1 ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED, + + 0, + "Hound Special Attack1" + }, +}; + +Task_t tlHoundAgitated[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, +}; + +Schedule_t slHoundAgitated[] = +{ + { + tlHoundAgitated, + ARRAYSIZE ( tlHoundAgitated ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Hound Agitated" + }, +}; + +Task_t tlHoundHopRetreat[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_HOP_BACK, 0 }, + { TASK_SET_SCHEDULE, (float)SCHED_TAKE_COVER_FROM_ENEMY }, +}; + +Schedule_t slHoundHopRetreat[] = +{ + { + tlHoundHopRetreat, + ARRAYSIZE ( tlHoundHopRetreat ), + 0, + 0, + "Hound Hop Retreat" + }, +}; + +// hound fails in combat with client in the PVS +Task_t tlHoundCombatFailPVS[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, + { TASK_WAIT_FACE_ENEMY, (float)1 }, +}; + +Schedule_t slHoundCombatFailPVS[] = +{ + { + tlHoundCombatFailPVS, + ARRAYSIZE ( tlHoundCombatFailPVS ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundCombatFailPVS" + }, +}; + +// hound fails in combat with no client in the PVS. Don't keep peeping! +Task_t tlHoundCombatFailNoPVS[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_HOUND_THREAT_DISPLAY, 0 }, + { TASK_WAIT_FACE_ENEMY, (float)2 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT_PVS, 0 }, +}; + +Schedule_t slHoundCombatFailNoPVS[] = +{ + { + tlHoundCombatFailNoPVS, + ARRAYSIZE ( tlHoundCombatFailNoPVS ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "HoundCombatFailNoPVS" + }, +}; + +DEFINE_CUSTOM_SCHEDULES( CMHoundeye ) +{ + slHoundGuardPack, + slHoundRangeAttack, + &slHoundRangeAttack[ 1 ], + slHoundSleep, + slHoundWakeLazy, + slHoundWakeUrgent, + slHoundSpecialAttack1, + slHoundAgitated, + slHoundHopRetreat, + slHoundCombatFailPVS, + slHoundCombatFailNoPVS, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMHoundeye, CMBaseMonster ); + +//========================================================= +// GetScheduleOfType +//========================================================= +Schedule_t* CMHoundeye :: GetScheduleOfType ( int Type ) +{ + if ( m_fAsleep ) + { + if ( HasConditions( bits_COND_NEW_ENEMY ) ) + { + // get up fast, to fight. + return &slHoundWakeUrgent[ 0 ]; + } + + else + { + // hound is waking up on its own + return &slHoundWakeLazy[ 0 ]; + } + } + switch ( Type ) + { + case SCHED_IDLE_STAND: + { + // we may want to sleep instead of stand! + if ( !m_fAsleep && RANDOM_LONG(0,29) < 1 ) + { + return &slHoundSleep[ 0 ]; + } + else + { + return CMBaseMonster :: GetScheduleOfType( Type ); + } + } + case SCHED_RANGE_ATTACK1: + { + return &slHoundRangeAttack[ 0 ]; + } + case SCHED_SPECIAL_ATTACK1: + { + return &slHoundSpecialAttack1[ 0 ]; + } + case SCHED_GUARD: + { + return &slHoundGuardPack[ 0 ]; + } + case SCHED_HOUND_AGITATED: + { + return &slHoundAgitated[ 0 ]; + } + case SCHED_HOUND_HOP_RETREAT: + { + return &slHoundHopRetreat[ 0 ]; + } + case SCHED_FAIL: + { + if ( m_MonsterState == MONSTERSTATE_COMBAT ) + { + if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) ) + { + // client in PVS + return &slHoundCombatFailPVS[ 0 ]; + } + else + { + // client has taken off! + return &slHoundCombatFailNoPVS[ 0 ]; + } + } + else + { + return CMBaseMonster :: GetScheduleOfType ( Type ); + } + } + default: + { + return CMBaseMonster :: GetScheduleOfType ( Type ); + } + } +} + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMHoundeye :: GetSchedule( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_COMBAT: + { +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + if ( RANDOM_FLOAT( 0 , 1 ) <= 0.4 ) + { + TraceResult tr; + UTIL_MakeVectors( pev->angles ); + UTIL_TraceHull( pev->origin, pev->origin + gpGlobals->v_forward * -128, dont_ignore_monsters, head_hull, ENT( pev ), &tr ); + + if ( tr.flFraction == 1.0 ) + { + // it's clear behind, so the hound will jump + return GetScheduleOfType ( SCHED_HOUND_HOP_RETREAT ); + } + } + + return GetScheduleOfType ( SCHED_TAKE_COVER_FROM_ENEMY ); + } + + if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) + { + return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); + } + break; + } + } + + return CMBaseMonster :: GetSchedule(); +} diff --git a/src/dlls/islave.cpp b/src/dlls/islave.cpp index baad32b..37c8df3 100644 --- a/src/dlls/islave.cpp +++ b/src/dlls/islave.cpp @@ -1,763 +1,763 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Alien slave monster -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "effects.h" -#include "weapons.h" - - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define ISLAVE_AE_CLAW ( 1 ) -#define ISLAVE_AE_CLAWRAKE ( 2 ) -#define ISLAVE_AE_ZAP_POWERUP ( 3 ) -#define ISLAVE_AE_ZAP_SHOOT ( 4 ) -#define ISLAVE_AE_ZAP_DONE ( 5 ) - - -const char *CMISlave::pAttackHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char *CMISlave::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CMISlave::pPainSounds[] = -{ - "aslave/slv_pain1.wav", - "aslave/slv_pain2.wav", -}; - -const char *CMISlave::pDeathSounds[] = -{ - "aslave/slv_die1.wav", - "aslave/slv_die2.wav", -}; - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMISlave :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MILITARY; -} - - -int CMISlave::IRelationship( CMBaseEntity *pTarget ) -{ - if ( (pTarget->IsPlayer()) ) - if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED )) - return R_NO; - return CMBaseMonster::IRelationship( pTarget ); -} - - -//========================================================= -// ALertSound - scream -//========================================================= -void CMISlave :: AlertSound( void ) -{ - if ( m_hEnemy != NULL ) - { - SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch); - } -} - -//========================================================= -// IdleSound -//========================================================= -void CMISlave :: IdleSound( void ) -{ - if (RANDOM_LONG( 0, 2 ) == 0) - { - SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch); - } - -#if 0 - int side = RANDOM_LONG( 0, 1 ) * 2 - 1; - - ClearBeams( ); - ArmBeam( side ); - - UTIL_MakeAimVectors( pev->angles ); - Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(vecSrc.x); // X - WRITE_COORD(vecSrc.y); // Y - WRITE_COORD(vecSrc.z); // Z - WRITE_BYTE( 8 ); // radius * 0.1 - WRITE_BYTE( 255 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 96 ); // b - WRITE_BYTE( 10 ); // time * 10 - WRITE_BYTE( 0 ); // decay * 0.1 - MESSAGE_END( ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 ); -#endif -} - -//========================================================= -// PainSound -//========================================================= -void CMISlave :: PainSound( void ) -{ - if (RANDOM_LONG( 0, 2 ) == 0) - { - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } -} - -//========================================================= -// DieSound -//========================================================= - -void CMISlave :: DeathSound( void ) -{ - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); -} - - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. -//========================================================= -int CMISlave :: ISoundMask ( void) -{ - return 0; -} - - -void CMISlave::Killed( entvars_t *pevAttacker, int iGib ) -{ - ClearBeams( ); - CMBaseMonster::Killed( pevAttacker, iGib ); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMISlave :: SetYawSpeed ( void ) -{ - int ys; - - switch ( m_Activity ) - { - case ACT_WALK: - ys = 50; - break; - case ACT_RUN: - ys = 70; - break; - case ACT_IDLE: - ys = 50; - break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMISlave :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame ); - switch( pEvent->event ) - { - case ISLAVE_AE_CLAW: - { - // SOUND HERE! - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.z = -18; - pHurt->v.punchangle.x = 5; - } - // Play a random attack hit sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - else - { - // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - } - break; - - case ISLAVE_AE_CLAWRAKE: - { - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.z = -18; - pHurt->v.punchangle.x = 5; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - else - { - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); - } - } - break; - - case ISLAVE_AE_ZAP_POWERUP: - { - UTIL_MakeAimVectors( pev->angles ); - - if (m_iBeams == 0) - { - Vector vecSrc = pev->origin + gpGlobals->v_forward * 2; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(vecSrc.x); // X - WRITE_COORD(vecSrc.y); // Y - WRITE_COORD(vecSrc.z); // Z - WRITE_BYTE( 12 ); // radius * 0.1 - WRITE_BYTE( 255 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 96 ); // b - WRITE_BYTE( 20 / pev->framerate ); // time * 10 - WRITE_BYTE( 0 ); // decay * 0.1 - MESSAGE_END( ); - - } -/*jlb - if (m_hDead != NULL) - { - WackBeam( -1, m_hDead ); - WackBeam( 1, m_hDead ); - } - else -jlb*/ - { - ArmBeam( -1 ); - ArmBeam( 1 ); - BeamGlow( ); - } - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 ); - pev->skin = m_iBeams / 2; - } - break; - - case ISLAVE_AE_ZAP_SHOOT: - { - ClearBeams( ); - -/*jlb - if (m_hDead != NULL) - { - Vector vecDest = m_hDead->v.origin + Vector( 0, 0, 38 ); - TraceResult trace; - UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead, &trace ); - - if ( !trace.fStartSolid ) - { - CMBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles ); - CMBaseMonster *pNewMonster = pNew->MyMonsterPointer( ); - pNew->pev->spawnflags |= 1; - WackBeam( -1, pNew ); - WackBeam( 1, pNew ); - UTIL_Remove( m_hDead ); - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); - break; - } - } -jlb*/ - ClearMultiDamage(); - - UTIL_MakeAimVectors( pev->angles ); - - ZapBeam( -1 ); - ZapBeam( 1 ); - - EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); - // STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); - - ApplyMultiDamage(pev, pev); - - m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 ); - } - break; - - case ISLAVE_AE_ZAP_DONE: - { - ClearBeams( ); - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// CheckRangeAttack1 - normal beam attack -//========================================================= -BOOL CMISlave :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if (m_flNextAttack > gpGlobals->time) - { - return FALSE; - } - - return CMBaseMonster::CheckRangeAttack1( flDot, flDist ); -} - -//========================================================= -// CheckRangeAttack2 - check bravery and try to resurect dead comrades -//========================================================= -BOOL CMISlave :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - return FALSE; - - if (m_flNextAttack > gpGlobals->time) - { - return FALSE; - } - -//jlb m_hDead = NULL; - m_iBravery = 0; - - edict_t *pEntity = NULL; - while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL) - { - TraceResult tr; - - UTIL_TraceLine( EyePosition( ), UTIL_EyePosition(pEntity), ignore_monsters, ENT(pev), &tr ); - if (tr.flFraction == 1.0 || tr.pHit == pEntity) - { - if (pEntity->v.deadflag == DEAD_DEAD) - { - float d = (pev->origin - pEntity->v.origin).Length(); - if (d < flDist) - { -//jlb m_hDead = pEntity; - flDist = d; - } - m_iBravery--; - } - else - { - m_iBravery++; - } - } - } -//jlb if (m_hDead != NULL) -//jlb return TRUE; -//jlb else - return FALSE; -} - - -//========================================================= -// StartTask -//========================================================= -void CMISlave :: StartTask ( Task_t *pTask ) -{ - ClearBeams( ); - - CMBaseMonster :: StartTask ( pTask ); -} - - -//========================================================= -// Spawn -//========================================================= -void CMISlave :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/islave.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.slaveHealth; - pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5; - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP; - - m_voicePitch = RANDOM_LONG( 85, 110 ); - - for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) - m_pBeam[i] = NULL; - - m_iBravery = 0; - m_flNextAttack = 0.0f; - - MonsterInit(); - pev->classname = MAKE_STRING( "monster_alien_slave" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Alien Slave" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMISlave :: Precache() -{ - int i; - - PRECACHE_MODEL("models/islave.mdl"); - PRECACHE_MODEL("sprites/lgtning.spr"); - PRECACHE_SOUND("debris/zap1.wav"); - PRECACHE_SOUND("debris/zap4.wav"); - PRECACHE_SOUND("weapons/electro4.wav"); - PRECACHE_SOUND("hassault/hw_shoot1.wav"); - PRECACHE_SOUND("zombie/zo_pain2.wav"); - PRECACHE_SOUND("headcrab/hc_headbite.wav"); - PRECACHE_SOUND("weapons/cbar_miss1.wav"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ ) - PRECACHE_SOUND((char *)pDeathSounds[i]); -} - - -//========================================================= -// TakeDamage - get provoked when injured -//========================================================= - -int CMISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - // don't slash one of your own - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pevAttacker)); - if (pMonster != NULL) - { - if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( pMonster ) < R_DL) - return 0; - } - - m_afMemory |= bits_MEMORY_PROVOKED; - return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - - -void CMISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if (bitsDamageType & DMG_SHOCK) - return; - - CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); -} - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - - -// primary range attack -Task_t tlSlaveAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, -}; - -Schedule_t slSlaveAttack1[] = -{ - { - tlSlaveAttack1, - ARRAYSIZE ( tlSlaveAttack1 ), - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_HEAR_SOUND | - bits_COND_HEAVY_DAMAGE, - 0, - "Slave Range Attack1" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMISlave ) -{ - slSlaveAttack1, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMISlave, CMBaseMonster ); - - -//========================================================= -//========================================================= -Schedule_t *CMISlave :: GetSchedule( void ) -{ - ClearBeams( ); - -/* - if (pev->spawnflags) - { - pev->spawnflags = 0; - return GetScheduleOfType( SCHED_RELOAD ); - } -*/ - - switch (m_MonsterState) - { - case MONSTERSTATE_COMBAT: -// dead enemy - if ( HasConditions( bits_COND_ENEMY_DEAD ) ) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster :: GetSchedule(); - } - - if (pev->health < 20 || m_iBravery < 0) - { - if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) - { - m_failSchedule = SCHED_CHASE_ENEMY; - if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) - { - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) - { - // ALERT( at_console, "exposed\n"); - return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); - } - } - } - break; - } - return CMBaseMonster::GetSchedule( ); -} - - -Schedule_t *CMISlave :: GetScheduleOfType ( int Type ) -{ - switch ( Type ) - { - case SCHED_FAIL: - if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) - { - return CMBaseMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ; - } - break; - case SCHED_RANGE_ATTACK1: - return slSlaveAttack1; - case SCHED_RANGE_ATTACK2: - return slSlaveAttack1; - } - return CMBaseMonster :: GetScheduleOfType( Type ); -} - - -//========================================================= -// ArmBeam - small beam from arm to nearby geometry -//========================================================= - -void CMISlave :: ArmBeam( int side ) -{ - TraceResult tr; - float flDist = 1.0; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - UTIL_MakeAimVectors( pev->angles ); - Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32; - - for (int i = 0; i < 3; i++) - { - Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 ); - TraceResult tr1; - UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1); - if (flDist > tr1.flFraction) - { - tr = tr1; - flDist = tr.flFraction; - } - } - - // Couldn't find anything close enough - if ( flDist == 1.0 ) - return; - - DecalGunshot( &tr, BULLET_PLAYER_CROWBAR ); - - m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - // m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); - m_pBeam[m_iBeams]->SetBrightness( 64 ); - m_pBeam[m_iBeams]->SetNoise( 80 ); - m_iBeams++; -} - - -//========================================================= -// BeamGlow - brighten all beams -//========================================================= -void CMISlave :: BeamGlow( ) -{ - int b = m_iBeams * 32; - if (b > 255) - b = 255; - - for (int i = 0; i < m_iBeams; i++) - { - if (m_pBeam[i]->GetBrightness() != 255) - { - m_pBeam[i]->SetBrightness( b ); - } - } -} - - -//========================================================= -// WackBeam - regenerate dead colleagues -//========================================================= -void CMISlave :: WackBeam( int side, edict_t *pEntity ) -{ - Vector vecDest; - float flDist = 1.0; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - if (pEntity == NULL) - return; - - m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( UTIL_Center(pEntity), entindex( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 80 ); - m_iBeams++; -} - -//========================================================= -// ZapBeam - heavy damage directly forward -//========================================================= -void CMISlave :: ZapBeam( int side ) -{ - Vector vecSrc, vecAim; - TraceResult tr; - edict_t *pEntity; - - if (m_iBeams >= ISLAVE_MAX_BEAMS) - return; - - vecSrc = pev->origin + gpGlobals->v_up * 36; - vecAim = ShootAtEnemy( vecSrc ); - float deflection = 0.01; - vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection ); - UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr); - - m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 50 ); - if (!m_pBeam[m_iBeams]) - return; - - m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); - m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); - m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 20 ); - m_iBeams++; - - pEntity = tr.pHit; - if (pEntity != NULL && pEntity->v.takedamage) - { - if (UTIL_IsPlayer(pEntity)) - UTIL_TraceAttack( pEntity, pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); - else if (pEntity->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); - } - } - - UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); -} - - -//========================================================= -// ClearBeams - remove all beams -//========================================================= -void CMISlave :: ClearBeams( ) -{ - for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) - { - if (m_pBeam[i]) - { - UTIL_Remove( m_pBeam[i]->edict() ); - m_pBeam[i] = NULL; - } - } - m_iBeams = 0; - pev->skin = 0; - - STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Alien slave monster +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "effects.h" +#include "weapons.h" + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ISLAVE_AE_CLAW ( 1 ) +#define ISLAVE_AE_CLAWRAKE ( 2 ) +#define ISLAVE_AE_ZAP_POWERUP ( 3 ) +#define ISLAVE_AE_ZAP_SHOOT ( 4 ) +#define ISLAVE_AE_ZAP_DONE ( 5 ) + + +const char *CMISlave::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CMISlave::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CMISlave::pPainSounds[] = +{ + "aslave/slv_pain1.wav", + "aslave/slv_pain2.wav", +}; + +const char *CMISlave::pDeathSounds[] = +{ + "aslave/slv_die1.wav", + "aslave/slv_die2.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMISlave :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MILITARY; +} + + +int CMISlave::IRelationship( CMBaseEntity *pTarget ) +{ + if ( (pTarget->IsPlayer()) ) + if ( (pev->spawnflags & SF_MONSTER_WAIT_UNTIL_PROVOKED ) && ! (m_afMemory & bits_MEMORY_PROVOKED )) + return R_NO; + return CMBaseMonster::IRelationship( pTarget ); +} + + +//========================================================= +// ALertSound - scream +//========================================================= +void CMISlave :: AlertSound( void ) +{ + if ( m_hEnemy != NULL ) + { + SENTENCEG_PlayRndSz(ENT(pev), "SLV_ALERT", 0.85, ATTN_NORM, 0, m_voicePitch); + } +} + +//========================================================= +// IdleSound +//========================================================= +void CMISlave :: IdleSound( void ) +{ + if (RANDOM_LONG( 0, 2 ) == 0) + { + SENTENCEG_PlayRndSz(ENT(pev), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch); + } + +#if 0 + int side = RANDOM_LONG( 0, 1 ) * 2 - 1; + + ClearBeams( ); + ArmBeam( side ); + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(vecSrc.x); // X + WRITE_COORD(vecSrc.y); // Y + WRITE_COORD(vecSrc.z); // Z + WRITE_BYTE( 8 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 96 ); // b + WRITE_BYTE( 10 ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap1.wav", 1, ATTN_NORM, 0, 100 ); +#endif +} + +//========================================================= +// PainSound +//========================================================= +void CMISlave :: PainSound( void ) +{ + if (RANDOM_LONG( 0, 2 ) == 0) + { + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } +} + +//========================================================= +// DieSound +//========================================================= + +void CMISlave :: DeathSound( void ) +{ + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pDeathSounds[ RANDOM_LONG(0,ARRAYSIZE(pDeathSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); +} + + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. +//========================================================= +int CMISlave :: ISoundMask ( void) +{ + return 0; +} + + +void CMISlave::Killed( entvars_t *pevAttacker, int iGib ) +{ + ClearBeams( ); + CMBaseMonster::Killed( pevAttacker, iGib ); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMISlave :: SetYawSpeed ( void ) +{ + int ys; + + switch ( m_Activity ) + { + case ACT_WALK: + ys = 50; + break; + case ACT_RUN: + ys = 70; + break; + case ACT_IDLE: + ys = 50; + break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMISlave :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame ); + switch( pEvent->event ) + { + case ISLAVE_AE_CLAW: + { + // SOUND HERE! + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClaw, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.z = -18; + pHurt->v.punchangle.x = 5; + } + // Play a random attack hit sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + else + { + // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + } + break; + + case ISLAVE_AE_CLAWRAKE: + { + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.slaveDmgClawrake, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.z = -18; + pHurt->v.punchangle.x = 5; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + else + { + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, m_voicePitch ); + } + } + break; + + case ISLAVE_AE_ZAP_POWERUP: + { + UTIL_MakeAimVectors( pev->angles ); + + if (m_iBeams == 0) + { + Vector vecSrc = pev->origin + gpGlobals->v_forward * 2; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(vecSrc.x); // X + WRITE_COORD(vecSrc.y); // Y + WRITE_COORD(vecSrc.z); // Z + WRITE_BYTE( 12 ); // radius * 0.1 + WRITE_BYTE( 255 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 96 ); // b + WRITE_BYTE( 20 / pev->framerate ); // time * 10 + WRITE_BYTE( 0 ); // decay * 0.1 + MESSAGE_END( ); + + } +/*jlb + if (m_hDead != NULL) + { + WackBeam( -1, m_hDead ); + WackBeam( 1, m_hDead ); + } + else +jlb*/ + { + ArmBeam( -1 ); + ArmBeam( 1 ); + BeamGlow( ); + } + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "debris/zap4.wav", 1, ATTN_NORM, 0, 100 + m_iBeams * 10 ); + pev->skin = m_iBeams / 2; + } + break; + + case ISLAVE_AE_ZAP_SHOOT: + { + ClearBeams( ); + +/*jlb + if (m_hDead != NULL) + { + Vector vecDest = m_hDead->v.origin + Vector( 0, 0, 38 ); + TraceResult trace; + UTIL_TraceHull( vecDest, vecDest, dont_ignore_monsters, human_hull, m_hDead, &trace ); + + if ( !trace.fStartSolid ) + { + CMBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->pev->origin, m_hDead->pev->angles ); + CMBaseMonster *pNewMonster = pNew->MyMonsterPointer( ); + pNew->pev->spawnflags |= 1; + WackBeam( -1, pNew ); + WackBeam( 1, pNew ); + UTIL_Remove( m_hDead ); + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); + break; + } + } +jlb*/ + ClearMultiDamage(); + + UTIL_MakeAimVectors( pev->angles ); + + ZapBeam( -1 ); + ZapBeam( 1 ); + + EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "hassault/hw_shoot1.wav", 1, ATTN_NORM, 0, RANDOM_LONG( 130, 160 ) ); + // STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); + + ApplyMultiDamage(pev, pev); + + m_flNextAttack = gpGlobals->time + RANDOM_FLOAT( 0.5, 4.0 ); + } + break; + + case ISLAVE_AE_ZAP_DONE: + { + ClearBeams( ); + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// CheckRangeAttack1 - normal beam attack +//========================================================= +BOOL CMISlave :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if (m_flNextAttack > gpGlobals->time) + { + return FALSE; + } + + return CMBaseMonster::CheckRangeAttack1( flDot, flDist ); +} + +//========================================================= +// CheckRangeAttack2 - check bravery and try to resurect dead comrades +//========================================================= +BOOL CMISlave :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + return FALSE; + + if (m_flNextAttack > gpGlobals->time) + { + return FALSE; + } + +//jlb m_hDead = NULL; + m_iBravery = 0; + + edict_t *pEntity = NULL; + while ((pEntity = UTIL_FindEntityByClassname( pEntity, "monster_alien_slave" )) != NULL) + { + TraceResult tr; + + UTIL_TraceLine( EyePosition( ), UTIL_EyePosition(pEntity), ignore_monsters, ENT(pev), &tr ); + if (tr.flFraction == 1.0 || tr.pHit == pEntity) + { + if (pEntity->v.deadflag == DEAD_DEAD) + { + float d = (pev->origin - pEntity->v.origin).Length(); + if (d < flDist) + { +//jlb m_hDead = pEntity; + flDist = d; + } + m_iBravery--; + } + else + { + m_iBravery++; + } + } + } +//jlb if (m_hDead != NULL) +//jlb return TRUE; +//jlb else + return FALSE; +} + + +//========================================================= +// StartTask +//========================================================= +void CMISlave :: StartTask ( Task_t *pTask ) +{ + ClearBeams( ); + + CMBaseMonster :: StartTask ( pTask ); +} + + +//========================================================= +// Spawn +//========================================================= +void CMISlave :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/islave.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.slaveHealth; + pev->view_ofs = Vector ( 0, 0, 64 );// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5; + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_RANGE_ATTACK2 | bits_CAP_DOORS_GROUP; + + m_voicePitch = RANDOM_LONG( 85, 110 ); + + for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) + m_pBeam[i] = NULL; + + m_iBravery = 0; + m_flNextAttack = 0.0f; + + MonsterInit(); + pev->classname = MAKE_STRING( "monster_alien_slave" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Alien Slave" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMISlave :: Precache() +{ + int i; + + PRECACHE_MODEL("models/islave.mdl"); + PRECACHE_MODEL("sprites/lgtning.spr"); + PRECACHE_SOUND("debris/zap1.wav"); + PRECACHE_SOUND("debris/zap4.wav"); + PRECACHE_SOUND("weapons/electro4.wav"); + PRECACHE_SOUND("hassault/hw_shoot1.wav"); + PRECACHE_SOUND("zombie/zo_pain2.wav"); + PRECACHE_SOUND("headcrab/hc_headbite.wav"); + PRECACHE_SOUND("weapons/cbar_miss1.wav"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pDeathSounds ); i++ ) + PRECACHE_SOUND((char *)pDeathSounds[i]); +} + + +//========================================================= +// TakeDamage - get provoked when injured +//========================================================= + +int CMISlave :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // don't slash one of your own + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pevAttacker)); + if (pMonster != NULL) + { + if ((bitsDamageType & DMG_SLASH) && pevAttacker && IRelationship( pMonster ) < R_DL) + return 0; + } + + m_afMemory |= bits_MEMORY_PROVOKED; + return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + + +void CMISlave::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if (bitsDamageType & DMG_SHOCK) + return; + + CMBaseMonster::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType ); +} + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + + +// primary range attack +Task_t tlSlaveAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, +}; + +Schedule_t slSlaveAttack1[] = +{ + { + tlSlaveAttack1, + ARRAYSIZE ( tlSlaveAttack1 ), + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_HEAR_SOUND | + bits_COND_HEAVY_DAMAGE, + 0, + "Slave Range Attack1" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMISlave ) +{ + slSlaveAttack1, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMISlave, CMBaseMonster ); + + +//========================================================= +//========================================================= +Schedule_t *CMISlave :: GetSchedule( void ) +{ + ClearBeams( ); + +/* + if (pev->spawnflags) + { + pev->spawnflags = 0; + return GetScheduleOfType( SCHED_RELOAD ); + } +*/ + + switch (m_MonsterState) + { + case MONSTERSTATE_COMBAT: +// dead enemy + if ( HasConditions( bits_COND_ENEMY_DEAD ) ) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster :: GetSchedule(); + } + + if (pev->health < 20 || m_iBravery < 0) + { + if (!HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) + { + m_failSchedule = SCHED_CHASE_ENEMY; + if (HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) + { + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + if ( HasConditions ( bits_COND_SEE_ENEMY ) && HasConditions ( bits_COND_ENEMY_FACING_ME ) ) + { + // ALERT( at_console, "exposed\n"); + return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ENEMY ); + } + } + } + break; + } + return CMBaseMonster::GetSchedule( ); +} + + +Schedule_t *CMISlave :: GetScheduleOfType ( int Type ) +{ + switch ( Type ) + { + case SCHED_FAIL: + if (HasConditions( bits_COND_CAN_MELEE_ATTACK1 )) + { + return CMBaseMonster :: GetScheduleOfType( SCHED_MELEE_ATTACK1 ); ; + } + break; + case SCHED_RANGE_ATTACK1: + return slSlaveAttack1; + case SCHED_RANGE_ATTACK2: + return slSlaveAttack1; + } + return CMBaseMonster :: GetScheduleOfType( Type ); +} + + +//========================================================= +// ArmBeam - small beam from arm to nearby geometry +//========================================================= + +void CMISlave :: ArmBeam( int side ) +{ + TraceResult tr; + float flDist = 1.0; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + UTIL_MakeAimVectors( pev->angles ); + Vector vecSrc = pev->origin + gpGlobals->v_up * 36 + gpGlobals->v_right * side * 16 + gpGlobals->v_forward * 32; + + for (int i = 0; i < 3; i++) + { + Vector vecAim = gpGlobals->v_right * side * RANDOM_FLOAT( 0, 1 ) + gpGlobals->v_up * RANDOM_FLOAT( -1, 1 ); + TraceResult tr1; + UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, dont_ignore_monsters, ENT( pev ), &tr1); + if (flDist > tr1.flFraction) + { + tr = tr1; + flDist = tr.flFraction; + } + } + + // Couldn't find anything close enough + if ( flDist == 1.0 ) + return; + + DecalGunshot( &tr, BULLET_PLAYER_CROWBAR ); + + m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + // m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetColor( 96, 128, 16 ); + m_pBeam[m_iBeams]->SetBrightness( 64 ); + m_pBeam[m_iBeams]->SetNoise( 80 ); + m_iBeams++; +} + + +//========================================================= +// BeamGlow - brighten all beams +//========================================================= +void CMISlave :: BeamGlow( ) +{ + int b = m_iBeams * 32; + if (b > 255) + b = 255; + + for (int i = 0; i < m_iBeams; i++) + { + if (m_pBeam[i]->GetBrightness() != 255) + { + m_pBeam[i]->SetBrightness( b ); + } + } +} + + +//========================================================= +// WackBeam - regenerate dead colleagues +//========================================================= +void CMISlave :: WackBeam( int side, edict_t *pEntity ) +{ + Vector vecDest; + float flDist = 1.0; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + if (pEntity == NULL) + return; + + m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( UTIL_Center(pEntity), entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetBrightness( 255 ); + m_pBeam[m_iBeams]->SetNoise( 80 ); + m_iBeams++; +} + +//========================================================= +// ZapBeam - heavy damage directly forward +//========================================================= +void CMISlave :: ZapBeam( int side ) +{ + Vector vecSrc, vecAim; + TraceResult tr; + edict_t *pEntity; + + if (m_iBeams >= ISLAVE_MAX_BEAMS) + return; + + vecSrc = pev->origin + gpGlobals->v_up * 36; + vecAim = ShootAtEnemy( vecSrc ); + float deflection = 0.01; + vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection ); + UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr); + + m_pBeam[m_iBeams] = CMBeam::BeamCreate( "sprites/lgtning.spr", 50 ); + if (!m_pBeam[m_iBeams]) + return; + + m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex( ) ); + m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 ); + m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); + m_pBeam[m_iBeams]->SetBrightness( 255 ); + m_pBeam[m_iBeams]->SetNoise( 20 ); + m_iBeams++; + + pEntity = tr.pHit; + if (pEntity != NULL && pEntity->v.takedamage) + { + if (UTIL_IsPlayer(pEntity)) + UTIL_TraceAttack( pEntity, pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); + else if (pEntity->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TraceAttack( pev, gSkillData.slaveDmgZap, vecAim, &tr, DMG_SHOCK ); + } + } + + UTIL_EmitAmbientSound( ENT(pev), tr.vecEndPos, "weapons/electro4.wav", 0.5, ATTN_NORM, 0, RANDOM_LONG( 140, 160 ) ); +} + + +//========================================================= +// ClearBeams - remove all beams +//========================================================= +void CMISlave :: ClearBeams( ) +{ + for (int i = 0; i < ISLAVE_MAX_BEAMS; i++) + { + if (m_pBeam[i]) + { + UTIL_Remove( m_pBeam[i]->edict() ); + m_pBeam[i] = NULL; + } + } + m_iBeams = 0; + pev->skin = 0; + + STOP_SOUND( ENT(pev), CHAN_WEAPON, "debris/zap4.wav" ); +} diff --git a/src/dlls/massn.cpp b/src/dlls/massn.cpp index f57c2d6..99a9965 100644 --- a/src/dlls/massn.cpp +++ b/src/dlls/massn.cpp @@ -1,311 +1,311 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Black Ops - Male Assassin -//========================================================= - -#include "extdll.h" -#include "plane.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "weapons.h" -#include "cmtalkmonster.h" -#include "effects.h" -#include "customentity.h" - -//========================================================= -// monster-specific DEFINE's -//========================================================= -#define MASSN_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! - -// Weapon flags -#define MASSN_9MMAR (1 << 0) -#define MASSN_HANDGRENADE (1 << 1) -#define MASSN_GRENADELAUNCHER (1 << 2) -#define MASSN_SNIPERRIFLE (1 << 3) - -// Body groups. -#define HEAD_GROUP 1 -#define GUN_GROUP 2 - -// Head values -#define HEAD_WHITE 0 -#define HEAD_BLACK 1 -#define HEAD_GOGGLES 2 - -// Gun values -#define GUN_MP5 0 -#define GUN_SNIPERRIFLE 1 -#define GUN_NONE 2 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define MASSN_AE_KICK ( 3 ) -#define MASSN_AE_BURST1 ( 4 ) -#define MASSN_AE_CAUGHT_ENEMY ( 10 ) // grunt established sight with an enemy (player only) that had previously eluded the squad. -#define MASSN_AE_DROP_GUN ( 11 ) // grunt (probably dead) is dropping his mp5. - -//========================================================= -// Override a few behaviours to make this grunt silent -//========================================================= -BOOL CMMassn::FOkToSpeak(void) -{ - return FALSE; -} - -void CMMassn::IdleSound(void) -{ -} - -void CMMassn::PainSound(void) -{ -} - -void CMMassn::DeathSound(void) -{ -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMMassn::Classify(void) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_HUMAN_MILITARY; -} - - -//========================================================= -// Shoot -//========================================================= -void CMMassn::Sniperrifle(void) -{ - if (m_hEnemy == 0) - { - return; - } - - Vector vecShootOrigin = GetGunPosition(); - Vector vecShootDir = ShootAtEnemy(vecShootOrigin); - - UTIL_MakeVectors(pev->angles); - - Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40, 90) + gpGlobals->v_up * RANDOM_FLOAT(75, 200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); - EjectBrass(vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_1DEGREES, 2048, BULLET_MONSTER_762, 0); // shoot +-7.5 degrees - - pev->effects |= EF_MUZZLEFLASH; - - // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. - // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue - //m_cAmmoLoaded--;// take away a bullet! - - Vector angDir = UTIL_VecToAngles(vecShootDir); - SetBlending(0, angDir.x); -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - Vector vecShootDir; - Vector vecShootOrigin; - - switch (pEvent->event) - { - case MASSN_AE_DROP_GUN: - { - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment(0, vecGunPos, vecGunAngles); - - // switch to body group with no gun. - SetBodygroup(GUN_GROUP, GUN_NONE); - } - break; - - - case MASSN_AE_BURST1: - { - if (FBitSet(pev->weapons, MASSN_9MMAR)) - { - Shoot(); - - // the first round of the three round burst plays the sound and puts a sound in the world sound list. - if (RANDOM_LONG(0, 1)) - { - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM); - } - else - { - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM); - } - } - else if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE)) - { - Sniperrifle(); - - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sniper_fire.wav", 1, ATTN_NORM); - } - } - break; - - case MASSN_AE_KICK: - { - edict_t *pHurt = Kick(); - - if (pHurt) - { - // SOUND HERE! - UTIL_MakeVectors(pev->angles); - pHurt->v.punchangle.x = 15; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; - if (UTIL_IsPlayer(pHurt)) - UTIL_TakeDamage( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB ); - else if (pHurt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - pMonster->TakeDamage( pev, pev, gSkillData.massnDmgKick, DMG_CLUB ); - } - } - } - break; - - case MASSN_AE_CAUGHT_ENEMY: - break; - - default: - CMHGrunt::HandleAnimEvent(pEvent); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMMassn::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/massn.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->effects = 0; - pev->health = gSkillData.massnHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_flNextGrenadeCheck = gpGlobals->time + 1; - m_flNextPainTime = gpGlobals->time; - m_iSentence = -1; - - //m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - - //m_fEnemyEluded = FALSE; - m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. - - m_HackedGunPos = Vector(0, 0, 55); - - if (pev->weapons == 0) - { - // weapons not specified, randomize - switch ( RANDOM_LONG( 0, 2 ) ) - { - case 0: - pev->weapons = MASSN_9MMAR | MASSN_HANDGRENADE; - break; - case 1: - pev->weapons = MASSN_9MMAR | MASSN_GRENADELAUNCHER; - break; - case 2: - pev->weapons = MASSN_SNIPERRIFLE; - break; - } - } - - if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE)) - { - SetBodygroup(GUN_GROUP, GUN_SNIPERRIFLE); - m_cClipSize = 5; - } - else - { - m_cClipSize = MASSN_CLIP_SIZE; - } - m_cAmmoLoaded = m_cClipSize; - - if (RANDOM_LONG(0, 99) < 80) - pev->skin = 0; // light skin - else - pev->skin = 1; // dark skin - - CMTalkMonster::g_talkWaitTime = 0; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_male_assassin" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Male Assassin" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMMassn::Precache() -{ - PRECACHE_MODEL("models/massn.mdl"); - - PRECACHE_SOUND("hgrunt/gr_mgun1.wav"); - PRECACHE_SOUND("hgrunt/gr_mgun2.wav"); - - PRECACHE_SOUND("hgrunt/gr_reload1.wav"); - - PRECACHE_SOUND("weapons/glauncher.wav"); - - PRECACHE_SOUND("weapons/sniper_bolt1.wav"); - PRECACHE_SOUND("weapons/sniper_fire.wav"); - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - // get voice pitch - if (RANDOM_LONG(0, 1)) - m_voicePitch = 109 + RANDOM_LONG(0, 7); - else - m_voicePitch = 100; - - m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Black Ops - Male Assassin +//========================================================= + +#include "extdll.h" +#include "plane.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "weapons.h" +#include "cmtalkmonster.h" +#include "effects.h" +#include "customentity.h" + +//========================================================= +// monster-specific DEFINE's +//========================================================= +#define MASSN_CLIP_SIZE 36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! + +// Weapon flags +#define MASSN_9MMAR (1 << 0) +#define MASSN_HANDGRENADE (1 << 1) +#define MASSN_GRENADELAUNCHER (1 << 2) +#define MASSN_SNIPERRIFLE (1 << 3) + +// Body groups. +#define HEAD_GROUP 1 +#define GUN_GROUP 2 + +// Head values +#define HEAD_WHITE 0 +#define HEAD_BLACK 1 +#define HEAD_GOGGLES 2 + +// Gun values +#define GUN_MP5 0 +#define GUN_SNIPERRIFLE 1 +#define GUN_NONE 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define MASSN_AE_KICK ( 3 ) +#define MASSN_AE_BURST1 ( 4 ) +#define MASSN_AE_CAUGHT_ENEMY ( 10 ) // grunt established sight with an enemy (player only) that had previously eluded the squad. +#define MASSN_AE_DROP_GUN ( 11 ) // grunt (probably dead) is dropping his mp5. + +//========================================================= +// Override a few behaviours to make this grunt silent +//========================================================= +BOOL CMMassn::FOkToSpeak(void) +{ + return FALSE; +} + +void CMMassn::IdleSound(void) +{ +} + +void CMMassn::PainSound(void) +{ +} + +void CMMassn::DeathSound(void) +{ +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMMassn::Classify(void) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_HUMAN_MILITARY; +} + + +//========================================================= +// Shoot +//========================================================= +void CMMassn::Sniperrifle(void) +{ + if (m_hEnemy == 0) + { + return; + } + + Vector vecShootOrigin = GetGunPosition(); + Vector vecShootDir = ShootAtEnemy(vecShootOrigin); + + UTIL_MakeVectors(pev->angles); + + Vector vecShellVelocity = gpGlobals->v_right * RANDOM_FLOAT(40, 90) + gpGlobals->v_up * RANDOM_FLOAT(75, 200) + gpGlobals->v_forward * RANDOM_FLOAT(-40, 40); + EjectBrass(vecShootOrigin - vecShootDir * 24, vecShellVelocity, pev->angles.y, m_iBrassShell, TE_BOUNCE_SHELL); + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_1DEGREES, 2048, BULLET_MONSTER_762, 0); // shoot +-7.5 degrees + + pev->effects |= EF_MUZZLEFLASH; + + // BUG - For some reason that still eludes me, grunts are completely unable to reload their weapons. + // As a temporary fix, give them infinite ammo. It will look bad I know... I gotta find a solution. -Giegue + //m_cAmmoLoaded--;// take away a bullet! + + Vector angDir = UTIL_VecToAngles(vecShootDir); + SetBlending(0, angDir.x); +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMMassn::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + Vector vecShootDir; + Vector vecShootOrigin; + + switch (pEvent->event) + { + case MASSN_AE_DROP_GUN: + { + Vector vecGunPos; + Vector vecGunAngles; + + GetAttachment(0, vecGunPos, vecGunAngles); + + // switch to body group with no gun. + SetBodygroup(GUN_GROUP, GUN_NONE); + } + break; + + + case MASSN_AE_BURST1: + { + if (FBitSet(pev->weapons, MASSN_9MMAR)) + { + Shoot(); + + // the first round of the three round burst plays the sound and puts a sound in the world sound list. + if (RANDOM_LONG(0, 1)) + { + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun1.wav", 1, ATTN_NORM); + } + else + { + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "hgrunt/gr_mgun2.wav", 1, ATTN_NORM); + } + } + else if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE)) + { + Sniperrifle(); + + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/sniper_fire.wav", 1, ATTN_NORM); + } + } + break; + + case MASSN_AE_KICK: + { + edict_t *pHurt = Kick(); + + if (pHurt) + { + // SOUND HERE! + UTIL_MakeVectors(pev->angles); + pHurt->v.punchangle.x = 15; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; + if (UTIL_IsPlayer(pHurt)) + UTIL_TakeDamage( pHurt, pev, pev, gSkillData.massnDmgKick, DMG_CLUB ); + else if (pHurt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + pMonster->TakeDamage( pev, pev, gSkillData.massnDmgKick, DMG_CLUB ); + } + } + } + break; + + case MASSN_AE_CAUGHT_ENEMY: + break; + + default: + CMHGrunt::HandleAnimEvent(pEvent); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMMassn::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/massn.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->effects = 0; + pev->health = gSkillData.massnHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_flNextGrenadeCheck = gpGlobals->time + 1; + m_flNextPainTime = gpGlobals->time; + m_iSentence = -1; + + //m_afCapability = bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + //m_fEnemyEluded = FALSE; + m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. + + m_HackedGunPos = Vector(0, 0, 55); + + if (pev->weapons == 0) + { + // weapons not specified, randomize + switch ( RANDOM_LONG( 0, 2 ) ) + { + case 0: + pev->weapons = MASSN_9MMAR | MASSN_HANDGRENADE; + break; + case 1: + pev->weapons = MASSN_9MMAR | MASSN_GRENADELAUNCHER; + break; + case 2: + pev->weapons = MASSN_SNIPERRIFLE; + break; + } + } + + if (FBitSet(pev->weapons, MASSN_SNIPERRIFLE)) + { + SetBodygroup(GUN_GROUP, GUN_SNIPERRIFLE); + m_cClipSize = 5; + } + else + { + m_cClipSize = MASSN_CLIP_SIZE; + } + m_cAmmoLoaded = m_cClipSize; + + if (RANDOM_LONG(0, 99) < 80) + pev->skin = 0; // light skin + else + pev->skin = 1; // dark skin + + CMTalkMonster::g_talkWaitTime = 0; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_male_assassin" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Male Assassin" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMMassn::Precache() +{ + PRECACHE_MODEL("models/massn.mdl"); + + PRECACHE_SOUND("hgrunt/gr_mgun1.wav"); + PRECACHE_SOUND("hgrunt/gr_mgun2.wav"); + + PRECACHE_SOUND("hgrunt/gr_reload1.wav"); + + PRECACHE_SOUND("weapons/glauncher.wav"); + + PRECACHE_SOUND("weapons/sniper_bolt1.wav"); + PRECACHE_SOUND("weapons/sniper_fire.wav"); + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + // get voice pitch + if (RANDOM_LONG(0, 1)) + m_voicePitch = 109 + RANDOM_LONG(0, 7); + else + m_voicePitch = 100; + + m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell +} diff --git a/src/dlls/monster_api.cpp b/src/dlls/monster_api.cpp index 1177bc0..d084ec1 100644 --- a/src/dlls/monster_api.cpp +++ b/src/dlls/monster_api.cpp @@ -1,163 +1,163 @@ -// -// Monster Mod is a modification based on Botman's original "Monster" plugin. -// The "forgotten" modification was made by Rick90. -// This is an attempt to recreate the plugin so it does not become lost again. -// -// Recreated by Giegue. -// -// monster_api.cpp -// - -/* - * This 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 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 code; 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 "extdll.h" -#include "meta_api.h" -#include "sdk_util.h" // UTIL_LogPrintf, etc - -// Must provide at least one of these.. -static META_FUNCTIONS gMetaFunctionTable = -{ - NULL, // pfnGetEntityAPI HL SDK; called before game DLL - NULL, // pfnGetEntityAPI_Post META; called after game DLL - GetEntityAPI2, // pfnGetEntityAPI2 HL SDK2; called before game DLL - GetEntityAPI2_Post, // pfnGetEntityAPI2_Post META; called after game DLL - NULL, // pfnGetNewDLLFunctions HL SDK2; called before game DLL - NULL, // pfnGetNewDLLFunctions_Post META; called after game DLL - GetEngineFunctions, // pfnGetEngineFunctions META; called before HL engine - GetEngineFunctions_Post, // pfnGetEngineFunctions_Post META; called after HL engine -}; - -// Description of plugin -plugin_info_t Plugin_info = { - META_INTERFACE_VERSION, // interface version - "MonsterMod", // name - "2.0", // version - "03/06/2020", // date in DD/MM/YYYY format - "botman, Rick90, Giegue", // original authors + recreation by... - "https://github.com/JulianR0/monstermod-redo", // url - "MONSTER", // logtag - PT_CHANGELEVEL, // (when) loadable - PT_CHANGELEVEL, // (when) unloadable -}; - -char *VNAME=Plugin_info.name; -char *VVERSION=Plugin_info.version; -char *VDATE=Plugin_info.date; -char *VAUTHOR=Plugin_info.author; -char *VURL=Plugin_info.url; -char *VLOGTAG=Plugin_info.logtag; -char *COMPILE_TIME=__DATE__ ", " __TIME__; - -// Global vars from metamod: -meta_globals_t *gpMetaGlobals; // metamod globals -gamedll_funcs_t *gpGamedllFuncs; // gameDLL function tables -mutil_funcs_t *gpMetaUtilFuncs; // metamod utility functions - -// CVars -cvar_t init_dllapi_log = {"monster_log", "0", FCVAR_EXTDLL, 0, NULL}; -cvar_t *dllapi_log = NULL; -cvar_t init_monster_spawn = {"monster_spawn", "1", FCVAR_EXTDLL, 0, NULL}; -cvar_t *monster_spawn = NULL; -cvar_t init_monster_show_deaths = {"monster_show_deaths", "1", FCVAR_EXTDLL, 0, NULL}; -cvar_t *monster_show_deaths = NULL; -cvar_t init_monster_show_info = {"monster_show_info", "1", FCVAR_EXTDLL, 0, NULL}; -cvar_t *monster_show_info = NULL; - - -// Metamod requesting info about this plugin: -// ifvers (given) interface_version metamod is using -// pPlugInfo (requested) struct with info about plugin -// pMetaUtilFuncs (given) table of utility functions provided by metamod -C_DLLEXPORT int Meta_Query(char *ifvers, plugin_info_t **pPlugInfo, - mutil_funcs_t *pMetaUtilFuncs) -{ - if(ifvers); // to satisfy gcc -Wunused - // Give metamod our plugin_info struct - *pPlugInfo=&Plugin_info; - // Get metamod utility function table. - gpMetaUtilFuncs=pMetaUtilFuncs; - return(TRUE); -} - -// Metamod attaching plugin to the server. -// now (given) current phase, ie during map, during changelevel, or at startup -// pFunctionTable (requested) table of function tables this plugin catches -// pMGlobals (given) global vars from metamod -// pGamedllFuncs (given) copy of function tables from game dll -C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) -{ - if(now); // to satisfy gcc -Wunused - if(!pMGlobals) - { - LOG_ERROR(PLID, "Meta_Attach called with null pMGlobals"); - return(FALSE); - } - gpMetaGlobals=pMGlobals; - if(!pFunctionTable) - { - LOG_ERROR(PLID, "Meta_Attach called with null pFunctionTable"); - return(FALSE); - } - memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS)); - gpGamedllFuncs=pGamedllFuncs; - - LOG_MESSAGE(PLID, "%s %s, %s", VNAME, VVERSION, VDATE); - LOG_MESSAGE(PLID, "by %s", VAUTHOR); - LOG_MESSAGE(PLID, "%s", VURL); - LOG_MESSAGE(PLID, "compiled: %s CDT", COMPILE_TIME); - - LOG_CONSOLE(PLID, "[%s] %s v%s, %s", VLOGTAG, VNAME, VVERSION, VDATE); - LOG_CONSOLE(PLID, "[%s] by %s", VLOGTAG, VAUTHOR); - - CVAR_REGISTER(&init_dllapi_log); - dllapi_log = CVAR_GET_POINTER("monster_log"); - - CVAR_REGISTER(&init_monster_spawn); - monster_spawn = CVAR_GET_POINTER("monster_spawn"); - - CVAR_REGISTER(&init_monster_show_deaths); - monster_show_deaths = CVAR_GET_POINTER("monster_show_deaths"); - - CVAR_REGISTER(&init_monster_show_info); - monster_show_info = CVAR_GET_POINTER("monster_show_info"); - - return(TRUE); -} - - -extern void monster_unload(void); - -// Metamod detaching plugin from the server. -// now (given) current phase, ie during map, etc -// reason (given) why detaching (refresh, console unload, forced unload, etc) -C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { - // remove all the monsters currently in the level... - monster_unload(); - return(TRUE); -} - +// +// Monster Mod is a modification based on Botman's original "Monster" plugin. +// The "forgotten" modification was made by Rick90. +// This is an attempt to recreate the plugin so it does not become lost again. +// +// Recreated by Giegue. +// +// monster_api.cpp +// + +/* + * This 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 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 code; 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 "extdll.h" +#include "meta_api.h" +#include "sdk_util.h" // UTIL_LogPrintf, etc + +// Must provide at least one of these.. +static META_FUNCTIONS gMetaFunctionTable = +{ + NULL, // pfnGetEntityAPI HL SDK; called before game DLL + NULL, // pfnGetEntityAPI_Post META; called after game DLL + GetEntityAPI2, // pfnGetEntityAPI2 HL SDK2; called before game DLL + GetEntityAPI2_Post, // pfnGetEntityAPI2_Post META; called after game DLL + NULL, // pfnGetNewDLLFunctions HL SDK2; called before game DLL + NULL, // pfnGetNewDLLFunctions_Post META; called after game DLL + GetEngineFunctions, // pfnGetEngineFunctions META; called before HL engine + GetEngineFunctions_Post, // pfnGetEngineFunctions_Post META; called after HL engine +}; + +// Description of plugin +plugin_info_t Plugin_info = { + META_INTERFACE_VERSION, // interface version + "MonsterMod", // name + "2.0", // version + "03/06/2020", // date in DD/MM/YYYY format + "botman, Rick90, Giegue", // original authors + recreation by... + "https://github.com/JulianR0/monstermod-redo", // url + "MONSTER", // logtag + PT_CHANGELEVEL, // (when) loadable + PT_CHANGELEVEL, // (when) unloadable +}; + +char *VNAME=Plugin_info.name; +char *VVERSION=Plugin_info.version; +char *VDATE=Plugin_info.date; +char *VAUTHOR=Plugin_info.author; +char *VURL=Plugin_info.url; +char *VLOGTAG=Plugin_info.logtag; +char *COMPILE_TIME=__DATE__ ", " __TIME__; + +// Global vars from metamod: +meta_globals_t *gpMetaGlobals; // metamod globals +gamedll_funcs_t *gpGamedllFuncs; // gameDLL function tables +mutil_funcs_t *gpMetaUtilFuncs; // metamod utility functions + +// CVars +cvar_t init_dllapi_log = {"monster_log", "0", FCVAR_EXTDLL, 0, NULL}; +cvar_t *dllapi_log = NULL; +cvar_t init_monster_spawn = {"monster_spawn", "1", FCVAR_EXTDLL, 0, NULL}; +cvar_t *monster_spawn = NULL; +cvar_t init_monster_show_deaths = {"monster_show_deaths", "1", FCVAR_EXTDLL, 0, NULL}; +cvar_t *monster_show_deaths = NULL; +cvar_t init_monster_show_info = {"monster_show_info", "1", FCVAR_EXTDLL, 0, NULL}; +cvar_t *monster_show_info = NULL; + + +// Metamod requesting info about this plugin: +// ifvers (given) interface_version metamod is using +// pPlugInfo (requested) struct with info about plugin +// pMetaUtilFuncs (given) table of utility functions provided by metamod +C_DLLEXPORT int Meta_Query(char *ifvers, plugin_info_t **pPlugInfo, + mutil_funcs_t *pMetaUtilFuncs) +{ + if(ifvers); // to satisfy gcc -Wunused + // Give metamod our plugin_info struct + *pPlugInfo=&Plugin_info; + // Get metamod utility function table. + gpMetaUtilFuncs=pMetaUtilFuncs; + return(TRUE); +} + +// Metamod attaching plugin to the server. +// now (given) current phase, ie during map, during changelevel, or at startup +// pFunctionTable (requested) table of function tables this plugin catches +// pMGlobals (given) global vars from metamod +// pGamedllFuncs (given) copy of function tables from game dll +C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) +{ + if(now); // to satisfy gcc -Wunused + if(!pMGlobals) + { + LOG_ERROR(PLID, "Meta_Attach called with null pMGlobals"); + return(FALSE); + } + gpMetaGlobals=pMGlobals; + if(!pFunctionTable) + { + LOG_ERROR(PLID, "Meta_Attach called with null pFunctionTable"); + return(FALSE); + } + memcpy(pFunctionTable, &gMetaFunctionTable, sizeof(META_FUNCTIONS)); + gpGamedllFuncs=pGamedllFuncs; + + LOG_MESSAGE(PLID, "%s %s, %s", VNAME, VVERSION, VDATE); + LOG_MESSAGE(PLID, "by %s", VAUTHOR); + LOG_MESSAGE(PLID, "%s", VURL); + LOG_MESSAGE(PLID, "compiled: %s CDT", COMPILE_TIME); + + LOG_CONSOLE(PLID, "[%s] %s v%s, %s", VLOGTAG, VNAME, VVERSION, VDATE); + LOG_CONSOLE(PLID, "[%s] by %s", VLOGTAG, VAUTHOR); + + CVAR_REGISTER(&init_dllapi_log); + dllapi_log = CVAR_GET_POINTER("monster_log"); + + CVAR_REGISTER(&init_monster_spawn); + monster_spawn = CVAR_GET_POINTER("monster_spawn"); + + CVAR_REGISTER(&init_monster_show_deaths); + monster_show_deaths = CVAR_GET_POINTER("monster_show_deaths"); + + CVAR_REGISTER(&init_monster_show_info); + monster_show_info = CVAR_GET_POINTER("monster_show_info"); + + return(TRUE); +} + + +extern void monster_unload(void); + +// Metamod detaching plugin from the server. +// now (given) current phase, ie during map, etc +// reason (given) why detaching (refresh, console unload, forced unload, etc) +C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { + // remove all the monsters currently in the level... + monster_unload(); + return(TRUE); +} + diff --git a/src/dlls/monster_config.cpp b/src/dlls/monster_config.cpp index a81ed94..c890049 100644 --- a/src/dlls/monster_config.cpp +++ b/src/dlls/monster_config.cpp @@ -1,433 +1,431 @@ -#include -#include -#include - -#ifndef __linux__ -#include -#else -#include -#endif - -#include "extdll.h" -#include "dllapi.h" -#include "meta_api.h" - -#include "monster_plugin.h" - -extern cvar_t *dllapi_log; - -extern monster_type_t monster_types[]; -extern int monster_spawn_count; -extern int node_spawn_count; - -bool get_input(FILE *fp, char *input) -{ - char line[1024]; - int len, pos; - - while (!feof(fp)) - { - if (fgets(line, 1023, fp) != NULL) - { - len = strlen(line); - - if (len == 0) - continue; // skip any null lines - - // remove any trailing newline, carriage return or whitespace... - while ((line[len-1] == '\n') || (line[len-1] == '\r') || isspace(line[len-1])) - { - line[len-1] = 0; - len--; - if (len == 0) - break; - } - - pos = 0; - - while (isspace(line[pos])) - pos++; // skip leading blanks - - if ((line[pos] == '/') && (line[pos+1] == '/')) - continue; // skip comment lines - - if (line[pos] == 0) - continue; // skip empty lines - - strcpy(input, &line[pos]); - return TRUE; - } - } - - return FALSE; // no input found -} - -void scan_monster_cfg(FILE *fp) -{ - // Let's make a full rework of this. -Giegue - char input[1024]; - float x, y, z; - bool badent, monster, node; - - while (get_input(fp, input)) - { - badent = monster = node = FALSE; - if (input[0] == '{') - { - // Proper start, initialize entity creation - // Temporary variables to store entity data - pKVD *data = (pKVD*)malloc(MAX_KEYVALUES*sizeof(*data)); // Entities should not have more than this many keyvalues - int kvd_index = 0; - while (get_input(fp, input)) - { - // It's the end of the entity structure? - if (input[0] == '}') - { - // Check if the classname of whatever we want to spawn is valid. - if (strcmp(data[kvd_index-1].key, "classname") == 0) - { - int mIndex; - for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++) - { - if (strcmp(data[kvd_index-1].value, monster_types[mIndex].name) == 0) - { - // Now that I think about it this looks slow and bad code >.> - - // A match is found. What is this? - if (strncmp(monster_types[mIndex].name, "monster", 7) == 0) - { - // It's a monster, add it to the list - if (monster_spawn_count == MAX_MONSTERS) - { - // Ouch! Not enough room. - LOG_MESSAGE(PLID, "ERROR: can't add monster, reached MAX_MONSTERS!"); // It will get spammy, sadly. - badent = TRUE; - } - else - { - monster_spawnpoint[monster_spawn_count].monster = mIndex; - monster_types[mIndex].need_to_precache = TRUE; - monster = TRUE; - } - } - else if (strcmp(monster_types[mIndex].name, "info_node") == 0) - { - // Normal node - if (node_spawn_count == MAX_NODES) - { - // The map can't be THAT big can it? - LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!"); // zee spam bOi - badent = TRUE; - } - else - node = TRUE; - } - else if (strcmp(monster_types[mIndex].name, "info_node_air") == 0) - { - // Aerial node - if (node_spawn_count == MAX_NODES) - { - // Ctrl+C --> Ctrl+V - LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!"); // poppo was here. - badent = TRUE; - } - else - { - node_spawnpoint[node_spawn_count].is_air_node = TRUE; - node = TRUE; - } - } - break; - } - } - if (monster_types[mIndex].name[0] == 0) - { - LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line - LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); - badent = TRUE; - } - } - else - { - // What are you doing?! - LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line - LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); - badent = TRUE; - } - - if (!badent) - { - // Make room for entity-specific keyvalues. - if (monster) - { - // The line is a little too long, no? - monster_spawnpoint[monster_spawn_count].keyvalue = (pKVD*)calloc(MAX_KEYVALUES, sizeof(*monster_spawnpoint[monster_spawn_count].keyvalue)); - } - - // Done. Let's process the keyvalues. - for (int i = 0; i < (kvd_index-1); i++) - { - // Any duplicate keyvalue is overwritten. - - if (strcmp(data[i].key, "origin") == 0) - { - if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3) - { - LOG_MESSAGE(PLID, "ERROR: invalid origin: %s", input); // print conflictive line - - // reset origin to g_vecZero - LOG_MESSAGE(PLID, "ERROR: entity will spawn at 0 0 0"); - x = y = z = 0; - } - if (monster) - { - monster_spawnpoint[monster_spawn_count].origin[0] = x; - monster_spawnpoint[monster_spawn_count].origin[1] = y; - monster_spawnpoint[monster_spawn_count].origin[2] = z; - } - else if (node) - { - node_spawnpoint[node_spawn_count].origin[0] = x; - node_spawnpoint[node_spawn_count].origin[1] = y; - node_spawnpoint[node_spawn_count].origin[2] = z; - } - } - else if (strcmp(data[i].key, "delay") == 0) - { - // ToDo: Remove this keyvalue. - // Monsters spawned directly should not respawn. - if (monster) - { - if (sscanf(data[i].value, "%f", &x) != 1) - { - LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line - - // default to 30 seconds - LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds"); - x = 30; - } - monster_spawnpoint[monster_spawn_count].delay = x; - } - } - else if (strcmp(data[i].key, "angles") == 0) - { - if (monster) - { - if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3) - { - LOG_MESSAGE(PLID, "ERROR: invalid angles: %s", input); // print conflictive line - - // reset angles to g_vecZero - LOG_MESSAGE(PLID, "ERROR: entity angles will be set to 0 0 0"); - x = y = z = 0; - } - monster_spawnpoint[monster_spawn_count].angles[0] = x; - monster_spawnpoint[monster_spawn_count].angles[1] = y; - monster_spawnpoint[monster_spawn_count].angles[2] = z; - } - } - else if (strcmp(data[i].key, "spawnflags") == 0) - { - if (monster) - { - if (sscanf(data[i].value, "%f", &x) != 1) - { - LOG_MESSAGE(PLID, "ERROR: invalid spawnflags: %s", input); // print conflictive line - - // default to no spawnflags - LOG_MESSAGE(PLID, "ERROR: entity spawnflags will be set to none (0)"); - x = 0; - } - monster_spawnpoint[monster_spawn_count].spawnflags = x; - } - } - else - { - // We do not know this keyvalue, but an specific entity might use it. - // Save it for later - if (monster) - { - strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key); - strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value); - } - } - } - - if (monster) - { - // Init monster - monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away - monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE; - monster_spawn_count++; - } - else if (node) - { - // Increase node count - node_spawn_count++; - } - - // Log on? Print all the entities that were added - if (dllapi_log->value) - { - // Classname only, or we will flood the server! - // No, I'm not making this idiotproof. Classname should be the last KVD entry on an entity! - LOG_CONSOLE(PLID, "[DEBUG] Added entity: %s", data[kvd_index-1].value); - } - } - - free( data ); - break; - } - - // Bruteforce to remove quotes - char parse[66] = {0}; - int skip = 0; - for (int i = 0; i < strlen(input); i++) - { - if (input[i] == '"') - { - skip++; - continue; - } - parse[i-skip] = input[i]; - } - parse[strlen(parse)] = '\0'; - - // Copy all keyvalues to the tempvar - // Key - char *copy = strtok(parse, " "); - strcpy(data[kvd_index].key, copy); - - // Value - copy = strtok(NULL, " "); - strcpy(data[kvd_index].value, ""); - while (copy != NULL) - { - // If the value is a vector, append necessary whitespaces - strcat(data[kvd_index].value, copy); - copy = strtok(NULL, " "); - if (copy != NULL) - strcat(data[kvd_index].value, " "); - } - - // Next KVD - kvd_index++; - } - } - } -} - -void process_monster_cfg(void) -{ - char game_dir[256]; - char filename[256]; - FILE *fp = NULL; - bool status = FALSE; // no error - - monster_spawn_count = 0; - - // find the directory name of the currently running MOD... - (*g_engfuncs.pfnGetGameDir)(game_dir); - - strcpy(filename, game_dir); -#ifdef __linux__ - strcat(filename, "/maps/"); -#else - strcat(filename, "\\maps\\"); -#endif - strcat(filename, STRING(gpGlobals->mapname)); - strcat(filename, "_monster.cfg"); - - // check if the map specific filename exists... - if (access(filename, 0) == 0) - { - if (dllapi_log->value) - { - //META_CONS("[MONSTER] Processing config file=%s", filename); - LOG_MESSAGE(PLID, "Processing config file=%s", filename); - } - - if ((fp = fopen(filename, "r")) == NULL) - { - //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); - LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); - - return; - } - - scan_monster_cfg(fp); - - fclose(fp); - } - - return; -} - - -bool scan_monster_precache_cfg(FILE *fp) -{ - char input[1024]; - bool found; - - while (get_input(fp, input)) - { - found = FALSE; - - for (int index=0; monster_types[index].name[0]; index++) - { - if (strcmp(input, monster_types[index].name) == 0) - { - monster_types[index].need_to_precache = TRUE; - found = TRUE; - break; - } - } - - if (found == FALSE) - { - //META_CONS("[MONSTER] ERROR: invalid precache monster name: %s", input); - LOG_MESSAGE(PLID, "ERROR: invalid precache monster name: %s", input); - } - } - - return FALSE; -} - - -bool process_monster_precache_cfg(void) -{ - char game_dir[256]; - char filename[256]; - FILE *fp = NULL; - bool status = FALSE; // no error - - // find the directory name of the currently running MOD... - (*g_engfuncs.pfnGetGameDir)(game_dir); - - strcpy(filename, game_dir); - strcat(filename, "/monster_precache.cfg"); - - // check if the map specific filename exists... - if (access(filename, 0) == 0) - { - if (dllapi_log->value) - { - //META_CONS("[MONSTER] Processing config file=%s", filename); - LOG_MESSAGE(PLID, "Processing config file=%s", filename); - } - - if ((fp = fopen(filename, "r")) == NULL) - { - //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); - LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); - - return TRUE; // return bad status - } - - status = scan_monster_precache_cfg(fp); - - fclose(fp); - } - - return status; -} +#include +#include +#include + +#ifndef __linux__ +#include +#else +#include +#endif + +#include "extdll.h" +#include "dllapi.h" +#include "meta_api.h" + +#include "monster_plugin.h" + +extern cvar_t *dllapi_log; + +extern monster_type_t monster_types[]; +extern int monster_spawn_count; +extern int node_spawn_count; + +bool get_input(FILE *fp, char *input) +{ + char line[1024]; + int len, pos; + + while (!feof(fp)) + { + if (fgets(line, 1023, fp) != NULL) + { + len = strlen(line); + + if (len == 0) + continue; // skip any null lines + + // remove any trailing newline, carriage return or whitespace... + while ((line[len-1] == '\n') || (line[len-1] == '\r') || isspace(line[len-1])) + { + line[len-1] = 0; + len--; + if (len == 0) + break; + } + + pos = 0; + + while (isspace(line[pos])) + pos++; // skip leading blanks + + if ((line[pos] == '/') && (line[pos+1] == '/')) + continue; // skip comment lines + + if (line[pos] == 0) + continue; // skip empty lines + + strcpy(input, &line[pos]); + return TRUE; + } + } + + return FALSE; // no input found +} + +void scan_monster_cfg(FILE *fp) +{ + // Let's make a full rework of this. -Giegue + char input[1024]; + float x, y, z; + bool badent, monster, node; + + while (get_input(fp, input)) + { + badent = monster = node = FALSE; + if (input[0] == '{') + { + // Proper start, initialize entity creation + // Temporary variables to store entity data + pKVD *data = (pKVD*)malloc(MAX_KEYVALUES*sizeof(*data)); // Entities should not have more than this many keyvalues + int kvd_index = 0; + while (get_input(fp, input)) + { + // It's the end of the entity structure? + if (input[0] == '}') + { + // Check if the classname of whatever we want to spawn is valid. + if (strcmp(data[kvd_index-1].key, "classname") == 0) + { + int mIndex; + for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++) + { + if (strcmp(data[kvd_index-1].value, monster_types[mIndex].name) == 0) + { + // Now that I think about it this looks slow and bad code >.> + + // A match is found. What is this? + if (strncmp(monster_types[mIndex].name, "monster", 7) == 0) + { + // It's a monster, add it to the list + if (monster_spawn_count == MAX_MONSTERS) + { + // Ouch! Not enough room. + LOG_MESSAGE(PLID, "ERROR: can't add monster, reached MAX_MONSTERS!"); // It will get spammy, sadly. + badent = TRUE; + } + else + { + monster_spawnpoint[monster_spawn_count].monster = mIndex; + monster_types[mIndex].need_to_precache = TRUE; + monster = TRUE; + } + } + else if (strcmp(monster_types[mIndex].name, "info_node") == 0) + { + // Normal node + if (node_spawn_count == MAX_NODES) + { + // The map can't be THAT big can it? + LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!"); // zee spam bOi + badent = TRUE; + } + else + node = TRUE; + } + else if (strcmp(monster_types[mIndex].name, "info_node_air") == 0) + { + // Aerial node + if (node_spawn_count == MAX_NODES) + { + // Ctrl+C --> Ctrl+V + LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!"); // poppo was here. + badent = TRUE; + } + else + { + node_spawnpoint[node_spawn_count].is_air_node = TRUE; + node = TRUE; + } + } + break; + } + } + if (monster_types[mIndex].name[0] == 0) + { + LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line + LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); + badent = TRUE; + } + } + else + { + // What are you doing?! + LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line + LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); + badent = TRUE; + } + + if (!badent) + { + // Make room for entity-specific keyvalues. + if (monster) + { + // The line is a little too long, no? + monster_spawnpoint[monster_spawn_count].keyvalue = (pKVD*)calloc(MAX_KEYVALUES, sizeof(*monster_spawnpoint[monster_spawn_count].keyvalue)); + } + + // Done. Let's process the keyvalues. + for (int i = 0; i < (kvd_index-1); i++) + { + // Any duplicate keyvalue is overwritten. + + if (strcmp(data[i].key, "origin") == 0) + { + if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3) + { + LOG_MESSAGE(PLID, "ERROR: invalid origin: %s", input); // print conflictive line + + // reset origin to g_vecZero + LOG_MESSAGE(PLID, "ERROR: entity will spawn at 0 0 0"); + x = y = z = 0; + } + if (monster) + { + monster_spawnpoint[monster_spawn_count].origin[0] = x; + monster_spawnpoint[monster_spawn_count].origin[1] = y; + monster_spawnpoint[monster_spawn_count].origin[2] = z; + } + else if (node) + { + node_spawnpoint[node_spawn_count].origin[0] = x; + node_spawnpoint[node_spawn_count].origin[1] = y; + node_spawnpoint[node_spawn_count].origin[2] = z; + } + } + else if (strcmp(data[i].key, "delay") == 0) + { + // ToDo: Remove this keyvalue. + // Monsters spawned directly should not respawn. + if (monster) + { + if (sscanf(data[i].value, "%f", &x) != 1) + { + LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line + + // default to 30 seconds + LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds"); + x = 30; + } + monster_spawnpoint[monster_spawn_count].delay = x; + } + } + else if (strcmp(data[i].key, "angles") == 0) + { + if (monster) + { + if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3) + { + LOG_MESSAGE(PLID, "ERROR: invalid angles: %s", input); // print conflictive line + + // reset angles to g_vecZero + LOG_MESSAGE(PLID, "ERROR: entity angles will be set to 0 0 0"); + x = y = z = 0; + } + monster_spawnpoint[monster_spawn_count].angles[0] = x; + monster_spawnpoint[monster_spawn_count].angles[1] = y; + monster_spawnpoint[monster_spawn_count].angles[2] = z; + } + } + else if (strcmp(data[i].key, "spawnflags") == 0) + { + if (monster) + { + if (sscanf(data[i].value, "%f", &x) != 1) + { + LOG_MESSAGE(PLID, "ERROR: invalid spawnflags: %s", input); // print conflictive line + + // default to no spawnflags + LOG_MESSAGE(PLID, "ERROR: entity spawnflags will be set to none (0)"); + x = 0; + } + monster_spawnpoint[monster_spawn_count].spawnflags = x; + } + } + else + { + // We do not know this keyvalue, but an specific entity might use it. + // Save it for later + if (monster) + { + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key); + strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value); + } + } + } + + if (monster) + { + // Init monster + monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away + monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE; + monster_spawn_count++; + } + else if (node) + { + // Increase node count + node_spawn_count++; + } + + // Log on? Print all the entities that were added + if (dllapi_log->value) + { + // Classname only, or we will flood the server! + // No, I'm not making this idiotproof. Classname should be the last KVD entry on an entity! + LOG_CONSOLE(PLID, "[DEBUG] Added entity: %s", data[kvd_index-1].value); + } + } + + free( data ); + break; + } + + // Bruteforce to remove quotes + char parse[66] = {0}; + int skip = 0; + for (unsigned i = 0; i < strlen(input); i++) + { + if (input[i] == '"') + { + skip++; + continue; + } + parse[i-skip] = input[i]; + } + parse[strlen(parse)] = '\0'; + + // Copy all keyvalues to the tempvar + // Key + char *copy = strtok(parse, " "); + strcpy(data[kvd_index].key, copy); + + // Value + copy = strtok(NULL, " "); + strcpy(data[kvd_index].value, ""); + while (copy != NULL) + { + // If the value is a vector, append necessary whitespaces + strcat(data[kvd_index].value, copy); + copy = strtok(NULL, " "); + if (copy != NULL) + strcat(data[kvd_index].value, " "); + } + + // Next KVD + kvd_index++; + } + } + } +} + +bool process_monster_cfg(void) +{ + char game_dir[256]; + char filename[256]; + FILE *fp = NULL; + + monster_spawn_count = 0; + + // find the directory name of the currently running MOD... + (*g_engfuncs.pfnGetGameDir)(game_dir); + + strcpy(filename, game_dir); +#ifdef __linux__ + strcat(filename, "/maps/"); +#else + strcat(filename, "\\maps\\"); +#endif + strcat(filename, STRING(gpGlobals->mapname)); + strcat(filename, "_monster.cfg"); + + // check if the map specific filename exists... + if (access(filename, 0) == 0) + { + if (dllapi_log->value) + { + //META_CONS("[MONSTER] Processing config file=%s", filename); + LOG_MESSAGE(PLID, "Processing config file=%s", filename); + } + + if ((fp = fopen(filename, "r")) == NULL) + { + //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); + LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); + return TRUE; // error + } + + scan_monster_cfg(fp); + + fclose(fp); + } + + return FALSE; // all ok +} + + +bool scan_monster_precache_cfg(FILE *fp) +{ + char input[1024]; + bool found; + + while (get_input(fp, input)) + { + found = FALSE; + + for (int index=0; monster_types[index].name[0]; index++) + { + if (strcmp(input, monster_types[index].name) == 0) + { + monster_types[index].need_to_precache = TRUE; + found = TRUE; + break; + } + } + + if (found == FALSE) + { + //META_CONS("[MONSTER] ERROR: invalid precache monster name: %s", input); + LOG_MESSAGE(PLID, "ERROR: invalid precache monster name: %s", input); + } + } + + return FALSE; +} + + +bool process_monster_precache_cfg(void) +{ + char game_dir[256]; + char filename[256]; + FILE *fp = NULL; + bool status = FALSE; // no error + + // find the directory name of the currently running MOD... + (*g_engfuncs.pfnGetGameDir)(game_dir); + + strcpy(filename, game_dir); + strcat(filename, "/monster_precache.cfg"); + + // check if the map specific filename exists... + if (access(filename, 0) == 0) + { + if (dllapi_log->value) + { + //META_CONS("[MONSTER] Processing config file=%s", filename); + LOG_MESSAGE(PLID, "Processing config file=%s", filename); + } + + if ((fp = fopen(filename, "r")) == NULL) + { + //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); + LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); + + return TRUE; // return bad status + } + + status = scan_monster_precache_cfg(fp); + + fclose(fp); + } + + return status; +} diff --git a/src/dlls/monster_mm.def b/src/dlls/monster_mm.def index a8f0241..24643a9 100644 --- a/src/dlls/monster_mm.def +++ b/src/dlls/monster_mm.def @@ -1,5 +1,5 @@ -LIBRARY monster_mm -EXPORTS - GiveFnptrsToDll @1 -SECTIONS - .data READ WRITE +LIBRARY monster_mm +EXPORTS + GiveFnptrsToDll @1 +SECTIONS + .data READ WRITE diff --git a/src/dlls/monster_mm.dsp b/src/dlls/monster_mm.dsp index a62baa6..817ad96 100644 --- a/src/dlls/monster_mm.dsp +++ b/src/dlls/monster_mm.dsp @@ -1,378 +1,417 @@ -# Microsoft Developer Studio Project File - Name="monster_mm" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=monster_mm - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "monster_mm.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "monster_mm.mak" CFG="monster_mm - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "monster_mm - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "monster_mm - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "monster_mm - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\..\metamod" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /def:".\monster_mm.def" -# Begin Custom Build - Copying to DLL folder -TargetPath=.\Release\monster_mm.dll -TargetName=monster_mm -InputPath=.\Release\monster_mm.dll -SOURCE="$(InputPath)" - -"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetPath) D:\Half-Life\valve\dlls - copy $(TargetPath) D:\Half-Life\tfc\dlls - copy $(TargetPath) D:\Half-Life\cstrike\dlls - copy $(TargetPath) D:\Half-Life\dmc\dlls - copy $(TargetPath) D:\Half-Life\dod\dlls - copy $(TargetPath) D:\Half-Life\firearms\dlls - copy $(TargetPath) D:\Half-Life\frontline\dlls - -# End Custom Build - -!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\..\metamod" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /GZ /c -# SUBTRACT CPP /YX -# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /def:".\monster_mm.def" /pdbtype:sept -# Begin Custom Build - Copying to DLL folder -TargetPath=.\Debug\monster_mm.dll -TargetName=monster_mm -InputPath=.\Debug\monster_mm.dll -SOURCE="$(InputPath)" - -"$(TargetName)" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - copy $(TargetPath) D:\Half-Life\valve\dlls - copy $(TargetPath) D:\Half-Life\tfc\dlls - copy $(TargetPath) D:\Half-Life\cstrike\dlls - copy $(TargetPath) D:\Half-Life\dmc\dlls - copy $(TargetPath) D:\Half-Life\dod\dlls - copy $(TargetPath) D:\Half-Life\firearms\dlls - copy $(TargetPath) D:\Half-Life\frontline\dlls - copy $(TargetPath) D:\Half-Life\gearbox\dlls - -# End Custom Build - -!ENDIF - -# Begin Target - -# Name "monster_mm - Win32 Release" -# Name "monster_mm - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\agrunt.cpp -# End Source File -# Begin Source File - -SOURCE=.\AI_BaseNPC_Schedule.cpp -# End Source File -# Begin Source File - -SOURCE=.\animating.cpp -# End Source File -# Begin Source File - -SOURCE=.\animation.cpp -# End Source File -# Begin Source File - -SOURCE=.\apache.cpp -# End Source File -# Begin Source File - -SOURCE=.\barney.cpp -# End Source File -# Begin Source File - -SOURCE=.\bigmomma.cpp -# End Source File -# Begin Source File - -SOURCE=.\bullsquid.cpp -# End Source File -# Begin Source File - -SOURCE=.\cmbase.cpp -# End Source File -# Begin Source File - -SOURCE=.\combat.cpp -# End Source File -# Begin Source File - -SOURCE=.\controller.cpp -# End Source File -# Begin Source File - -SOURCE=.\defaultai.cpp -# End Source File -# Begin Source File - -SOURCE=.\dllapi.cpp -# End Source File -# Begin Source File - -SOURCE=.\effects.cpp -# End Source File -# Begin Source File - -SOURCE=.\flyingmonster.cpp -# End Source File -# Begin Source File - -SOURCE=.\ggrenade.cpp -# End Source File -# Begin Source File - -SOURCE=.\h_ai.cpp -# End Source File -# Begin Source File - -SOURCE=.\h_export.cpp -# End Source File -# Begin Source File - -SOURCE=.\hassassin.cpp -# End Source File -# Begin Source File - -SOURCE=.\headcrab.cpp -# End Source File -# Begin Source File - -SOURCE=.\hgrunt.cpp -# End Source File -# Begin Source File - -SOURCE=.\hornet.cpp -# End Source File -# Begin Source File - -SOURCE=.\houndeye.cpp -# End Source File -# Begin Source File - -SOURCE=.\islave.cpp -# End Source File -# Begin Source File - -SOURCE=.\monster_api.cpp -# End Source File -# Begin Source File - -SOURCE=.\monster_config.cpp -# End Source File -# Begin Source File - -SOURCE=.\monsters.cpp -# End Source File -# Begin Source File - -SOURCE=.\monsterstate.cpp -# End Source File -# Begin Source File - -SOURCE=.\nodes.cpp -# End Source File -# Begin Source File - -SOURCE=.\scientist.cpp -# End Source File -# Begin Source File - -SOURCE=.\skill.cpp -# End Source File -# Begin Source File - -SOURCE=.\sound.cpp -# End Source File -# Begin Source File - -SOURCE=.\squeakgrenade.cpp -# End Source File -# Begin Source File - -SOURCE=.\subs.cpp -# End Source File -# Begin Source File - -SOURCE=.\talkmonster.cpp -# End Source File -# Begin Source File - -SOURCE=.\util.cpp -# End Source File -# Begin Source File - -SOURCE=.\weapons.cpp -# End Source File -# Begin Source File - -SOURCE=.\zombie.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\activity.h -# End Source File -# Begin Source File - -SOURCE=.\animation.h -# End Source File -# Begin Source File - -SOURCE=.\cdll_dll.h -# End Source File -# Begin Source File - -SOURCE=.\cmbase.h -# End Source File -# Begin Source File - -SOURCE=.\cmbasemonster.h -# End Source File -# Begin Source File - -SOURCE=.\cmflyingmonster.h -# End Source File -# Begin Source File - -SOURCE=.\cmtalkmonster.h -# End Source File -# Begin Source File - -SOURCE=.\decals.h -# End Source File -# Begin Source File - -SOURCE=.\doors.h -# End Source File -# Begin Source File - -SOURCE=.\effects.h -# End Source File -# Begin Source File - -SOURCE=.\enginecallback.h -# End Source File -# Begin Source File - -SOURCE=.\explode.h -# End Source File -# Begin Source File - -SOURCE=.\extdll.h -# End Source File -# Begin Source File - -SOURCE=.\func_break.h -# End Source File -# Begin Source File - -SOURCE=.\monster_plugin.h -# End Source File -# Begin Source File - -SOURCE=.\monsters.h -# End Source File -# Begin Source File - -SOURCE=.\nodes.h -# End Source File -# Begin Source File - -SOURCE=.\schedule.h -# End Source File -# Begin Source File - -SOURCE=.\skill.h -# End Source File -# Begin Source File - -SOURCE=.\util.h -# End Source File -# Begin Source File - -SOURCE=.\vector.h -# End Source File -# Begin Source File - -SOURCE=.\weapons.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# End Target -# End Project +# Microsoft Developer Studio Project File - Name="monster_mm" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=monster_mm - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "monster_mm.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "monster_mm.mak" CFG="monster_mm - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "monster_mm - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "monster_mm - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "monster_mm - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\metamod" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /def:".\monster_mm.def" + +!ELSEIF "$(CFG)" == "monster_mm - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\dlls" /I "..\common" /I "..\engine" /I "..\pm_shared" /I "..\metamod" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "monster_mm_EXPORTS" /D strcasecmp=stricmp /D strncasecmp=_strnicmp /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /def:".\monster_mm.def" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "monster_mm - Win32 Release" +# Name "monster_mm - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\agrunt.cpp +# End Source File +# Begin Source File + +SOURCE=.\AI_BaseNPC_Schedule.cpp +# End Source File +# Begin Source File + +SOURCE=.\animating.cpp +# End Source File +# Begin Source File + +SOURCE=.\animation.cpp +# End Source File +# Begin Source File + +SOURCE=.\apache.cpp +# End Source File +# Begin Source File + +SOURCE=.\barney.cpp +# End Source File +# Begin Source File + +SOURCE=.\bigmomma.cpp +# End Source File +# Begin Source File + +SOURCE=.\bullsquid.cpp +# End Source File +# Begin Source File + +SOURCE=.\cmbase.cpp +# End Source File +# Begin Source File + +SOURCE=.\combat.cpp +# End Source File +# Begin Source File + +SOURCE=.\controller.cpp +# End Source File +# Begin Source File + +SOURCE=.\defaultai.cpp +# End Source File +# Begin Source File + +SOURCE=.\dllapi.cpp +# End Source File +# Begin Source File + +SOURCE=.\effects.cpp +# End Source File +# Begin Source File + +SOURCE=.\explode.cpp +# End Source File +# Begin Source File + +SOURCE=.\flyingmonster.cpp +# End Source File +# Begin Source File + +SOURCE=.\gargantua.cpp +# End Source File +# Begin Source File + +SOURCE=.\ggrenade.cpp +# End Source File +# Begin Source File + +SOURCE=.\gonome.cpp +# End Source File +# Begin Source File + +SOURCE=.\h_ai.cpp +# End Source File +# Begin Source File + +SOURCE=.\h_export.cpp +# End Source File +# Begin Source File + +SOURCE=.\hassassin.cpp +# End Source File +# Begin Source File + +SOURCE=.\headcrab.cpp +# End Source File +# Begin Source File + +SOURCE=.\hgrunt.cpp +# End Source File +# Begin Source File + +SOURCE=.\hornet.cpp +# End Source File +# Begin Source File + +SOURCE=.\houndeye.cpp +# End Source File +# Begin Source File + +SOURCE=.\islave.cpp +# End Source File +# Begin Source File + +SOURCE=.\massn.cpp +# End Source File +# Begin Source File + +SOURCE=.\monster_api.cpp +# End Source File +# Begin Source File + +SOURCE=.\monster_config.cpp +# End Source File +# Begin Source File + +SOURCE=.\monsters.cpp +# End Source File +# Begin Source File + +SOURCE=.\monsterstate.cpp +# End Source File +# Begin Source File + +SOURCE=.\nodes.cpp +# End Source File +# Begin Source File + +SOURCE=.\otis.cpp +# End Source File +# Begin Source File + +SOURCE=.\pitdrone.cpp +# End Source File +# Begin Source File + +SOURCE=.\scientist.cpp +# End Source File +# Begin Source File + +SOURCE=.\shock.cpp +# End Source File +# Begin Source File + +SOURCE=.\shockroach.cpp +# End Source File +# Begin Source File + +SOURCE=.\skill.cpp +# End Source File +# Begin Source File + +SOURCE=.\sound.cpp +# End Source File +# Begin Source File + +SOURCE=.\sporegrenade.cpp +# End Source File +# Begin Source File + +SOURCE=.\squeakgrenade.cpp +# End Source File +# Begin Source File + +SOURCE=.\strooper.cpp +# End Source File +# Begin Source File + +SOURCE=.\subs.cpp +# End Source File +# Begin Source File + +SOURCE=.\talkmonster.cpp +# End Source File +# Begin Source File + +SOURCE=.\turret.cpp +# End Source File +# Begin Source File + +SOURCE=.\util.cpp +# End Source File +# Begin Source File + +SOURCE=.\voltigore.cpp +# End Source File +# Begin Source File + +SOURCE=.\weapons.cpp +# End Source File +# Begin Source File + +SOURCE=.\zombie.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\activity.h +# End Source File +# Begin Source File + +SOURCE=.\activitymap.h +# End Source File +# Begin Source File + +SOURCE=.\animation.h +# End Source File +# Begin Source File + +SOURCE=.\cdll_dll.h +# End Source File +# Begin Source File + +SOURCE=.\cmbase.h +# End Source File +# Begin Source File + +SOURCE=.\cmbasemonster.h +# End Source File +# Begin Source File + +SOURCE=.\cmflyingmonster.h +# End Source File +# Begin Source File + +SOURCE=.\cmtalkmonster.h +# End Source File +# Begin Source File + +SOURCE=.\decals.h +# End Source File +# Begin Source File + +SOURCE=.\defaultai.h +# End Source File +# Begin Source File + +SOURCE=.\doors.h +# End Source File +# Begin Source File + +SOURCE=.\effects.h +# End Source File +# Begin Source File + +SOURCE=.\enginecallback.h +# End Source File +# Begin Source File + +SOURCE=.\explode.h +# End Source File +# Begin Source File + +SOURCE=.\extdll.h +# End Source File +# Begin Source File + +SOURCE=.\func_break.h +# End Source File +# Begin Source File + +SOURCE=.\hornet.h +# End Source File +# Begin Source File + +SOURCE=.\monster_plugin.h +# End Source File +# Begin Source File + +SOURCE=.\monsterevent.h +# End Source File +# Begin Source File + +SOURCE=.\monsters.h +# End Source File +# Begin Source File + +SOURCE=.\nodes.h +# End Source File +# Begin Source File + +SOURCE=.\plane.h +# End Source File +# Begin Source File + +SOURCE=.\schedule.h +# End Source File +# Begin Source File + +SOURCE=.\shock.h +# End Source File +# Begin Source File + +SOURCE=.\skill.h +# End Source File +# Begin Source File + +SOURCE=.\util.h +# End Source File +# Begin Source File + +SOURCE=.\vector.h +# End Source File +# Begin Source File + +SOURCE=.\weapons.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/dlls/monster_plugin.h b/src/dlls/monster_plugin.h index c963ad9..030cbfa 100644 --- a/src/dlls/monster_plugin.h +++ b/src/dlls/monster_plugin.h @@ -1,74 +1,74 @@ -// -// monster_plugin.h -// - -#ifndef MONSTER_PLUGIN_H -#define MONSTER_PLUGIN_H - -typedef struct pKVD -{ - char key[33]; - char value[33]; -}; - -#define MAX_KEYVALUES 32 - -typedef struct -{ - char *name; - bool need_to_precache; -} monster_type_t; - - -class CMBaseMonster; - -typedef struct -{ - int monster_index; - edict_t *monster_pent; - bool killed; - int respawn_index; - CMBaseMonster *pMonster; -} monster_t; - -#define MAX_MONSTER_ENTS 400 // increased from 200 so it can hold non-monster entities - -extern monster_t monsters[MAX_MONSTER_ENTS]; - -typedef struct { - Vector origin; - Vector angles; - float delay; - unsigned char monster; - int spawnflags; - pKVD *keyvalue; - float respawn_time; - bool need_to_respawn; -} monster_spawnpoint_t; - -#define MAX_MONSTERS 100 -extern monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS]; - -// this is here to store if a node we want to spawn is an ordinary one, or a flying one -typedef struct -{ - Vector origin; - bool is_air_node; -} node_spawnpoint_t; - -// nodes.cpp defines 1024 max nodes, but that amount is likely to trigger a -// no free edicts crash if the server num_edicts is low. Increase if needed. -#define MAX_NODES 256 -extern node_spawnpoint_t node_spawnpoint[MAX_NODES]; - -extern DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball -extern DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud -extern DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion -extern DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model -extern DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood -extern DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood -extern DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam -extern DLL_GLOBAL const char *g_pModelNameLaser; -extern DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot - -#endif +// +// monster_plugin.h +// + +#ifndef MONSTER_PLUGIN_H +#define MONSTER_PLUGIN_H + +typedef struct +{ + char key[33]; + char value[33]; +} pKVD; + +#define MAX_KEYVALUES 32 + +typedef struct +{ + char *name; + bool need_to_precache; +} monster_type_t; + + +class CMBaseMonster; + +typedef struct +{ + int monster_index; + edict_t *monster_pent; + bool killed; + int respawn_index; + CMBaseMonster *pMonster; +} monster_t; + +#define MAX_MONSTER_ENTS 400 // increased from 200 so it can hold non-monster entities + +extern monster_t monsters[MAX_MONSTER_ENTS]; + +typedef struct { + Vector origin; + Vector angles; + float delay; + unsigned char monster; + int spawnflags; + pKVD *keyvalue; + float respawn_time; + bool need_to_respawn; +} monster_spawnpoint_t; + +#define MAX_MONSTERS 100 +extern monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS]; + +// this is here to store if a node we want to spawn is an ordinary one, or a flying one +typedef struct +{ + Vector origin; + bool is_air_node; +} node_spawnpoint_t; + +// nodes.cpp defines 1024 max nodes, but that amount is likely to trigger a +// no free edicts crash if the server num_edicts is low. Increase if needed. +#define MAX_NODES 256 +extern node_spawnpoint_t node_spawnpoint[MAX_NODES]; + +extern DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball +extern DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud +extern DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion +extern DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model +extern DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood +extern DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood +extern DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam +extern DLL_GLOBAL const char *g_pModelNameLaser; +extern DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot + +#endif diff --git a/src/dlls/monsterevent.h b/src/dlls/monsterevent.h index 0193b69..ee2d634 100644 --- a/src/dlls/monsterevent.h +++ b/src/dlls/monsterevent.h @@ -1,34 +1,34 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef MONSTEREVENT_H -#define MONSTEREVENT_H - -typedef struct -{ - int event; - char *options; -} MonsterEvent_t; - -#define EVENT_SPECIFIC 0 -#define EVENT_SCRIPTED 1000 -#define EVENT_SHARED 2000 -#define EVENT_CLIENT 5000 - -#define MONSTER_EVENT_BODYDROP_LIGHT 2001 -#define MONSTER_EVENT_BODYDROP_HEAVY 2002 - -#define MONSTER_EVENT_SWISHSOUND 2010 - -#endif // MONSTEREVENT_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef MONSTEREVENT_H +#define MONSTEREVENT_H + +typedef struct +{ + int event; + char *options; +} MonsterEvent_t; + +#define EVENT_SPECIFIC 0 +#define EVENT_SCRIPTED 1000 +#define EVENT_SHARED 2000 +#define EVENT_CLIENT 5000 + +#define MONSTER_EVENT_BODYDROP_LIGHT 2001 +#define MONSTER_EVENT_BODYDROP_HEAVY 2002 + +#define MONSTER_EVENT_SWISHSOUND 2010 + +#endif // MONSTEREVENT_H diff --git a/src/dlls/monsters.cpp b/src/dlls/monsters.cpp index 44f304f..ef1a59f 100644 --- a/src/dlls/monsters.cpp +++ b/src/dlls/monsters.cpp @@ -1,2927 +1,2927 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -/* - -===== monsters.cpp ======================================================== - - Monster-related utility code - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "nodes.h" -#include "monsters.h" -#include "animation.h" -#include "weapons.h" -#include "decals.h" - -#define MONSTER_CUT_CORNER_DIST 8 // 8 means the monster's bounding box is contained without the box of the node in WC - - -Vector VecBModelOrigin( entvars_t* pevBModel ); - -extern DLL_GLOBAL BOOL g_fDrawLines; - -extern CGraph WorldGraph;// the world node graph - - - -//========================================================= -// Eat - makes a monster full for a little while. -//========================================================= -void CMBaseMonster :: Eat ( float flFullDuration ) -{ - m_flHungryTime = gpGlobals->time + flFullDuration; -} - -//========================================================= -// FShouldEat - returns true if a monster is hungry. -//========================================================= -BOOL CMBaseMonster :: FShouldEat ( void ) -{ - if ( m_flHungryTime > gpGlobals->time ) - { - return FALSE; - } - - return TRUE; -} - -//========================================================= -// BarnacleVictimBitten - called -// by Barnacle victims when the barnacle pulls their head -// into its mouth -//========================================================= -void CMBaseMonster :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) -{ - Schedule_t *pNewSchedule; - - pNewSchedule = GetScheduleOfType( SCHED_BARNACLE_VICTIM_CHOMP ); - - if ( pNewSchedule ) - { - ChangeSchedule( pNewSchedule ); - } -} - -//========================================================= -// BarnacleVictimReleased - called by barnacle victims when -// the host barnacle is killed. -//========================================================= -void CMBaseMonster :: BarnacleVictimReleased ( void ) -{ - m_IdealMonsterState = MONSTERSTATE_IDLE; - - pev->velocity = g_vecZero; - pev->movetype = MOVETYPE_STEP; -} - - -//========================================================= -// FValidateHintType - tells use whether or not the monster cares -// about the type of Hint Node given -//========================================================= -BOOL CMBaseMonster :: FValidateHintType ( short sHint ) -{ - return FALSE; -} - -//========================================================= -// Look - Base class monster function to find enemies or -// food by sight. iDistance is distance ( in units ) that the -// monster can see. -// -// Sets the sight bits of the m_afConditions mask to indicate -// which types of entities were sighted. -// Function also sets the Looker's m_pLink -// to the head of a link list that contains all visible ents. -// (linked via each ent's m_pLink field) -// -//========================================================= -void CMBaseMonster :: Look ( int iDistance ) -{ - int iSighted = 0; - - // DON'T let visibility information from last frame sit around! - ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); - - m_edictList_count = 0; - - edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with - - // See no evil if prisoner is set - if ( !FBitSet( pev->spawnflags, SF_MONSTER_PRISONER ) ) - { - edict_t *pList[100]; - - Vector delta = Vector( iDistance, iDistance, iDistance ); - - // Find only monsters/clients in box, NOT limited to PVS - int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); - for ( int i = 0; i < count; i++ ) - { - pSightEnt = pList[i]; - // !!!temporarily only considering other monsters and clients, don't see prisoners - if ( pSightEnt != this->edict() && - !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && - pSightEnt->v.health > 0 ) - { - // is this a player AND are they alive? - if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt)) - { - // the looker will want to consider this entity - // don't check anything else about an entity that can't be seen. - if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && - !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) ) - { - m_edictList[m_edictList_count] = pSightEnt; - m_edictList_count++; - - // if we see a client, remember that (mostly for scripted AI) - iSighted |= bits_COND_SEE_CLIENT; - - // is this monster NOT a scientist? - if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0) - { - iSighted |= bits_COND_SEE_DISLIKE; - - if ( pSightEnt == m_hEnemy ) - { - // we know this ent is visible, so if it also happens to be our enemy, store that now. - iSighted |= bits_COND_SEE_ENEMY; - } - } - } - } - else if (pSightEnt->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt)); - - // the looker will want to consider this entity - // don't check anything else about an entity that can't be seen, or an entity that you don't care about. - if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && - !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) ) - { - m_edictList[m_edictList_count] = pSightEnt; - m_edictList_count++; - - if ( ENT(pMonster->pev) == m_hEnemy ) - { - // we know this ent is visible, so if it also happens to be our enemy, store that now. - iSighted |= bits_COND_SEE_ENEMY; - } - - // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when - // we see monsters other than the Enemy. - switch ( IRelationship ( pMonster ) ) - { - case R_NM: - iSighted |= bits_COND_SEE_NEMESIS; - break; - case R_HT: - iSighted |= bits_COND_SEE_HATE; - break; - case R_DL: - iSighted |= bits_COND_SEE_DISLIKE; - break; - case R_FR: - iSighted |= bits_COND_SEE_FEAR; - break; - case R_AL: - break; - default: - ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pMonster->pev->classname ) ); - break; - } - } - } - } - } - } - - SetConditions( iSighted ); -} - - -//========================================================= -// Monster Think - calls out to core AI functions and handles this -// monster's specific animation events -//========================================================= -void CMBaseMonster :: MonsterThink ( void ) -{ - pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking. - - - RunAI(); - - float flInterval = StudioFrameAdvance( ); // animate -// start or end a fidget -// This needs a better home -- switching animations over time should be encapsulated on a per-activity basis -// perhaps MaintainActivity() or a ShiftAnimationOverTime() or something. - if ( m_MonsterState != MONSTERSTATE_SCRIPT && m_MonsterState != MONSTERSTATE_DEAD && m_Activity == ACT_IDLE && m_fSequenceFinished ) - { - int iSequence; - - if ( m_fSequenceLoops ) - { - // animation does loop, which means we're playing subtle idle. Might need to - // fidget. - iSequence = LookupActivity ( m_Activity ); - } - else - { - // animation that just ended doesn't loop! That means we just finished a fidget - // and should return to our heaviest weighted idle (the subtle one) - iSequence = LookupActivityHeaviest ( m_Activity ); - } - if ( iSequence != ACTIVITY_NOT_AVAILABLE ) - { - pev->sequence = iSequence; // Set to new anim (if it's there) - ResetSequenceInfo( ); - } - } - - DispatchAnimEvents( flInterval ); - - if ( !MovementIsComplete() ) - { - Move( flInterval ); - } -#if _DEBUG - else - { - if ( !TaskIsRunning() && !TaskIsComplete() ) - ALERT( at_error, "Schedule stalled!!\n" ); - } -#endif -} - -//========================================================= -// CMBaseMonster - USE - will make a monster angry at whomever -// activated it. -//========================================================= -void CMBaseMonster :: MonsterUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - m_IdealMonsterState = MONSTERSTATE_ALERT; -} - -//========================================================= -// Ignore conditions - before a set of conditions is allowed -// to interrupt a monster's schedule, this function removes -// conditions that we have flagged to interrupt the current -// schedule, but may not want to interrupt the schedule every -// time. (Pain, for instance) -//========================================================= -int CMBaseMonster :: IgnoreConditions ( void ) -{ - int iIgnoreConditions = 0; - - if ( !FShouldEat() ) - { - // not hungry? Ignore food smell. - iIgnoreConditions |= bits_COND_SMELL_FOOD; - } - - return iIgnoreConditions; -} - -//========================================================= -// RouteClear - zeroes out the monster's route array and goal -//========================================================= -void CMBaseMonster :: RouteClear ( void ) -{ - RouteNew(); - m_movementGoal = MOVEGOAL_NONE; - m_movementActivity = ACT_IDLE; - Forget( bits_MEMORY_MOVE_FAILED ); -} - -//========================================================= -// Route New - clears out a route to be changed, but keeps -// goal intact. -//========================================================= -void CMBaseMonster :: RouteNew ( void ) -{ - m_Route[ 0 ].iType = 0; - m_iRouteIndex = 0; -} - -//========================================================= -// FRouteClear - returns TRUE is the Route is cleared out -// ( invalid ) -//========================================================= -BOOL CMBaseMonster :: FRouteClear ( void ) -{ - if ( m_Route[ m_iRouteIndex ].iType == 0 || m_movementGoal == MOVEGOAL_NONE ) - return TRUE; - - return FALSE; -} - -//========================================================= -// FRefreshRoute - after calculating a path to the monster's -// target, this function copies as many waypoints as possible -// from that path to the monster's Route array -//========================================================= -BOOL CMBaseMonster :: FRefreshRoute ( void ) -{ - edict_t *pPathCorner; - int i; - BOOL returnCode; - - RouteNew(); - - returnCode = FALSE; - - switch( m_movementGoal ) - { - case MOVEGOAL_PATHCORNER: - { - // monster is on a path_corner loop - pPathCorner = m_pGoalEnt; - i = 0; - - while ( pPathCorner && i < ROUTE_SIZE ) - { - m_Route[ i ].iType = bits_MF_TO_PATHCORNER; - m_Route[ i ].vecLocation = pPathCorner->v.origin; - - pPathCorner = UTIL_GetNextTarget(pPathCorner); - - // Last path_corner in list? - if ( !pPathCorner ) - m_Route[i].iType |= bits_MF_IS_GOAL; - - i++; - } - } - returnCode = TRUE; - break; - - case MOVEGOAL_ENEMY: - returnCode = BuildRoute( m_vecEnemyLKP, bits_MF_TO_ENEMY, m_hEnemy ); - break; - - case MOVEGOAL_LOCATION: - returnCode = BuildRoute( m_vecMoveGoal, bits_MF_TO_LOCATION, NULL ); - break; - - case MOVEGOAL_TARGETENT: - if (m_hTargetEnt != NULL) - { - returnCode = BuildRoute( m_hTargetEnt->v.origin, bits_MF_TO_TARGETENT, m_hTargetEnt ); - } - break; - - case MOVEGOAL_NODE: - returnCode = FGetNodeRoute( m_vecMoveGoal ); -// if ( returnCode ) -// RouteSimplify( NULL ); - break; - } - - return returnCode; -} - - -BOOL CMBaseMonster::MoveToEnemy( Activity movementAct, float waitTime ) -{ - m_movementActivity = movementAct; - m_moveWaitTime = waitTime; - - m_movementGoal = MOVEGOAL_ENEMY; - return FRefreshRoute(); -} - - -BOOL CMBaseMonster::MoveToLocation( Activity movementAct, float waitTime, const Vector &goal ) -{ - m_movementActivity = movementAct; - m_moveWaitTime = waitTime; - - m_movementGoal = MOVEGOAL_LOCATION; - m_vecMoveGoal = goal; - return FRefreshRoute(); -} - - -BOOL CMBaseMonster::MoveToTarget( Activity movementAct, float waitTime ) -{ - m_movementActivity = movementAct; - m_moveWaitTime = waitTime; - - m_movementGoal = MOVEGOAL_TARGETENT; - return FRefreshRoute(); -} - - -BOOL CMBaseMonster::MoveToNode( Activity movementAct, float waitTime, const Vector &goal ) -{ - m_movementActivity = movementAct; - m_moveWaitTime = waitTime; - - m_movementGoal = MOVEGOAL_NODE; - m_vecMoveGoal = goal; - return FRefreshRoute(); -} - - -int ShouldSimplify( int routeType ) -{ - routeType &= ~bits_MF_IS_GOAL; - - if ( (routeType == bits_MF_TO_PATHCORNER) || (routeType & bits_MF_DONT_SIMPLIFY) ) - return FALSE; - return TRUE; -} - -//========================================================= -// RouteSimplify -// -// Attempts to make the route more direct by cutting out -// unnecessary nodes & cutting corners. -// -//========================================================= -void CMBaseMonster :: RouteSimplify( edict_t *pTargetEnt ) -{ - // BUGBUG: this doesn't work 100% yet - int i, count, outCount; - Vector vecStart; - WayPoint_t outRoute[ ROUTE_SIZE * 2 ]; // Any points except the ends can turn into 2 points in the simplified route - - count = 0; - - for ( i = m_iRouteIndex; i < ROUTE_SIZE; i++ ) - { - if ( !m_Route[i].iType ) - break; - else - count++; - if ( m_Route[i].iType & bits_MF_IS_GOAL ) - break; - } - // Can't simplify a direct route! - if ( count < 2 ) - { -// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 0, 255 ); - return; - } - - outCount = 0; - vecStart = pev->origin; - for ( i = 0; i < count-1; i++ ) - { - // Don't eliminate path_corners - if ( !ShouldSimplify( m_Route[m_iRouteIndex+i].iType ) ) - { - outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; - outCount++; - } - else if ( CheckLocalMove ( vecStart, m_Route[m_iRouteIndex+i+1].vecLocation, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - // Skip vert - continue; - } - else - { - Vector vecTest, vecSplit; - - // Halfway between this and next - vecTest = (m_Route[m_iRouteIndex+i+1].vecLocation + m_Route[m_iRouteIndex+i].vecLocation) * 0.5; - - // Halfway between this and previous - vecSplit = (m_Route[m_iRouteIndex+i].vecLocation + vecStart) * 0.5; - - int iType = (m_Route[m_iRouteIndex+i].iType | bits_MF_TO_DETOUR) & ~bits_MF_NOT_TO_MASK; - if ( CheckLocalMove ( vecStart, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - outRoute[outCount].iType = iType; - outRoute[outCount].vecLocation = vecTest; - } - else if ( CheckLocalMove ( vecSplit, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - outRoute[outCount].iType = iType; - outRoute[outCount].vecLocation = vecSplit; - outRoute[outCount+1].iType = iType; - outRoute[outCount+1].vecLocation = vecTest; - outCount++; // Adding an extra point - } - else - { - outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; - } - } - // Get last point - vecStart = outRoute[ outCount ].vecLocation; - outCount++; - } - ASSERT( i < count ); - outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; - outCount++; - - // Terminate - outRoute[outCount].iType = 0; - ASSERT( outCount < (ROUTE_SIZE*2) ); - -// Copy the simplified route, disable for testing - m_iRouteIndex = 0; - for ( i = 0; i < ROUTE_SIZE && i < outCount; i++ ) - { - m_Route[i] = outRoute[i]; - } - - // Terminate route - if ( i < ROUTE_SIZE ) - m_Route[i].iType = 0; - -// Debug, test movement code -#if 0 -// if ( CVAR_GET_FLOAT( "simplify" ) != 0 ) - DrawRoute( pev, outRoute, 0, 255, 0, 0 ); -// else - DrawRoute( pev, m_Route, m_iRouteIndex, 0, 255, 0 ); -#endif -} - -//========================================================= -// FBecomeProne - tries to send a monster into PRONE state. -// right now only used when a barnacle snatches someone, so -// may have some special case stuff for that. -//========================================================= -BOOL CMBaseMonster :: FBecomeProne ( void ) -{ - if ( FBitSet ( pev->flags, FL_ONGROUND ) ) - { - pev->flags -= FL_ONGROUND; - } - - m_IdealMonsterState = MONSTERSTATE_PRONE; - return TRUE; -} - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMBaseMonster :: CheckRangeAttack1 ( float flDot, float flDist ) -{ - if ( flDist > 64 && flDist <= 784 && flDot >= 0.5 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack2 -//========================================================= -BOOL CMBaseMonster :: CheckRangeAttack2 ( float flDot, float flDist ) -{ - if ( flDist > 64 && flDist <= 512 && flDot >= 0.5 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckMeleeAttack1 -//========================================================= -BOOL CMBaseMonster :: CheckMeleeAttack1 ( float flDot, float flDist ) -{ - // Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb) - if ( flDist <= 64 && flDot >= 0.7 && m_hEnemy != NULL && FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckMeleeAttack2 -//========================================================= -BOOL CMBaseMonster :: CheckMeleeAttack2 ( float flDot, float flDist ) -{ - if ( flDist <= 64 && flDot >= 0.7 ) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// CheckAttacks - sets all of the bits for attacks that the -// monster is capable of carrying out on the passed entity. -//========================================================= -void CMBaseMonster :: CheckAttacks ( edict_t *pTarget, float flDist ) -{ - Vector2D vec2LOS; - float flDot; - - UTIL_MakeVectors ( pev->angles ); - - vec2LOS = ( pTarget->v.origin - pev->origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - - // we know the enemy is in front now. We'll find which attacks the monster is capable of by - // checking for corresponding Activities in the model file, then do the simple checks to validate - // those attack types. - - // Clear all attack conditions - ClearConditions( bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK1 |bits_COND_CAN_MELEE_ATTACK2 ); - - if ( m_afCapability & bits_CAP_RANGE_ATTACK1 ) - { - if ( CheckRangeAttack1 ( flDot, flDist ) ) - SetConditions( bits_COND_CAN_RANGE_ATTACK1 ); - } - if ( m_afCapability & bits_CAP_RANGE_ATTACK2 ) - { - if ( CheckRangeAttack2 ( flDot, flDist ) ) - SetConditions( bits_COND_CAN_RANGE_ATTACK2 ); - } - if ( m_afCapability & bits_CAP_MELEE_ATTACK1 ) - { - if ( CheckMeleeAttack1 ( flDot, flDist ) ) - SetConditions( bits_COND_CAN_MELEE_ATTACK1 ); - } - if ( m_afCapability & bits_CAP_MELEE_ATTACK2 ) - { - if ( CheckMeleeAttack2 ( flDot, flDist ) ) - SetConditions( bits_COND_CAN_MELEE_ATTACK2 ); - } -} - -//========================================================= -// CanCheckAttacks - prequalifies a monster to do more fine -// checking of potential attacks. -//========================================================= -BOOL CMBaseMonster :: FCanCheckAttacks ( void ) -{ - if ( HasConditions(bits_COND_SEE_ENEMY) && !HasConditions( bits_COND_ENEMY_TOOFAR ) ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// CheckEnemy - part of the Condition collection process, -// gets and stores data and conditions pertaining to a monster's -// enemy. Returns TRUE if Enemy LKP was updated. -//========================================================= -int CMBaseMonster :: CheckEnemy ( edict_t *pEnemy ) -{ - float flDistToEnemy; - int iUpdatedLKP;// set this to TRUE if you update the EnemyLKP in this function. - - iUpdatedLKP = FALSE; - ClearConditions ( bits_COND_ENEMY_FACING_ME ); - - if ( !UTIL_FVisible( pEnemy, ENT(pev) ) ) - { - ASSERT(!HasConditions(bits_COND_SEE_ENEMY)); - SetConditions( bits_COND_ENEMY_OCCLUDED ); - } - else - ClearConditions( bits_COND_ENEMY_OCCLUDED ); - - if ( !UTIL_IsAlive(pEnemy) ) - { - SetConditions ( bits_COND_ENEMY_DEAD ); - ClearConditions( bits_COND_SEE_ENEMY | bits_COND_ENEMY_OCCLUDED ); - return FALSE; - } - - Vector vecEnemyPos = pEnemy->v.origin; - // distance to enemy's origin - flDistToEnemy = ( vecEnemyPos - pev->origin ).Length(); - vecEnemyPos.z += pEnemy->v.size.z * 0.5; - // distance to enemy's head - float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length(); - if (flDistToEnemy2 < flDistToEnemy) - flDistToEnemy = flDistToEnemy2; - else - { - // distance to enemy's feet - vecEnemyPos.z -= pEnemy->v.size.z; - float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length(); - if (flDistToEnemy2 < flDistToEnemy) - flDistToEnemy = flDistToEnemy2; - } - - if ( HasConditions( bits_COND_SEE_ENEMY ) ) - { - edict_t *pEnemyMonster; - - iUpdatedLKP = TRUE; - m_vecEnemyLKP = pEnemy->v.origin; - - pEnemyMonster = pEnemy; - - if ( pEnemyMonster ) - { - if ( UTIL_FInViewCone(pEnemyMonster, ENT(this->pev), m_flFieldOfView) ) - { - SetConditions ( bits_COND_ENEMY_FACING_ME ); - } - else - ClearConditions( bits_COND_ENEMY_FACING_ME ); - } - - if (pEnemy->v.velocity != Vector( 0, 0, 0)) - { - // trail the enemy a bit - m_vecEnemyLKP = m_vecEnemyLKP - pEnemy->v.velocity * RANDOM_FLOAT( -0.05, 0 ); - } - else - { - // UNDONE: use pev->oldorigin? - } - } - else if ( !HasConditions(bits_COND_ENEMY_OCCLUDED|bits_COND_SEE_ENEMY) && ( flDistToEnemy <= 256 ) ) - { - // if the enemy is not occluded, and unseen, that means it is behind or beside the monster. - // if the enemy is near enough the monster, we go ahead and let the monster know where the - // enemy is. - iUpdatedLKP = TRUE; - m_vecEnemyLKP = pEnemy->v.origin; - } - - if ( flDistToEnemy >= m_flDistTooFar ) - { - // enemy is very far away from monster - SetConditions( bits_COND_ENEMY_TOOFAR ); - } - else - ClearConditions( bits_COND_ENEMY_TOOFAR ); - - if ( FCanCheckAttacks() ) - { - CheckAttacks ( m_hEnemy, flDistToEnemy ); - } - - if ( m_movementGoal == MOVEGOAL_ENEMY ) - { - for ( int i = m_iRouteIndex; i < ROUTE_SIZE; i++ ) - { - if ( m_Route[ i ].iType == (bits_MF_IS_GOAL|bits_MF_TO_ENEMY) ) - { - // UNDONE: Should we allow monsters to override this distance (80?) - if ( (m_Route[ i ].vecLocation - m_vecEnemyLKP).Length() > 80 ) - { - // Refresh - FRefreshRoute(); - return iUpdatedLKP; - } - } - } - } - - return iUpdatedLKP; -} - -//========================================================= -// PushEnemy - remember the last few enemies, always remember the player -//========================================================= -void CMBaseMonster :: PushEnemy( edict_t *pEnemy, Vector &vecLastKnownPos ) -{ - int i; - - if (pEnemy == NULL) - return; - - // UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one. - for (i = 0; i < MAX_OLD_ENEMIES; i++) - { - if (m_hOldEnemy[i] == pEnemy) - return; - if (m_hOldEnemy[i] == NULL) // someone died, reuse their slot - break; - } - if (i >= MAX_OLD_ENEMIES) - return; - - m_hOldEnemy[i] = pEnemy; - m_vecOldEnemy[i] = vecLastKnownPos; -} - -//========================================================= -// PopEnemy - try remembering the last few enemies -//========================================================= -BOOL CMBaseMonster :: PopEnemy( ) -{ - // UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one. - for (int i = MAX_OLD_ENEMIES - 1; i >= 0; i--) - { - if (m_hOldEnemy[i] != NULL) - { - if (UTIL_IsAlive(m_hOldEnemy[i])) // cheat and know when they die - { - m_hEnemy = m_hOldEnemy[i]; - m_vecEnemyLKP = m_vecOldEnemy[i]; - // ALERT( at_console, "remembering\n"); - return TRUE; - } - else - { - m_hOldEnemy[i] = NULL; - } - } - } - return FALSE; -} - -//========================================================= -// SetActivity -//========================================================= -void CMBaseMonster :: SetActivity ( Activity NewActivity ) -{ - int iSequence; - - iSequence = LookupActivity ( NewActivity ); - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( pev->sequence != iSequence || !m_fSequenceLoops ) - { - // don't reset frame between walk and run - if ( !(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(NewActivity == ACT_WALK || NewActivity == ACT_RUN)) - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo( ); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT ( at_aiconsole, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // In case someone calls this with something other than the ideal activity - m_IdealActivity = m_Activity; - - -} - -//========================================================= -// SetSequenceByName -//========================================================= -void CMBaseMonster :: SetSequenceByName ( char *szSequence ) -{ - int iSequence; - - iSequence = LookupSequence ( szSequence ); - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence > ACTIVITY_NOT_AVAILABLE ) - { - if ( pev->sequence != iSequence || !m_fSequenceLoops ) - { - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo( ); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT ( at_aiconsole, "%s has no sequence named:%f\n", STRING(pev->classname), szSequence ); - pev->sequence = 0; // Set to the reset anim (if it's there) - } -} - -//========================================================= -// CheckLocalMove - returns TRUE if the caller can walk a -// straight line from its current origin to the given -// location. If so, don't use the node graph! -// -// if a valid pointer to a int is passed, the function -// will fill that int with the distance that the check -// reached before hitting something. THIS ONLY HAPPENS -// IF THE LOCAL MOVE CHECK FAILS! -// -// !!!PERFORMANCE - should we try to load balance this? -// DON"T USE SETORIGIN! -//========================================================= -#define LOCAL_STEP_SIZE 16 -int CMBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) -{ - Vector vecStartPos;// record monster's position before trying the move - float flYaw; - float flDist; - float flStep, stepSize; - int iReturn; - - vecStartPos = pev->origin; - - - flYaw = UTIL_VecToYaw ( vecEnd - vecStart );// build a yaw that points to the goal. - flDist = ( vecEnd - vecStart ).Length2D();// get the distance. - iReturn = LOCALMOVE_VALID;// assume everything will be ok. - - // move the monster to the start of the local move that's to be checked. - UTIL_SetOrigin( pev, vecStart );// !!!BUGBUG - won't this fire triggers? - nope, SetOrigin doesn't fire - - if ( !(pev->flags & (FL_FLY|FL_SWIM)) ) - { - DROP_TO_FLOOR( ENT( pev ) );//make sure monster is on the floor! - } - - //pev->origin.z = vecStartPos.z;//!!!HACKHACK - -// pev->origin = vecStart; - -/* - if ( flDist > 1024 ) - { - // !!!PERFORMANCE - this operation may be too CPU intensive to try checks this large. - // We don't lose much here, because a distance this great is very likely - // to have something in the way. - - // since we've actually moved the monster during the check, undo the move. - pev->origin = vecStartPos; - return FALSE; - } -*/ - // this loop takes single steps to the goal. - for ( flStep = 0 ; flStep < flDist ; flStep += LOCAL_STEP_SIZE ) - { - stepSize = LOCAL_STEP_SIZE; - - if ( (flStep + LOCAL_STEP_SIZE) >= (flDist-1) ) - stepSize = (flDist - flStep) - 1; - -// UTIL_ParticleEffect ( pev->origin, g_vecZero, 255, 25 ); - - if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, WALKMOVE_CHECKONLY ) ) - {// can't take the next step, fail! - - if ( pflDist != NULL ) - { - *pflDist = flStep; - } - if ( pTarget && pTarget == gpGlobals->trace_ent ) - { - // if this step hits target ent, the move is legal. - iReturn = LOCALMOVE_VALID; - break; - } - else - { - // If we're going toward an entity, and we're almost getting there, it's OK. -// if ( pTarget && fabs( flDist - iStep ) < LOCAL_STEP_SIZE ) -// fReturn = TRUE; -// else - iReturn = LOCALMOVE_INVALID; - break; - } - - } - } - - if ( iReturn == LOCALMOVE_VALID && !(pev->flags & (FL_FLY|FL_SWIM) ) && (!pTarget || (pTarget->v.flags & FL_ONGROUND)) ) - { - // The monster can move to a spot UNDER the target, but not to it. Don't try to triangulate, go directly to the node graph. - // UNDONE: Magic # 64 -- this used to be pev->size.z but that won't work for small creatures like the headcrab - if ( fabs(vecEnd.z - pev->origin.z) > 64 ) - { - iReturn = LOCALMOVE_INVALID_DONT_TRIANGULATE; - } - } - /* - // uncommenting this block will draw a line representing the nearest legal move. - WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); - WRITE_COORD(MSG_BROADCAST, pev->origin.x); - WRITE_COORD(MSG_BROADCAST, pev->origin.y); - WRITE_COORD(MSG_BROADCAST, pev->origin.z); - WRITE_COORD(MSG_BROADCAST, vecStart.x); - WRITE_COORD(MSG_BROADCAST, vecStart.y); - WRITE_COORD(MSG_BROADCAST, vecStart.z); - */ - - // since we've actually moved the monster during the check, undo the move. - UTIL_SetOrigin( pev, vecStartPos ); - - return iReturn; -} - - -//========================================================= -// AdvanceRoute - poorly named function that advances the -// m_iRouteIndex. If it goes beyond ROUTE_SIZE, the route -// is refreshed. -//========================================================= -void CMBaseMonster :: AdvanceRoute ( float distance ) -{ - - if ( m_iRouteIndex == ROUTE_SIZE - 1 ) - { - // time to refresh the route. - if ( !FRefreshRoute() ) - { - ALERT ( at_aiconsole, "Can't Refresh Route!!\n" ); - } - } - else - { - if ( ! (m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL) ) - { - // If we've just passed a path_corner, advance m_pGoalEnt - if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_PATHCORNER ) - m_pGoalEnt = UTIL_GetNextTarget(m_pGoalEnt); - - // IF both waypoints are nodes, then check for a link for a door and operate it. - // - if ( (m_Route[m_iRouteIndex].iType & bits_MF_TO_NODE) == bits_MF_TO_NODE - && (m_Route[m_iRouteIndex+1].iType & bits_MF_TO_NODE) == bits_MF_TO_NODE) - { - //ALERT(at_aiconsole, "SVD: Two nodes. "); - - int iSrcNode = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex].vecLocation, this ); - int iDestNode = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex+1].vecLocation, this ); - - int iLink; - WorldGraph.HashSearch(iSrcNode, iDestNode, iLink); - - if ( iLink >= 0 && WorldGraph.m_pLinkPool[iLink].m_pLinkEnt != NULL ) - { - //ALERT(at_aiconsole, "A link. "); - if ( WorldGraph.HandleLinkEnt ( iSrcNode, WorldGraph.m_pLinkPool[iLink].m_pLinkEnt, m_afCapability, CGraph::NODEGRAPH_DYNAMIC ) ) - { - //ALERT(at_aiconsole, "usable."); - entvars_t *pevDoor = WorldGraph.m_pLinkPool[iLink].m_pLinkEnt; - if (pevDoor) - { -// m_flMoveWaitFinished = OpenDoorAndWait( pevDoor ); -// ALERT( at_aiconsole, "Wating for door %.2f\n", m_flMoveWaitFinished-gpGlobals->time ); - } - } - } - //ALERT(at_aiconsole, "\n"); - } - m_iRouteIndex++; - } - else // At goal!!! - { - if ( distance < m_flGroundSpeed * 0.2 /* FIX */ ) - { - MovementComplete(); - } - } - } -} - - -int CMBaseMonster :: RouteClassify( int iMoveFlag ) -{ - int movementGoal; - - movementGoal = MOVEGOAL_NONE; - - if ( iMoveFlag & bits_MF_TO_TARGETENT ) - movementGoal = MOVEGOAL_TARGETENT; - else if ( iMoveFlag & bits_MF_TO_ENEMY ) - movementGoal = MOVEGOAL_ENEMY; - else if ( iMoveFlag & bits_MF_TO_PATHCORNER ) - movementGoal = MOVEGOAL_PATHCORNER; - else if ( iMoveFlag & bits_MF_TO_NODE ) - movementGoal = MOVEGOAL_NODE; - else if ( iMoveFlag & bits_MF_TO_LOCATION ) - movementGoal = MOVEGOAL_LOCATION; - - return movementGoal; -} - -//========================================================= -// BuildRoute -//========================================================= -BOOL CMBaseMonster :: BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget ) -{ - float flDist; - Vector vecApex; - int iLocalMove; - - RouteNew(); - m_movementGoal = RouteClassify( iMoveFlag ); - -// so we don't end up with no moveflags - m_Route[ 0 ].vecLocation = vecGoal; - m_Route[ 0 ].iType = iMoveFlag | bits_MF_IS_GOAL; - -// check simple local move - iLocalMove = CheckLocalMove( pev->origin, vecGoal, pTarget, &flDist ); - - if ( iLocalMove == LOCALMOVE_VALID ) - { - // monster can walk straight there! - return TRUE; - } -// try to triangulate around any obstacles. - else if ( iLocalMove != LOCALMOVE_INVALID_DONT_TRIANGULATE && FTriangulate( pev->origin, vecGoal, flDist, pTarget, &vecApex ) ) - { - // there is a slightly more complicated path that allows the monster to reach vecGoal - m_Route[ 0 ].vecLocation = vecApex; - m_Route[ 0 ].iType = (iMoveFlag | bits_MF_TO_DETOUR); - - m_Route[ 1 ].vecLocation = vecGoal; - m_Route[ 1 ].iType = iMoveFlag | bits_MF_IS_GOAL; - - /* - WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); - WRITE_COORD(MSG_BROADCAST, vecApex.x ); - WRITE_COORD(MSG_BROADCAST, vecApex.y ); - WRITE_COORD(MSG_BROADCAST, vecApex.z ); - WRITE_COORD(MSG_BROADCAST, vecApex.x ); - WRITE_COORD(MSG_BROADCAST, vecApex.y ); - WRITE_COORD(MSG_BROADCAST, vecApex.z + 128 ); - */ - - RouteSimplify( pTarget ); - return TRUE; - } - -// last ditch, try nodes - if ( FGetNodeRoute( vecGoal ) ) - { -// ALERT ( at_console, "Can get there on nodes\n" ); - m_vecMoveGoal = vecGoal; - RouteSimplify( pTarget ); - return TRUE; - } - - // b0rk - return FALSE; -} - - -//========================================================= -// InsertWaypoint - Rebuilds the existing route so that the -// supplied vector and moveflags are the first waypoint in -// the route, and fills the rest of the route with as much -// of the pre-existing route as possible -//========================================================= -void CMBaseMonster :: InsertWaypoint ( Vector vecLocation, int afMoveFlags ) -{ - int i, type; - - - // we have to save some Index and Type information from the real - // path_corner or node waypoint that the monster was trying to reach. This makes sure that data necessary - // to refresh the original path exists even in the new waypoints that don't correspond directy to a path_corner - // or node. - type = afMoveFlags | (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK); - - for ( i = ROUTE_SIZE-1; i > 0; i-- ) - m_Route[i] = m_Route[i-1]; - - m_Route[ m_iRouteIndex ].vecLocation = vecLocation; - m_Route[ m_iRouteIndex ].iType = type; -} - -//========================================================= -// FTriangulate - tries to overcome local obstacles by -// triangulating a path around them. -// -// iApexDist is how far the obstruction that we are trying -// to triangulate around is from the monster. -//========================================================= -BOOL CMBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ) -{ - Vector vecDir; - Vector vecForward; - Vector vecLeft;// the spot we'll try to triangulate to on the left - Vector vecRight;// the spot we'll try to triangulate to on the right - Vector vecTop;// the spot we'll try to triangulate to on the top - Vector vecBottom;// the spot we'll try to triangulate to on the bottom - Vector vecFarSide;// the spot that we'll move to after hitting the triangulated point, before moving on to our normal goal. - int i; - float sizeX, sizeZ; - - // If the hull width is less than 24, use 24 because CheckLocalMove uses a min of - // 24. - sizeX = pev->size.x; - if (sizeX < 24.0) - sizeX = 24.0; - else if (sizeX > 48.0) - sizeX = 48.0; - sizeZ = pev->size.z; - //if (sizeZ < 24.0) - // sizeZ = 24.0; - - vecForward = ( vecEnd - vecStart ).Normalize(); - - Vector vecDirUp(0,0,1); - vecDir = CrossProduct ( vecForward, vecDirUp); - - // start checking right about where the object is, picking two equidistant starting points, one on - // the left, one on the right. As we progress through the loop, we'll push these away from the obstacle, - // hoping to find a way around on either side. pev->size.x is added to the ApexDist in order to help select - // an apex point that insures that the monster is sufficiently past the obstacle before trying to turn back - // onto its original course. - - vecLeft = pev->origin + ( vecForward * ( flDist + sizeX ) ) - vecDir * ( sizeX * 3 ); - vecRight = pev->origin + ( vecForward * ( flDist + sizeX ) ) + vecDir * ( sizeX * 3 ); - if (pev->movetype == MOVETYPE_FLY) - { - vecTop = pev->origin + (vecForward * flDist) + (vecDirUp * sizeZ * 3); - vecBottom = pev->origin + (vecForward * flDist) - (vecDirUp * sizeZ * 3); - } - - vecFarSide = m_Route[ m_iRouteIndex ].vecLocation; - - vecDir = vecDir * sizeX * 2; - if (pev->movetype == MOVETYPE_FLY) - vecDirUp = vecDirUp * sizeZ * 2; - - for ( i = 0 ; i < 8; i++ ) - { -// Debug, Draw the triangulation -#if 0 - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( vecRight.x ); - WRITE_COORD( vecRight.y ); - WRITE_COORD( vecRight.z ); - MESSAGE_END(); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( vecLeft.x ); - WRITE_COORD( vecLeft.y ); - WRITE_COORD( vecLeft.z ); - MESSAGE_END(); -#endif - -#if 0 - if (pev->movetype == MOVETYPE_FLY) - { - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( vecTop.x ); - WRITE_COORD( vecTop.y ); - WRITE_COORD( vecTop.z ); - MESSAGE_END(); - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( vecBottom.x ); - WRITE_COORD( vecBottom.y ); - WRITE_COORD( vecBottom.z ); - MESSAGE_END(); - } -#endif - - if ( CheckLocalMove( pev->origin, vecRight, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( CheckLocalMove ( vecRight, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( pApex ) - { - *pApex = vecRight; - } - - return TRUE; - } - } - if ( CheckLocalMove( pev->origin, vecLeft, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( CheckLocalMove ( vecLeft, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( pApex ) - { - *pApex = vecLeft; - } - - return TRUE; - } - } - - if (pev->movetype == MOVETYPE_FLY) - { - if ( CheckLocalMove( pev->origin, vecTop, pTargetEnt, NULL ) == LOCALMOVE_VALID) - { - if ( CheckLocalMove ( vecTop, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( pApex ) - { - *pApex = vecTop; - //ALERT(at_aiconsole, "triangulate over\n"); - } - - return TRUE; - } - } -#if 1 - if ( CheckLocalMove( pev->origin, vecBottom, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( CheckLocalMove ( vecBottom, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) - { - if ( pApex ) - { - *pApex = vecBottom; - //ALERT(at_aiconsole, "triangulate under\n"); - } - - return TRUE; - } - } -#endif - } - - vecRight = vecRight + vecDir; - vecLeft = vecLeft - vecDir; - if (pev->movetype == MOVETYPE_FLY) - { - vecTop = vecTop + vecDirUp; - vecBottom = vecBottom - vecDirUp; - } - } - - return FALSE; -} - -//========================================================= -// Move - take a single step towards the next ROUTE location -//========================================================= -#define DIST_TO_CHECK 200 - -void CMBaseMonster :: Move ( float flInterval ) -{ - float flWaypointDist; - float flCheckDist; - float flDist;// how far the lookahead check got before hitting an object. - Vector vecDir; - Vector vecApex; - edict_t *pTargetEnt; - - // Don't move if no valid route - if ( FRouteClear() ) - { - // If we still have a movement goal, then this is probably a route truncated by SimplifyRoute() - // so refresh it. - if ( m_movementGoal == MOVEGOAL_NONE || !FRefreshRoute() ) - { - ALERT( at_aiconsole, "Tried to move with no route!\n" ); - TaskFail(); - return; - } - } - - if ( m_flMoveWaitFinished > gpGlobals->time ) - return; - -// Debug, test movement code -#if 0 -// if ( CVAR_GET_FLOAT("stopmove" ) != 0 ) - { - if ( m_movementGoal == MOVEGOAL_ENEMY ) - RouteSimplify( m_hEnemy ); - else - RouteSimplify( m_hTargetEnt ); - FRefreshRoute(); - return; - } -#else -// Debug, draw the route -// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 200, 0 ); -#endif - - // if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer - // to that entity for the CheckLocalMove and Triangulate functions. - pTargetEnt = NULL; - - // local move to waypoint. - vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize(); - flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D(); - - MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation ); - ChangeYaw ( pev->yaw_speed ); - - // if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint - if ( flWaypointDist < DIST_TO_CHECK ) - { - flCheckDist = flWaypointDist; - } - else - { - flCheckDist = DIST_TO_CHECK; - } - - if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY ) - { - // only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR ) - pTargetEnt = m_hEnemy; - } - else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT ) - { - pTargetEnt = m_hTargetEnt; - } - - // !!!BUGBUG - CheckDist should be derived from ground speed. - // If this fails, it should be because of some dynamic entity blocking this guy. - // We've already checked this path, so we should wait and time out if the entity doesn't move - flDist = 0; - if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID ) - { - // Can't move, stop - Stop(); - // Blocking entity is in global trace_ent - CMBaseMonster *pBlocker = GetClassPtr((CMBaseMonster *)VARS(gpGlobals->trace_ent)); - if (pBlocker) - { - Blocked( pBlocker->edict() ); - } - - if ( pBlocker && m_moveWaitTime > 0 && pBlocker->IsMoving() && !pBlocker->IsPlayer() && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 ) - { - // Can we still move toward our target? - if ( flDist < m_flGroundSpeed ) - { - // No, Wait for a second - m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime; - return; - } - // Ok, still enough room to take a step - } - else - { - // try to triangulate around whatever is in the way. - if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) ) - { - InsertWaypoint( vecApex, bits_MF_TO_DETOUR ); - RouteSimplify( pTargetEnt ); - } - else - { -// ALERT ( at_aiconsole, "Couldn't Triangulate\n" ); - Stop(); - // Only do this once until your route is cleared - if ( m_moveWaitTime > 0 && !(m_afMemory & bits_MEMORY_MOVE_FAILED) ) - { - FRefreshRoute(); - if ( FRouteClear() ) - { - TaskFail(); - } - else - { - // Don't get stuck - if ( (gpGlobals->time - m_flMoveWaitFinished) < 0.2 ) - Remember( bits_MEMORY_MOVE_FAILED ); - - m_flMoveWaitFinished = gpGlobals->time + 0.1; - } - } - else - { -//jlb TaskFail(); - ALERT( at_aiconsole, "%s Failed to move (%d)!\n", STRING(pev->classname), HasMemory( bits_MEMORY_MOVE_FAILED ) ); - //ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z ); - } - return; - } - } - } - - // close enough to the target, now advance to the next target. This is done before actually reaching - // the target so that we get a nice natural turn while moving. - if ( ShouldAdvanceRoute( flWaypointDist ) )///!!!BUGBUG- magic number - { - AdvanceRoute( flWaypointDist ); - } - - // Might be waiting for a door - if ( m_flMoveWaitFinished > gpGlobals->time ) - { - Stop(); - return; - } - - // UNDONE: this is a hack to quit moving farther than it has looked ahead. - if (flCheckDist < m_flGroundSpeed * flInterval) - { - flInterval = flCheckDist / m_flGroundSpeed; - // ALERT( at_console, "%.02f\n", flInterval ); - } - MoveExecute( pTargetEnt, vecDir, flInterval ); - - if ( MovementIsComplete() ) - { - Stop(); - RouteClear(); - } -} - - -BOOL CMBaseMonster:: ShouldAdvanceRoute( float flWaypointDist ) -{ - if ( flWaypointDist <= MONSTER_CUT_CORNER_DIST ) - { - // ALERT( at_console, "cut %f\n", flWaypointDist ); - return TRUE; - } - - return FALSE; -} - - -void CMBaseMonster::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) -{ -// float flYaw = UTIL_VecToYaw ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin );// build a yaw that points to the goal. -// WALK_MOVE( ENT(pev), flYaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL ); - if ( m_IdealActivity != m_movementActivity ) - m_IdealActivity = m_movementActivity; - - float flTotal = m_flGroundSpeed * pev->framerate * flInterval; - float flStep; - while (flTotal > 0.001) - { - // don't walk more than 16 units or stairs stop working - flStep = min( 16.0f, flTotal ); - UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flStep, MOVE_NORMAL ); - flTotal -= flStep; - } - // ALERT( at_console, "dist %f\n", m_flGroundSpeed * pev->framerate * flInterval ); -} - - -//========================================================= -// MonsterInit - after a monster is spawned, it needs to -// be dropped into the world, checked for mobility problems, -// and put on the proper path, if any. This function does -// all of those things after the monster spawns. Any -// initialization that should take place for all monsters -// goes here. -//========================================================= -void CMBaseMonster :: MonsterInit ( void ) -{ - // Set fields common to all monsters - pev->effects = 0; - pev->takedamage = DAMAGE_AIM; - pev->ideal_yaw = pev->angles.y; - pev->max_health = pev->health; - pev->deadflag = DEAD_NO; - m_IdealMonsterState = MONSTERSTATE_IDLE;// Assume monster will be idle, until proven otherwise - - m_IdealActivity = ACT_IDLE; - - SetBits (pev->flags, FL_MONSTER); - if ( pev->spawnflags & SF_MONSTER_HITMONSTERCLIP ) - pev->flags |= FL_MONSTERCLIP; - - ClearSchedule(); - RouteClear(); - InitBoneControllers( ); // FIX: should be done in Spawn - - m_iHintNode = NO_NODE; - - m_afMemory = MEMORY_CLEAR; - - m_hEnemy = NULL; - m_hTargetEnt = NULL; - - for (int i=0; i < MAX_OLD_ENEMIES; i++) - m_hOldEnemy[ i ] = NULL; - - m_flDistTooFar = 1024.0; - m_flDistLook = 2048.0; - - // set eye position - SetEyePosition(); - - SetThink( &CMBaseMonster::MonsterInitThink ); - pev->nextthink = gpGlobals->time + 0.1; - SetUse ( &CMBaseMonster::MonsterUse ); -} - -//========================================================= -// MonsterInitThink - Calls StartMonster. Startmonster is -// virtual, but this function cannot be -//========================================================= -void CMBaseMonster :: MonsterInitThink ( void ) -{ - StartMonster(); -} - -//========================================================= -// StartMonster - final bit of initization before a monster -// is turned over to the AI. -//========================================================= -void CMBaseMonster :: StartMonster ( void ) -{ - // update capabilities - if ( LookupActivity ( ACT_RANGE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE ) - { - m_afCapability |= bits_CAP_RANGE_ATTACK1; - } - if ( LookupActivity ( ACT_RANGE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE ) - { - m_afCapability |= bits_CAP_RANGE_ATTACK2; - } - if ( LookupActivity ( ACT_MELEE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE ) - { - m_afCapability |= bits_CAP_MELEE_ATTACK1; - } - if ( LookupActivity ( ACT_MELEE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE ) - { - m_afCapability |= bits_CAP_MELEE_ATTACK2; - } - - // Raise monster off the floor one unit, then drop to floor - if ( (pev->movetype != MOVETYPE_FLY) && !FBitSet( pev->spawnflags, SF_MONSTER_FALL_TO_GROUND ) ) - { - pev->origin.z += 1; - DROP_TO_FLOOR ( ENT(pev) ); - - // Try to move the monster to make sure it's not stuck in a brush. - if (!WALK_MOVE ( ENT(pev), 0, 0, WALKMOVE_NORMAL ) ) - { -//jlb ALERT(at_console, "Monster %s stuck in wall", STRING(pev->classname)); -//jlb stuck -//jlb pev->effects = EF_BRIGHTFIELD; - } - } - else - { - pev->flags &= ~FL_ONGROUND; - } - - if ( !FStringNull(pev->target) )// this monster has a target - { - // Find the monster's initial target entity, stash it - m_pGoalEnt = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ); - - if ( !m_pGoalEnt ) - { - ALERT(at_error, "ReadyMonster()--%s couldn't find target %s", STRING(pev->classname), STRING(pev->target)); - } - else - { - // Monster will start turning towards his destination - MakeIdealYaw ( m_pGoalEnt->v.origin ); - - // JAY: How important is this error message? Big Momma doesn't obey this rule, so I took it out. -#if 0 - // At this point, we expect only a path_corner as initial goal - if (!FClassnameIs( m_pGoalEnt->pev, "path_corner")) - { - ALERT(at_warning, "ReadyMonster--monster's initial goal '%s' is not a path_corner", STRING(pev->target)); - } -#endif - - // set the monster up to walk a path corner path. - // !!!BUGBUG - this is a minor bit of a hack. - // JAYJAY - m_movementGoal = MOVEGOAL_PATHCORNER; - - if ( pev->movetype == MOVETYPE_FLY ) - m_movementActivity = ACT_FLY; - else - m_movementActivity = ACT_WALK; - - if ( !FRefreshRoute() ) - { - ALERT ( at_aiconsole, "Can't Create Route!\n" ); - } - SetState( MONSTERSTATE_IDLE ); - ChangeSchedule( GetScheduleOfType( SCHED_IDLE_WALK ) ); - } - } - - //SetState ( m_IdealMonsterState ); - //SetActivity ( m_IdealActivity ); - - // Delay drop to floor to make sure each door in the level has had its chance to spawn - // Spread think times so that they don't all happen at the same time (Carmack) - SetThink ( &CMBaseMonster::CallMonsterThink ); - pev->nextthink += RANDOM_FLOAT(0.1, 0.4); // spread think times. - - if ( !FStringNull(pev->targetname) )// wait until triggered - { - SetState( MONSTERSTATE_IDLE ); - // UNDONE: Some scripted sequence monsters don't have an idle? - SetActivity( ACT_IDLE ); - ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) ); - } -} - - -void CMBaseMonster :: MovementComplete( void ) -{ - switch( m_iTaskStatus ) - { - case TASKSTATUS_NEW: - case TASKSTATUS_RUNNING: - m_iTaskStatus = TASKSTATUS_RUNNING_TASK; - break; - - case TASKSTATUS_RUNNING_MOVEMENT: - TaskComplete(); - break; - - case TASKSTATUS_RUNNING_TASK: - ALERT( at_error, "Movement completed twice!\n" ); - break; - - case TASKSTATUS_COMPLETE: - break; - } - m_movementGoal = MOVEGOAL_NONE; -} - - -int CMBaseMonster::TaskIsRunning( void ) -{ - if ( m_iTaskStatus != TASKSTATUS_COMPLETE && - m_iTaskStatus != TASKSTATUS_RUNNING_MOVEMENT ) - return 1; - - return 0; -} - -//========================================================= -// IRelationship - returns an integer that describes the -// relationship between two types of monster. -//========================================================= -int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget ) -{ - static int iEnemy[16][16] = - { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK - /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, - /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL, R_DL, R_DL }, - /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL, R_DL, R_DL }, - /*HUMANPASSIVE*/{ R_NO ,R_FR ,R_AL ,R_AL ,R_HT ,R_DL ,R_DL ,R_HT ,R_DL ,R_DL ,R_NO ,R_AL, R_NO, R_NO, R_FR, R_FR }, - /*HUMANMILITAR*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_AL ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_HT, R_HT }, - /*ALIENMILITAR*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_HT ,R_AL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_HT }, - /*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, - /*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_NO, R_NO }, - /*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO, R_FR, R_NO }, - /*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_DL }, - /*INSECT*/ { R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO, R_NO, R_NO }, - /*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO, R_DL, R_DL }, - /*PBIOWEAPON*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_DL, R_DL, R_DL }, - /*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO, R_DL, R_DL }, - /*RXPITDRONE*/ { R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }, - /*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL } - }; - - return iEnemy[ Classify() ][ pTarget->Classify() ]; -} - -//========================================================= -// FindCover - tries to find a nearby node that will hide -// the caller from its enemy. -// -// If supplied, search will return a node at least as far -// away as MinDist, but no farther than MaxDist. -// if MaxDist isn't supplied, it defaults to a reasonable -// value -//========================================================= -// UNDONE: Should this find the nearest node? - -//float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) - -BOOL CMBaseMonster :: FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ) -{ - int i; - int iMyHullIndex; - int iMyNode; - int iThreatNode; - float flDist; - Vector vecLookersOffset; - TraceResult tr; - - if ( !flMaxDist ) - { - // user didn't supply a MaxDist, so work up a crazy one. - flMaxDist = 784; - } - - if ( flMinDist > 0.5 * flMaxDist) - { -#if _DEBUG - ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); -#endif - flMinDist = 0.5 * flMaxDist; - } - - if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) - { - ALERT ( at_aiconsole, "Graph not ready for findcover!\n" ); - return FALSE; - } - - iMyNode = WorldGraph.FindNearestNode( pev->origin, this ); - iThreatNode = WorldGraph.FindNearestNode ( vecThreat, this ); - iMyHullIndex = WorldGraph.HullIndex( this ); - - if ( iMyNode == NO_NODE ) - { - ALERT ( at_aiconsole, "FindCover() - %s has no nearest node!\n", STRING(pev->classname)); - return FALSE; - } - if ( iThreatNode == NO_NODE ) - { - // ALERT ( at_aiconsole, "FindCover() - Threat has no nearest node!\n" ); - iThreatNode = iMyNode; - // return FALSE; - } - - vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes - - // we'll do a rough sample to find nodes that are relatively nearby - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes; - - CNode &node = WorldGraph.Node( nodeNumber ); - WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here. - - // could use an optimization here!! - flDist = ( pev->origin - node.m_vecOrigin ).Length(); - - // DON'T do the trace check on a node that is farther away than a node that we've already found to - // provide cover! Also make sure the node is within the mins/maxs of the search. - if ( flDist >= flMinDist && flDist < flMaxDist ) - { - UTIL_TraceLine ( node.m_vecOrigin + vecViewOffset, vecLookersOffset, ignore_monsters, ignore_glass, ENT(pev), &tr ); - - // if this node will block the threat's line of sight to me... - if ( tr.flFraction != 1.0 ) - { - // ..and is also closer to me than the threat, or the same distance from myself and the threat the node is good. - if ( ( iMyNode == iThreatNode ) || WorldGraph.PathLength( iMyNode, nodeNumber, iMyHullIndex, m_afCapability ) <= WorldGraph.PathLength( iThreatNode, nodeNumber, iMyHullIndex, m_afCapability ) ) - { - if ( FValidateCover ( node.m_vecOrigin ) && MoveToLocation( ACT_RUN, 0, node.m_vecOrigin ) ) - { - /* - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - - WRITE_COORD( node.m_vecOrigin.x ); - WRITE_COORD( node.m_vecOrigin.y ); - WRITE_COORD( node.m_vecOrigin.z ); - - WRITE_COORD( vecLookersOffset.x ); - WRITE_COORD( vecLookersOffset.y ); - WRITE_COORD( vecLookersOffset.z ); - MESSAGE_END(); - */ - - return TRUE; - } - } - } - } - } - return FALSE; -} - - -//========================================================= -// BuildNearestRoute - tries to build a route as close to the target -// as possible, even if there isn't a path to the final point. -// -// If supplied, search will return a node at least as far -// away as MinDist from vecThreat, but no farther than MaxDist. -// if MaxDist isn't supplied, it defaults to a reasonable -// value -//========================================================= -BOOL CMBaseMonster :: BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ) -{ - int i; - int iMyHullIndex; - int iMyNode; - float flDist; - Vector vecLookersOffset; - TraceResult tr; - - if ( !flMaxDist ) - { - // user didn't supply a MaxDist, so work up a crazy one. - flMaxDist = 784; - } - - if ( flMinDist > 0.5 * flMaxDist) - { -#if _DEBUG - ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); -#endif - flMinDist = 0.5 * flMaxDist; - } - - if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) - { - ALERT ( at_aiconsole, "Graph not ready for BuildNearestRoute!\n" ); - return FALSE; - } - - iMyNode = WorldGraph.FindNearestNode( pev->origin, this ); - iMyHullIndex = WorldGraph.HullIndex( this ); - - if ( iMyNode == NO_NODE ) - { - ALERT ( at_aiconsole, "BuildNearestRoute() - %s has no nearest node!\n", STRING(pev->classname)); - return FALSE; - } - - vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes - - // we'll do a rough sample to find nodes that are relatively nearby - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes; - - CNode &node = WorldGraph.Node( nodeNumber ); - WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here. - - // can I get there? - if (WorldGraph.NextNodeInRoute( iMyNode, nodeNumber, iMyHullIndex, 0 ) != iMyNode) - { - flDist = ( vecThreat - node.m_vecOrigin ).Length(); - - // is it close? - if ( flDist > flMinDist && flDist < flMaxDist) - { - // can I see where I want to be from there? - UTIL_TraceLine( node.m_vecOrigin + pev->view_ofs, vecLookersOffset, ignore_monsters, edict(), &tr ); - - if (tr.flFraction == 1.0) - { - // try to actually get there - if ( BuildRoute ( node.m_vecOrigin, bits_MF_TO_LOCATION, NULL ) ) - { - flMaxDist = flDist; - m_vecMoveGoal = node.m_vecOrigin; - return TRUE; // UNDONE: keep looking for something closer! - } - } - } - } - } - - return FALSE; -} - - - -//========================================================= -// BestVisibleEnemy - this functions searches the link -// list whose head is the caller's m_pLink field, and returns -// a pointer to the enemy entity in that list that is nearest the -// caller. -// -// !!!UNDONE - currently, this only returns the closest enemy. -// we'll want to consider distance, relationship, attack types, back turned, etc. -//========================================================= -edict_t *CMBaseMonster :: BestVisibleEnemy ( void ) -{ - int iNearest; - int iDist; - int iBestRelationship; - edict_t *pReturn; - edict_t *pEnt; - int edictList_index = 0; - - iNearest = 8192;// so first visible entity will become the closest. - - iBestRelationship = R_NO; - - pReturn = NULL; - - while (edictList_index < m_edictList_count) - { - pEnt = m_edictList[edictList_index]; - - if ( UTIL_IsPlayer(pEnt) ) - { - // it's a player... - iDist = ( pEnt->v.origin - pev->origin ).Length(); - - if ( iDist <= iNearest ) - { - iNearest = iDist; - iBestRelationship = R_NM; // player is always nemsis - pReturn = pEnt; - } - } - else if (pEnt->v.euser4 != NULL) - { - CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt)); - if ( pNextEnt->IsAlive() ) - { - if ( IRelationship( pNextEnt) > iBestRelationship ) - { - // this entity is disliked MORE than the entity that we - // currently think is the best visible enemy. No need to do - // a distance check, just get mad at this one for now. - iBestRelationship = IRelationship ( pNextEnt ); - iNearest = ( pNextEnt->pev->origin - pev->origin ).Length(); - pReturn = pEnt; - } - else if ( IRelationship( pNextEnt) == iBestRelationship ) - { - // this entity is disliked just as much as the entity that - // we currently think is the best visible enemy, so we only - // get mad at it if it is closer. - iDist = ( pNextEnt->pev->origin - pev->origin ).Length(); - - if ( iDist <= iNearest ) - { - iNearest = iDist; - iBestRelationship = IRelationship ( pNextEnt ); - pReturn = pEnt; - } - } - } - } - - edictList_index++; - } - - return pReturn; -} - - -//========================================================= -// MakeIdealYaw - gets a yaw value for the caller that would -// face the supplied vector. Value is stuffed into the monster's -// ideal_yaw -//========================================================= -void CMBaseMonster :: MakeIdealYaw( Vector vecTarget ) -{ - Vector vecProjection; - - // strafing monster needs to face 90 degrees away from its goal - if ( m_movementActivity == ACT_STRAFE_LEFT ) - { - vecProjection.x = -vecTarget.y; - vecProjection.y = vecTarget.x; - - pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin ); - } - else if ( m_movementActivity == ACT_STRAFE_RIGHT ) - { - vecProjection.x = vecTarget.y; - vecProjection.y = vecTarget.x; - - pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin ); - } - else - { - pev->ideal_yaw = UTIL_VecToYaw ( vecTarget - pev->origin ); - } -} - -//========================================================= -// FlYawDiff - returns the difference ( in degrees ) between -// monster's current yaw and ideal_yaw -// -// Positive result is left turn, negative is right turn -//========================================================= -float CMBaseMonster::FlYawDiff ( void ) -{ - float flCurrentYaw; - - flCurrentYaw = UTIL_AngleMod( pev->angles.y ); - - if ( flCurrentYaw == pev->ideal_yaw ) - { - return 0; - } - - - return UTIL_AngleDiff( pev->ideal_yaw, flCurrentYaw ); -} - - -//========================================================= -// Changeyaw - turns a monster towards its ideal_yaw -//========================================================= -float CMBaseMonster::ChangeYaw ( int yawSpeed ) -{ - float ideal, current, move, speed; - - current = UTIL_AngleMod( pev->angles.y ); - ideal = pev->ideal_yaw; - if (current != ideal) - { - speed = (float)yawSpeed * gpGlobals->frametime * 10; - move = ideal - current; - - if (ideal > current) - { - if (move >= 180) - move = move - 360; - } - else - { - if (move <= -180) - move = move + 360; - } - - if (move > 0) - {// turning to the monster's left - if (move > speed) - move = speed; - } - else - {// turning to the monster's right - if (move < -speed) - move = -speed; - } - - pev->angles.y = UTIL_AngleMod (current + move); - - // turn head in desired direction only if they have a turnable head - if (m_afCapability & bits_CAP_TURN_HEAD) - { - float yaw = pev->ideal_yaw - pev->angles.y; - if (yaw > 180) yaw -= 360; - if (yaw < -180) yaw += 360; - // yaw *= 0.8; - SetBoneController( 0, yaw ); - } - } - else - move = 0; - - return move; -} - -//========================================================= -// VecToYaw - turns a directional vector into a yaw value -// that points down that vector. -//========================================================= -float CMBaseMonster::VecToYaw ( Vector vecDir ) -{ - if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0) - return pev->angles.y; - - return UTIL_VecToYaw( vecDir ); -} - - -//========================================================= -// SetEyePosition -// -// queries the monster's model for $eyeposition and copies -// that vector to the monster's view_ofs -// -//========================================================= -void CMBaseMonster :: SetEyePosition ( void ) -{ - Vector vecEyePosition; - void *pmodel = GET_MODEL_PTR( ENT(pev) ); - - GetEyePosition( pmodel, vecEyePosition ); - - pev->view_ofs = vecEyePosition; - - if ( pev->view_ofs == g_vecZero ) - { - ALERT ( at_aiconsole, "%s has no view_ofs!\n", STRING ( pev->classname ) ); - } -} - -void CMBaseMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case MONSTER_EVENT_BODYDROP_HEAVY: - if ( pev->flags & FL_ONGROUND ) - { - if ( RANDOM_LONG( 0, 1 ) == 0 ) - { - EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM, 0, 90 ); - } - else - { - EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM, 0, 90 ); - } - } - break; - - case MONSTER_EVENT_BODYDROP_LIGHT: - if ( pev->flags & FL_ONGROUND ) - { - if ( RANDOM_LONG( 0, 1 ) == 0 ) - { - EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM ); - } - else - { - EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM ); - } - } - break; - - case MONSTER_EVENT_SWISHSOUND: - { - // NO MONSTER may use this anim event unless that monster's precache precaches this sound!!! - EMIT_SOUND( ENT(pev), CHAN_BODY, "zombie/claw_miss2.wav", 1, ATTN_NORM ); - break; - } - - default: - ALERT( at_aiconsole, "Unhandled animation event %d for %s\n", pEvent->event, STRING(pev->classname) ); - break; - - } -} - - -// Combat - -Vector CMBaseMonster :: GetGunPosition( ) -{ - UTIL_MakeVectors(pev->angles); - - // Vector vecSrc = pev->origin + gpGlobals->v_forward * 10; - //vecSrc.z = pevShooter->absmin.z + pevShooter->size.z * 0.7; - //vecSrc.z = pev->origin.z + (pev->view_ofs.z - 4); - Vector vecSrc = pev->origin - + gpGlobals->v_forward * m_HackedGunPos.y - + gpGlobals->v_right * m_HackedGunPos.x - + gpGlobals->v_up * m_HackedGunPos.z; - - return vecSrc; -} - - - - - -//========================================================= -// NODE GRAPH -//========================================================= - - - - - -//========================================================= -// FGetNodeRoute - tries to build an entire node path from -// the callers origin to the passed vector. If this is -// possible, ROUTE_SIZE waypoints will be copied into the -// callers m_Route. TRUE is returned if the operation -// succeeds (path is valid) or FALSE if failed (no path -// exists ) -//========================================================= -BOOL CMBaseMonster :: FGetNodeRoute ( Vector vecDest ) -{ - int iPath[ MAX_PATH_SIZE ]; - int iSrcNode, iDestNode; - int iResult; - int i; - int iNumToCopy; - - iSrcNode = WorldGraph.FindNearestNode ( pev->origin, this ); - iDestNode = WorldGraph.FindNearestNode ( vecDest, this ); - - if ( iSrcNode == -1 ) - { - // no node nearest self -// ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near self!\n" ); - return FALSE; - } - else if ( iDestNode == -1 ) - { - // no node nearest target -// ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near target!\n" ); - return FALSE; - } - - // valid src and dest nodes were found, so it's safe to proceed with - // find shortest path - int iNodeHull = WorldGraph.HullIndex( this ); // make this a monster virtual function - iResult = WorldGraph.FindShortestPath ( iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability ); - - if ( !iResult ) - { -#if 1 - ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode ); - return FALSE; -#else - BOOL bRoutingSave = WorldGraph.m_fRoutingComplete; - WorldGraph.m_fRoutingComplete = FALSE; - iResult = WorldGraph.FindShortestPath(iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability); - WorldGraph.m_fRoutingComplete = bRoutingSave; - if ( !iResult ) - { - ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode ); - return FALSE; - } - else - { - ALERT ( at_aiconsole, "Routing is inconsistent!" ); - } -#endif - } - - // there's a valid path within iPath now, so now we will fill the route array - // up with as many of the waypoints as it will hold. - - // don't copy ROUTE_SIZE entries if the path returned is shorter - // than ROUTE_SIZE!!! - if ( iResult < ROUTE_SIZE ) - { - iNumToCopy = iResult; - } - else - { - iNumToCopy = ROUTE_SIZE; - } - - for ( i = 0 ; i < iNumToCopy; i++ ) - { - m_Route[ i ].vecLocation = WorldGraph.m_pNodes[ iPath[ i ] ].m_vecOrigin; - m_Route[ i ].iType = bits_MF_TO_NODE; - } - - if ( iNumToCopy < ROUTE_SIZE ) - { - m_Route[ iNumToCopy ].vecLocation = vecDest; - m_Route[ iNumToCopy ].iType |= bits_MF_IS_GOAL; - } - - return TRUE; -} - -//========================================================= -// FindHintNode -//========================================================= -int CMBaseMonster :: FindHintNode ( void ) -{ - int i; - TraceResult tr; - - if ( !WorldGraph.m_fGraphPresent ) - { - ALERT ( at_aiconsole, "find_hintnode: graph not ready!\n" ); - return NO_NODE; - } - - if ( WorldGraph.m_iLastActiveIdleSearch >= WorldGraph.m_cNodes ) - { - WorldGraph.m_iLastActiveIdleSearch = 0; - } - - for ( i = 0; i < WorldGraph.m_cNodes ; i++ ) - { - int nodeNumber = (i + WorldGraph.m_iLastActiveIdleSearch) % WorldGraph.m_cNodes; - CNode &node = WorldGraph.Node( nodeNumber ); - - if ( node.m_sHintType ) - { - // this node has a hint. Take it if it is visible, the monster likes it, and the monster has an animation to match the hint's activity. - if ( FValidateHintType ( node.m_sHintType ) ) - { - if ( !node.m_sHintActivity || LookupActivity ( node.m_sHintActivity ) != ACTIVITY_NOT_AVAILABLE ) - { - UTIL_TraceLine ( pev->origin + pev->view_ofs, node.m_vecOrigin + pev->view_ofs, ignore_monsters, ENT(pev), &tr ); - - if ( tr.flFraction == 1.0 ) - { - WorldGraph.m_iLastActiveIdleSearch = nodeNumber + 1; // next monster that searches for hint nodes will start where we left off. - return nodeNumber;// take it! - } - } - } - } - } - - WorldGraph.m_iLastActiveIdleSearch = 0;// start at the top of the list for the next search. - - return NO_NODE; -} - - -void CMBaseMonster::ReportAIState( void ) -{ - ALERT_TYPE level = at_console; - - static const char *pStateNames[] = { "None", "Idle", "Combat", "Alert", "Hunt", "Prone", "Scripted", "Dead" }; - - ALERT( level, "%s: ", STRING(pev->classname) ); - if ( (int)m_MonsterState < ARRAYSIZE(pStateNames) ) - ALERT( level, "State: %s, ", pStateNames[m_MonsterState] ); - int i = 0; - while ( activity_map[i].type != 0 ) - { - if ( activity_map[i].type == (int)m_Activity ) - { - ALERT( level, "Activity %s, ", activity_map[i].name ); - break; - } - i++; - } - - if ( m_pSchedule ) - { - const char *pName = NULL; - pName = m_pSchedule->pName; - if ( !pName ) - pName = "Unknown"; - ALERT( level, "Schedule %s, ", pName ); - Task_t *pTask = GetTask(); - if ( pTask ) - ALERT( level, "Task %d (#%d), ", pTask->iTask, m_iScheduleIndex ); - } - else - ALERT( level, "No Schedule, " ); - - if ( m_hEnemy != NULL ) - ALERT( level, "\nEnemy is %s", STRING(m_hEnemy->v.classname) ); - else - ALERT( level, "No enemy" ); - - if ( IsMoving() ) - { - ALERT( level, " Moving " ); - if ( m_flMoveWaitFinished > gpGlobals->time ) - ALERT( level, ": Stopped for %.2f. ", m_flMoveWaitFinished - gpGlobals->time ); - else if ( m_IdealActivity == GetStoppedActivity() ) - ALERT( level, ": In stopped anim. " ); - } - - ALERT( level, "\n" ); - ALERT( level, "Yaw speed:%3.1f,Health: %3.1f\n", pev->yaw_speed, pev->health ); - if ( pev->spawnflags & SF_MONSTER_PRISONER ) - ALERT( level, " PRISONER! " ); - if ( pev->spawnflags & SF_MONSTER_PREDISASTER ) - ALERT( level, " Pre-Disaster! " ); - ALERT( level, "\n" ); -} - -//========================================================= -// KeyValue -// -// !!! netname entvar field is used in squadmonster for groupname!!! -//========================================================= -void CMBaseMonster :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "TriggerTarget")) - { - m_iszTriggerTarget = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "TriggerCondition")) - { - m_iTriggerCondition = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "displayname")) - { - m_szMonsterName = ALLOC_STRING( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "classify")) - { - m_iClassifyOverride = atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - { - CMBaseToggle::KeyValue( pkvd ); - } -} - -//========================================================= -// FCheckAITrigger - checks the monster's AI Trigger Conditions, -// if there is a condition, then checks to see if condition is -// met. If yes, the monster's TriggerTarget is fired. -// -// Returns TRUE if the target is fired. -//========================================================= -BOOL CMBaseMonster :: FCheckAITrigger ( void ) -{ - BOOL fFireTarget; - - if ( m_iTriggerCondition == AITRIGGER_NONE ) - { - // no conditions, so this trigger is never fired. - return FALSE; - } - - fFireTarget = FALSE; - - switch ( m_iTriggerCondition ) - { - case AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER: - if ( m_hEnemy != NULL && UTIL_IsPlayer(m_hEnemy) && HasConditions ( bits_COND_SEE_ENEMY ) ) - { - fFireTarget = TRUE; - } - break; - case AITRIGGER_SEEPLAYER_UNCONDITIONAL: - if ( HasConditions ( bits_COND_SEE_CLIENT ) ) - { - fFireTarget = TRUE; - } - break; - case AITRIGGER_SEEPLAYER_NOT_IN_COMBAT: - if ( HasConditions ( bits_COND_SEE_CLIENT ) && - m_MonsterState != MONSTERSTATE_COMBAT && - m_MonsterState != MONSTERSTATE_PRONE && - m_MonsterState != MONSTERSTATE_SCRIPT) - { - fFireTarget = TRUE; - } - break; - case AITRIGGER_TAKEDAMAGE: - if ( m_afConditions & ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - fFireTarget = TRUE; - } - break; - case AITRIGGER_DEATH: - if ( pev->deadflag != DEAD_NO ) - { - fFireTarget = TRUE; - } - break; - case AITRIGGER_HALFHEALTH: - if ( IsAlive() && pev->health <= ( pev->max_health / 2 ) ) - { - fFireTarget = TRUE; - } - break; -/* - - // !!!UNDONE - no persistant game state that allows us to track these two. - - case AITRIGGER_SQUADMEMBERDIE: - break; - case AITRIGGER_SQUADLEADERDIE: - break; -*/ - case AITRIGGER_HEARWORLD: - break; - case AITRIGGER_HEARPLAYER: - break; - case AITRIGGER_HEARCOMBAT: - break; - } - - if ( fFireTarget ) - { - // fire the target, then set the trigger conditions to NONE so we don't fire again - ALERT ( at_aiconsole, "AI Trigger Fire Target\n" ); - FireTargets( STRING( m_iszTriggerTarget ), this->edict(), this->edict(), USE_TOGGLE, 0 ); - m_iTriggerCondition = AITRIGGER_NONE; - return TRUE; - } - - return FALSE; -} - -//========================================================= -// FindLateralCover - attempts to locate a spot in the world -// directly to the left or right of the caller that will -// conceal them from view of pSightEnt -//========================================================= -#define COVER_CHECKS 5// how many checks are made -#define COVER_DELTA 48// distance between checks - -BOOL CMBaseMonster :: FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset ) -{ - TraceResult tr; - Vector vecBestOnLeft; - Vector vecBestOnRight; - Vector vecLeftTest; - Vector vecRightTest; - Vector vecStepRight; - int i; - - UTIL_MakeVectors ( pev->angles ); - vecStepRight = gpGlobals->v_right * COVER_DELTA; - vecStepRight.z = 0; - - vecLeftTest = vecRightTest = pev->origin; - - for ( i = 0 ; i < COVER_CHECKS ; i++ ) - { - vecLeftTest = vecLeftTest - vecStepRight; - vecRightTest = vecRightTest + vecStepRight; - - // it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first. - UTIL_TraceLine( vecThreat + vecViewOffset, vecLeftTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr); - - if (tr.flFraction != 1.0) - { - if ( FValidateCover ( vecLeftTest ) && CheckLocalMove( pev->origin, vecLeftTest, NULL, NULL ) == LOCALMOVE_VALID ) - { - if ( MoveToLocation( ACT_RUN, 0, vecLeftTest ) ) - { - return TRUE; - } - } - } - - // it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first. - UTIL_TraceLine(vecThreat + vecViewOffset, vecRightTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr); - - if ( tr.flFraction != 1.0 ) - { - if ( FValidateCover ( vecRightTest ) && CheckLocalMove( pev->origin, vecRightTest, NULL, NULL ) == LOCALMOVE_VALID ) - { - if ( MoveToLocation( ACT_RUN, 0, vecRightTest ) ) - { - return TRUE; - } - } - } - } - - return FALSE; -} - - -Vector CMBaseMonster :: ShootAtEnemy( const Vector &shootOrigin ) -{ - edict_t *pEnemy = m_hEnemy; - - if ( pEnemy ) - { - return ( (UTIL_BodyTarget(pEnemy, shootOrigin ) - pEnemy->v.origin) + m_vecEnemyLKP - shootOrigin ).Normalize(); - } - else - return gpGlobals->v_forward; -} - - - -//========================================================= -// FacingIdeal - tells us if a monster is facing its ideal -// yaw. Created this function because many spots in the -// code were checking the yawdiff against this magic -// number. Nicer to have it in one place if we're gonna -// be stuck with it. -//========================================================= -BOOL CMBaseMonster :: FacingIdeal( void ) -{ - if ( fabs( FlYawDiff() ) <= 0.006 )//!!!BUGBUG - no magic numbers!!! - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// FCanActiveIdle -//========================================================= -BOOL CMBaseMonster :: FCanActiveIdle ( void ) -{ - /* - if ( m_MonsterState == MONSTERSTATE_IDLE && m_IdealMonsterState == MONSTERSTATE_IDLE && !IsMoving() ) - { - return TRUE; - } - */ - return FALSE; -} - - -void CMBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) -{ - if ( pszSentence && IsAlive() ) - { - if ( pszSentence[0] == '!' ) - EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM ); -//jlb else -//jlb SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM ); - } -} - - -void CMBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CMBaseEntity *pListener ) -{ - PlaySentence( pszSentence, duration, volume, attenuation ); -} - - -void CMBaseMonster::SentenceStop( void ) -{ - EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE ); -} - - -void CMBaseMonster::CorpseFallThink( void ) -{ - if ( pev->flags & FL_ONGROUND ) - { - SetThink ( NULL ); - - SetSequenceBox( ); - UTIL_SetOrigin( pev, pev->origin );// link into world. - } - else - pev->nextthink = gpGlobals->time + 0.1; -} - -// Call after animation/pose is set up -void CMBaseMonster :: MonsterInitDead( void ) -{ - InitBoneControllers(); - - pev->solid = SOLID_BBOX; - pev->movetype = MOVETYPE_TOSS;// so he'll fall to ground - - pev->frame = 0; - ResetSequenceInfo( ); - pev->framerate = 0; - - // Copy health - pev->max_health = pev->health; - pev->deadflag = DEAD_DEAD; - - UTIL_SetSize(pev, g_vecZero, g_vecZero ); - UTIL_SetOrigin( pev, pev->origin ); - - // Setup health counters, etc. - BecomeDead(); - SetThink( &CMBaseMonster::CorpseFallThink ); - pev->nextthink = gpGlobals->time + 0.5; -} - -//========================================================= -// BBoxIsFlat - check to see if the monster's bounding box -// is lying flat on a surface (traces from all four corners -// are same length.) -//========================================================= -BOOL CMBaseMonster :: BBoxFlat ( void ) -{ - TraceResult tr; - Vector vecPoint; - float flXSize, flYSize; - float flLength; - float flLength2; - - flXSize = pev->size.x / 2; - flYSize = pev->size.y / 2; - - vecPoint.x = pev->origin.x + flXSize; - vecPoint.y = pev->origin.y + flYSize; - vecPoint.z = pev->origin.z; - - UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); - flLength = (vecPoint - tr.vecEndPos).Length(); - - vecPoint.x = pev->origin.x - flXSize; - vecPoint.y = pev->origin.y - flYSize; - - UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); - flLength2 = (vecPoint - tr.vecEndPos).Length(); - if ( flLength2 > flLength ) - { - return FALSE; - } - flLength = flLength2; - - vecPoint.x = pev->origin.x - flXSize; - vecPoint.y = pev->origin.y + flYSize; - UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); - flLength2 = (vecPoint - tr.vecEndPos).Length(); - if ( flLength2 > flLength ) - { - return FALSE; - } - flLength = flLength2; - - vecPoint.x = pev->origin.x + flXSize; - vecPoint.y = pev->origin.y - flYSize; - UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); - flLength2 = (vecPoint - tr.vecEndPos).Length(); - if ( flLength2 > flLength ) - { - return FALSE; - } - flLength = flLength2; - - return TRUE; -} - -//========================================================= -// Get Enemy - tries to find the best suitable enemy for the monster. -//========================================================= -BOOL CMBaseMonster :: GetEnemy ( void ) -{ - edict_t *pNewEnemy; - - if ( HasConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_NEMESIS) ) - { - pNewEnemy = BestVisibleEnemy(); - - if ( pNewEnemy != m_hEnemy && pNewEnemy != NULL) - { - // DO NOT mess with the monster's m_hEnemy pointer unless the schedule the monster is currently running will be interrupted - // by COND_NEW_ENEMY. This will eliminate the problem of monsters getting a new enemy while they are in a schedule that doesn't care, - // and then not realizing it by the time they get to a schedule that does. I don't feel this is a good permanent fix. - - if ( m_pSchedule ) - { - if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY ) - { - PushEnemy( m_hEnemy, m_vecEnemyLKP ); - SetConditions(bits_COND_NEW_ENEMY); - m_hEnemy = pNewEnemy; - m_vecEnemyLKP = m_hEnemy->v.origin; - } - // if the new enemy has an owner, take that one as well -//jlb if (pNewEnemy->v.owner != NULL) -//jlb { -//jlb edict_t *pOwner = pNewEnemy->v.owner; -//jlb if ( pOwner && (pOwner->v.flags & FL_MONSTER) && IRelationship( pOwner ) != R_NO ) -//jlb PushEnemy( pOwner, m_vecEnemyLKP ); -//jlb } - } - } - } - - // do we see a player? Allies use this to set the m_hEnemy pointer... - if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL)) - { - m_hEnemy = BestVisibleEnemy(); - m_hTargetEnt = m_hEnemy; - m_vecEnemyLKP = m_hEnemy->v.origin; - } - - // remember old enemies - if (m_hEnemy == NULL && PopEnemy( )) - { - if ( m_pSchedule ) - { - if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY ) - { - SetConditions(bits_COND_NEW_ENEMY); - } - } - } - - if ( m_hEnemy != NULL ) - { - // monster has an enemy. - return TRUE; - } - - return FALSE;// monster has no enemy -} - - -BOOL CMBaseMonster :: ShouldFadeOnDeath( void ) -{ - // if flagged to fade out or I have an owner (I came from a monster spawner) - if ( (pev->spawnflags & SF_MONSTER_FADECORPSE) || !FNullEnt( pev->owner ) ) - return TRUE; - - return FALSE; -} - - -void CMBaseMonster :: TaskFail( void ) -{ - SetConditions(bits_COND_TASK_FAILED); -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +/* + +===== monsters.cpp ======================================================== + + Monster-related utility code + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "nodes.h" +#include "monsters.h" +#include "animation.h" +#include "weapons.h" +#include "decals.h" + +#define MONSTER_CUT_CORNER_DIST 8 // 8 means the monster's bounding box is contained without the box of the node in WC + + +Vector VecBModelOrigin( entvars_t* pevBModel ); + +extern DLL_GLOBAL BOOL g_fDrawLines; + +extern CGraph WorldGraph;// the world node graph + + + +//========================================================= +// Eat - makes a monster full for a little while. +//========================================================= +void CMBaseMonster :: Eat ( float flFullDuration ) +{ + m_flHungryTime = gpGlobals->time + flFullDuration; +} + +//========================================================= +// FShouldEat - returns true if a monster is hungry. +//========================================================= +BOOL CMBaseMonster :: FShouldEat ( void ) +{ + if ( m_flHungryTime > gpGlobals->time ) + { + return FALSE; + } + + return TRUE; +} + +//========================================================= +// BarnacleVictimBitten - called +// by Barnacle victims when the barnacle pulls their head +// into its mouth +//========================================================= +void CMBaseMonster :: BarnacleVictimBitten ( entvars_t *pevBarnacle ) +{ + Schedule_t *pNewSchedule; + + pNewSchedule = GetScheduleOfType( SCHED_BARNACLE_VICTIM_CHOMP ); + + if ( pNewSchedule ) + { + ChangeSchedule( pNewSchedule ); + } +} + +//========================================================= +// BarnacleVictimReleased - called by barnacle victims when +// the host barnacle is killed. +//========================================================= +void CMBaseMonster :: BarnacleVictimReleased ( void ) +{ + m_IdealMonsterState = MONSTERSTATE_IDLE; + + pev->velocity = g_vecZero; + pev->movetype = MOVETYPE_STEP; +} + + +//========================================================= +// FValidateHintType - tells use whether or not the monster cares +// about the type of Hint Node given +//========================================================= +BOOL CMBaseMonster :: FValidateHintType ( short sHint ) +{ + return FALSE; +} + +//========================================================= +// Look - Base class monster function to find enemies or +// food by sight. iDistance is distance ( in units ) that the +// monster can see. +// +// Sets the sight bits of the m_afConditions mask to indicate +// which types of entities were sighted. +// Function also sets the Looker's m_pLink +// to the head of a link list that contains all visible ents. +// (linked via each ent's m_pLink field) +// +//========================================================= +void CMBaseMonster :: Look ( int iDistance ) +{ + int iSighted = 0; + + // DON'T let visibility information from last frame sit around! + ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); + + m_edictList_count = 0; + + edict_t *pSightEnt = NULL;// the current visible entity that we're dealing with + + // See no evil if prisoner is set + if ( !FBitSet( pev->spawnflags, SF_MONSTER_PRISONER ) ) + { + edict_t *pList[100]; + + Vector delta = Vector( iDistance, iDistance, iDistance ); + + // Find only monsters/clients in box, NOT limited to PVS + int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); + for ( int i = 0; i < count; i++ ) + { + pSightEnt = pList[i]; + // !!!temporarily only considering other monsters and clients, don't see prisoners + if ( pSightEnt != this->edict() && + !FBitSet( pSightEnt->v.spawnflags, SF_MONSTER_PRISONER ) && + pSightEnt->v.health > 0 ) + { + // is this a player AND are they alive? + if (UTIL_IsPlayer(pSightEnt) && UTIL_IsAlive(pSightEnt)) + { + // the looker will want to consider this entity + // don't check anything else about an entity that can't be seen. + if ( UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && + !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) ) + { + m_edictList[m_edictList_count] = pSightEnt; + m_edictList_count++; + + // if we see a client, remember that (mostly for scripted AI) + iSighted |= bits_COND_SEE_CLIENT; + + // is this monster NOT a scientist? + if (strcmp(STRING(pev->model), "models/scientist.mdl") != 0) + { + iSighted |= bits_COND_SEE_DISLIKE; + + if ( pSightEnt == m_hEnemy ) + { + // we know this ent is visible, so if it also happens to be our enemy, store that now. + iSighted |= bits_COND_SEE_ENEMY; + } + } + } + } + else if (pSightEnt->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pSightEnt)); + + // the looker will want to consider this entity + // don't check anything else about an entity that can't be seen, or an entity that you don't care about. + if ( IRelationship( pMonster ) != R_NO && UTIL_FInViewCone( pSightEnt, ENT(pev), m_flFieldOfView ) && + !FBitSet( pSightEnt->v.flags, FL_NOTARGET ) && UTIL_FVisible( pSightEnt, ENT(pev) ) ) + { + m_edictList[m_edictList_count] = pSightEnt; + m_edictList_count++; + + if ( ENT(pMonster->pev) == m_hEnemy ) + { + // we know this ent is visible, so if it also happens to be our enemy, store that now. + iSighted |= bits_COND_SEE_ENEMY; + } + + // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when + // we see monsters other than the Enemy. + switch ( IRelationship ( pMonster ) ) + { + case R_NM: + iSighted |= bits_COND_SEE_NEMESIS; + break; + case R_HT: + iSighted |= bits_COND_SEE_HATE; + break; + case R_DL: + iSighted |= bits_COND_SEE_DISLIKE; + break; + case R_FR: + iSighted |= bits_COND_SEE_FEAR; + break; + case R_AL: + break; + default: + ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pMonster->pev->classname ) ); + break; + } + } + } + } + } + } + + SetConditions( iSighted ); +} + + +//========================================================= +// Monster Think - calls out to core AI functions and handles this +// monster's specific animation events +//========================================================= +void CMBaseMonster :: MonsterThink ( void ) +{ + pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking. + + + RunAI(); + + float flInterval = StudioFrameAdvance( ); // animate +// start or end a fidget +// This needs a better home -- switching animations over time should be encapsulated on a per-activity basis +// perhaps MaintainActivity() or a ShiftAnimationOverTime() or something. + if ( m_MonsterState != MONSTERSTATE_SCRIPT && m_MonsterState != MONSTERSTATE_DEAD && m_Activity == ACT_IDLE && m_fSequenceFinished ) + { + int iSequence; + + if ( m_fSequenceLoops ) + { + // animation does loop, which means we're playing subtle idle. Might need to + // fidget. + iSequence = LookupActivity ( m_Activity ); + } + else + { + // animation that just ended doesn't loop! That means we just finished a fidget + // and should return to our heaviest weighted idle (the subtle one) + iSequence = LookupActivityHeaviest ( m_Activity ); + } + if ( iSequence != ACTIVITY_NOT_AVAILABLE ) + { + pev->sequence = iSequence; // Set to new anim (if it's there) + ResetSequenceInfo( ); + } + } + + DispatchAnimEvents( flInterval ); + + if ( !MovementIsComplete() ) + { + Move( flInterval ); + } +#if _DEBUG + else + { + if ( !TaskIsRunning() && !TaskIsComplete() ) + ALERT( at_error, "Schedule stalled!!\n" ); + } +#endif +} + +//========================================================= +// CMBaseMonster - USE - will make a monster angry at whomever +// activated it. +//========================================================= +void CMBaseMonster :: MonsterUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + m_IdealMonsterState = MONSTERSTATE_ALERT; +} + +//========================================================= +// Ignore conditions - before a set of conditions is allowed +// to interrupt a monster's schedule, this function removes +// conditions that we have flagged to interrupt the current +// schedule, but may not want to interrupt the schedule every +// time. (Pain, for instance) +//========================================================= +int CMBaseMonster :: IgnoreConditions ( void ) +{ + int iIgnoreConditions = 0; + + if ( !FShouldEat() ) + { + // not hungry? Ignore food smell. + iIgnoreConditions |= bits_COND_SMELL_FOOD; + } + + return iIgnoreConditions; +} + +//========================================================= +// RouteClear - zeroes out the monster's route array and goal +//========================================================= +void CMBaseMonster :: RouteClear ( void ) +{ + RouteNew(); + m_movementGoal = MOVEGOAL_NONE; + m_movementActivity = ACT_IDLE; + Forget( bits_MEMORY_MOVE_FAILED ); +} + +//========================================================= +// Route New - clears out a route to be changed, but keeps +// goal intact. +//========================================================= +void CMBaseMonster :: RouteNew ( void ) +{ + m_Route[ 0 ].iType = 0; + m_iRouteIndex = 0; +} + +//========================================================= +// FRouteClear - returns TRUE is the Route is cleared out +// ( invalid ) +//========================================================= +BOOL CMBaseMonster :: FRouteClear ( void ) +{ + if ( m_Route[ m_iRouteIndex ].iType == 0 || m_movementGoal == MOVEGOAL_NONE ) + return TRUE; + + return FALSE; +} + +//========================================================= +// FRefreshRoute - after calculating a path to the monster's +// target, this function copies as many waypoints as possible +// from that path to the monster's Route array +//========================================================= +BOOL CMBaseMonster :: FRefreshRoute ( void ) +{ + edict_t *pPathCorner; + int i; + BOOL returnCode; + + RouteNew(); + + returnCode = FALSE; + + switch( m_movementGoal ) + { + case MOVEGOAL_PATHCORNER: + { + // monster is on a path_corner loop + pPathCorner = m_pGoalEnt; + i = 0; + + while ( pPathCorner && i < ROUTE_SIZE ) + { + m_Route[ i ].iType = bits_MF_TO_PATHCORNER; + m_Route[ i ].vecLocation = pPathCorner->v.origin; + + pPathCorner = UTIL_GetNextTarget(pPathCorner); + + // Last path_corner in list? + if ( !pPathCorner ) + m_Route[i].iType |= bits_MF_IS_GOAL; + + i++; + } + } + returnCode = TRUE; + break; + + case MOVEGOAL_ENEMY: + returnCode = BuildRoute( m_vecEnemyLKP, bits_MF_TO_ENEMY, m_hEnemy ); + break; + + case MOVEGOAL_LOCATION: + returnCode = BuildRoute( m_vecMoveGoal, bits_MF_TO_LOCATION, NULL ); + break; + + case MOVEGOAL_TARGETENT: + if (m_hTargetEnt != NULL) + { + returnCode = BuildRoute( m_hTargetEnt->v.origin, bits_MF_TO_TARGETENT, m_hTargetEnt ); + } + break; + + case MOVEGOAL_NODE: + returnCode = FGetNodeRoute( m_vecMoveGoal ); +// if ( returnCode ) +// RouteSimplify( NULL ); + break; + } + + return returnCode; +} + + +BOOL CMBaseMonster::MoveToEnemy( Activity movementAct, float waitTime ) +{ + m_movementActivity = movementAct; + m_moveWaitTime = waitTime; + + m_movementGoal = MOVEGOAL_ENEMY; + return FRefreshRoute(); +} + + +BOOL CMBaseMonster::MoveToLocation( Activity movementAct, float waitTime, const Vector &goal ) +{ + m_movementActivity = movementAct; + m_moveWaitTime = waitTime; + + m_movementGoal = MOVEGOAL_LOCATION; + m_vecMoveGoal = goal; + return FRefreshRoute(); +} + + +BOOL CMBaseMonster::MoveToTarget( Activity movementAct, float waitTime ) +{ + m_movementActivity = movementAct; + m_moveWaitTime = waitTime; + + m_movementGoal = MOVEGOAL_TARGETENT; + return FRefreshRoute(); +} + + +BOOL CMBaseMonster::MoveToNode( Activity movementAct, float waitTime, const Vector &goal ) +{ + m_movementActivity = movementAct; + m_moveWaitTime = waitTime; + + m_movementGoal = MOVEGOAL_NODE; + m_vecMoveGoal = goal; + return FRefreshRoute(); +} + + +int ShouldSimplify( int routeType ) +{ + routeType &= ~bits_MF_IS_GOAL; + + if ( (routeType == bits_MF_TO_PATHCORNER) || (routeType & bits_MF_DONT_SIMPLIFY) ) + return FALSE; + return TRUE; +} + +//========================================================= +// RouteSimplify +// +// Attempts to make the route more direct by cutting out +// unnecessary nodes & cutting corners. +// +//========================================================= +void CMBaseMonster :: RouteSimplify( edict_t *pTargetEnt ) +{ + // BUGBUG: this doesn't work 100% yet + int i, count, outCount; + Vector vecStart; + WayPoint_t outRoute[ ROUTE_SIZE * 2 ]; // Any points except the ends can turn into 2 points in the simplified route + + count = 0; + + for ( i = m_iRouteIndex; i < ROUTE_SIZE; i++ ) + { + if ( !m_Route[i].iType ) + break; + else + count++; + if ( m_Route[i].iType & bits_MF_IS_GOAL ) + break; + } + // Can't simplify a direct route! + if ( count < 2 ) + { +// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 0, 255 ); + return; + } + + outCount = 0; + vecStart = pev->origin; + for ( i = 0; i < count-1; i++ ) + { + // Don't eliminate path_corners + if ( !ShouldSimplify( m_Route[m_iRouteIndex+i].iType ) ) + { + outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; + outCount++; + } + else if ( CheckLocalMove ( vecStart, m_Route[m_iRouteIndex+i+1].vecLocation, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + // Skip vert + continue; + } + else + { + Vector vecTest, vecSplit; + + // Halfway between this and next + vecTest = (m_Route[m_iRouteIndex+i+1].vecLocation + m_Route[m_iRouteIndex+i].vecLocation) * 0.5; + + // Halfway between this and previous + vecSplit = (m_Route[m_iRouteIndex+i].vecLocation + vecStart) * 0.5; + + int iType = (m_Route[m_iRouteIndex+i].iType | bits_MF_TO_DETOUR) & ~bits_MF_NOT_TO_MASK; + if ( CheckLocalMove ( vecStart, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + outRoute[outCount].iType = iType; + outRoute[outCount].vecLocation = vecTest; + } + else if ( CheckLocalMove ( vecSplit, vecTest, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + outRoute[outCount].iType = iType; + outRoute[outCount].vecLocation = vecSplit; + outRoute[outCount+1].iType = iType; + outRoute[outCount+1].vecLocation = vecTest; + outCount++; // Adding an extra point + } + else + { + outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; + } + } + // Get last point + vecStart = outRoute[ outCount ].vecLocation; + outCount++; + } + ASSERT( i < count ); + outRoute[outCount] = m_Route[ m_iRouteIndex + i ]; + outCount++; + + // Terminate + outRoute[outCount].iType = 0; + ASSERT( outCount < (ROUTE_SIZE*2) ); + +// Copy the simplified route, disable for testing + m_iRouteIndex = 0; + for ( i = 0; i < ROUTE_SIZE && i < outCount; i++ ) + { + m_Route[i] = outRoute[i]; + } + + // Terminate route + if ( i < ROUTE_SIZE ) + m_Route[i].iType = 0; + +// Debug, test movement code +#if 0 +// if ( CVAR_GET_FLOAT( "simplify" ) != 0 ) + DrawRoute( pev, outRoute, 0, 255, 0, 0 ); +// else + DrawRoute( pev, m_Route, m_iRouteIndex, 0, 255, 0 ); +#endif +} + +//========================================================= +// FBecomeProne - tries to send a monster into PRONE state. +// right now only used when a barnacle snatches someone, so +// may have some special case stuff for that. +//========================================================= +BOOL CMBaseMonster :: FBecomeProne ( void ) +{ + if ( FBitSet ( pev->flags, FL_ONGROUND ) ) + { + pev->flags -= FL_ONGROUND; + } + + m_IdealMonsterState = MONSTERSTATE_PRONE; + return TRUE; +} + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMBaseMonster :: CheckRangeAttack1 ( float flDot, float flDist ) +{ + if ( flDist > 64 && flDist <= 784 && flDot >= 0.5 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack2 +//========================================================= +BOOL CMBaseMonster :: CheckRangeAttack2 ( float flDot, float flDist ) +{ + if ( flDist > 64 && flDist <= 512 && flDot >= 0.5 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckMeleeAttack1 +//========================================================= +BOOL CMBaseMonster :: CheckMeleeAttack1 ( float flDot, float flDist ) +{ + // Decent fix to keep folks from kicking/punching hornets and snarks is to check the onground flag(sjb) + if ( flDist <= 64 && flDot >= 0.7 && m_hEnemy != NULL && FBitSet ( m_hEnemy->v.flags, FL_ONGROUND ) ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckMeleeAttack2 +//========================================================= +BOOL CMBaseMonster :: CheckMeleeAttack2 ( float flDot, float flDist ) +{ + if ( flDist <= 64 && flDot >= 0.7 ) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// CheckAttacks - sets all of the bits for attacks that the +// monster is capable of carrying out on the passed entity. +//========================================================= +void CMBaseMonster :: CheckAttacks ( edict_t *pTarget, float flDist ) +{ + Vector2D vec2LOS; + float flDot; + + UTIL_MakeVectors ( pev->angles ); + + vec2LOS = ( pTarget->v.origin - pev->origin ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); + + // we know the enemy is in front now. We'll find which attacks the monster is capable of by + // checking for corresponding Activities in the model file, then do the simple checks to validate + // those attack types. + + // Clear all attack conditions + ClearConditions( bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK1 |bits_COND_CAN_MELEE_ATTACK2 ); + + if ( m_afCapability & bits_CAP_RANGE_ATTACK1 ) + { + if ( CheckRangeAttack1 ( flDot, flDist ) ) + SetConditions( bits_COND_CAN_RANGE_ATTACK1 ); + } + if ( m_afCapability & bits_CAP_RANGE_ATTACK2 ) + { + if ( CheckRangeAttack2 ( flDot, flDist ) ) + SetConditions( bits_COND_CAN_RANGE_ATTACK2 ); + } + if ( m_afCapability & bits_CAP_MELEE_ATTACK1 ) + { + if ( CheckMeleeAttack1 ( flDot, flDist ) ) + SetConditions( bits_COND_CAN_MELEE_ATTACK1 ); + } + if ( m_afCapability & bits_CAP_MELEE_ATTACK2 ) + { + if ( CheckMeleeAttack2 ( flDot, flDist ) ) + SetConditions( bits_COND_CAN_MELEE_ATTACK2 ); + } +} + +//========================================================= +// CanCheckAttacks - prequalifies a monster to do more fine +// checking of potential attacks. +//========================================================= +BOOL CMBaseMonster :: FCanCheckAttacks ( void ) +{ + if ( HasConditions(bits_COND_SEE_ENEMY) && !HasConditions( bits_COND_ENEMY_TOOFAR ) ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// CheckEnemy - part of the Condition collection process, +// gets and stores data and conditions pertaining to a monster's +// enemy. Returns TRUE if Enemy LKP was updated. +//========================================================= +int CMBaseMonster :: CheckEnemy ( edict_t *pEnemy ) +{ + float flDistToEnemy; + int iUpdatedLKP;// set this to TRUE if you update the EnemyLKP in this function. + + iUpdatedLKP = FALSE; + ClearConditions ( bits_COND_ENEMY_FACING_ME ); + + if ( !UTIL_FVisible( pEnemy, ENT(pev) ) ) + { + ASSERT(!HasConditions(bits_COND_SEE_ENEMY)); + SetConditions( bits_COND_ENEMY_OCCLUDED ); + } + else + ClearConditions( bits_COND_ENEMY_OCCLUDED ); + + if ( !UTIL_IsAlive(pEnemy) ) + { + SetConditions ( bits_COND_ENEMY_DEAD ); + ClearConditions( bits_COND_SEE_ENEMY | bits_COND_ENEMY_OCCLUDED ); + return FALSE; + } + + Vector vecEnemyPos = pEnemy->v.origin; + // distance to enemy's origin + flDistToEnemy = ( vecEnemyPos - pev->origin ).Length(); + vecEnemyPos.z += pEnemy->v.size.z * 0.5; + // distance to enemy's head + float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length(); + if (flDistToEnemy2 < flDistToEnemy) + flDistToEnemy = flDistToEnemy2; + else + { + // distance to enemy's feet + vecEnemyPos.z -= pEnemy->v.size.z; + float flDistToEnemy2 = (vecEnemyPos - pev->origin).Length(); + if (flDistToEnemy2 < flDistToEnemy) + flDistToEnemy = flDistToEnemy2; + } + + if ( HasConditions( bits_COND_SEE_ENEMY ) ) + { + edict_t *pEnemyMonster; + + iUpdatedLKP = TRUE; + m_vecEnemyLKP = pEnemy->v.origin; + + pEnemyMonster = pEnemy; + + if ( pEnemyMonster ) + { + if ( UTIL_FInViewCone(pEnemyMonster, ENT(this->pev), m_flFieldOfView) ) + { + SetConditions ( bits_COND_ENEMY_FACING_ME ); + } + else + ClearConditions( bits_COND_ENEMY_FACING_ME ); + } + + if (pEnemy->v.velocity != Vector( 0, 0, 0)) + { + // trail the enemy a bit + m_vecEnemyLKP = m_vecEnemyLKP - pEnemy->v.velocity * RANDOM_FLOAT( -0.05, 0 ); + } + else + { + // UNDONE: use pev->oldorigin? + } + } + else if ( !HasConditions(bits_COND_ENEMY_OCCLUDED|bits_COND_SEE_ENEMY) && ( flDistToEnemy <= 256 ) ) + { + // if the enemy is not occluded, and unseen, that means it is behind or beside the monster. + // if the enemy is near enough the monster, we go ahead and let the monster know where the + // enemy is. + iUpdatedLKP = TRUE; + m_vecEnemyLKP = pEnemy->v.origin; + } + + if ( flDistToEnemy >= m_flDistTooFar ) + { + // enemy is very far away from monster + SetConditions( bits_COND_ENEMY_TOOFAR ); + } + else + ClearConditions( bits_COND_ENEMY_TOOFAR ); + + if ( FCanCheckAttacks() ) + { + CheckAttacks ( m_hEnemy, flDistToEnemy ); + } + + if ( m_movementGoal == MOVEGOAL_ENEMY ) + { + for ( int i = m_iRouteIndex; i < ROUTE_SIZE; i++ ) + { + if ( m_Route[ i ].iType == (bits_MF_IS_GOAL|bits_MF_TO_ENEMY) ) + { + // UNDONE: Should we allow monsters to override this distance (80?) + if ( (m_Route[ i ].vecLocation - m_vecEnemyLKP).Length() > 80 ) + { + // Refresh + FRefreshRoute(); + return iUpdatedLKP; + } + } + } + } + + return iUpdatedLKP; +} + +//========================================================= +// PushEnemy - remember the last few enemies, always remember the player +//========================================================= +void CMBaseMonster :: PushEnemy( edict_t *pEnemy, Vector &vecLastKnownPos ) +{ + int i; + + if (pEnemy == NULL) + return; + + // UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one. + for (i = 0; i < MAX_OLD_ENEMIES; i++) + { + if (m_hOldEnemy[i] == pEnemy) + return; + if (m_hOldEnemy[i] == NULL) // someone died, reuse their slot + break; + } + if (i >= MAX_OLD_ENEMIES) + return; + + m_hOldEnemy[i] = pEnemy; + m_vecOldEnemy[i] = vecLastKnownPos; +} + +//========================================================= +// PopEnemy - try remembering the last few enemies +//========================================================= +BOOL CMBaseMonster :: PopEnemy( ) +{ + // UNDONE: blah, this is bad, we should use a stack but I'm too lazy to code one. + for (int i = MAX_OLD_ENEMIES - 1; i >= 0; i--) + { + if (m_hOldEnemy[i] != NULL) + { + if (UTIL_IsAlive(m_hOldEnemy[i])) // cheat and know when they die + { + m_hEnemy = m_hOldEnemy[i]; + m_vecEnemyLKP = m_vecOldEnemy[i]; + // ALERT( at_console, "remembering\n"); + return TRUE; + } + else + { + m_hOldEnemy[i] = NULL; + } + } + } + return FALSE; +} + +//========================================================= +// SetActivity +//========================================================= +void CMBaseMonster :: SetActivity ( Activity NewActivity ) +{ + int iSequence; + + iSequence = LookupActivity ( NewActivity ); + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( pev->sequence != iSequence || !m_fSequenceLoops ) + { + // don't reset frame between walk and run + if ( !(m_Activity == ACT_WALK || m_Activity == ACT_RUN) || !(NewActivity == ACT_WALK || NewActivity == ACT_RUN)) + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo( ); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT ( at_aiconsole, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // In case someone calls this with something other than the ideal activity + m_IdealActivity = m_Activity; + + +} + +//========================================================= +// SetSequenceByName +//========================================================= +void CMBaseMonster :: SetSequenceByName ( char *szSequence ) +{ + int iSequence; + + iSequence = LookupSequence ( szSequence ); + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence > ACTIVITY_NOT_AVAILABLE ) + { + if ( pev->sequence != iSequence || !m_fSequenceLoops ) + { + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo( ); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT ( at_aiconsole, "%s has no sequence named:%f\n", STRING(pev->classname), szSequence ); + pev->sequence = 0; // Set to the reset anim (if it's there) + } +} + +//========================================================= +// CheckLocalMove - returns TRUE if the caller can walk a +// straight line from its current origin to the given +// location. If so, don't use the node graph! +// +// if a valid pointer to a int is passed, the function +// will fill that int with the distance that the check +// reached before hitting something. THIS ONLY HAPPENS +// IF THE LOCAL MOVE CHECK FAILS! +// +// !!!PERFORMANCE - should we try to load balance this? +// DON"T USE SETORIGIN! +//========================================================= +#define LOCAL_STEP_SIZE 16 +int CMBaseMonster :: CheckLocalMove ( const Vector &vecStart, const Vector &vecEnd, edict_t *pTarget, float *pflDist ) +{ + Vector vecStartPos;// record monster's position before trying the move + float flYaw; + float flDist; + float flStep, stepSize; + int iReturn; + + vecStartPos = pev->origin; + + + flYaw = UTIL_VecToYaw ( vecEnd - vecStart );// build a yaw that points to the goal. + flDist = ( vecEnd - vecStart ).Length2D();// get the distance. + iReturn = LOCALMOVE_VALID;// assume everything will be ok. + + // move the monster to the start of the local move that's to be checked. + UTIL_SetOrigin( pev, vecStart );// !!!BUGBUG - won't this fire triggers? - nope, SetOrigin doesn't fire + + if ( !(pev->flags & (FL_FLY|FL_SWIM)) ) + { + DROP_TO_FLOOR( ENT( pev ) );//make sure monster is on the floor! + } + + //pev->origin.z = vecStartPos.z;//!!!HACKHACK + +// pev->origin = vecStart; + +/* + if ( flDist > 1024 ) + { + // !!!PERFORMANCE - this operation may be too CPU intensive to try checks this large. + // We don't lose much here, because a distance this great is very likely + // to have something in the way. + + // since we've actually moved the monster during the check, undo the move. + pev->origin = vecStartPos; + return FALSE; + } +*/ + // this loop takes single steps to the goal. + for ( flStep = 0 ; flStep < flDist ; flStep += LOCAL_STEP_SIZE ) + { + stepSize = LOCAL_STEP_SIZE; + + if ( (flStep + LOCAL_STEP_SIZE) >= (flDist-1) ) + stepSize = (flDist - flStep) - 1; + +// UTIL_ParticleEffect ( pev->origin, g_vecZero, 255, 25 ); + + if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, WALKMOVE_CHECKONLY ) ) + {// can't take the next step, fail! + + if ( pflDist != NULL ) + { + *pflDist = flStep; + } + if ( pTarget && pTarget == gpGlobals->trace_ent ) + { + // if this step hits target ent, the move is legal. + iReturn = LOCALMOVE_VALID; + break; + } + else + { + // If we're going toward an entity, and we're almost getting there, it's OK. +// if ( pTarget && fabs( flDist - iStep ) < LOCAL_STEP_SIZE ) +// fReturn = TRUE; +// else + iReturn = LOCALMOVE_INVALID; + break; + } + + } + } + + if ( iReturn == LOCALMOVE_VALID && !(pev->flags & (FL_FLY|FL_SWIM) ) && (!pTarget || (pTarget->v.flags & FL_ONGROUND)) ) + { + // The monster can move to a spot UNDER the target, but not to it. Don't try to triangulate, go directly to the node graph. + // UNDONE: Magic # 64 -- this used to be pev->size.z but that won't work for small creatures like the headcrab + if ( fabs(vecEnd.z - pev->origin.z) > 64 ) + { + iReturn = LOCALMOVE_INVALID_DONT_TRIANGULATE; + } + } + /* + // uncommenting this block will draw a line representing the nearest legal move. + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); + WRITE_COORD(MSG_BROADCAST, pev->origin.x); + WRITE_COORD(MSG_BROADCAST, pev->origin.y); + WRITE_COORD(MSG_BROADCAST, pev->origin.z); + WRITE_COORD(MSG_BROADCAST, vecStart.x); + WRITE_COORD(MSG_BROADCAST, vecStart.y); + WRITE_COORD(MSG_BROADCAST, vecStart.z); + */ + + // since we've actually moved the monster during the check, undo the move. + UTIL_SetOrigin( pev, vecStartPos ); + + return iReturn; +} + + +//========================================================= +// AdvanceRoute - poorly named function that advances the +// m_iRouteIndex. If it goes beyond ROUTE_SIZE, the route +// is refreshed. +//========================================================= +void CMBaseMonster :: AdvanceRoute ( float distance ) +{ + + if ( m_iRouteIndex == ROUTE_SIZE - 1 ) + { + // time to refresh the route. + if ( !FRefreshRoute() ) + { + ALERT ( at_aiconsole, "Can't Refresh Route!!\n" ); + } + } + else + { + if ( ! (m_Route[ m_iRouteIndex ].iType & bits_MF_IS_GOAL) ) + { + // If we've just passed a path_corner, advance m_pGoalEnt + if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_PATHCORNER ) + m_pGoalEnt = UTIL_GetNextTarget(m_pGoalEnt); + + // IF both waypoints are nodes, then check for a link for a door and operate it. + // + if ( (m_Route[m_iRouteIndex].iType & bits_MF_TO_NODE) == bits_MF_TO_NODE + && (m_Route[m_iRouteIndex+1].iType & bits_MF_TO_NODE) == bits_MF_TO_NODE) + { + //ALERT(at_aiconsole, "SVD: Two nodes. "); + + int iSrcNode = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex].vecLocation, this ); + int iDestNode = WorldGraph.FindNearestNode(m_Route[m_iRouteIndex+1].vecLocation, this ); + + int iLink; + WorldGraph.HashSearch(iSrcNode, iDestNode, iLink); + + if ( iLink >= 0 && WorldGraph.m_pLinkPool[iLink].m_pLinkEnt != NULL ) + { + //ALERT(at_aiconsole, "A link. "); + if ( WorldGraph.HandleLinkEnt ( iSrcNode, WorldGraph.m_pLinkPool[iLink].m_pLinkEnt, m_afCapability, CGraph::NODEGRAPH_DYNAMIC ) ) + { + //ALERT(at_aiconsole, "usable."); + entvars_t *pevDoor = WorldGraph.m_pLinkPool[iLink].m_pLinkEnt; + if (pevDoor) + { +// m_flMoveWaitFinished = OpenDoorAndWait( pevDoor ); +// ALERT( at_aiconsole, "Wating for door %.2f\n", m_flMoveWaitFinished-gpGlobals->time ); + } + } + } + //ALERT(at_aiconsole, "\n"); + } + m_iRouteIndex++; + } + else // At goal!!! + { + if ( distance < m_flGroundSpeed * 0.2 /* FIX */ ) + { + MovementComplete(); + } + } + } +} + + +int CMBaseMonster :: RouteClassify( int iMoveFlag ) +{ + int movementGoal; + + movementGoal = MOVEGOAL_NONE; + + if ( iMoveFlag & bits_MF_TO_TARGETENT ) + movementGoal = MOVEGOAL_TARGETENT; + else if ( iMoveFlag & bits_MF_TO_ENEMY ) + movementGoal = MOVEGOAL_ENEMY; + else if ( iMoveFlag & bits_MF_TO_PATHCORNER ) + movementGoal = MOVEGOAL_PATHCORNER; + else if ( iMoveFlag & bits_MF_TO_NODE ) + movementGoal = MOVEGOAL_NODE; + else if ( iMoveFlag & bits_MF_TO_LOCATION ) + movementGoal = MOVEGOAL_LOCATION; + + return movementGoal; +} + +//========================================================= +// BuildRoute +//========================================================= +BOOL CMBaseMonster :: BuildRoute ( const Vector &vecGoal, int iMoveFlag, edict_t *pTarget ) +{ + float flDist; + Vector vecApex; + int iLocalMove; + + RouteNew(); + m_movementGoal = RouteClassify( iMoveFlag ); + +// so we don't end up with no moveflags + m_Route[ 0 ].vecLocation = vecGoal; + m_Route[ 0 ].iType = iMoveFlag | bits_MF_IS_GOAL; + +// check simple local move + iLocalMove = CheckLocalMove( pev->origin, vecGoal, pTarget, &flDist ); + + if ( iLocalMove == LOCALMOVE_VALID ) + { + // monster can walk straight there! + return TRUE; + } +// try to triangulate around any obstacles. + else if ( iLocalMove != LOCALMOVE_INVALID_DONT_TRIANGULATE && FTriangulate( pev->origin, vecGoal, flDist, pTarget, &vecApex ) ) + { + // there is a slightly more complicated path that allows the monster to reach vecGoal + m_Route[ 0 ].vecLocation = vecApex; + m_Route[ 0 ].iType = (iMoveFlag | bits_MF_TO_DETOUR); + + m_Route[ 1 ].vecLocation = vecGoal; + m_Route[ 1 ].iType = iMoveFlag | bits_MF_IS_GOAL; + + /* + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); + WRITE_COORD(MSG_BROADCAST, vecApex.x ); + WRITE_COORD(MSG_BROADCAST, vecApex.y ); + WRITE_COORD(MSG_BROADCAST, vecApex.z ); + WRITE_COORD(MSG_BROADCAST, vecApex.x ); + WRITE_COORD(MSG_BROADCAST, vecApex.y ); + WRITE_COORD(MSG_BROADCAST, vecApex.z + 128 ); + */ + + RouteSimplify( pTarget ); + return TRUE; + } + +// last ditch, try nodes + if ( FGetNodeRoute( vecGoal ) ) + { +// ALERT ( at_console, "Can get there on nodes\n" ); + m_vecMoveGoal = vecGoal; + RouteSimplify( pTarget ); + return TRUE; + } + + // b0rk + return FALSE; +} + + +//========================================================= +// InsertWaypoint - Rebuilds the existing route so that the +// supplied vector and moveflags are the first waypoint in +// the route, and fills the rest of the route with as much +// of the pre-existing route as possible +//========================================================= +void CMBaseMonster :: InsertWaypoint ( Vector vecLocation, int afMoveFlags ) +{ + int i, type; + + + // we have to save some Index and Type information from the real + // path_corner or node waypoint that the monster was trying to reach. This makes sure that data necessary + // to refresh the original path exists even in the new waypoints that don't correspond directy to a path_corner + // or node. + type = afMoveFlags | (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK); + + for ( i = ROUTE_SIZE-1; i > 0; i-- ) + m_Route[i] = m_Route[i-1]; + + m_Route[ m_iRouteIndex ].vecLocation = vecLocation; + m_Route[ m_iRouteIndex ].iType = type; +} + +//========================================================= +// FTriangulate - tries to overcome local obstacles by +// triangulating a path around them. +// +// iApexDist is how far the obstruction that we are trying +// to triangulate around is from the monster. +//========================================================= +BOOL CMBaseMonster :: FTriangulate ( const Vector &vecStart , const Vector &vecEnd, float flDist, edict_t *pTargetEnt, Vector *pApex ) +{ + Vector vecDir; + Vector vecForward; + Vector vecLeft;// the spot we'll try to triangulate to on the left + Vector vecRight;// the spot we'll try to triangulate to on the right + Vector vecTop;// the spot we'll try to triangulate to on the top + Vector vecBottom;// the spot we'll try to triangulate to on the bottom + Vector vecFarSide;// the spot that we'll move to after hitting the triangulated point, before moving on to our normal goal. + int i; + float sizeX, sizeZ; + + // If the hull width is less than 24, use 24 because CheckLocalMove uses a min of + // 24. + sizeX = pev->size.x; + if (sizeX < 24.0) + sizeX = 24.0; + else if (sizeX > 48.0) + sizeX = 48.0; + sizeZ = pev->size.z; + //if (sizeZ < 24.0) + // sizeZ = 24.0; + + vecForward = ( vecEnd - vecStart ).Normalize(); + + Vector vecDirUp(0,0,1); + vecDir = CrossProduct ( vecForward, vecDirUp); + + // start checking right about where the object is, picking two equidistant starting points, one on + // the left, one on the right. As we progress through the loop, we'll push these away from the obstacle, + // hoping to find a way around on either side. pev->size.x is added to the ApexDist in order to help select + // an apex point that insures that the monster is sufficiently past the obstacle before trying to turn back + // onto its original course. + + vecLeft = pev->origin + ( vecForward * ( flDist + sizeX ) ) - vecDir * ( sizeX * 3 ); + vecRight = pev->origin + ( vecForward * ( flDist + sizeX ) ) + vecDir * ( sizeX * 3 ); + if (pev->movetype == MOVETYPE_FLY) + { + vecTop = pev->origin + (vecForward * flDist) + (vecDirUp * sizeZ * 3); + vecBottom = pev->origin + (vecForward * flDist) - (vecDirUp * sizeZ * 3); + } + + vecFarSide = m_Route[ m_iRouteIndex ].vecLocation; + + vecDir = vecDir * sizeX * 2; + if (pev->movetype == MOVETYPE_FLY) + vecDirUp = vecDirUp * sizeZ * 2; + + for ( i = 0 ; i < 8; i++ ) + { +// Debug, Draw the triangulation +#if 0 + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_COORD( vecRight.x ); + WRITE_COORD( vecRight.y ); + WRITE_COORD( vecRight.z ); + MESSAGE_END(); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_COORD( vecLeft.x ); + WRITE_COORD( vecLeft.y ); + WRITE_COORD( vecLeft.z ); + MESSAGE_END(); +#endif + +#if 0 + if (pev->movetype == MOVETYPE_FLY) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_COORD( vecTop.x ); + WRITE_COORD( vecTop.y ); + WRITE_COORD( vecTop.z ); + MESSAGE_END(); + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_COORD( vecBottom.x ); + WRITE_COORD( vecBottom.y ); + WRITE_COORD( vecBottom.z ); + MESSAGE_END(); + } +#endif + + if ( CheckLocalMove( pev->origin, vecRight, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( CheckLocalMove ( vecRight, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( pApex ) + { + *pApex = vecRight; + } + + return TRUE; + } + } + if ( CheckLocalMove( pev->origin, vecLeft, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( CheckLocalMove ( vecLeft, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( pApex ) + { + *pApex = vecLeft; + } + + return TRUE; + } + } + + if (pev->movetype == MOVETYPE_FLY) + { + if ( CheckLocalMove( pev->origin, vecTop, pTargetEnt, NULL ) == LOCALMOVE_VALID) + { + if ( CheckLocalMove ( vecTop, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( pApex ) + { + *pApex = vecTop; + //ALERT(at_aiconsole, "triangulate over\n"); + } + + return TRUE; + } + } +#if 1 + if ( CheckLocalMove( pev->origin, vecBottom, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( CheckLocalMove ( vecBottom, vecFarSide, pTargetEnt, NULL ) == LOCALMOVE_VALID ) + { + if ( pApex ) + { + *pApex = vecBottom; + //ALERT(at_aiconsole, "triangulate under\n"); + } + + return TRUE; + } + } +#endif + } + + vecRight = vecRight + vecDir; + vecLeft = vecLeft - vecDir; + if (pev->movetype == MOVETYPE_FLY) + { + vecTop = vecTop + vecDirUp; + vecBottom = vecBottom - vecDirUp; + } + } + + return FALSE; +} + +//========================================================= +// Move - take a single step towards the next ROUTE location +//========================================================= +#define DIST_TO_CHECK 200 + +void CMBaseMonster :: Move ( float flInterval ) +{ + float flWaypointDist; + float flCheckDist; + float flDist;// how far the lookahead check got before hitting an object. + Vector vecDir; + Vector vecApex; + edict_t *pTargetEnt; + + // Don't move if no valid route + if ( FRouteClear() ) + { + // If we still have a movement goal, then this is probably a route truncated by SimplifyRoute() + // so refresh it. + if ( m_movementGoal == MOVEGOAL_NONE || !FRefreshRoute() ) + { + ALERT( at_aiconsole, "Tried to move with no route!\n" ); + TaskFail(); + return; + } + } + + if ( m_flMoveWaitFinished > gpGlobals->time ) + return; + +// Debug, test movement code +#if 0 +// if ( CVAR_GET_FLOAT("stopmove" ) != 0 ) + { + if ( m_movementGoal == MOVEGOAL_ENEMY ) + RouteSimplify( m_hEnemy ); + else + RouteSimplify( m_hTargetEnt ); + FRefreshRoute(); + return; + } +#else +// Debug, draw the route +// DrawRoute( pev, m_Route, m_iRouteIndex, 0, 200, 0 ); +#endif + + // if the monster is moving directly towards an entity (enemy for instance), we'll set this pointer + // to that entity for the CheckLocalMove and Triangulate functions. + pTargetEnt = NULL; + + // local move to waypoint. + vecDir = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Normalize(); + flWaypointDist = ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin ).Length2D(); + + MakeIdealYaw ( m_Route[ m_iRouteIndex ].vecLocation ); + ChangeYaw ( pev->yaw_speed ); + + // if the waypoint is closer than CheckDist, CheckDist is the dist to waypoint + if ( flWaypointDist < DIST_TO_CHECK ) + { + flCheckDist = flWaypointDist; + } + else + { + flCheckDist = DIST_TO_CHECK; + } + + if ( (m_Route[ m_iRouteIndex ].iType & (~bits_MF_NOT_TO_MASK)) == bits_MF_TO_ENEMY ) + { + // only on a PURE move to enemy ( i.e., ONLY MF_TO_ENEMY set, not MF_TO_ENEMY and DETOUR ) + pTargetEnt = m_hEnemy; + } + else if ( (m_Route[ m_iRouteIndex ].iType & ~bits_MF_NOT_TO_MASK) == bits_MF_TO_TARGETENT ) + { + pTargetEnt = m_hTargetEnt; + } + + // !!!BUGBUG - CheckDist should be derived from ground speed. + // If this fails, it should be because of some dynamic entity blocking this guy. + // We've already checked this path, so we should wait and time out if the entity doesn't move + flDist = 0; + if ( CheckLocalMove ( pev->origin, pev->origin + vecDir * flCheckDist, pTargetEnt, &flDist ) != LOCALMOVE_VALID ) + { + // Can't move, stop + Stop(); + // Blocking entity is in global trace_ent + CMBaseMonster *pBlocker = GetClassPtr((CMBaseMonster *)VARS(gpGlobals->trace_ent)); + if (pBlocker) + { + Blocked( pBlocker->edict() ); + } + + if ( pBlocker && m_moveWaitTime > 0 && pBlocker->IsMoving() && !pBlocker->IsPlayer() && (gpGlobals->time-m_flMoveWaitFinished) > 3.0 ) + { + // Can we still move toward our target? + if ( flDist < m_flGroundSpeed ) + { + // No, Wait for a second + m_flMoveWaitFinished = gpGlobals->time + m_moveWaitTime; + return; + } + // Ok, still enough room to take a step + } + else + { + // try to triangulate around whatever is in the way. + if ( FTriangulate( pev->origin, m_Route[ m_iRouteIndex ].vecLocation, flDist, pTargetEnt, &vecApex ) ) + { + InsertWaypoint( vecApex, bits_MF_TO_DETOUR ); + RouteSimplify( pTargetEnt ); + } + else + { +// ALERT ( at_aiconsole, "Couldn't Triangulate\n" ); + Stop(); + // Only do this once until your route is cleared + if ( m_moveWaitTime > 0 && !(m_afMemory & bits_MEMORY_MOVE_FAILED) ) + { + FRefreshRoute(); + if ( FRouteClear() ) + { + TaskFail(); + } + else + { + // Don't get stuck + if ( (gpGlobals->time - m_flMoveWaitFinished) < 0.2 ) + Remember( bits_MEMORY_MOVE_FAILED ); + + m_flMoveWaitFinished = gpGlobals->time + 0.1; + } + } + else + { +//jlb TaskFail(); + ALERT( at_aiconsole, "%s Failed to move (%d)!\n", STRING(pev->classname), HasMemory( bits_MEMORY_MOVE_FAILED ) ); + //ALERT( at_aiconsole, "%f, %f, %f\n", pev->origin.z, (pev->origin + (vecDir * flCheckDist)).z, m_Route[m_iRouteIndex].vecLocation.z ); + } + return; + } + } + } + + // close enough to the target, now advance to the next target. This is done before actually reaching + // the target so that we get a nice natural turn while moving. + if ( ShouldAdvanceRoute( flWaypointDist ) )///!!!BUGBUG- magic number + { + AdvanceRoute( flWaypointDist ); + } + + // Might be waiting for a door + if ( m_flMoveWaitFinished > gpGlobals->time ) + { + Stop(); + return; + } + + // UNDONE: this is a hack to quit moving farther than it has looked ahead. + if (flCheckDist < m_flGroundSpeed * flInterval) + { + flInterval = flCheckDist / m_flGroundSpeed; + // ALERT( at_console, "%.02f\n", flInterval ); + } + MoveExecute( pTargetEnt, vecDir, flInterval ); + + if ( MovementIsComplete() ) + { + Stop(); + RouteClear(); + } +} + + +BOOL CMBaseMonster:: ShouldAdvanceRoute( float flWaypointDist ) +{ + if ( flWaypointDist <= MONSTER_CUT_CORNER_DIST ) + { + // ALERT( at_console, "cut %f\n", flWaypointDist ); + return TRUE; + } + + return FALSE; +} + + +void CMBaseMonster::MoveExecute( edict_t *pTargetEnt, const Vector &vecDir, float flInterval ) +{ +// float flYaw = UTIL_VecToYaw ( m_Route[ m_iRouteIndex ].vecLocation - pev->origin );// build a yaw that points to the goal. +// WALK_MOVE( ENT(pev), flYaw, m_flGroundSpeed * flInterval, WALKMOVE_NORMAL ); + if ( m_IdealActivity != m_movementActivity ) + m_IdealActivity = m_movementActivity; + + float flTotal = m_flGroundSpeed * pev->framerate * flInterval; + float flStep; + while (flTotal > 0.001) + { + // don't walk more than 16 units or stairs stop working + flStep = min( 16.0f, flTotal ); + UTIL_MoveToOrigin ( ENT(pev), m_Route[ m_iRouteIndex ].vecLocation, flStep, MOVE_NORMAL ); + flTotal -= flStep; + } + // ALERT( at_console, "dist %f\n", m_flGroundSpeed * pev->framerate * flInterval ); +} + + +//========================================================= +// MonsterInit - after a monster is spawned, it needs to +// be dropped into the world, checked for mobility problems, +// and put on the proper path, if any. This function does +// all of those things after the monster spawns. Any +// initialization that should take place for all monsters +// goes here. +//========================================================= +void CMBaseMonster :: MonsterInit ( void ) +{ + // Set fields common to all monsters + pev->effects = 0; + pev->takedamage = DAMAGE_AIM; + pev->ideal_yaw = pev->angles.y; + pev->max_health = pev->health; + pev->deadflag = DEAD_NO; + m_IdealMonsterState = MONSTERSTATE_IDLE;// Assume monster will be idle, until proven otherwise + + m_IdealActivity = ACT_IDLE; + + SetBits (pev->flags, FL_MONSTER); + if ( pev->spawnflags & SF_MONSTER_HITMONSTERCLIP ) + pev->flags |= FL_MONSTERCLIP; + + ClearSchedule(); + RouteClear(); + InitBoneControllers( ); // FIX: should be done in Spawn + + m_iHintNode = NO_NODE; + + m_afMemory = MEMORY_CLEAR; + + m_hEnemy = NULL; + m_hTargetEnt = NULL; + + for (int i=0; i < MAX_OLD_ENEMIES; i++) + m_hOldEnemy[ i ] = NULL; + + m_flDistTooFar = 1024.0; + m_flDistLook = 2048.0; + + // set eye position + SetEyePosition(); + + SetThink( &CMBaseMonster::MonsterInitThink ); + pev->nextthink = gpGlobals->time + 0.1; + SetUse ( &CMBaseMonster::MonsterUse ); +} + +//========================================================= +// MonsterInitThink - Calls StartMonster. Startmonster is +// virtual, but this function cannot be +//========================================================= +void CMBaseMonster :: MonsterInitThink ( void ) +{ + StartMonster(); +} + +//========================================================= +// StartMonster - final bit of initization before a monster +// is turned over to the AI. +//========================================================= +void CMBaseMonster :: StartMonster ( void ) +{ + // update capabilities + if ( LookupActivity ( ACT_RANGE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE ) + { + m_afCapability |= bits_CAP_RANGE_ATTACK1; + } + if ( LookupActivity ( ACT_RANGE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE ) + { + m_afCapability |= bits_CAP_RANGE_ATTACK2; + } + if ( LookupActivity ( ACT_MELEE_ATTACK1 ) != ACTIVITY_NOT_AVAILABLE ) + { + m_afCapability |= bits_CAP_MELEE_ATTACK1; + } + if ( LookupActivity ( ACT_MELEE_ATTACK2 ) != ACTIVITY_NOT_AVAILABLE ) + { + m_afCapability |= bits_CAP_MELEE_ATTACK2; + } + + // Raise monster off the floor one unit, then drop to floor + if ( (pev->movetype != MOVETYPE_FLY) && !FBitSet( pev->spawnflags, SF_MONSTER_FALL_TO_GROUND ) ) + { + pev->origin.z += 1; + DROP_TO_FLOOR ( ENT(pev) ); + + // Try to move the monster to make sure it's not stuck in a brush. + if (!WALK_MOVE ( ENT(pev), 0, 0, WALKMOVE_NORMAL ) ) + { +//jlb ALERT(at_console, "Monster %s stuck in wall", STRING(pev->classname)); +//jlb stuck +//jlb pev->effects = EF_BRIGHTFIELD; + } + } + else + { + pev->flags &= ~FL_ONGROUND; + } + + if ( !FStringNull(pev->target) )// this monster has a target + { + // Find the monster's initial target entity, stash it + m_pGoalEnt = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING( pev->target ) ); + + if ( !m_pGoalEnt ) + { + ALERT(at_error, "ReadyMonster()--%s couldn't find target %s", STRING(pev->classname), STRING(pev->target)); + } + else + { + // Monster will start turning towards his destination + MakeIdealYaw ( m_pGoalEnt->v.origin ); + + // JAY: How important is this error message? Big Momma doesn't obey this rule, so I took it out. +#if 0 + // At this point, we expect only a path_corner as initial goal + if (!FClassnameIs( m_pGoalEnt->pev, "path_corner")) + { + ALERT(at_warning, "ReadyMonster--monster's initial goal '%s' is not a path_corner", STRING(pev->target)); + } +#endif + + // set the monster up to walk a path corner path. + // !!!BUGBUG - this is a minor bit of a hack. + // JAYJAY + m_movementGoal = MOVEGOAL_PATHCORNER; + + if ( pev->movetype == MOVETYPE_FLY ) + m_movementActivity = ACT_FLY; + else + m_movementActivity = ACT_WALK; + + if ( !FRefreshRoute() ) + { + ALERT ( at_aiconsole, "Can't Create Route!\n" ); + } + SetState( MONSTERSTATE_IDLE ); + ChangeSchedule( GetScheduleOfType( SCHED_IDLE_WALK ) ); + } + } + + //SetState ( m_IdealMonsterState ); + //SetActivity ( m_IdealActivity ); + + // Delay drop to floor to make sure each door in the level has had its chance to spawn + // Spread think times so that they don't all happen at the same time (Carmack) + SetThink ( &CMBaseMonster::CallMonsterThink ); + pev->nextthink += RANDOM_FLOAT(0.1, 0.4); // spread think times. + + if ( !FStringNull(pev->targetname) )// wait until triggered + { + SetState( MONSTERSTATE_IDLE ); + // UNDONE: Some scripted sequence monsters don't have an idle? + SetActivity( ACT_IDLE ); + ChangeSchedule( GetScheduleOfType( SCHED_WAIT_TRIGGER ) ); + } +} + + +void CMBaseMonster :: MovementComplete( void ) +{ + switch( m_iTaskStatus ) + { + case TASKSTATUS_NEW: + case TASKSTATUS_RUNNING: + m_iTaskStatus = TASKSTATUS_RUNNING_TASK; + break; + + case TASKSTATUS_RUNNING_MOVEMENT: + TaskComplete(); + break; + + case TASKSTATUS_RUNNING_TASK: + ALERT( at_error, "Movement completed twice!\n" ); + break; + + case TASKSTATUS_COMPLETE: + break; + } + m_movementGoal = MOVEGOAL_NONE; +} + + +int CMBaseMonster::TaskIsRunning( void ) +{ + if ( m_iTaskStatus != TASKSTATUS_COMPLETE && + m_iTaskStatus != TASKSTATUS_RUNNING_MOVEMENT ) + return 1; + + return 0; +} + +//========================================================= +// IRelationship - returns an integer that describes the +// relationship between two types of monster. +//========================================================= +int CMBaseMonster::IRelationship ( CMBaseEntity *pTarget ) +{ + static int iEnemy[16][16] = + { // NONE MACH PLYR HPASS HMIL AMIL APASS AMONST APREY APRED INSECT PLRALY PBWPN ABWPN RXPIT RXSHK + /*NONE*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, + /*MACHINE*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_DL, R_DL, R_DL, R_DL }, + /*PLAYER*/ { R_NO ,R_DL ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_DL, R_DL, R_DL, R_DL }, + /*HUMANPASSIVE*/{ R_NO ,R_FR ,R_AL ,R_AL ,R_HT ,R_DL ,R_DL ,R_HT ,R_DL ,R_DL ,R_NO ,R_AL, R_NO, R_NO, R_FR, R_FR }, + /*HUMANMILITAR*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_AL ,R_HT ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_HT, R_HT }, + /*ALIENMILITAR*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_HT ,R_AL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_HT }, + /*ALIENPASSIVE*/{ R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO, R_NO, R_NO, R_NO, R_NO }, + /*ALIENMONSTER*/{ R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_NO, R_NO }, + /*ALIENPREY */{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_NO ,R_FR ,R_NO ,R_DL, R_NO, R_NO, R_FR, R_NO }, + /*ALIENPREDATO*/{ R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO ,R_NO ,R_HT ,R_NO ,R_NO ,R_DL, R_NO, R_NO, R_DL, R_DL }, + /*INSECT*/ { R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR ,R_FR ,R_FR ,R_FR ,R_NO ,R_FR, R_NO, R_NO, R_NO, R_NO }, + /*PLAYERALLY*/ { R_NO ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_NO, R_DL, R_DL }, + /*PBIOWEAPON*/ { R_NO ,R_NO ,R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_NO, R_NO, R_DL, R_DL, R_DL }, + /*ABIOWEAPON*/ { R_NO ,R_NO ,R_DL ,R_DL ,R_DL ,R_AL ,R_NO ,R_DL ,R_DL ,R_NO ,R_NO ,R_DL, R_DL, R_NO, R_DL, R_DL }, + /*RXPITDRONE*/ { R_NO ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_DL ,R_NO ,R_DL ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL }, + /*RXSHOCKTRP*/ { R_NO ,R_DL ,R_HT ,R_DL ,R_HT ,R_HT ,R_DL ,R_NO ,R_NO ,R_DL ,R_NO ,R_DL, R_NO, R_NO, R_AL, R_AL } + }; + + return iEnemy[ Classify() ][ pTarget->Classify() ]; +} + +//========================================================= +// FindCover - tries to find a nearby node that will hide +// the caller from its enemy. +// +// If supplied, search will return a node at least as far +// away as MinDist, but no farther than MaxDist. +// if MaxDist isn't supplied, it defaults to a reasonable +// value +//========================================================= +// UNDONE: Should this find the nearest node? + +//float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) + +BOOL CMBaseMonster :: FindCover ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ) +{ + int i; + int iMyHullIndex; + int iMyNode; + int iThreatNode; + float flDist; + Vector vecLookersOffset; + TraceResult tr; + + if ( !flMaxDist ) + { + // user didn't supply a MaxDist, so work up a crazy one. + flMaxDist = 784; + } + + if ( flMinDist > 0.5 * flMaxDist) + { +#if _DEBUG + ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); +#endif + flMinDist = 0.5 * flMaxDist; + } + + if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) + { + ALERT ( at_aiconsole, "Graph not ready for findcover!\n" ); + return FALSE; + } + + iMyNode = WorldGraph.FindNearestNode( pev->origin, this ); + iThreatNode = WorldGraph.FindNearestNode ( vecThreat, this ); + iMyHullIndex = WorldGraph.HullIndex( this ); + + if ( iMyNode == NO_NODE ) + { + ALERT ( at_aiconsole, "FindCover() - %s has no nearest node!\n", STRING(pev->classname)); + return FALSE; + } + if ( iThreatNode == NO_NODE ) + { + // ALERT ( at_aiconsole, "FindCover() - Threat has no nearest node!\n" ); + iThreatNode = iMyNode; + // return FALSE; + } + + vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes + + // we'll do a rough sample to find nodes that are relatively nearby + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes; + + CNode &node = WorldGraph.Node( nodeNumber ); + WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here. + + // could use an optimization here!! + flDist = ( pev->origin - node.m_vecOrigin ).Length(); + + // DON'T do the trace check on a node that is farther away than a node that we've already found to + // provide cover! Also make sure the node is within the mins/maxs of the search. + if ( flDist >= flMinDist && flDist < flMaxDist ) + { + UTIL_TraceLine ( node.m_vecOrigin + vecViewOffset, vecLookersOffset, ignore_monsters, ignore_glass, ENT(pev), &tr ); + + // if this node will block the threat's line of sight to me... + if ( tr.flFraction != 1.0 ) + { + // ..and is also closer to me than the threat, or the same distance from myself and the threat the node is good. + if ( ( iMyNode == iThreatNode ) || WorldGraph.PathLength( iMyNode, nodeNumber, iMyHullIndex, m_afCapability ) <= WorldGraph.PathLength( iThreatNode, nodeNumber, iMyHullIndex, m_afCapability ) ) + { + if ( FValidateCover ( node.m_vecOrigin ) && MoveToLocation( ACT_RUN, 0, node.m_vecOrigin ) ) + { + /* + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + + WRITE_COORD( node.m_vecOrigin.x ); + WRITE_COORD( node.m_vecOrigin.y ); + WRITE_COORD( node.m_vecOrigin.z ); + + WRITE_COORD( vecLookersOffset.x ); + WRITE_COORD( vecLookersOffset.y ); + WRITE_COORD( vecLookersOffset.z ); + MESSAGE_END(); + */ + + return TRUE; + } + } + } + } + } + return FALSE; +} + + +//========================================================= +// BuildNearestRoute - tries to build a route as close to the target +// as possible, even if there isn't a path to the final point. +// +// If supplied, search will return a node at least as far +// away as MinDist from vecThreat, but no farther than MaxDist. +// if MaxDist isn't supplied, it defaults to a reasonable +// value +//========================================================= +BOOL CMBaseMonster :: BuildNearestRoute ( Vector vecThreat, Vector vecViewOffset, float flMinDist, float flMaxDist ) +{ + int i; + int iMyHullIndex; + int iMyNode; + float flDist; + Vector vecLookersOffset; + TraceResult tr; + + if ( !flMaxDist ) + { + // user didn't supply a MaxDist, so work up a crazy one. + flMaxDist = 784; + } + + if ( flMinDist > 0.5 * flMaxDist) + { +#if _DEBUG + ALERT ( at_console, "FindCover MinDist (%.0f) too close to MaxDist (%.0f)\n", flMinDist, flMaxDist ); +#endif + flMinDist = 0.5 * flMaxDist; + } + + if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) + { + ALERT ( at_aiconsole, "Graph not ready for BuildNearestRoute!\n" ); + return FALSE; + } + + iMyNode = WorldGraph.FindNearestNode( pev->origin, this ); + iMyHullIndex = WorldGraph.HullIndex( this ); + + if ( iMyNode == NO_NODE ) + { + ALERT ( at_aiconsole, "BuildNearestRoute() - %s has no nearest node!\n", STRING(pev->classname)); + return FALSE; + } + + vecLookersOffset = vecThreat + vecViewOffset;// calculate location of enemy's eyes + + // we'll do a rough sample to find nodes that are relatively nearby + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + int nodeNumber = (i + WorldGraph.m_iLastCoverSearch) % WorldGraph.m_cNodes; + + CNode &node = WorldGraph.Node( nodeNumber ); + WorldGraph.m_iLastCoverSearch = nodeNumber + 1; // next monster that searches for cover node will start where we left off here. + + // can I get there? + if (WorldGraph.NextNodeInRoute( iMyNode, nodeNumber, iMyHullIndex, 0 ) != iMyNode) + { + flDist = ( vecThreat - node.m_vecOrigin ).Length(); + + // is it close? + if ( flDist > flMinDist && flDist < flMaxDist) + { + // can I see where I want to be from there? + UTIL_TraceLine( node.m_vecOrigin + pev->view_ofs, vecLookersOffset, ignore_monsters, edict(), &tr ); + + if (tr.flFraction == 1.0) + { + // try to actually get there + if ( BuildRoute ( node.m_vecOrigin, bits_MF_TO_LOCATION, NULL ) ) + { + flMaxDist = flDist; + m_vecMoveGoal = node.m_vecOrigin; + return TRUE; // UNDONE: keep looking for something closer! + } + } + } + } + } + + return FALSE; +} + + + +//========================================================= +// BestVisibleEnemy - this functions searches the link +// list whose head is the caller's m_pLink field, and returns +// a pointer to the enemy entity in that list that is nearest the +// caller. +// +// !!!UNDONE - currently, this only returns the closest enemy. +// we'll want to consider distance, relationship, attack types, back turned, etc. +//========================================================= +edict_t *CMBaseMonster :: BestVisibleEnemy ( void ) +{ + int iNearest; + int iDist; + int iBestRelationship; + edict_t *pReturn; + edict_t *pEnt; + int edictList_index = 0; + + iNearest = 8192;// so first visible entity will become the closest. + + iBestRelationship = R_NO; + + pReturn = NULL; + + while (edictList_index < m_edictList_count) + { + pEnt = m_edictList[edictList_index]; + + if ( UTIL_IsPlayer(pEnt) ) + { + // it's a player... + iDist = ( pEnt->v.origin - pev->origin ).Length(); + + if ( iDist <= iNearest ) + { + iNearest = iDist; + iBestRelationship = R_NM; // player is always nemsis + pReturn = pEnt; + } + } + else if (pEnt->v.euser4 != NULL) + { + CMBaseMonster *pNextEnt = GetClassPtr((CMBaseMonster *)VARS(pEnt)); + if ( pNextEnt->IsAlive() ) + { + if ( IRelationship( pNextEnt) > iBestRelationship ) + { + // this entity is disliked MORE than the entity that we + // currently think is the best visible enemy. No need to do + // a distance check, just get mad at this one for now. + iBestRelationship = IRelationship ( pNextEnt ); + iNearest = ( pNextEnt->pev->origin - pev->origin ).Length(); + pReturn = pEnt; + } + else if ( IRelationship( pNextEnt) == iBestRelationship ) + { + // this entity is disliked just as much as the entity that + // we currently think is the best visible enemy, so we only + // get mad at it if it is closer. + iDist = ( pNextEnt->pev->origin - pev->origin ).Length(); + + if ( iDist <= iNearest ) + { + iNearest = iDist; + iBestRelationship = IRelationship ( pNextEnt ); + pReturn = pEnt; + } + } + } + } + + edictList_index++; + } + + return pReturn; +} + + +//========================================================= +// MakeIdealYaw - gets a yaw value for the caller that would +// face the supplied vector. Value is stuffed into the monster's +// ideal_yaw +//========================================================= +void CMBaseMonster :: MakeIdealYaw( Vector vecTarget ) +{ + Vector vecProjection; + + // strafing monster needs to face 90 degrees away from its goal + if ( m_movementActivity == ACT_STRAFE_LEFT ) + { + vecProjection.x = -vecTarget.y; + vecProjection.y = vecTarget.x; + + pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin ); + } + else if ( m_movementActivity == ACT_STRAFE_RIGHT ) + { + vecProjection.x = vecTarget.y; + vecProjection.y = vecTarget.x; + + pev->ideal_yaw = UTIL_VecToYaw( vecProjection - pev->origin ); + } + else + { + pev->ideal_yaw = UTIL_VecToYaw ( vecTarget - pev->origin ); + } +} + +//========================================================= +// FlYawDiff - returns the difference ( in degrees ) between +// monster's current yaw and ideal_yaw +// +// Positive result is left turn, negative is right turn +//========================================================= +float CMBaseMonster::FlYawDiff ( void ) +{ + float flCurrentYaw; + + flCurrentYaw = UTIL_AngleMod( pev->angles.y ); + + if ( flCurrentYaw == pev->ideal_yaw ) + { + return 0; + } + + + return UTIL_AngleDiff( pev->ideal_yaw, flCurrentYaw ); +} + + +//========================================================= +// Changeyaw - turns a monster towards its ideal_yaw +//========================================================= +float CMBaseMonster::ChangeYaw ( int yawSpeed ) +{ + float ideal, current, move, speed; + + current = UTIL_AngleMod( pev->angles.y ); + ideal = pev->ideal_yaw; + if (current != ideal) + { + speed = (float)yawSpeed * gpGlobals->frametime * 10; + move = ideal - current; + + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + + if (move > 0) + {// turning to the monster's left + if (move > speed) + move = speed; + } + else + {// turning to the monster's right + if (move < -speed) + move = -speed; + } + + pev->angles.y = UTIL_AngleMod (current + move); + + // turn head in desired direction only if they have a turnable head + if (m_afCapability & bits_CAP_TURN_HEAD) + { + float yaw = pev->ideal_yaw - pev->angles.y; + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + // yaw *= 0.8; + SetBoneController( 0, yaw ); + } + } + else + move = 0; + + return move; +} + +//========================================================= +// VecToYaw - turns a directional vector into a yaw value +// that points down that vector. +//========================================================= +float CMBaseMonster::VecToYaw ( Vector vecDir ) +{ + if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0) + return pev->angles.y; + + return UTIL_VecToYaw( vecDir ); +} + + +//========================================================= +// SetEyePosition +// +// queries the monster's model for $eyeposition and copies +// that vector to the monster's view_ofs +// +//========================================================= +void CMBaseMonster :: SetEyePosition ( void ) +{ + Vector vecEyePosition; + void *pmodel = GET_MODEL_PTR( ENT(pev) ); + + GetEyePosition( pmodel, vecEyePosition ); + + pev->view_ofs = vecEyePosition; + + if ( pev->view_ofs == g_vecZero ) + { + ALERT ( at_aiconsole, "%s has no view_ofs!\n", STRING ( pev->classname ) ); + } +} + +void CMBaseMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case MONSTER_EVENT_BODYDROP_HEAVY: + if ( pev->flags & FL_ONGROUND ) + { + if ( RANDOM_LONG( 0, 1 ) == 0 ) + { + EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM, 0, 90 ); + } + else + { + EMIT_SOUND_DYN( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM, 0, 90 ); + } + } + break; + + case MONSTER_EVENT_BODYDROP_LIGHT: + if ( pev->flags & FL_ONGROUND ) + { + if ( RANDOM_LONG( 0, 1 ) == 0 ) + { + EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop3.wav", 1, ATTN_NORM ); + } + else + { + EMIT_SOUND( ENT(pev), CHAN_BODY, "common/bodydrop4.wav", 1, ATTN_NORM ); + } + } + break; + + case MONSTER_EVENT_SWISHSOUND: + { + // NO MONSTER may use this anim event unless that monster's precache precaches this sound!!! + EMIT_SOUND( ENT(pev), CHAN_BODY, "zombie/claw_miss2.wav", 1, ATTN_NORM ); + break; + } + + default: + ALERT( at_aiconsole, "Unhandled animation event %d for %s\n", pEvent->event, STRING(pev->classname) ); + break; + + } +} + + +// Combat + +Vector CMBaseMonster :: GetGunPosition( ) +{ + UTIL_MakeVectors(pev->angles); + + // Vector vecSrc = pev->origin + gpGlobals->v_forward * 10; + //vecSrc.z = pevShooter->absmin.z + pevShooter->size.z * 0.7; + //vecSrc.z = pev->origin.z + (pev->view_ofs.z - 4); + Vector vecSrc = pev->origin + + gpGlobals->v_forward * m_HackedGunPos.y + + gpGlobals->v_right * m_HackedGunPos.x + + gpGlobals->v_up * m_HackedGunPos.z; + + return vecSrc; +} + + + + + +//========================================================= +// NODE GRAPH +//========================================================= + + + + + +//========================================================= +// FGetNodeRoute - tries to build an entire node path from +// the callers origin to the passed vector. If this is +// possible, ROUTE_SIZE waypoints will be copied into the +// callers m_Route. TRUE is returned if the operation +// succeeds (path is valid) or FALSE if failed (no path +// exists ) +//========================================================= +BOOL CMBaseMonster :: FGetNodeRoute ( Vector vecDest ) +{ + int iPath[ MAX_PATH_SIZE ]; + int iSrcNode, iDestNode; + int iResult; + int i; + int iNumToCopy; + + iSrcNode = WorldGraph.FindNearestNode ( pev->origin, this ); + iDestNode = WorldGraph.FindNearestNode ( vecDest, this ); + + if ( iSrcNode == -1 ) + { + // no node nearest self +// ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near self!\n" ); + return FALSE; + } + else if ( iDestNode == -1 ) + { + // no node nearest target +// ALERT ( at_aiconsole, "FGetNodeRoute: No valid node near target!\n" ); + return FALSE; + } + + // valid src and dest nodes were found, so it's safe to proceed with + // find shortest path + int iNodeHull = WorldGraph.HullIndex( this ); // make this a monster virtual function + iResult = WorldGraph.FindShortestPath ( iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability ); + + if ( !iResult ) + { +#if 1 + ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode ); + return FALSE; +#else + BOOL bRoutingSave = WorldGraph.m_fRoutingComplete; + WorldGraph.m_fRoutingComplete = FALSE; + iResult = WorldGraph.FindShortestPath(iPath, iSrcNode, iDestNode, iNodeHull, m_afCapability); + WorldGraph.m_fRoutingComplete = bRoutingSave; + if ( !iResult ) + { + ALERT ( at_aiconsole, "No Path from %d to %d!\n", iSrcNode, iDestNode ); + return FALSE; + } + else + { + ALERT ( at_aiconsole, "Routing is inconsistent!" ); + } +#endif + } + + // there's a valid path within iPath now, so now we will fill the route array + // up with as many of the waypoints as it will hold. + + // don't copy ROUTE_SIZE entries if the path returned is shorter + // than ROUTE_SIZE!!! + if ( iResult < ROUTE_SIZE ) + { + iNumToCopy = iResult; + } + else + { + iNumToCopy = ROUTE_SIZE; + } + + for ( i = 0 ; i < iNumToCopy; i++ ) + { + m_Route[ i ].vecLocation = WorldGraph.m_pNodes[ iPath[ i ] ].m_vecOrigin; + m_Route[ i ].iType = bits_MF_TO_NODE; + } + + if ( iNumToCopy < ROUTE_SIZE ) + { + m_Route[ iNumToCopy ].vecLocation = vecDest; + m_Route[ iNumToCopy ].iType |= bits_MF_IS_GOAL; + } + + return TRUE; +} + +//========================================================= +// FindHintNode +//========================================================= +int CMBaseMonster :: FindHintNode ( void ) +{ + int i; + TraceResult tr; + + if ( !WorldGraph.m_fGraphPresent ) + { + ALERT ( at_aiconsole, "find_hintnode: graph not ready!\n" ); + return NO_NODE; + } + + if ( WorldGraph.m_iLastActiveIdleSearch >= WorldGraph.m_cNodes ) + { + WorldGraph.m_iLastActiveIdleSearch = 0; + } + + for ( i = 0; i < WorldGraph.m_cNodes ; i++ ) + { + int nodeNumber = (i + WorldGraph.m_iLastActiveIdleSearch) % WorldGraph.m_cNodes; + CNode &node = WorldGraph.Node( nodeNumber ); + + if ( node.m_sHintType ) + { + // this node has a hint. Take it if it is visible, the monster likes it, and the monster has an animation to match the hint's activity. + if ( FValidateHintType ( node.m_sHintType ) ) + { + if ( !node.m_sHintActivity || LookupActivity ( node.m_sHintActivity ) != ACTIVITY_NOT_AVAILABLE ) + { + UTIL_TraceLine ( pev->origin + pev->view_ofs, node.m_vecOrigin + pev->view_ofs, ignore_monsters, ENT(pev), &tr ); + + if ( tr.flFraction == 1.0 ) + { + WorldGraph.m_iLastActiveIdleSearch = nodeNumber + 1; // next monster that searches for hint nodes will start where we left off. + return nodeNumber;// take it! + } + } + } + } + } + + WorldGraph.m_iLastActiveIdleSearch = 0;// start at the top of the list for the next search. + + return NO_NODE; +} + + +void CMBaseMonster::ReportAIState( void ) +{ + ALERT_TYPE level = at_console; + + static const char *pStateNames[] = { "None", "Idle", "Combat", "Alert", "Hunt", "Prone", "Scripted", "Dead" }; + + ALERT( level, "%s: ", STRING(pev->classname) ); + if ( (int)m_MonsterState < ARRAYSIZE(pStateNames) ) + ALERT( level, "State: %s, ", pStateNames[m_MonsterState] ); + int i = 0; + while ( activity_map[i].type != 0 ) + { + if ( activity_map[i].type == (int)m_Activity ) + { + ALERT( level, "Activity %s, ", activity_map[i].name ); + break; + } + i++; + } + + if ( m_pSchedule ) + { + const char *pName = NULL; + pName = m_pSchedule->pName; + if ( !pName ) + pName = "Unknown"; + ALERT( level, "Schedule %s, ", pName ); + Task_t *pTask = GetTask(); + if ( pTask ) + ALERT( level, "Task %d (#%d), ", pTask->iTask, m_iScheduleIndex ); + } + else + ALERT( level, "No Schedule, " ); + + if ( m_hEnemy != NULL ) + ALERT( level, "\nEnemy is %s", STRING(m_hEnemy->v.classname) ); + else + ALERT( level, "No enemy" ); + + if ( IsMoving() ) + { + ALERT( level, " Moving " ); + if ( m_flMoveWaitFinished > gpGlobals->time ) + ALERT( level, ": Stopped for %.2f. ", m_flMoveWaitFinished - gpGlobals->time ); + else if ( m_IdealActivity == GetStoppedActivity() ) + ALERT( level, ": In stopped anim. " ); + } + + ALERT( level, "\n" ); + ALERT( level, "Yaw speed:%3.1f,Health: %3.1f\n", pev->yaw_speed, pev->health ); + if ( pev->spawnflags & SF_MONSTER_PRISONER ) + ALERT( level, " PRISONER! " ); + if ( pev->spawnflags & SF_MONSTER_PREDISASTER ) + ALERT( level, " Pre-Disaster! " ); + ALERT( level, "\n" ); +} + +//========================================================= +// KeyValue +// +// !!! netname entvar field is used in squadmonster for groupname!!! +//========================================================= +void CMBaseMonster :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "TriggerTarget")) + { + m_iszTriggerTarget = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "TriggerCondition")) + { + m_iTriggerCondition = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "displayname")) + { + m_szMonsterName = ALLOC_STRING( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "classify")) + { + m_iClassifyOverride = atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + { + CMBaseToggle::KeyValue( pkvd ); + } +} + +//========================================================= +// FCheckAITrigger - checks the monster's AI Trigger Conditions, +// if there is a condition, then checks to see if condition is +// met. If yes, the monster's TriggerTarget is fired. +// +// Returns TRUE if the target is fired. +//========================================================= +BOOL CMBaseMonster :: FCheckAITrigger ( void ) +{ + BOOL fFireTarget; + + if ( m_iTriggerCondition == AITRIGGER_NONE ) + { + // no conditions, so this trigger is never fired. + return FALSE; + } + + fFireTarget = FALSE; + + switch ( m_iTriggerCondition ) + { + case AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER: + if ( m_hEnemy != NULL && UTIL_IsPlayer(m_hEnemy) && HasConditions ( bits_COND_SEE_ENEMY ) ) + { + fFireTarget = TRUE; + } + break; + case AITRIGGER_SEEPLAYER_UNCONDITIONAL: + if ( HasConditions ( bits_COND_SEE_CLIENT ) ) + { + fFireTarget = TRUE; + } + break; + case AITRIGGER_SEEPLAYER_NOT_IN_COMBAT: + if ( HasConditions ( bits_COND_SEE_CLIENT ) && + m_MonsterState != MONSTERSTATE_COMBAT && + m_MonsterState != MONSTERSTATE_PRONE && + m_MonsterState != MONSTERSTATE_SCRIPT) + { + fFireTarget = TRUE; + } + break; + case AITRIGGER_TAKEDAMAGE: + if ( m_afConditions & ( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + fFireTarget = TRUE; + } + break; + case AITRIGGER_DEATH: + if ( pev->deadflag != DEAD_NO ) + { + fFireTarget = TRUE; + } + break; + case AITRIGGER_HALFHEALTH: + if ( IsAlive() && pev->health <= ( pev->max_health / 2 ) ) + { + fFireTarget = TRUE; + } + break; +/* + + // !!!UNDONE - no persistant game state that allows us to track these two. + + case AITRIGGER_SQUADMEMBERDIE: + break; + case AITRIGGER_SQUADLEADERDIE: + break; +*/ + case AITRIGGER_HEARWORLD: + break; + case AITRIGGER_HEARPLAYER: + break; + case AITRIGGER_HEARCOMBAT: + break; + } + + if ( fFireTarget ) + { + // fire the target, then set the trigger conditions to NONE so we don't fire again + ALERT ( at_aiconsole, "AI Trigger Fire Target\n" ); + FireTargets( STRING( m_iszTriggerTarget ), this->edict(), this->edict(), USE_TOGGLE, 0 ); + m_iTriggerCondition = AITRIGGER_NONE; + return TRUE; + } + + return FALSE; +} + +//========================================================= +// FindLateralCover - attempts to locate a spot in the world +// directly to the left or right of the caller that will +// conceal them from view of pSightEnt +//========================================================= +#define COVER_CHECKS 5// how many checks are made +#define COVER_DELTA 48// distance between checks + +BOOL CMBaseMonster :: FindLateralCover ( const Vector &vecThreat, const Vector &vecViewOffset ) +{ + TraceResult tr; + Vector vecBestOnLeft; + Vector vecBestOnRight; + Vector vecLeftTest; + Vector vecRightTest; + Vector vecStepRight; + int i; + + UTIL_MakeVectors ( pev->angles ); + vecStepRight = gpGlobals->v_right * COVER_DELTA; + vecStepRight.z = 0; + + vecLeftTest = vecRightTest = pev->origin; + + for ( i = 0 ; i < COVER_CHECKS ; i++ ) + { + vecLeftTest = vecLeftTest - vecStepRight; + vecRightTest = vecRightTest + vecStepRight; + + // it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first. + UTIL_TraceLine( vecThreat + vecViewOffset, vecLeftTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr); + + if (tr.flFraction != 1.0) + { + if ( FValidateCover ( vecLeftTest ) && CheckLocalMove( pev->origin, vecLeftTest, NULL, NULL ) == LOCALMOVE_VALID ) + { + if ( MoveToLocation( ACT_RUN, 0, vecLeftTest ) ) + { + return TRUE; + } + } + } + + // it's faster to check the SightEnt's visibility to the potential spot than to check the local move, so we do that first. + UTIL_TraceLine(vecThreat + vecViewOffset, vecRightTest + pev->view_ofs, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr); + + if ( tr.flFraction != 1.0 ) + { + if ( FValidateCover ( vecRightTest ) && CheckLocalMove( pev->origin, vecRightTest, NULL, NULL ) == LOCALMOVE_VALID ) + { + if ( MoveToLocation( ACT_RUN, 0, vecRightTest ) ) + { + return TRUE; + } + } + } + } + + return FALSE; +} + + +Vector CMBaseMonster :: ShootAtEnemy( const Vector &shootOrigin ) +{ + edict_t *pEnemy = m_hEnemy; + + if ( pEnemy ) + { + return ( (UTIL_BodyTarget(pEnemy, shootOrigin ) - pEnemy->v.origin) + m_vecEnemyLKP - shootOrigin ).Normalize(); + } + else + return gpGlobals->v_forward; +} + + + +//========================================================= +// FacingIdeal - tells us if a monster is facing its ideal +// yaw. Created this function because many spots in the +// code were checking the yawdiff against this magic +// number. Nicer to have it in one place if we're gonna +// be stuck with it. +//========================================================= +BOOL CMBaseMonster :: FacingIdeal( void ) +{ + if ( fabs( FlYawDiff() ) <= 0.006 )//!!!BUGBUG - no magic numbers!!! + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// FCanActiveIdle +//========================================================= +BOOL CMBaseMonster :: FCanActiveIdle ( void ) +{ + /* + if ( m_MonsterState == MONSTERSTATE_IDLE && m_IdealMonsterState == MONSTERSTATE_IDLE && !IsMoving() ) + { + return TRUE; + } + */ + return FALSE; +} + + +void CMBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) +{ + if ( pszSentence && IsAlive() ) + { + if ( pszSentence[0] == '!' ) + EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM ); +//jlb else +//jlb SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM ); + } +} + + +void CMBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CMBaseEntity *pListener ) +{ + PlaySentence( pszSentence, duration, volume, attenuation ); +} + + +void CMBaseMonster::SentenceStop( void ) +{ + EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE ); +} + + +void CMBaseMonster::CorpseFallThink( void ) +{ + if ( pev->flags & FL_ONGROUND ) + { + SetThink ( NULL ); + + SetSequenceBox( ); + UTIL_SetOrigin( pev, pev->origin );// link into world. + } + else + pev->nextthink = gpGlobals->time + 0.1; +} + +// Call after animation/pose is set up +void CMBaseMonster :: MonsterInitDead( void ) +{ + InitBoneControllers(); + + pev->solid = SOLID_BBOX; + pev->movetype = MOVETYPE_TOSS;// so he'll fall to ground + + pev->frame = 0; + ResetSequenceInfo( ); + pev->framerate = 0; + + // Copy health + pev->max_health = pev->health; + pev->deadflag = DEAD_DEAD; + + UTIL_SetSize(pev, g_vecZero, g_vecZero ); + UTIL_SetOrigin( pev, pev->origin ); + + // Setup health counters, etc. + BecomeDead(); + SetThink( &CMBaseMonster::CorpseFallThink ); + pev->nextthink = gpGlobals->time + 0.5; +} + +//========================================================= +// BBoxIsFlat - check to see if the monster's bounding box +// is lying flat on a surface (traces from all four corners +// are same length.) +//========================================================= +BOOL CMBaseMonster :: BBoxFlat ( void ) +{ + TraceResult tr; + Vector vecPoint; + float flXSize, flYSize; + float flLength; + float flLength2; + + flXSize = pev->size.x / 2; + flYSize = pev->size.y / 2; + + vecPoint.x = pev->origin.x + flXSize; + vecPoint.y = pev->origin.y + flYSize; + vecPoint.z = pev->origin.z; + + UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); + flLength = (vecPoint - tr.vecEndPos).Length(); + + vecPoint.x = pev->origin.x - flXSize; + vecPoint.y = pev->origin.y - flYSize; + + UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); + flLength2 = (vecPoint - tr.vecEndPos).Length(); + if ( flLength2 > flLength ) + { + return FALSE; + } + flLength = flLength2; + + vecPoint.x = pev->origin.x - flXSize; + vecPoint.y = pev->origin.y + flYSize; + UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); + flLength2 = (vecPoint - tr.vecEndPos).Length(); + if ( flLength2 > flLength ) + { + return FALSE; + } + flLength = flLength2; + + vecPoint.x = pev->origin.x + flXSize; + vecPoint.y = pev->origin.y - flYSize; + UTIL_TraceLine ( vecPoint, vecPoint - Vector ( 0, 0, 100 ), ignore_monsters, ENT(pev), &tr ); + flLength2 = (vecPoint - tr.vecEndPos).Length(); + if ( flLength2 > flLength ) + { + return FALSE; + } + flLength = flLength2; + + return TRUE; +} + +//========================================================= +// Get Enemy - tries to find the best suitable enemy for the monster. +//========================================================= +BOOL CMBaseMonster :: GetEnemy ( void ) +{ + edict_t *pNewEnemy; + + if ( HasConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_NEMESIS) ) + { + pNewEnemy = BestVisibleEnemy(); + + if ( pNewEnemy != m_hEnemy && pNewEnemy != NULL) + { + // DO NOT mess with the monster's m_hEnemy pointer unless the schedule the monster is currently running will be interrupted + // by COND_NEW_ENEMY. This will eliminate the problem of monsters getting a new enemy while they are in a schedule that doesn't care, + // and then not realizing it by the time they get to a schedule that does. I don't feel this is a good permanent fix. + + if ( m_pSchedule ) + { + if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY ) + { + PushEnemy( m_hEnemy, m_vecEnemyLKP ); + SetConditions(bits_COND_NEW_ENEMY); + m_hEnemy = pNewEnemy; + m_vecEnemyLKP = m_hEnemy->v.origin; + } + // if the new enemy has an owner, take that one as well +//jlb if (pNewEnemy->v.owner != NULL) +//jlb { +//jlb edict_t *pOwner = pNewEnemy->v.owner; +//jlb if ( pOwner && (pOwner->v.flags & FL_MONSTER) && IRelationship( pOwner ) != R_NO ) +//jlb PushEnemy( pOwner, m_vecEnemyLKP ); +//jlb } + } + } + } + + // do we see a player? Allies use this to set the m_hEnemy pointer... + if (HasConditions(bits_COND_SEE_CLIENT) && (m_hEnemy == NULL)) + { + m_hEnemy = BestVisibleEnemy(); + m_hTargetEnt = m_hEnemy; + m_vecEnemyLKP = m_hEnemy->v.origin; + } + + // remember old enemies + if (m_hEnemy == NULL && PopEnemy( )) + { + if ( m_pSchedule ) + { + if ( m_pSchedule->iInterruptMask & bits_COND_NEW_ENEMY ) + { + SetConditions(bits_COND_NEW_ENEMY); + } + } + } + + if ( m_hEnemy != NULL ) + { + // monster has an enemy. + return TRUE; + } + + return FALSE;// monster has no enemy +} + + +BOOL CMBaseMonster :: ShouldFadeOnDeath( void ) +{ + // if flagged to fade out or I have an owner (I came from a monster spawner) + if ( (pev->spawnflags & SF_MONSTER_FADECORPSE) || !FNullEnt( pev->owner ) ) + return TRUE; + + return FALSE; +} + + +void CMBaseMonster :: TaskFail( void ) +{ + SetConditions(bits_COND_TASK_FAILED); +} + diff --git a/src/dlls/monsters.h b/src/dlls/monsters.h index fe8e632..03104f9 100644 --- a/src/dlls/monsters.h +++ b/src/dlls/monsters.h @@ -1,181 +1,181 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#ifndef MONSTERS_H -#define MONSTERS_H - -/* - -===== monsters.h ======================================================== - - Header file for monster-related utility code - -*/ - -// CHECKLOCALMOVE result types -#define LOCALMOVE_INVALID 0 // move is not possible -#define LOCALMOVE_INVALID_DONT_TRIANGULATE 1 // move is not possible, don't try to triangulate -#define LOCALMOVE_VALID 2 // move is possible - -// Hit Group standards -#define HITGROUP_GENERIC 0 -#define HITGROUP_HEAD 1 -#define HITGROUP_CHEST 2 -#define HITGROUP_STOMACH 3 -#define HITGROUP_LEFTARM 4 -#define HITGROUP_RIGHTARM 5 -#define HITGROUP_LEFTLEG 6 -#define HITGROUP_RIGHTLEG 7 - - -// Monster Spawnflags -#define SF_MONSTER_WAIT_TILL_SEEN 1// spawnflag that makes monsters wait until player can see them before attacking. -#define SF_MONSTER_GAG 2 // no idle noises from this monster -#define SF_MONSTER_HITMONSTERCLIP 4 -// 8 -#define SF_MONSTER_PRISONER 16 // monster won't attack anyone, no one will attacke him. -// 32 -// 64 -#define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked -#define SF_MONSTER_PREDISASTER 256 //this is a predisaster scientist or barney. Influences how they speak. -#define SF_MONSTER_FADECORPSE 512 // Fade out corpse after death -#define SF_MONSTER_FALL_TO_GROUND 0x80000000 - -// specialty spawnflags -#define SF_MONSTER_TURRET_AUTOACTIVATE 32 -#define SF_MONSTER_TURRET_STARTINACTIVE 64 -#define SF_MONSTER_WAIT_UNTIL_PROVOKED 64 // don't attack the player unless provoked - - - -// MoveToOrigin stuff -#define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal -#define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck. - - -// MoveToOrigin stuff -#define MOVE_NORMAL 0// normal move in the direction monster is facing -#define MOVE_STRAFE 1// moves in direction specified, no matter which way monster is facing - -// spawn flags 256 and above are already taken by the engine -extern void UTIL_MoveToOrigin( edict_t* pent, const Vector &vecGoal, float flDist, int iMoveType ); - -Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj = 1.0 ); -Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj = 1.0 ); -extern DLL_GLOBAL Vector g_vecAttackDir; -extern DLL_GLOBAL CONSTANT float g_flMeleeRange; -extern DLL_GLOBAL CONSTANT float g_flMediumRange; -extern DLL_GLOBAL CONSTANT float g_flLongRange; -extern void EjectBrass (const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ); -extern void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ); - -BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget ); -BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize = 0.0 ); - -// monster to monster relationship types -#define R_AL -2 // (ALLY) pals. Good alternative to R_NO when applicable. -#define R_FR -1// (FEAR)will run -#define R_NO 0// (NO RELATIONSHIP) disregard -#define R_DL 1// (DISLIKE) will attack -#define R_HT 2// (HATE)will attack this character instead of any visible DISLIKEd characters -#define R_NM 3// (NEMESIS) A monster Will ALWAYS attack its nemsis, no matter what - - -// these bits represent the monster's memory -#define MEMORY_CLEAR 0 -#define bits_MEMORY_PROVOKED ( 1 << 0 )// right now only used for houndeyes. -#define bits_MEMORY_INCOVER ( 1 << 1 )// monster knows it is in a covered position. -#define bits_MEMORY_SUSPICIOUS ( 1 << 2 )// Ally is suspicious of the player, and will move to provoked more easily -#define bits_MEMORY_PATH_FINISHED ( 1 << 3 )// Finished monster path (just used by big momma for now) -#define bits_MEMORY_ON_PATH ( 1 << 4 )// Moving on a path -#define bits_MEMORY_MOVE_FAILED ( 1 << 5 )// Movement has already failed -#define bits_MEMORY_FLINCHED ( 1 << 6 )// Has already flinched -#define bits_MEMORY_KILLED ( 1 << 7 )// HACKHACK -- remember that I've already called my Killed() -#define bits_MEMORY_CUSTOM4 ( 1 << 28 ) // Monster-specific memory -#define bits_MEMORY_CUSTOM3 ( 1 << 29 ) // Monster-specific memory -#define bits_MEMORY_CUSTOM2 ( 1 << 30 ) // Monster-specific memory -#define bits_MEMORY_CUSTOM1 ( 1 << 31 ) // Monster-specific memory - -// trigger conditions for scripted AI -// these MUST match the CHOICES interface in halflife.fgd for the base monster -enum -{ - AITRIGGER_NONE = 0, - AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER, - AITRIGGER_TAKEDAMAGE, - AITRIGGER_HALFHEALTH, - AITRIGGER_DEATH, - AITRIGGER_SQUADMEMBERDIE, - AITRIGGER_SQUADLEADERDIE, - AITRIGGER_HEARWORLD, - AITRIGGER_HEARPLAYER, - AITRIGGER_HEARCOMBAT, - AITRIGGER_SEEPLAYER_UNCONDITIONAL, - AITRIGGER_SEEPLAYER_NOT_IN_COMBAT, -}; -/* - 0 : "No Trigger" - 1 : "See Player" - 2 : "Take Damage" - 3 : "50% Health Remaining" - 4 : "Death" - 5 : "Squad Member Dead" - 6 : "Squad Leader Dead" - 7 : "Hear World" - 8 : "Hear Player" - 9 : "Hear Combat" -*/ - -// -// A gib is a chunk of a body, or a piece of wood/metal/rocks/etc. -// -class CMGib : public CMBaseEntity -{ -public: - void Spawn( const char *szGibModel ); - void EXPORT BounceGibTouch ( edict_t *pOther ); - void EXPORT StickyGibTouch ( edict_t *pOther ); - void EXPORT WaitTillLand( void ); - void LimitVelocity( void ); - - virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } - static void SpawnHeadGib( entvars_t *pevVictim ); - static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ); - static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human ); - static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ); - - int m_bloodColor; - int m_cBloodDecals; - int m_material; - float m_lifeTime; -}; - - -#define CUSTOM_SCHEDULES\ - virtual Schedule_t *ScheduleFromName( const char *pName );\ - static Schedule_t *m_scheduleList[]; - -#define DEFINE_CUSTOM_SCHEDULES(derivedClass)\ - Schedule_t *derivedClass::m_scheduleList[] = - -#define IMPLEMENT_CUSTOM_SCHEDULES(derivedClass, baseClass)\ - Schedule_t *derivedClass::ScheduleFromName( const char *pName )\ - {\ - Schedule_t *pSchedule = ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) );\ - if ( !pSchedule )\ - return baseClass::ScheduleFromName(pName);\ - return pSchedule;\ - } - -#endif //MONSTERS_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#ifndef MONSTERS_H +#define MONSTERS_H + +/* + +===== monsters.h ======================================================== + + Header file for monster-related utility code + +*/ + +// CHECKLOCALMOVE result types +#define LOCALMOVE_INVALID 0 // move is not possible +#define LOCALMOVE_INVALID_DONT_TRIANGULATE 1 // move is not possible, don't try to triangulate +#define LOCALMOVE_VALID 2 // move is possible + +// Hit Group standards +#define HITGROUP_GENERIC 0 +#define HITGROUP_HEAD 1 +#define HITGROUP_CHEST 2 +#define HITGROUP_STOMACH 3 +#define HITGROUP_LEFTARM 4 +#define HITGROUP_RIGHTARM 5 +#define HITGROUP_LEFTLEG 6 +#define HITGROUP_RIGHTLEG 7 + + +// Monster Spawnflags +#define SF_MONSTER_WAIT_TILL_SEEN 1// spawnflag that makes monsters wait until player can see them before attacking. +#define SF_MONSTER_GAG 2 // no idle noises from this monster +#define SF_MONSTER_HITMONSTERCLIP 4 +// 8 +#define SF_MONSTER_PRISONER 16 // monster won't attack anyone, no one will attacke him. +// 32 +// 64 +#define SF_MONSTER_WAIT_FOR_SCRIPT 128 //spawnflag that makes monsters wait to check for attacking until the script is done or they've been attacked +#define SF_MONSTER_PREDISASTER 256 //this is a predisaster scientist or barney. Influences how they speak. +#define SF_MONSTER_FADECORPSE 512 // Fade out corpse after death +#define SF_MONSTER_FALL_TO_GROUND 0x80000000 + +// specialty spawnflags +#define SF_MONSTER_TURRET_AUTOACTIVATE 32 +#define SF_MONSTER_TURRET_STARTINACTIVE 64 +#define SF_MONSTER_WAIT_UNTIL_PROVOKED 64 // don't attack the player unless provoked + + + +// MoveToOrigin stuff +#define MOVE_START_TURN_DIST 64 // when this far away from moveGoal, start turning to face next goal +#define MOVE_STUCK_DIST 32 // if a monster can't step this far, it is stuck. + + +// MoveToOrigin stuff +#define MOVE_NORMAL 0// normal move in the direction monster is facing +#define MOVE_STRAFE 1// moves in direction specified, no matter which way monster is facing + +// spawn flags 256 and above are already taken by the engine +extern void UTIL_MoveToOrigin( edict_t* pent, const Vector &vecGoal, float flDist, int iMoveType ); + +Vector VecCheckToss ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flGravityAdj = 1.0 ); +Vector VecCheckThrow ( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj = 1.0 ); +extern DLL_GLOBAL Vector g_vecAttackDir; +extern DLL_GLOBAL CONSTANT float g_flMeleeRange; +extern DLL_GLOBAL CONSTANT float g_flMediumRange; +extern DLL_GLOBAL CONSTANT float g_flLongRange; +extern void EjectBrass (const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ); +extern void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ); + +BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget ); +BOOL FBoxVisible ( entvars_t *pevLooker, entvars_t *pevTarget, Vector &vecTargetOrigin, float flSize = 0.0 ); + +// monster to monster relationship types +#define R_AL -2 // (ALLY) pals. Good alternative to R_NO when applicable. +#define R_FR -1// (FEAR)will run +#define R_NO 0// (NO RELATIONSHIP) disregard +#define R_DL 1// (DISLIKE) will attack +#define R_HT 2// (HATE)will attack this character instead of any visible DISLIKEd characters +#define R_NM 3// (NEMESIS) A monster Will ALWAYS attack its nemsis, no matter what + + +// these bits represent the monster's memory +#define MEMORY_CLEAR 0 +#define bits_MEMORY_PROVOKED ( 1 << 0 )// right now only used for houndeyes. +#define bits_MEMORY_INCOVER ( 1 << 1 )// monster knows it is in a covered position. +#define bits_MEMORY_SUSPICIOUS ( 1 << 2 )// Ally is suspicious of the player, and will move to provoked more easily +#define bits_MEMORY_PATH_FINISHED ( 1 << 3 )// Finished monster path (just used by big momma for now) +#define bits_MEMORY_ON_PATH ( 1 << 4 )// Moving on a path +#define bits_MEMORY_MOVE_FAILED ( 1 << 5 )// Movement has already failed +#define bits_MEMORY_FLINCHED ( 1 << 6 )// Has already flinched +#define bits_MEMORY_KILLED ( 1 << 7 )// HACKHACK -- remember that I've already called my Killed() +#define bits_MEMORY_CUSTOM4 ( 1 << 28 ) // Monster-specific memory +#define bits_MEMORY_CUSTOM3 ( 1 << 29 ) // Monster-specific memory +#define bits_MEMORY_CUSTOM2 ( 1 << 30 ) // Monster-specific memory +#define bits_MEMORY_CUSTOM1 ( 1 << 31 ) // Monster-specific memory + +// trigger conditions for scripted AI +// these MUST match the CHOICES interface in halflife.fgd for the base monster +enum +{ + AITRIGGER_NONE = 0, + AITRIGGER_SEEPLAYER_ANGRY_AT_PLAYER, + AITRIGGER_TAKEDAMAGE, + AITRIGGER_HALFHEALTH, + AITRIGGER_DEATH, + AITRIGGER_SQUADMEMBERDIE, + AITRIGGER_SQUADLEADERDIE, + AITRIGGER_HEARWORLD, + AITRIGGER_HEARPLAYER, + AITRIGGER_HEARCOMBAT, + AITRIGGER_SEEPLAYER_UNCONDITIONAL, + AITRIGGER_SEEPLAYER_NOT_IN_COMBAT, +}; +/* + 0 : "No Trigger" + 1 : "See Player" + 2 : "Take Damage" + 3 : "50% Health Remaining" + 4 : "Death" + 5 : "Squad Member Dead" + 6 : "Squad Leader Dead" + 7 : "Hear World" + 8 : "Hear Player" + 9 : "Hear Combat" +*/ + +// +// A gib is a chunk of a body, or a piece of wood/metal/rocks/etc. +// +class CMGib : public CMBaseEntity +{ +public: + void Spawn( const char *szGibModel ); + void EXPORT BounceGibTouch ( edict_t *pOther ); + void EXPORT StickyGibTouch ( edict_t *pOther ); + void EXPORT WaitTillLand( void ); + void LimitVelocity( void ); + + virtual int ObjectCaps( void ) { return (CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; } + static void SpawnHeadGib( entvars_t *pevVictim ); + static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human ); + static void SpawnRandomGibs( entvars_t *pevVictim, int cGibs, const char *pGibModel, int human ); + static void SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs ); + + int m_bloodColor; + int m_cBloodDecals; + int m_material; + float m_lifeTime; +}; + + +#define CUSTOM_SCHEDULES\ + virtual Schedule_t *ScheduleFromName( const char *pName );\ + static Schedule_t *m_scheduleList[]; + +#define DEFINE_CUSTOM_SCHEDULES(derivedClass)\ + Schedule_t *derivedClass::m_scheduleList[] = + +#define IMPLEMENT_CUSTOM_SCHEDULES(derivedClass, baseClass)\ + Schedule_t *derivedClass::ScheduleFromName( const char *pName )\ + {\ + Schedule_t *pSchedule = ScheduleInList( pName, m_scheduleList, ARRAYSIZE(m_scheduleList) );\ + if ( !pSchedule )\ + return baseClass::ScheduleFromName(pName);\ + return pSchedule;\ + } + +#endif //MONSTERS_H diff --git a/src/dlls/monsterstate.cpp b/src/dlls/monsterstate.cpp index 4329f5d..082ffb9 100644 --- a/src/dlls/monsterstate.cpp +++ b/src/dlls/monsterstate.cpp @@ -1,214 +1,214 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// monsterstate.cpp - base class monster functions for -// controlling core AI. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "nodes.h" -#include "monsters.h" -#include "animation.h" - -//========================================================= -// SetState -//========================================================= -void CMBaseMonster :: SetState ( MONSTERSTATE State ) -{ -/* - if ( State != m_MonsterState ) - { - ALERT ( at_aiconsole, "State Changed to %d\n", State ); - } -*/ - - switch( State ) - { - - // Drop enemy pointers when going to idle - case MONSTERSTATE_IDLE: - - if (( m_hEnemy != NULL ) && - ( strcmp(STRING(pev->model), "models/scientist.mdl") != 0)) - { - m_hEnemy = NULL;// not allowed to have an enemy anymore. - ALERT ( at_aiconsole, "Stripped\n" ); - } - break; - } - - m_MonsterState = State; - m_IdealMonsterState = State; -} - -//========================================================= -// RunAI -//========================================================= -void CMBaseMonster :: RunAI ( void ) -{ - // to test model's eye height - //UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); - - // IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state - // once we have sounds for that state. - if ( ( m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT ) && RANDOM_LONG(0,99) == 0 && !(pev->flags & SF_MONSTER_GAG) ) - { - IdleSound(); - } - - if ( m_MonsterState != MONSTERSTATE_NONE && - m_MonsterState != MONSTERSTATE_PRONE && - m_MonsterState != MONSTERSTATE_DEAD )// don't bother with this crap if monster is prone. - { - // collect some sensory Condition information. - // don't let monsters outside of the player's PVS act up, or most of the interesting - // things will happen before the player gets there! - // UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave - // an area where monsters are fighting, and the fight will continue. - if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) ) - { - Look( m_flDistLook ); -//jlb Listen();// check for audible sounds. - - // now filter conditions. - ClearConditions( IgnoreConditions() ); - - GetEnemy(); - } - - // do these calculations if monster has an enemy. - if ( m_hEnemy != NULL ) - { - CheckEnemy( m_hEnemy ); - } - - CheckAmmo(); - } - - FCheckAITrigger(); - - PrescheduleThink(); - - MaintainSchedule(); - - // if the monster didn't use these conditions during the above call to MaintainSchedule() or CheckAITrigger() - // we throw them out cause we don't want them sitting around through the lifespan of a schedule - // that doesn't use them. - m_afConditions &= ~( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); -} - -//========================================================= -// GetIdealState - surveys the Conditions information available -// and finds the best new state for a monster. -//========================================================= -MONSTERSTATE CMBaseMonster :: GetIdealState ( void ) -{ - int iConditions; - - iConditions = IScheduleFlags(); - - // If no schedule conditions, the new ideal state is probably the reason we're in here. - switch ( m_MonsterState ) - { - case MONSTERSTATE_IDLE: - - /* - IDLE goes to ALERT upon hearing a sound - -IDLE goes to ALERT upon being injured - IDLE goes to ALERT upon seeing food - -IDLE goes to COMBAT upon sighting an enemy - IDLE goes to HUNT upon smelling food - */ - { - if ( iConditions & bits_COND_NEW_ENEMY ) - { - // new enemy! This means an idle monster has seen someone it dislikes, or - // that a monster in combat has found a more suitable target to attack - m_IdealMonsterState = MONSTERSTATE_COMBAT; - } - else if ( iConditions & bits_COND_LIGHT_DAMAGE ) - { - MakeIdealYaw ( m_vecEnemyLKP ); - m_IdealMonsterState = MONSTERSTATE_ALERT; - } - else if ( iConditions & bits_COND_HEAVY_DAMAGE ) - { - MakeIdealYaw ( m_vecEnemyLKP ); - m_IdealMonsterState = MONSTERSTATE_ALERT; - } - else if ( iConditions & bits_COND_HEAR_SOUND ) - { - } - else if ( iConditions & (bits_COND_SMELL | bits_COND_SMELL_FOOD) ) - { - m_IdealMonsterState = MONSTERSTATE_ALERT; - } - - break; - } - case MONSTERSTATE_ALERT: - /* - ALERT goes to IDLE upon becoming bored - -ALERT goes to COMBAT upon sighting an enemy - ALERT goes to HUNT upon hearing a noise - */ - { - if ( iConditions & (bits_COND_NEW_ENEMY|bits_COND_SEE_ENEMY) ) - { - // see an enemy we MUST attack - m_IdealMonsterState = MONSTERSTATE_COMBAT; - } - else if ( iConditions & bits_COND_HEAR_SOUND ) - { - m_IdealMonsterState = MONSTERSTATE_ALERT; - } - break; - } - case MONSTERSTATE_COMBAT: - /* - COMBAT goes to HUNT upon losing sight of enemy - COMBAT goes to ALERT upon death of enemy - */ - { - if ( m_hEnemy == NULL ) - { - m_IdealMonsterState = MONSTERSTATE_ALERT; - // pev->effects = EF_BRIGHTFIELD; - ALERT ( at_aiconsole, "***Combat state with no enemy!\n" ); - } - break; - } - case MONSTERSTATE_HUNT: - /* - HUNT goes to ALERT upon seeing food - HUNT goes to ALERT upon being injured - HUNT goes to IDLE if goal touched - HUNT goes to COMBAT upon seeing enemy - */ - { - break; - } - - case MONSTERSTATE_DEAD: - m_IdealMonsterState = MONSTERSTATE_DEAD; - break; - } - - return m_IdealMonsterState; -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monsterstate.cpp - base class monster functions for +// controlling core AI. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "nodes.h" +#include "monsters.h" +#include "animation.h" + +//========================================================= +// SetState +//========================================================= +void CMBaseMonster :: SetState ( MONSTERSTATE State ) +{ +/* + if ( State != m_MonsterState ) + { + ALERT ( at_aiconsole, "State Changed to %d\n", State ); + } +*/ + + switch( State ) + { + + // Drop enemy pointers when going to idle + case MONSTERSTATE_IDLE: + + if (( m_hEnemy != NULL ) && + ( strcmp(STRING(pev->model), "models/scientist.mdl") != 0)) + { + m_hEnemy = NULL;// not allowed to have an enemy anymore. + ALERT ( at_aiconsole, "Stripped\n" ); + } + break; + } + + m_MonsterState = State; + m_IdealMonsterState = State; +} + +//========================================================= +// RunAI +//========================================================= +void CMBaseMonster :: RunAI ( void ) +{ + // to test model's eye height + //UTIL_ParticleEffect ( pev->origin + pev->view_ofs, g_vecZero, 255, 10 ); + + // IDLE sound permitted in ALERT state is because monsters were silent in ALERT state. Only play IDLE sound in IDLE state + // once we have sounds for that state. + if ( ( m_MonsterState == MONSTERSTATE_IDLE || m_MonsterState == MONSTERSTATE_ALERT ) && RANDOM_LONG(0,99) == 0 && !(pev->flags & SF_MONSTER_GAG) ) + { + IdleSound(); + } + + if ( m_MonsterState != MONSTERSTATE_NONE && + m_MonsterState != MONSTERSTATE_PRONE && + m_MonsterState != MONSTERSTATE_DEAD )// don't bother with this crap if monster is prone. + { + // collect some sensory Condition information. + // don't let monsters outside of the player's PVS act up, or most of the interesting + // things will happen before the player gets there! + // UPDATE: We now let COMBAT state monsters think and act fully outside of player PVS. This allows the player to leave + // an area where monsters are fighting, and the fight will continue. + if ( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) || ( m_MonsterState == MONSTERSTATE_COMBAT ) ) + { + Look( m_flDistLook ); +//jlb Listen();// check for audible sounds. + + // now filter conditions. + ClearConditions( IgnoreConditions() ); + + GetEnemy(); + } + + // do these calculations if monster has an enemy. + if ( m_hEnemy != NULL ) + { + CheckEnemy( m_hEnemy ); + } + + CheckAmmo(); + } + + FCheckAITrigger(); + + PrescheduleThink(); + + MaintainSchedule(); + + // if the monster didn't use these conditions during the above call to MaintainSchedule() or CheckAITrigger() + // we throw them out cause we don't want them sitting around through the lifespan of a schedule + // that doesn't use them. + m_afConditions &= ~( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ); +} + +//========================================================= +// GetIdealState - surveys the Conditions information available +// and finds the best new state for a monster. +//========================================================= +MONSTERSTATE CMBaseMonster :: GetIdealState ( void ) +{ + int iConditions; + + iConditions = IScheduleFlags(); + + // If no schedule conditions, the new ideal state is probably the reason we're in here. + switch ( m_MonsterState ) + { + case MONSTERSTATE_IDLE: + + /* + IDLE goes to ALERT upon hearing a sound + -IDLE goes to ALERT upon being injured + IDLE goes to ALERT upon seeing food + -IDLE goes to COMBAT upon sighting an enemy + IDLE goes to HUNT upon smelling food + */ + { + if ( iConditions & bits_COND_NEW_ENEMY ) + { + // new enemy! This means an idle monster has seen someone it dislikes, or + // that a monster in combat has found a more suitable target to attack + m_IdealMonsterState = MONSTERSTATE_COMBAT; + } + else if ( iConditions & bits_COND_LIGHT_DAMAGE ) + { + MakeIdealYaw ( m_vecEnemyLKP ); + m_IdealMonsterState = MONSTERSTATE_ALERT; + } + else if ( iConditions & bits_COND_HEAVY_DAMAGE ) + { + MakeIdealYaw ( m_vecEnemyLKP ); + m_IdealMonsterState = MONSTERSTATE_ALERT; + } + else if ( iConditions & bits_COND_HEAR_SOUND ) + { + } + else if ( iConditions & (bits_COND_SMELL | bits_COND_SMELL_FOOD) ) + { + m_IdealMonsterState = MONSTERSTATE_ALERT; + } + + break; + } + case MONSTERSTATE_ALERT: + /* + ALERT goes to IDLE upon becoming bored + -ALERT goes to COMBAT upon sighting an enemy + ALERT goes to HUNT upon hearing a noise + */ + { + if ( iConditions & (bits_COND_NEW_ENEMY|bits_COND_SEE_ENEMY) ) + { + // see an enemy we MUST attack + m_IdealMonsterState = MONSTERSTATE_COMBAT; + } + else if ( iConditions & bits_COND_HEAR_SOUND ) + { + m_IdealMonsterState = MONSTERSTATE_ALERT; + } + break; + } + case MONSTERSTATE_COMBAT: + /* + COMBAT goes to HUNT upon losing sight of enemy + COMBAT goes to ALERT upon death of enemy + */ + { + if ( m_hEnemy == NULL ) + { + m_IdealMonsterState = MONSTERSTATE_ALERT; + // pev->effects = EF_BRIGHTFIELD; + ALERT ( at_aiconsole, "***Combat state with no enemy!\n" ); + } + break; + } + case MONSTERSTATE_HUNT: + /* + HUNT goes to ALERT upon seeing food + HUNT goes to ALERT upon being injured + HUNT goes to IDLE if goal touched + HUNT goes to COMBAT upon seeing enemy + */ + { + break; + } + + case MONSTERSTATE_DEAD: + m_IdealMonsterState = MONSTERSTATE_DEAD; + break; + } + + return m_IdealMonsterState; +} + diff --git a/src/dlls/nodes.cpp b/src/dlls/nodes.cpp index 88423ba..85b9320 100644 --- a/src/dlls/nodes.cpp +++ b/src/dlls/nodes.cpp @@ -1,3630 +1,3630 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// nodes.cpp - AI node tree stuff. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "nodes.h" -#include "animation.h" -#include "doors.h" - -//#if !defined ( _WIN32 ) -#include -#include -#include -#include // mkdir -//#endif - -#define HULL_STEP_SIZE 16// how far the test hull moves on each step -#define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier) - -// to help eliminate node clutter by level designers, this is used to cap how many other nodes -// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()". -#define MAX_NODE_INITIAL_LINKS 128 -#define MAX_NODES 1024 - -Vector VecBModelOrigin( entvars_t* pevBModel ); - -CGraph WorldGraph; - -#ifdef __linux__ -#include -#define CreateDirectory(p, n) mkdir(p, 0777) -#endif -//========================================================= -// CGraph - InitGraph - prepares the graph for use. Frees any -// memory currently in use by the world graph, NULLs -// all pointers, and zeros the node count. -//========================================================= -void CGraph :: InitGraph( void) -{ - - // Make the graph unavailable - // - m_fGraphPresent = FALSE; - m_fGraphPointersSet = FALSE; - m_fRoutingComplete = FALSE; - - // Free the link pool - // - if ( m_pLinkPool ) - { - free ( m_pLinkPool ); - m_pLinkPool = NULL; - } - - // Free the node info - // - if ( m_pNodes ) - { - free ( m_pNodes ); - m_pNodes = NULL; - } - - if ( m_di ) - { - free ( m_di ); - m_di = NULL; - } - - // Free the routing info. - // - if ( m_pRouteInfo ) - { - free ( m_pRouteInfo ); - m_pRouteInfo = NULL; - } - - if (m_pHashLinks) - { - free(m_pHashLinks); - m_pHashLinks = NULL; - } - - // Zero node and link counts - // - m_cNodes = 0; - m_cLinks = 0; - m_nRouteInfo = 0; - - m_iLastActiveIdleSearch = 0; - m_iLastCoverSearch = 0; -} - -//========================================================= -// CGraph - AllocNodes - temporary function that mallocs a -// reasonable number of nodes so we can build the path which -// will be saved to disk. -//========================================================= -int CGraph :: AllocNodes ( void ) -{ -// malloc all of the nodes - WorldGraph.m_pNodes = (CNode *)calloc ( sizeof ( CNode ), MAX_NODES ); - -// could not malloc space for all the nodes! - if ( !WorldGraph.m_pNodes ) - { - ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d nodes!\n", WorldGraph.m_cNodes ); - return FALSE; - } - - return TRUE; -} - -//========================================================= -// CGraph - LinkEntForLink - sometimes the ent that blocks -// a path is a usable door, in which case the monster just -// needs to face the door and fire it. In other cases, the -// monster needs to operate a button or lever to get the -// door to open. This function will return a pointer to the -// button if the monster needs to hit a button to open the -// door, or returns a pointer to the door if the monster -// need only use the door. -// -// pNode is the node the monster will be standing on when it -// will need to stop and trigger the ent. -//========================================================= -entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) -{ - edict_t *pentSearch; - edict_t *pentTrigger; - entvars_t *pevTrigger; - entvars_t *pevLinkEnt; - TraceResult tr; - - pevLinkEnt = pLink->m_pLinkEnt; - if ( !pevLinkEnt ) - return NULL; - - pentSearch = NULL;// start search at the top of the ent list. - - if ( FClassnameIs ( pevLinkEnt, "func_door" ) || FClassnameIs ( pevLinkEnt, "func_door_rotating" ) ) - { - - ///!!!UNDONE - check for TOGGLE or STAY open doors here. If a door is in the way, and is - // TOGGLE or STAY OPEN, even monsters that can't open doors can go that way. - - if ( ( pevLinkEnt->spawnflags & SF_DOOR_USE_ONLY ) ) - {// door is use only, so the door is all the monster has to worry about - return pevLinkEnt; - } - - while ( 1 ) - { - pentTrigger = FIND_ENTITY_BY_TARGET ( pentSearch, STRING( pevLinkEnt->targetname ) );// find the button or trigger - - if ( FNullEnt( pentTrigger ) ) - {// no trigger found - - // right now this is a problem among auto-open doors, or any door that opens through the use - // of a trigger brush. Trigger brushes have no models, and don't show up in searches. Just allow - // monsters to open these sorts of doors for now. - return pevLinkEnt; - } - - pentSearch = pentTrigger; - pevTrigger = VARS( pentTrigger ); - - if ( FClassnameIs(pevTrigger, "func_button") || FClassnameIs(pevTrigger, "func_rot_button" ) ) - {// only buttons are handled right now. - - // trace from the node to the trigger, make sure it's one we can see from the node. - // !!!HACKHACK Use bodyqueue here cause there are no ents we really wish to ignore! - UTIL_TraceLine ( pNode->m_vecOrigin, VecBModelOrigin( pevTrigger ), ignore_monsters, NULL, &tr ); - - - if ( VARS(tr.pHit) == pevTrigger ) - {// good to go! - return VARS( tr.pHit ); - } - } - } - } - else - { - ALERT ( at_aiconsole, "Unsupported PathEnt:\n'%s'\n", STRING ( pevLinkEnt->classname ) ); - return NULL; - } -} - -//========================================================= -// CGraph - HandleLinkEnt - a brush ent is between two -// nodes that would otherwise be able to see each other. -// Given the monster's capability, determine whether -// or not the monster can go this way. -//========================================================= -int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType ) -{ - edict_t *pentWorld; - CMBaseEntity *pDoor; - TraceResult tr; - - if ( !m_fGraphPresent || !m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return FALSE; - } - - if ( FNullEnt ( pevLinkEnt ) ) - { - ALERT ( at_aiconsole, "dead path ent!\n" ); - return TRUE; - } - pentWorld = NULL; - -// func_door - if ( FClassnameIs( pevLinkEnt, "func_door" ) || FClassnameIs( pevLinkEnt, "func_door_rotating" ) ) - {// ent is a door. - - pDoor = ( CMBaseEntity::Instance( pevLinkEnt ) ); - - if ( ( pevLinkEnt->spawnflags & SF_DOOR_USE_ONLY ) ) - {// door is use only. - - if ( ( afCapMask & bits_CAP_OPEN_DOORS ) ) - {// let monster right through if he can open doors - return TRUE; - } - else - { - // monster should try for it if the door is open and looks as if it will stay that way - if ( pDoor->GetToggleState()== TS_AT_TOP && ( pevLinkEnt->spawnflags & SF_DOOR_NO_AUTO_RETURN ) ) - { - return TRUE; - } - - return FALSE; - } - } - else - {// door must be opened with a button or trigger field. - - // monster should try for it if the door is open and looks as if it will stay that way - if ( pDoor->GetToggleState() == TS_AT_TOP && ( pevLinkEnt->spawnflags & SF_DOOR_NO_AUTO_RETURN ) ) - { - return TRUE; - } - if ( ( afCapMask & bits_CAP_OPEN_DOORS ) ) - { - if ( !( pevLinkEnt->spawnflags & SF_DOOR_NOMONSTERS ) || queryType == NODEGRAPH_STATIC ) - return TRUE; - } - - return FALSE; - } - } -// func_breakable - else if ( FClassnameIs( pevLinkEnt, "func_breakable" ) && queryType == NODEGRAPH_STATIC ) - { - return TRUE; - } - else - { - ALERT ( at_aiconsole, "Unhandled Ent in Path %s\n", STRING( pevLinkEnt->classname ) ); - return FALSE; - } - - return FALSE; -} - -#if 0 -//========================================================= -// FindNearestLink - finds the connection (line) nearest -// the given point. Returns FALSE if fails, or TRUE if it -// has stuffed the index into the nearest link pool connection -// into the passed int pointer, and a BOOL telling whether or -// not the point is along the line into the passed BOOL pointer. -//========================================================= -int CGraph :: FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine ) -{ - int i, j;// loops - - int iNearestLink;// index into the link pool, this is the nearest node at any time. - float flMinDist;// the distance of of the nearest case so far - float flDistToLine;// the distance of the current test case - - BOOL fCurrentAlongLine; - BOOL fSuccess; - - //float flConstant;// line constant - Vector vecSpot1, vecSpot2; - Vector2D vec2Spot1, vec2Spot2, vec2TestPoint; - Vector2D vec2Normal;// line normal - Vector2D vec2Line; - - TraceResult tr; - - iNearestLink = -1;// prepare for failure - fSuccess = FALSE; - - flMinDist = 9999;// anything will be closer than this - -// go through all of the nodes, and each node's connections - int cSkip = 0;// how many links proper pairing allowed us to skip - int cChecked = 0;// how many links were checked - - for ( i = 0 ; i < m_cNodes ; i++ ) - { - vecSpot1 = m_pNodes[ i ].m_vecOrigin; - - if ( m_pNodes[ i ].m_cNumLinks <= 0 ) - {// this shouldn't happen! - ALERT ( at_aiconsole, "**Node %d has no links\n", i ); - continue; - } - - for ( j = 0 ; j < m_pNodes[ i ].m_cNumLinks ; j++ ) - { - /* - !!!This optimization only works when the node graph consists of properly linked pairs. - if ( INodeLink ( i, j ) <= i ) - { - // since we're going through the nodes in order, don't check - // any connections whose second node is lower in the list - // than the node we're currently working with. This eliminates - // redundant checks. - cSkip++; - continue; - } - */ - - vecSpot2 = PNodeLink ( i, j )->m_vecOrigin; - - // these values need a little attention now and then, or sometimes ramps cause trouble. - if ( fabs ( vecSpot1.z - vecTestPoint.z ) > 48 && fabs ( vecSpot2.z - vecTestPoint.z ) > 48 ) - { - // if both endpoints of the line are 32 units or more above or below the monster, - // the monster won't be able to get to them, so we do a bit of trivial rejection here. - // this may change if monsters are allowed to jump down. - // - // !!!LATER: some kind of clever X/Y hashing should be used here, too - continue; - } - -// now we have two endpoints for a line segment that we've not already checked. -// since all lines that make it this far are within -/+ 32 units of the test point's -// Z Plane, we can get away with doing the point->line check in 2d. - - cChecked++; - - vec2Spot1 = vecSpot1.Make2D(); - vec2Spot2 = vecSpot2.Make2D(); - vec2TestPoint = vecTestPoint.Make2D(); - - // get the line normal. - vec2Line = ( vec2Spot1 - vec2Spot2 ).Normalize(); - vec2Normal.x = -vec2Line.y; - vec2Normal.y = vec2Line.x; - - if ( DotProduct ( vec2Line, ( vec2TestPoint - vec2Spot1 ) ) > 0 ) - {// point outside of line - flDistToLine = ( vec2TestPoint - vec2Spot1 ).Length(); - fCurrentAlongLine = FALSE; - } - else if ( DotProduct ( vec2Line, ( vec2TestPoint - vec2Spot2 ) ) < 0 ) - {// point outside of line - flDistToLine = ( vec2TestPoint - vec2Spot2 ).Length(); - fCurrentAlongLine = FALSE; - } - else - {// point inside line - flDistToLine = fabs( DotProduct ( vec2TestPoint - vec2Spot2, vec2Normal ) ); - fCurrentAlongLine = TRUE; - } - - if ( flDistToLine < flMinDist ) - {// just found a line nearer than any other so far - - UTIL_TraceLine ( vecTestPoint, SourceNode( i, j ).m_vecOrigin, ignore_monsters, NULL, &tr ); - - if ( tr.flFraction != 1.0 ) - {// crap. can't see the first node of this link, try to see the other - - UTIL_TraceLine ( vecTestPoint, DestNode( i, j ).m_vecOrigin, ignore_monsters, NULL, &tr ); - if ( tr.flFraction != 1.0 ) - {// can't use this link, cause can't see either node! - continue; - } - - } - - fSuccess = TRUE;// we know there will be something to return. - flMinDist = flDistToLine; - iNearestLink = m_pNodes [ i ].m_iFirstLink + j; - *piNearestLink = m_pNodes[ i ].m_iFirstLink + j; - *pfAlongLine = fCurrentAlongLine; - } - } - } - -/* - if ( fSuccess ) - { - WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); - WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); - - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.x ); - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.y ); - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.z + NODE_HEIGHT); - - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.x ); - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.y ); - WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.z + NODE_HEIGHT); - } -*/ - - ALERT ( at_aiconsole, "%d Checked\n", cChecked ); - return fSuccess; -} - -#endif - -int CGraph::HullIndex( const CMBaseEntity *pEntity ) -{ - if ( pEntity->pev->movetype == MOVETYPE_FLY) - return NODE_FLY_HULL; - - if ( pEntity->pev->mins == Vector( -12, -12, 0 ) ) - return NODE_SMALL_HULL; - else if ( pEntity->pev->mins == VEC_HUMAN_HULL_MIN ) - return NODE_HUMAN_HULL; - else if ( pEntity->pev->mins == Vector ( -32, -32, 0 ) ) - return NODE_LARGE_HULL; - -// ALERT ( at_aiconsole, "Unknown Hull Mins!\n" ); - return NODE_HUMAN_HULL; -} - - -int CGraph::NodeType( const CMBaseEntity *pEntity ) -{ - if ( pEntity->pev->movetype == MOVETYPE_FLY) - { - if (pEntity->pev->waterlevel != 0) - { - return bits_NODE_WATER; - } - else - { - return bits_NODE_AIR; - } - } - return bits_NODE_LAND; -} - - -// Sum up graph weights on the path from iStart to iDest to determine path length -float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) -{ - float distance = 0; - int iNext; - - int iMaxLoop = m_cNodes; - - int iCurrentNode = iStart; - int iCap = CapIndex( afCapMask ); - - while (iCurrentNode != iDest) - { - if (iMaxLoop-- <= 0) - { - ALERT( at_console, "Route Failure\n" ); - return 0; - } - - iNext = NextNodeInRoute( iCurrentNode, iDest, iHull, iCap ); - if (iCurrentNode == iNext) - { - //ALERT(at_aiconsole, "SVD: Can't get there from here..\n"); - return 0; - } - - int iLink; - HashSearch(iCurrentNode, iNext, iLink); - if (iLink < 0) - { - ALERT(at_console, "HashLinks is broken from %d to %d.\n", iCurrentNode, iDest); - return 0; - } - CLink &link = Link(iLink); - distance += link.m_flWeight; - - iCurrentNode = iNext; - } - - return distance; -} - - -// Parse the routing table at iCurrentNode for the next node on the shortest path to iDest -int CGraph::NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap ) -{ - int iNext = iCurrentNode; - int nCount = iDest+1; - char *pRoute = m_pRouteInfo + m_pNodes[ iCurrentNode ].m_pNextBestNode[iHull][iCap]; - - // Until we decode the next best node - // - while (nCount > 0) - { - char ch = *pRoute++; - //ALERT(at_aiconsole, "C(%d)", ch); - if (ch < 0) - { - // Sequence phrase - // - ch = -ch; - if (nCount <= ch) - { - iNext = iDest; - nCount = 0; - //ALERT(at_aiconsole, "SEQ: iNext/iDest=%d\n", iNext); - } - else - { - //ALERT(at_aiconsole, "SEQ: nCount + ch (%d + %d)\n", nCount, ch); - nCount = nCount - ch; - } - } - else - { - //ALERT(at_aiconsole, "C(%d)", *pRoute); - - // Repeat phrase - // - if (nCount <= ch+1) - { - iNext = iCurrentNode + *pRoute; - if (iNext >= m_cNodes) iNext -= m_cNodes; - else if (iNext < 0) iNext += m_cNodes; - nCount = 0; - //ALERT(at_aiconsole, "REP: iNext=%d\n", iNext); - } - else - { - //ALERT(at_aiconsole, "REP: nCount - ch+1 (%d - %d+1)\n", nCount, ch); - nCount = nCount - ch - 1; - } - pRoute++; - } - } - - return iNext; -} - - -//========================================================= -// CGraph - FindShortestPath -// -// accepts a capability mask (afCapMask), and will only -// find a path usable by a monster with those capabilities -// returns the number of nodes copied into supplied array -//========================================================= -int CGraph :: FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask) -{ - int iVisitNode; - int iCurrentNode; - int iNumPathNodes; - int iHullMask; - - if ( !m_fGraphPresent || !m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available or built - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return FALSE; - } - - if ( iStart < 0 || iStart > m_cNodes ) - {// The start node is bad? - ALERT ( at_aiconsole, "Can't build a path, iStart is %d!\n", iStart ); - return FALSE; - } - - if (iStart == iDest) - { - piPath[0] = iStart; - piPath[1] = iDest; - return 2; - } - - // Is routing information present. - // - if (m_fRoutingComplete) - { - int iCap = CapIndex( afCapMask ); - - iNumPathNodes = 0; - piPath[iNumPathNodes++] = iStart; - iCurrentNode = iStart; - int iNext; - - //ALERT(at_aiconsole, "GOAL: %d to %d\n", iStart, iDest); - - // Until we arrive at the destination - // - while (iCurrentNode != iDest) - { - iNext = NextNodeInRoute( iCurrentNode, iDest, iHull, iCap ); - if (iCurrentNode == iNext) - { - //ALERT(at_aiconsole, "SVD: Can't get there from here..\n"); - return 0; - break; - } - if (iNumPathNodes >= MAX_PATH_SIZE) - { - //ALERT(at_aiconsole, "SVD: Don't return the entire path.\n"); - break; - } - piPath[iNumPathNodes++] = iNext; - iCurrentNode = iNext; - } - //ALERT( at_aiconsole, "SVD: Path with %d nodes.\n", iNumPathNodes); - } - else - { - CQueuePriority queue; - - switch( iHull ) - { - case NODE_SMALL_HULL: - iHullMask = bits_LINK_SMALL_HULL; - break; - case NODE_HUMAN_HULL: - iHullMask = bits_LINK_HUMAN_HULL; - break; - case NODE_LARGE_HULL: - iHullMask = bits_LINK_LARGE_HULL; - break; - case NODE_FLY_HULL: - iHullMask = bits_LINK_FLY_HULL; - break; - } - - // Mark all the nodes as unvisited. - // - int i; - for ( i = 0; i < m_cNodes; i++) - { - m_pNodes[ i ].m_flClosestSoFar = -1.0; - } - - m_pNodes[ iStart ].m_flClosestSoFar = 0.0; - m_pNodes[ iStart ].m_iPreviousNode = iStart;// tag this as the origin node - queue.Insert( iStart, 0.0 );// insert start node - - while ( !queue.Empty() ) - { - // now pull a node out of the queue - float flCurrentDistance; - iCurrentNode = queue.Remove(flCurrentDistance); - - // For straight-line weights, the following Shortcut works. For arbitrary weights, - // it doesn't. - // - if (iCurrentNode == iDest) break; - - CNode *pCurrentNode = &m_pNodes[ iCurrentNode ]; - - for ( i = 0 ; i < pCurrentNode->m_cNumLinks ; i++ ) - {// run through all of this node's neighbors - - iVisitNode = INodeLink ( iCurrentNode, i ); - if ( ( m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_afLinkInfo & iHullMask ) != iHullMask ) - {// monster is too large to walk this connection - //ALERT ( at_aiconsole, "fat ass %d/%d\n",m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_afLinkInfo, iMonsterHull ); - continue; - } - // check the connection from the current node to the node we're about to mark visited and push into the queue - if ( m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_pLinkEnt != NULL ) - {// there's a brush ent in the way! Don't mark this node or put it into the queue unless the monster can negotiate it - - if ( !HandleLinkEnt ( iCurrentNode, m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_pLinkEnt, afCapMask, NODEGRAPH_STATIC ) ) - {// monster should not try to go this way. - continue; - } - } - float flOurDistance = flCurrentDistance + m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i].m_flWeight; - if ( m_pNodes[ iVisitNode ].m_flClosestSoFar < -0.5 - || flOurDistance < m_pNodes[ iVisitNode ].m_flClosestSoFar - 0.001 ) - { - m_pNodes[iVisitNode].m_flClosestSoFar = flOurDistance; - m_pNodes[iVisitNode].m_iPreviousNode = iCurrentNode; - - queue.Insert ( iVisitNode, flOurDistance ); - } - } - } - if ( m_pNodes[iDest].m_flClosestSoFar < -0.5 ) - {// Destination is unreachable, no path found. - return 0; - } - - // the queue is not empty - - // now we must walk backwards through the m_iPreviousNode field, and count how many connections there are in the path - iCurrentNode = iDest; - iNumPathNodes = 1;// count the dest - - while ( iCurrentNode != iStart ) - { - iNumPathNodes++; - iCurrentNode = m_pNodes[ iCurrentNode ].m_iPreviousNode; - } - - iCurrentNode = iDest; - for ( i = iNumPathNodes - 1 ; i >= 0 ; i-- ) - { - piPath[ i ] = iCurrentNode; - iCurrentNode = m_pNodes [ iCurrentNode ].m_iPreviousNode; - } - } - -#if 0 - - if (m_fRoutingComplete) - { - // This will draw the entire path that was generated for the monster. - - for ( int i = 0 ; i < iNumPathNodes - 1 ; i++ ) - { - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - - WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.x ); - WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.y ); - WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.z + NODE_HEIGHT ); - - WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.x ); - WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.y ); - WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.z + NODE_HEIGHT ); - MESSAGE_END(); - } - } - -#endif -#if 0 // MAZE map - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - - WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.x ); - WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.y ); - WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.z + NODE_HEIGHT ); - - WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.x ); - WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.y ); - WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.z + NODE_HEIGHT ); - MESSAGE_END(); -#endif - - return iNumPathNodes; -} - -inline ULONG Hash(void *p, int len) -{ - CRC32_t ulCrc; - CRC32_INIT(&ulCrc); - CRC32_PROCESS_BUFFER(&ulCrc, p, len); - return CRC32_FINAL(ulCrc); -} - -void inline CalcBounds(int &Lower, int &Upper, int Goal, int Best) -{ - int Temp = 2*Goal - Best; - if (Best > Goal) - { - Lower = max(0, Temp); - Upper = Best; - } - else - { - Upper = min(255, Temp); - Lower = Best; - } -} - -// Convert from [-8192,8192] to [0, 255] -// -inline int CALC_RANGE(int x, int lower, int upper) -{ - return NUM_RANGES*(x-lower)/((upper-lower+1)); -} - - -void inline UpdateRange(int &minValue, int &maxValue, int Goal, int Best) -{ - int Lower, Upper; - CalcBounds(Lower, Upper, Goal, Best); - if (Upper < maxValue) maxValue = Upper; - if (minValue < Lower) minValue = Lower; -} - -void CGraph :: CheckNode(Vector vecOrigin, int iNode) -{ - // Have we already seen this point before?. - // - if (m_di[iNode].m_CheckedEvent == m_CheckedCounter) return; - m_di[iNode].m_CheckedEvent = m_CheckedCounter; - - float flDist = ( vecOrigin - m_pNodes[ iNode ].m_vecOriginPeek ).Length(); - - if ( flDist < m_flShortest ) - { - TraceResult tr; - - // make sure that vecOrigin can trace to this node! - UTIL_TraceLine ( vecOrigin, m_pNodes[ iNode ].m_vecOriginPeek, ignore_monsters, 0, &tr ); - - if ( tr.flFraction == 1.0 ) - { - m_iNearest = iNode; - m_flShortest = flDist; - - UpdateRange(m_minX, m_maxX, CALC_RANGE(vecOrigin.x, m_RegionMin[0], m_RegionMax[0]), m_pNodes[iNode].m_Region[0]); - UpdateRange(m_minY, m_maxY, CALC_RANGE(vecOrigin.y, m_RegionMin[1], m_RegionMax[1]), m_pNodes[iNode].m_Region[1]); - UpdateRange(m_minZ, m_maxZ, CALC_RANGE(vecOrigin.z, m_RegionMin[2], m_RegionMax[2]), m_pNodes[iNode].m_Region[2]); - - // From maxCircle, calculate maximum bounds box. All points must be - // simultaneously inside all bounds of the box. - // - m_minBoxX = CALC_RANGE(vecOrigin.x - flDist, m_RegionMin[0], m_RegionMax[0]); - m_maxBoxX = CALC_RANGE(vecOrigin.x + flDist, m_RegionMin[0], m_RegionMax[0]); - m_minBoxY = CALC_RANGE(vecOrigin.y - flDist, m_RegionMin[1], m_RegionMax[1]); - m_maxBoxY = CALC_RANGE(vecOrigin.y + flDist, m_RegionMin[1], m_RegionMax[1]); - m_minBoxZ = CALC_RANGE(vecOrigin.z - flDist, m_RegionMin[2], m_RegionMax[2]); - m_maxBoxZ = CALC_RANGE(vecOrigin.z + flDist, m_RegionMin[2], m_RegionMax[2]); - } - } -} - -//========================================================= -// CGraph - FindNearestNode - returns the index of the node nearest -// the given vector -1 is failure (couldn't find a valid -// near node ) -//========================================================= -int CGraph :: FindNearestNode ( const Vector &vecOrigin, CMBaseEntity *pEntity ) -{ - return FindNearestNode( vecOrigin, NodeType( pEntity ) ); -} - -int CGraph :: FindNearestNode ( const Vector &vecOrigin, int afNodeTypes ) -{ - int i; - TraceResult tr; - - if ( !m_fGraphPresent || !m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return -1; - } - - // Check with the cache - // - ULONG iHash = (CACHE_SIZE-1) & Hash((void *)(const float *)vecOrigin, sizeof(vecOrigin)); - if (m_Cache[iHash].v == vecOrigin) - { - //ALERT(at_aiconsole, "Cache Hit.\n"); - return m_Cache[iHash].n; - } - else - { - //ALERT(at_aiconsole, "Cache Miss.\n"); - } - - // Mark all points as unchecked. - // - m_CheckedCounter++; - if (m_CheckedCounter == 0) - { - for (int i = 0; i < m_cNodes; i++) - { - m_di[i].m_CheckedEvent = 0; - } - m_CheckedCounter++; - } - - m_iNearest = -1; - m_flShortest = 999999.0; // just a big number. - - // If we can find a visible point, then let CalcBounds set the limits, but if - // we have no visible point at all to start with, then don't restrict the limits. - // -#if 1 - m_minX = 0; m_maxX = 255; - m_minY = 0; m_maxY = 255; - m_minZ = 0; m_maxZ = 255; - m_minBoxX = 0; m_maxBoxX = 255; - m_minBoxY = 0; m_maxBoxY = 255; - m_minBoxZ = 0; m_maxBoxZ = 255; -#else - m_minBoxX = CALC_RANGE(vecOrigin.x - flDist, m_RegionMin[0], m_RegionMax[0]); - m_maxBoxX = CALC_RANGE(vecOrigin.x + flDist, m_RegionMin[0], m_RegionMax[0]); - m_minBoxY = CALC_RANGE(vecOrigin.y - flDist, m_RegionMin[1], m_RegionMax[1]); - m_maxBoxY = CALC_RANGE(vecOrigin.y + flDist, m_RegionMin[1], m_RegionMax[1]); - m_minBoxZ = CALC_RANGE(vecOrigin.z - flDist, m_RegionMin[2], m_RegionMax[2]); - m_maxBoxZ = CALC_RANGE(vecOrigin.z + flDist, m_RegionMin[2], m_RegionMax[2]) - CalcBounds(m_minX, m_maxX, CALC_RANGE(vecOrigin.x, m_RegionMin[0], m_RegionMax[0]), m_pNodes[m_iNearest].m_Region[0]); - CalcBounds(m_minY, m_maxY, CALC_RANGE(vecOrigin.y, m_RegionMin[1], m_RegionMax[1]), m_pNodes[m_iNearest].m_Region[1]); - CalcBounds(m_minZ, m_maxZ, CALC_RANGE(vecOrigin.z, m_RegionMin[2], m_RegionMax[2]), m_pNodes[m_iNearest].m_Region[2]); -#endif - - int halfX = (m_minX+m_maxX)/2; - int halfY = (m_minY+m_maxY)/2; - int halfZ = (m_minZ+m_maxZ)/2; - - int j; - - for (i = halfX; i >= m_minX; i--) - { - for (j = m_RangeStart[0][i]; j <= m_RangeEnd[0][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[0]].m_afNodeInfo & afNodeTypes)) continue; - - int rgY = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[1]; - if (rgY > m_maxBoxY) break; - if (rgY < m_minBoxY) continue; - - int rgZ = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[2]; - if (rgZ < m_minBoxZ) continue; - if (rgZ > m_maxBoxZ) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[0]); - } - } - - for (i = max(m_minY,halfY+1); i <= m_maxY; i++) - { - for (j = m_RangeStart[1][i]; j <= m_RangeEnd[1][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[1]].m_afNodeInfo & afNodeTypes)) continue; - - int rgZ = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[2]; - if (rgZ > m_maxBoxZ) break; - if (rgZ < m_minBoxZ) continue; - int rgX = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[0]; - if (rgX < m_minBoxX) continue; - if (rgX > m_maxBoxX) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[1]); - } - } - - for (i = min(m_maxZ,halfZ); i >= m_minZ; i--) - { - for (j = m_RangeStart[2][i]; j <= m_RangeEnd[2][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[2]].m_afNodeInfo & afNodeTypes)) continue; - - int rgX = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[0]; - if (rgX > m_maxBoxX) break; - if (rgX < m_minBoxX) continue; - int rgY = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[1]; - if (rgY < m_minBoxY) continue; - if (rgY > m_maxBoxY) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[2]); - } - } - - for (i = max(m_minX,halfX+1); i <= m_maxX; i++) - { - for (j = m_RangeStart[0][i]; j <= m_RangeEnd[0][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[0]].m_afNodeInfo & afNodeTypes)) continue; - - int rgY = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[1]; - if (rgY > m_maxBoxY) break; - if (rgY < m_minBoxY) continue; - - int rgZ = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[2]; - if (rgZ < m_minBoxZ) continue; - if (rgZ > m_maxBoxZ) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[0]); - } - } - - for (i = min(m_maxY,halfY); i >= m_minY; i--) - { - for (j = m_RangeStart[1][i]; j <= m_RangeEnd[1][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[1]].m_afNodeInfo & afNodeTypes)) continue; - - int rgZ = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[2]; - if (rgZ > m_maxBoxZ) break; - if (rgZ < m_minBoxZ) continue; - int rgX = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[0]; - if (rgX < m_minBoxX) continue; - if (rgX > m_maxBoxX) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[1]); - } - } - - for (i = max(m_minZ,halfZ+1); i <= m_maxZ; i++) - { - for (j = m_RangeStart[2][i]; j <= m_RangeEnd[2][i]; j++) - { - if (!(m_pNodes[m_di[j].m_SortedBy[2]].m_afNodeInfo & afNodeTypes)) continue; - - int rgX = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[0]; - if (rgX > m_maxBoxX) break; - if (rgX < m_minBoxX) continue; - int rgY = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[1]; - if (rgY < m_minBoxY) continue; - if (rgY > m_maxBoxY) continue; - CheckNode(vecOrigin, m_di[j].m_SortedBy[2]); - } - } - -#if 0 - // Verify our answers. - // - int iNearestCheck = -1; - m_flShortest = 8192;// find nodes within this radius - - for ( i = 0 ; i < m_cNodes ; i++ ) - { - float flDist = ( vecOrigin - m_pNodes[ i ].m_vecOriginPeek ).Length(); - - if ( flDist < m_flShortest ) - { - // make sure that vecOrigin can trace to this node! - UTIL_TraceLine ( vecOrigin, m_pNodes[ i ].m_vecOriginPeek, ignore_monsters, 0, &tr ); - - if ( tr.flFraction == 1.0 ) - { - iNearestCheck = i; - m_flShortest = flDist; - } - } - } - - if (iNearestCheck != m_iNearest) - { - ALERT( at_aiconsole, "NOT closest %d(%f,%f,%f) %d(%f,%f,%f).\n", - iNearestCheck, - m_pNodes[iNearestCheck].m_vecOriginPeek.x, - m_pNodes[iNearestCheck].m_vecOriginPeek.y, - m_pNodes[iNearestCheck].m_vecOriginPeek.z, - m_iNearest, - (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.x), - (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.y), - (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.z)); - } - if (m_iNearest == -1) - { - ALERT(at_aiconsole, "All that work for nothing.\n"); - } -#endif - m_Cache[iHash].v = vecOrigin; - m_Cache[iHash].n = m_iNearest; - return m_iNearest; -} - -//========================================================= -// CGraph - ShowNodeConnections - draws a line from the given node -// to all connected nodes -//========================================================= -void CGraph :: ShowNodeConnections ( int iNode ) -{ - Vector vecSpot; - CNode *pNode; - CNode *pLinkNode; - int i; - - if ( !m_fGraphPresent || !m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available or built - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return; - } - - if ( iNode < 0 ) - { - ALERT( at_aiconsole, "Can't show connections for node %d\n", iNode ); - return; - } - - pNode = &m_pNodes[ iNode ]; - - UTIL_ParticleEffect( pNode->m_vecOrigin, g_vecZero, 255, 20 );// show node position - - if ( pNode->m_cNumLinks <= 0 ) - {// no connections! - ALERT ( at_aiconsole, "**No Connections!\n" ); - } - - for ( i = 0 ; i < pNode->m_cNumLinks ; i++ ) - { - - pLinkNode = &Node( NodeLink( iNode, i).m_iDestNode ); - vecSpot = pLinkNode->m_vecOrigin; - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - - WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.x ); - WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.y ); - WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.z + NODE_HEIGHT ); - - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z + NODE_HEIGHT ); - MESSAGE_END(); - - } -} - -//========================================================= -// CGraph - LinkVisibleNodes - the first, most basic -// function of node graph creation, this connects every -// node to every other node that it can see. Expects a -// pointer to an empty connection pool and a file pointer -// to write progress to. Returns the total number of initial -// links. -// -// If there's a problem with this process, the index -// of the offending node will be written to piBadNode -//========================================================= -int CGraph :: LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode ) -{ - int i,j,z; - edict_t *pTraceEnt; - int cTotalLinks, cLinksThisNode, cMaxInitialLinks; - TraceResult tr; - - // !!!BUGBUG - this function returns 0 if there is a problem in the middle of connecting the graph - // it also returns 0 if none of the nodes in a level can see each other. piBadNode is ALWAYS read - // by BuildNodeGraph() if this function returns a 0, so make sure that it doesn't get some random - // number back. - *piBadNode = 0; - - - if ( m_cNodes <= 0 ) - { - ALERT ( at_aiconsole, "No Nodes!\n" ); - return FALSE; - } - - // if the file pointer is bad, don't blow up, just don't write the - // file. - if ( !file ) - { - ALERT ( at_aiconsole, "**LinkVisibleNodes:\ncan't write to file." ); - } - else - { - fprintf ( file, "----------------------------------------------------------------------------\n" ); - fprintf ( file, "LinkVisibleNodes - Initial Connections\n" ); - fprintf ( file, "----------------------------------------------------------------------------\n" ); - } - - cTotalLinks = 0;// start with no connections - - // to keep track of the maximum number of initial links any node had so far. - // this lets us keep an eye on MAX_NODE_INITIAL_LINKS to ensure that we are - // being generous enough. - cMaxInitialLinks = 0; - - for ( i = 0 ; i < m_cNodes ; i++ ) - { - cLinksThisNode = 0;// reset this count for each node. - - if ( file ) - { - fprintf ( file, "Node #%4d:\n\n", i ); - } - - for ( z = 0 ; z < MAX_NODE_INITIAL_LINKS ; z++ ) - {// clear out the important fields in the link pool for this node - pLinkPool [ cTotalLinks + z ].m_iSrcNode = i;// so each link knows which node it originates from - pLinkPool [ cTotalLinks + z ].m_iDestNode = 0; - pLinkPool [ cTotalLinks + z ].m_pLinkEnt = NULL; - } - - m_pNodes [ i ].m_iFirstLink = cTotalLinks; - - // now build a list of every other node that this node can see - for ( j = 0 ; j < m_cNodes ; j++ ) - { - if ( j == i ) - {// don't connect to self! - continue; - } - -#if 0 - - if ( (m_pNodes[ i ].m_afNodeInfo & bits_NODE_WATER) != (m_pNodes[ j ].m_afNodeInfo & bits_NODE_WATER) ) - { - // don't connect water nodes to air nodes or land nodes. It just wouldn't be prudent at this juncture. - continue; - } -#else - if ( (m_pNodes[ i ].m_afNodeInfo & bits_NODE_GROUP_REALM) != (m_pNodes[ j ].m_afNodeInfo & bits_NODE_GROUP_REALM) ) - { - // don't connect air nodes to water nodes to land nodes. It just wouldn't be prudent at this juncture. - continue; - } -#endif - - tr.pHit = NULL;// clear every time so we don't get stuck with last trace's hit ent - pTraceEnt = 0; - - UTIL_TraceLine ( m_pNodes[ i ].m_vecOrigin, - m_pNodes[ j ].m_vecOrigin, - ignore_monsters, - NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about - &tr ); - - - if ( tr.fStartSolid ) - continue; - - if ( tr.flFraction != 1.0 ) - {// trace hit a brush ent, trace backwards to make sure that this ent is the only thing in the way. - - pTraceEnt = tr.pHit;// store the ent that the trace hit, for comparison - - UTIL_TraceLine ( m_pNodes[ j ].m_vecOrigin, - m_pNodes[ i ].m_vecOrigin, - ignore_monsters, - NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about - &tr ); - - -// there is a solid_bsp ent in the way of these two nodes, so we must record several things about in order to keep -// track of it in the pathfinding code, as well as through save and restore of the node graph. ANY data that is manipulated -// as part of the process of adding a LINKENT to a connection here must also be done in CGraph::SetGraphPointers, where reloaded -// graphs are prepared for use. - if ( tr.pHit == pTraceEnt && !FClassnameIs( tr.pHit, "worldspawn" ) ) - { - // get a pointer - pLinkPool [ cTotalLinks ].m_pLinkEnt = VARS( tr.pHit ); - - // record the modelname, so that we can save/load node trees - memcpy( pLinkPool [ cTotalLinks ].m_szLinkEntModelname, STRING( VARS(tr.pHit)->model ), 4 ); - - // set the flag for this ent that indicates that it is attached to the world graph - // if this ent is removed from the world, it must also be removed from the connections - // that it formerly blocked. - if ( !FBitSet( VARS( tr.pHit )->flags, FL_GRAPHED ) ) - { - VARS( tr.pHit )->flags += FL_GRAPHED; - } - } - else - {// even if the ent wasn't there, these nodes couldn't be connected. Skip. - continue; - } - } - - if ( file ) - { - fprintf ( file, "%4d", j ); - - if ( !FNullEnt( pLinkPool[ cTotalLinks ].m_pLinkEnt ) ) - {// record info about the ent in the way, if any. - fprintf ( file, " Entity on connection: %s, name: %s Model: %s", STRING( VARS( pTraceEnt )->classname ), STRING ( VARS( pTraceEnt )->targetname ), STRING ( VARS(tr.pHit)->model ) ); - } - - fprintf ( file, "\n", j ); - } - - pLinkPool [ cTotalLinks ].m_iDestNode = j; - cLinksThisNode++; - cTotalLinks++; - - // If we hit this, either a level designer is placing too many nodes in the same area, or - // we need to allow for a larger initial link pool. - if ( cLinksThisNode == MAX_NODE_INITIAL_LINKS ) - { - ALERT ( at_aiconsole, "**LinkVisibleNodes:\nNode %d has NodeLinks > MAX_NODE_INITIAL_LINKS", i ); - fprintf ( file, "** NODE %d HAS NodeLinks > MAX_NODE_INITIAL_LINKS **\n", i ); - *piBadNode = i; - return FALSE; - } - else if ( cTotalLinks > MAX_NODE_INITIAL_LINKS * m_cNodes ) - {// this is paranoia - ALERT ( at_aiconsole, "**LinkVisibleNodes:\nTotalLinks > MAX_NODE_INITIAL_LINKS * NUMNODES" ); - *piBadNode = i; - return FALSE; - } - - if ( cLinksThisNode == 0 ) - { - fprintf ( file, "**NO INITIAL LINKS**\n" ); - } - - // record the connection info in the link pool - WorldGraph.m_pNodes [ i ].m_cNumLinks = cLinksThisNode; - - // keep track of the most initial links ANY node had, so we can figure out - // if we have a large enough default link pool - if ( cLinksThisNode > cMaxInitialLinks ) - { - cMaxInitialLinks = cLinksThisNode; - } - } - - - if ( file ) - { - fprintf ( file, "----------------------------------------------------------------------------\n" ); - } - } - - fprintf ( file, "\n%4d Total Initial Connections - %4d Maximum connections for a single node.\n", cTotalLinks, cMaxInitialLinks ); - fprintf ( file, "----------------------------------------------------------------------------\n\n\n" ); - - return cTotalLinks; -} - -//========================================================= -// CGraph - RejectInlineLinks - expects a pointer to a link -// pool, and a pointer to and already-open file ( if you -// want status reports written to disk ). RETURNS the number -// of connections that were rejected -//========================================================= -int CGraph :: RejectInlineLinks ( CLink *pLinkPool, FILE *file ) -{ - int i,j,k; - - int cRejectedLinks; - - BOOL fRestartLoop;// have to restart the J loop if we eliminate a link. - - CNode *pSrcNode; - CNode *pCheckNode;// the node we are testing for (one of pSrcNode's connections) - CNode *pTestNode;// the node we are checking against ( also one of pSrcNode's connections) - - float flDistToTestNode, flDistToCheckNode; - - Vector2D vec2DirToTestNode, vec2DirToCheckNode; - - if ( file ) - { - fprintf ( file, "----------------------------------------------------------------------------\n" ); - fprintf ( file, "InLine Rejection:\n" ); - fprintf ( file, "----------------------------------------------------------------------------\n" ); - } - - cRejectedLinks = 0; - - for ( i = 0 ; i < m_cNodes ; i++ ) - { - pSrcNode = &m_pNodes[ i ]; - - if ( file ) - { - fprintf ( file, "Node %3d:\n", i ); - } - - for ( j = 0 ; j < pSrcNode->m_cNumLinks ; j++ ) - { - pCheckNode = &m_pNodes[ pLinkPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode ]; - - vec2DirToCheckNode = ( pCheckNode->m_vecOrigin - pSrcNode->m_vecOrigin ).Make2D(); - flDistToCheckNode = vec2DirToCheckNode.Length(); - vec2DirToCheckNode = vec2DirToCheckNode.Normalize(); - - pLinkPool[ pSrcNode->m_iFirstLink + j ].m_flWeight = flDistToCheckNode; - - fRestartLoop = FALSE; - for ( k = 0 ; k < pSrcNode->m_cNumLinks && !fRestartLoop ; k++ ) - { - if ( k == j ) - {// don't check against same node - continue; - } - - pTestNode = &m_pNodes [ pLinkPool[ pSrcNode->m_iFirstLink + k ].m_iDestNode ]; - - vec2DirToTestNode = ( pTestNode->m_vecOrigin - pSrcNode->m_vecOrigin ).Make2D(); - - flDistToTestNode = vec2DirToTestNode.Length(); - vec2DirToTestNode = vec2DirToTestNode.Normalize(); - - if ( DotProduct ( vec2DirToCheckNode, vec2DirToTestNode ) >= 0.998 ) - { - // there's a chance that TestNode intersects the line to CheckNode. If so, we should disconnect the link to CheckNode. - if ( flDistToTestNode < flDistToCheckNode ) - { - if ( file ) - { - fprintf ( file, "REJECTED NODE %3d through Node %3d, Dot = %8f\n", pLinkPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode, pLinkPool[ pSrcNode->m_iFirstLink + k ].m_iDestNode, DotProduct ( vec2DirToCheckNode, vec2DirToTestNode ) ); - } - - pLinkPool[ pSrcNode->m_iFirstLink + j ] = pLinkPool[ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; - pSrcNode->m_cNumLinks--; - j--; - - cRejectedLinks++;// keeping track of how many links are cut, so that we can return that value. - - fRestartLoop = TRUE; - } - } - } - } - - if ( file ) - { - fprintf ( file, "----------------------------------------------------------------------------\n\n" ); - } - } - - return cRejectedLinks; -} - -//========================================================= -// TestHull is a modelless clip hull that verifies reachable -// nodes by walking from every node to each of it's connections -//========================================================= -class CTestHull : public CMBaseMonster -{ - -public: - void Spawn( entvars_t *pevMasterNode ); - virtual int ObjectCaps( void ) { return CMBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - void EXPORT CallBuildNodeGraph ( void ); - void BuildNodeGraph ( void ); - void EXPORT ShowBadNode ( void ); - void EXPORT DropDelay ( void ); - void EXPORT PathFind ( void ); - - Vector vecBadNodeOrigin; -}; - -//========================================================= -// CTestHull::Spawn -//========================================================= -void CTestHull :: Spawn( entvars_t *pevMasterNode ) -{ - SET_MODEL(ENT(pev), "models/player.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - pev->effects = 0; - pev->health = 50; - pev->yaw_speed = 8; - - if ( WorldGraph.m_fGraphPresent ) - {// graph loaded from disk, so we don't need the test hull - SetThink ( &CTestHull::SUB_Remove ); - pev->nextthink = gpGlobals->time; - } - else - { - SetThink ( &CTestHull::DropDelay ); - pev->nextthink = gpGlobals->time + 1; - } - - // Make this invisible - // UNDONE: Shouldn't we just use EF_NODRAW? This doesn't need to go to the client. - pev->rendermode = kRenderTransTexture; - pev->renderamt = 0; - - pev->classname = MAKE_STRING("testhull"); -} - -//========================================================= -// TestHull::DropDelay - spawns TestHull on top of -// the 0th node and drops it to the ground. -//========================================================= -void CTestHull::DropDelay ( void ) -{ - //UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding..." ); - - UTIL_SetOrigin ( VARS(pev), WorldGraph.m_pNodes[ 0 ].m_vecOrigin ); - - SetThink ( &CTestHull::CallBuildNodeGraph ); - - pev->nextthink = gpGlobals->time + 2; // think called earlier, so add extra second. -Giegue -} - -//========================================================= -// nodes start out as ents in the world. As they are spawned, -// the node info is recorded then the ents are discarded. -//========================================================= -void CNodeEnt :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "hinttype")) - { - m_sHintType = (short)atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - - if (FStrEq(pkvd->szKeyName, "activity")) - { - m_sHintActivity = (short)atoi( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else - CMBaseEntity::KeyValue( pkvd ); -} - -//========================================================= -//========================================================= -void CNodeEnt :: Spawn( void ) -{ - pev->movetype = MOVETYPE_NONE; - pev->solid = SOLID_NOT;// always solid_not - - if ( WorldGraph.m_fGraphPresent ) - {// graph loaded from disk, so discard all these node ents as soon as they spawn - REMOVE_ENTITY( edict() ); - return; - } - - if ( WorldGraph.m_cNodes == 0 ) - { - // this is the first node to spawn, spawn the test hull entity that builds and walks the node tree - CTestHull *pHull = CreateClassPtr((CTestHull *)NULL); - pHull->Spawn( pev ); - } - - if ( WorldGraph.m_cNodes >= MAX_NODES ) - { - ALERT ( at_aiconsole, "cNodes > MAX_NODES\n" ); - return; - } - - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_vecOriginPeek = - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_vecOrigin = pev->origin; - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_flHintYaw = pev->angles.y; - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_sHintType = m_sHintType; - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_sHintActivity = m_sHintActivity; - - if (FClassnameIs( pev, "info_node_air" )) - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_afNodeInfo = bits_NODE_AIR; - else - WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_afNodeInfo = 0; - - WorldGraph.m_cNodes++; - - REMOVE_ENTITY( edict() ); -} - -//========================================================= -// CTestHull - ShowBadNode - makes a bad node fizzle. When -// there's a problem with node graph generation, the test -// hull will be placed up the bad node's location and will generate -// particles -//========================================================= -void CTestHull :: ShowBadNode( void ) -{ - pev->movetype = MOVETYPE_FLY; - pev->angles.y = pev->angles.y + 4; - - UTIL_MakeVectors ( pev->angles ); - - UTIL_ParticleEffect ( pev->origin, g_vecZero, 255, 25 ); - UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * 64, g_vecZero, 255, 25 ); - UTIL_ParticleEffect ( pev->origin - gpGlobals->v_forward * 64, g_vecZero, 255, 25 ); - UTIL_ParticleEffect ( pev->origin + gpGlobals->v_right * 64, g_vecZero, 255, 25 ); - UTIL_ParticleEffect ( pev->origin - gpGlobals->v_right * 64, g_vecZero, 255, 25 ); - - pev->nextthink = gpGlobals->time + 0.1; -} - -extern BOOL gTouchDisabled; -void CTestHull::CallBuildNodeGraph( void ) -{ - // TOUCH HACK -- Don't allow this entity to call anyone's "touch" function - gTouchDisabled = TRUE; - BuildNodeGraph(); - gTouchDisabled = FALSE; - // Undo TOUCH HACK -} - -//========================================================= -// BuildNodeGraph - think function called by the empty walk -// hull that is spawned by the first node to spawn. This -// function links all nodes that can see each other, then -// eliminates all inline links, then uses a monster-sized -// hull that walks between each node and each of its links -// to ensure that a monster can actually fit through the space -//========================================================= -void CTestHull :: BuildNodeGraph( void ) -{ - TraceResult tr; - FILE *file; - - char szNrpFilename [MAX_PATH];// text node report filename - - CLink *pTempPool; // temporary link pool - - CNode *pSrcNode;// node we're currently working with - CNode *pDestNode;// the other node in comparison operations - - BOOL fSkipRemainingHulls;//if smallest hull can't fit, don't check any others - BOOL fPairsValid;// are all links in the graph evenly paired? - - int i, j, hull; - - int iBadNode;// this is the node that caused graph generation to fail - - int cMaxInitialLinks = 0; - int cMaxValidLinks = 0; - - int iPoolIndex = 0; - int cPoolLinks;// number of links in the pool. - - Vector vecDirToCheckNode; - Vector vecDirToTestNode; - Vector vecStepCheckDir; - Vector vecTraceSpot; - Vector vecSpot; - - Vector2D vec2DirToCheckNode; - Vector2D vec2DirToTestNode; - Vector2D vec2StepCheckDir; - Vector2D vec2TraceSpot; - Vector2D vec2Spot; - - float flYaw;// use this stuff to walk the hull between nodes - float flDist; - int step; - - SetThink ( &CTestHull::SUB_Remove );// no matter what happens, the hull gets rid of itself. - pev->nextthink = gpGlobals->time; - -// malloc a swollen temporary connection pool that we trim down after we know exactly how many connections there are. - pTempPool = (CLink *)calloc ( sizeof ( CLink ) , ( WorldGraph.m_cNodes * MAX_NODE_INITIAL_LINKS ) ); - if ( !pTempPool ) - { - ALERT ( at_aiconsole, "**Could not malloc TempPool!\n" ); - return; - } - - - // make sure directories have been made - GET_GAME_DIR( szNrpFilename ); - strcat( szNrpFilename, "/maps" ); - CreateDirectory( szNrpFilename, NULL ); - strcat( szNrpFilename, "/graphs" ); - CreateDirectory( szNrpFilename, NULL ); - - strcat( szNrpFilename, "/" ); - strcat( szNrpFilename, STRING( gpGlobals->mapname ) ); - strcat( szNrpFilename, ".nrp" ); - - file = fopen ( szNrpFilename, "w+" ); - - if ( !file ) - {// file error - ALERT ( at_aiconsole, "Couldn't create %s!\n", szNrpFilename ); - - if ( pTempPool ) - { - free ( pTempPool ); - } - - return; - } - - fprintf( file, "Node Graph Report for map: %s.bsp\n", STRING(gpGlobals->mapname) ); - fprintf ( file, "%d Total Nodes\n\n", WorldGraph.m_cNodes ); - - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - {// print all node numbers and their locations to the file. - WorldGraph.m_pNodes[ i ].m_cNumLinks = 0; - WorldGraph.m_pNodes[ i ].m_iFirstLink = 0; - memset(WorldGraph.m_pNodes[ i ].m_pNextBestNode, 0, sizeof(WorldGraph.m_pNodes[ i ].m_pNextBestNode)); - - fprintf ( file, "Node# %4d\n", i ); - fprintf ( file, "Location %4d,%4d,%4d\n",(int)WorldGraph.m_pNodes[ i ].m_vecOrigin.x, (int)WorldGraph.m_pNodes[ i ].m_vecOrigin.y, (int)WorldGraph.m_pNodes[ i ].m_vecOrigin.z ); - fprintf ( file, "HintType: %4d\n", WorldGraph.m_pNodes[ i ].m_sHintType ); - fprintf ( file, "HintActivity: %4d\n", WorldGraph.m_pNodes[ i ].m_sHintActivity ); - fprintf ( file, "HintYaw: %4f\n", WorldGraph.m_pNodes[ i ].m_flHintYaw ); - fprintf ( file, "-------------------------------------------------------------------------------\n" ); - } - fprintf ( file, "\n\n" ); - - - // Automatically recognize WATER nodes and drop the LAND nodes to the floor. - // - for ( i = 0; i < WorldGraph.m_cNodes; i++) - { - if (WorldGraph.m_pNodes[ i ].m_afNodeInfo & bits_NODE_AIR) - { - // do nothing - } - else if (UTIL_PointContents(WorldGraph.m_pNodes[ i ].m_vecOrigin) == CONTENTS_WATER) - { - WorldGraph.m_pNodes[ i ].m_afNodeInfo |= bits_NODE_WATER; - } - else - { - WorldGraph.m_pNodes[ i ].m_afNodeInfo |= bits_NODE_LAND; - - // trace to the ground, then pop up 8 units and place node there to make it - // easier for them to connect (think stairs, chairs, and bumps in the floor). - // After the routing is done, push them back down. - // - TraceResult tr; - - UTIL_TraceLine ( WorldGraph.m_pNodes[i].m_vecOrigin, - WorldGraph.m_pNodes[i].m_vecOrigin - Vector ( 0, 0, 384 ), - ignore_monsters, - NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about - &tr ); - - // This trace is ONLY used if we hit an entity flagged with FL_WORLDBRUSH - TraceResult trEnt; - UTIL_TraceLine ( WorldGraph.m_pNodes[i].m_vecOrigin, - WorldGraph.m_pNodes[i].m_vecOrigin - Vector ( 0, 0, 384 ), - dont_ignore_monsters, - NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about - &trEnt ); - - - // Did we hit something closer than the floor? - if ( trEnt.flFraction < tr.flFraction ) - { - // If it was a world brush entity, copy the node location - if ( trEnt.pHit && (trEnt.pHit->v.flags & FL_WORLDBRUSH) ) - tr.vecEndPos = trEnt.vecEndPos; - } - - WorldGraph.m_pNodes[i].m_vecOriginPeek.z = - WorldGraph.m_pNodes[i].m_vecOrigin.z = tr.vecEndPos.z + NODE_HEIGHT; - } - } - - cPoolLinks = WorldGraph.LinkVisibleNodes( pTempPool, file, &iBadNode ); - - if ( !cPoolLinks ) - { - ALERT ( at_aiconsole, "**ConnectVisibleNodes FAILED!\n" ); - - SetThink ( &CTestHull::ShowBadNode );// send the hull off to show the offending node. - //pev->solid = SOLID_NOT; - pev->origin = WorldGraph.m_pNodes[ iBadNode ].m_vecOrigin; - - if ( pTempPool ) - { - free ( pTempPool ); - } - - if ( file ) - {// close the file - fclose ( file ); - } - - return; - } - -// send the walkhull to all of this node's connections now. We'll do this here since -// so much of it relies on being able to control the test hull. - fprintf ( file, "----------------------------------------------------------------------------\n" ); - fprintf ( file, "Walk Rejection:\n"); - - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - pSrcNode = &WorldGraph.m_pNodes[ i ]; - - fprintf ( file, "-------------------------------------------------------------------------------\n"); - fprintf ( file, "Node %4d:\n\n", i ); - - for ( j = 0 ; j < pSrcNode->m_cNumLinks ; j++ ) - { - // assume that all hulls can walk this link, then eliminate the ones that can't. - pTempPool [ pSrcNode->m_iFirstLink + j ].m_afLinkInfo = bits_LINK_SMALL_HULL | bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL | bits_LINK_FLY_HULL; - - - // do a check for each hull size. - - // if we can't fit a tiny hull through a connection, no other hulls with fit either, so we - // should just fall out of the loop. Do so by setting the SkipRemainingHulls flag. - fSkipRemainingHulls = FALSE; - for ( hull = 0 ; hull < MAX_NODE_HULLS; hull++ ) - { - if (fSkipRemainingHulls && (hull == NODE_HUMAN_HULL || hull == NODE_LARGE_HULL)) // skip the remaining walk hulls - continue; - - switch ( hull ) - { - case NODE_SMALL_HULL: - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); - break; - case NODE_HUMAN_HULL: - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); - break; - case NODE_LARGE_HULL: - UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); - break; - case NODE_FLY_HULL: - UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); - // UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - break; - } - - UTIL_SetOrigin ( pev, pSrcNode->m_vecOrigin );// place the hull on the node - - if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) - { - ALERT ( at_aiconsole, "OFFGROUND!\n" ); - } - - // now build a yaw that points to the dest node, and get the distance. - if ( j < 0 ) - { - ALERT ( at_aiconsole, "**** j = %d ****\n", j ); - if ( pTempPool ) - { - free ( pTempPool ); - } - - if ( file ) - {// close the file - fclose ( file ); - } - return; - } - - pDestNode = &WorldGraph.m_pNodes [ pTempPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode ]; - - vecSpot = pDestNode->m_vecOrigin; - //vecSpot.z = pev->origin.z; - - if (hull < NODE_FLY_HULL) - { - int SaveFlags = pev->flags; - int MoveMode = WALKMOVE_WORLDONLY; - if (pSrcNode->m_afNodeInfo & bits_NODE_WATER) - { - pev->flags |= FL_SWIM; - MoveMode = WALKMOVE_NORMAL; - } - - flYaw = UTIL_VecToYaw ( pDestNode->m_vecOrigin - pev->origin ); - - flDist = ( vecSpot - pev->origin ).Length2D(); - - int fWalkFailed = FALSE; - - // in this loop we take tiny steps from the current node to the nodes that it links to, one at a time. - // pev->angles.y = flYaw; - for ( step = 0 ; step < flDist && !fWalkFailed ; step += HULL_STEP_SIZE ) - { - float stepSize = HULL_STEP_SIZE; - - if ( (step + stepSize) >= (flDist-1) ) - stepSize = (flDist - step) - 1; - - if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, MoveMode ) ) - {// can't take the next step - - fWalkFailed = TRUE; - break; - } - } - - if (!fWalkFailed && (pev->origin - vecSpot).Length() > 64) - { - // ALERT( at_console, "bogus walk\n"); - // we thought we - fWalkFailed = TRUE; - } - - if (fWalkFailed) - { - - //pTempPool[ pSrcNode->m_iFirstLink + j ] = pTempPool [ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; - - // now me must eliminate the hull that couldn't walk this connection - switch ( hull ) - { - case NODE_SMALL_HULL: // if this hull can't fit, nothing can, so drop the connection - fprintf ( file, "NODE_SMALL_HULL step %f\n", step ); - pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_SMALL_HULL | bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL); - fSkipRemainingHulls = TRUE;// don't bother checking larger hulls - break; - case NODE_HUMAN_HULL: - fprintf ( file, "NODE_HUMAN_HULL step %f\n", step ); - pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL); - fSkipRemainingHulls = TRUE;// don't bother checking larger hulls - break; - case NODE_LARGE_HULL: - fprintf ( file, "NODE_LARGE_HULL step %f\n", step ); - pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_LARGE_HULL; - break; - } - } - pev->flags = SaveFlags; - } - else - { - TraceResult tr; - - UTIL_TraceHull( pSrcNode->m_vecOrigin + Vector( 0, 0, 32 ), pDestNode->m_vecOriginPeek + Vector( 0, 0, 32 ), ignore_monsters, large_hull, ENT( pev ), &tr ); - if (tr.fStartSolid || tr.flFraction < 1.0) - { - pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_FLY_HULL; - } - } - } - - if (pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo == 0) - { - fprintf ( file, "Rejected Node %3d - Unreachable by ", pTempPool [ pSrcNode->m_iFirstLink + j ].m_iDestNode ); - pTempPool[ pSrcNode->m_iFirstLink + j ] = pTempPool [ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; - fprintf ( file, "Any Hull\n" ); - - pSrcNode->m_cNumLinks--; - cPoolLinks--;// we just removed a link, so decrement the total number of links in the pool. - j--; - } - - } - } - fprintf ( file, "-------------------------------------------------------------------------------\n\n\n"); - - cPoolLinks -= WorldGraph.RejectInlineLinks ( pTempPool, file ); - -// now malloc a pool just large enough to hold the links that are actually used - WorldGraph.m_pLinkPool = (CLink *) calloc ( sizeof ( CLink ), cPoolLinks ); - - if ( !WorldGraph.m_pLinkPool ) - {// couldn't make the link pool! - ALERT ( at_aiconsole, "Couldn't malloc LinkPool!\n" ); - if ( pTempPool ) - { - free ( pTempPool ); - } - if ( file ) - {// close the file - fclose ( file ); - } - - return; - } - WorldGraph.m_cLinks = cPoolLinks; - -//copy only the used portions of the TempPool into the graph's link pool - int iFinalPoolIndex = 0; - int iOldFirstLink; - - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - iOldFirstLink = WorldGraph.m_pNodes[ i ].m_iFirstLink;// store this, because we have to re-assign it before entering the copy loop - - WorldGraph.m_pNodes[ i ].m_iFirstLink = iFinalPoolIndex; - - for ( j = 0 ; j < WorldGraph.m_pNodes[ i ].m_cNumLinks ; j++ ) - { - WorldGraph.m_pLinkPool[ iFinalPoolIndex++ ] = pTempPool[ iOldFirstLink + j ]; - } - } - - - // Node sorting numbers linked nodes close to each other - // - WorldGraph.SortNodes(); - - // This is used for HashSearch - // - WorldGraph.BuildLinkLookups(); - - fPairsValid = TRUE; // assume that the connection pairs are all valid to start - - fprintf ( file, "\n\n-------------------------------------------------------------------------------\n"); - fprintf ( file, "Link Pairings:\n"); - -// link integrity check. The idea here is that if Node A links to Node B, node B should -// link to node A. If not, we have a situation that prevents us from using a basic -// optimization in the FindNearestLink function. - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - for ( j = 0 ; j < WorldGraph.m_pNodes[ i ].m_cNumLinks ; j++ ) - { - int iLink; - WorldGraph.HashSearch(WorldGraph.INodeLink(i,j), i, iLink); - if (iLink < 0) - { - fPairsValid = FALSE;// unmatched link pair. - fprintf ( file, "WARNING: Node %3d does not connect back to Node %3d\n", WorldGraph.INodeLink(i, j), i); - } - } - } - - // !!!LATER - if all connections are properly paired, when can enable an optimization in the pathfinding code - // (in the find nearest line function) - if ( fPairsValid ) - { - fprintf ( file, "\nAll Connections are Paired!\n"); - } - - fprintf ( file, "-------------------------------------------------------------------------------\n"); - fprintf ( file, "\n\n-------------------------------------------------------------------------------\n"); - fprintf ( file, "Total Number of Connections in Pool: %d\n", cPoolLinks ); - fprintf ( file, "-------------------------------------------------------------------------------\n"); - fprintf ( file, "Connection Pool: %d bytes\n", sizeof ( CLink ) * cPoolLinks ); - fprintf ( file, "-------------------------------------------------------------------------------\n"); - - - ALERT ( at_aiconsole, "%d Nodes, %d Connections\n", WorldGraph.m_cNodes, cPoolLinks ); - - // This is used for FindNearestNode - // - WorldGraph.BuildRegionTables(); - - - // Push all of the LAND nodes down to the ground now. Leave the water and air nodes alone. - // - for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) - { - if ((WorldGraph.m_pNodes[ i ].m_afNodeInfo & bits_NODE_LAND)) - { - WorldGraph.m_pNodes[ i ].m_vecOrigin.z -= NODE_HEIGHT; - } - } - - - if ( pTempPool ) - {// free the temp pool - free ( pTempPool ); - } - - if ( file ) - { - fclose ( file ); - } - - // We now have some graphing capabilities. - // - WorldGraph.m_fGraphPresent = TRUE;//graph is in memory. - WorldGraph.m_fGraphPointersSet = TRUE;// since the graph was generated, the pointers are ready - WorldGraph.m_fRoutingComplete = FALSE; // Optimal routes aren't computed, yet. - - // Compute and compress the routing information. - // - WorldGraph.ComputeStaticRoutingTables(); - -// save the node graph for this level - WorldGraph.FSaveGraph( (char *)STRING( gpGlobals->mapname ) ); - ALERT( at_console, "Done.\n"); -} - - -//========================================================= -// returns a hardcoded path. -//========================================================= -void CTestHull :: PathFind ( void ) -{ - int iPath[ 50 ]; - int iPathSize; - int i; - CNode *pNode, *pNextNode; - - if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return; - } - - iPathSize = WorldGraph.FindShortestPath ( iPath, 0, 19, 0, 0 ); // UNDONE use hull constant - - if ( !iPathSize ) - { - ALERT ( at_aiconsole, "No Path!\n" ); - return; - } - - ALERT ( at_aiconsole, "%d\n", iPathSize ); - - pNode = &WorldGraph.m_pNodes[ iPath [ 0 ] ]; - - for ( i = 0 ; i < iPathSize - 1 ; i++ ) - { - - pNextNode = &WorldGraph.m_pNodes[ iPath [ i + 1 ] ]; - - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SHOWLINE); - - WRITE_COORD( pNode->m_vecOrigin.x ); - WRITE_COORD( pNode->m_vecOrigin.y ); - WRITE_COORD( pNode->m_vecOrigin.z + NODE_HEIGHT ); - - WRITE_COORD( pNextNode->m_vecOrigin.x); - WRITE_COORD( pNextNode->m_vecOrigin.y); - WRITE_COORD( pNextNode->m_vecOrigin.z + NODE_HEIGHT); - MESSAGE_END(); - - pNode = pNextNode; - } - -} - - -//========================================================= -// CStack Constructor -//========================================================= -CStack :: CStack( void ) -{ - m_level = 0; -} - -//========================================================= -// pushes a value onto the stack -//========================================================= -void CStack :: Push( int value ) -{ - if ( m_level >= MAX_STACK_NODES ) - { - printf("Error!\n"); - return; - } - m_stack[m_level] = value; - m_level++; -} - -//========================================================= -// pops a value off of the stack -//========================================================= -int CStack :: Pop( void ) -{ - if ( m_level <= 0 ) - return -1; - - m_level--; - return m_stack[ m_level ]; -} - -//========================================================= -// returns the value on the top of the stack -//========================================================= -int CStack :: Top ( void ) -{ - return m_stack[ m_level - 1 ]; -} - -//========================================================= -// copies every element on the stack into an array LIFO -//========================================================= -void CStack :: CopyToArray ( int *piArray ) -{ - int i; - - for ( i = 0 ; i < m_level ; i++ ) - { - piArray[ i ] = m_stack[ i ]; - } -} - -//========================================================= -// CQueue constructor -//========================================================= -CQueue :: CQueue( void ) -{ - m_cSize = 0; - m_head = 0; - m_tail = -1; -} - -//========================================================= -// inserts a value into the queue -//========================================================= -void CQueue :: Insert ( int iValue, float fPriority ) -{ - - if ( Full() ) - { - printf ( "Queue is full!\n" ); - return; - } - - m_tail++; - - if ( m_tail == MAX_STACK_NODES ) - {//wrap around - m_tail = 0; - } - - m_queue[ m_tail ].Id = iValue; - m_queue[ m_tail ].Priority = fPriority; - m_cSize++; -} - -//========================================================= -// removes a value from the queue (FIFO) -//========================================================= -int CQueue :: Remove ( float &fPriority ) -{ - if ( m_head == MAX_STACK_NODES ) - {// wrap - m_head = 0; - } - - m_cSize--; - fPriority = m_queue[ m_head ].Priority; - return m_queue[ m_head++ ].Id; -} - -//========================================================= -// CQueue constructor -//========================================================= -CQueuePriority :: CQueuePriority( void ) -{ - m_cSize = 0; -} - -//========================================================= -// inserts a value into the priority queue -//========================================================= -void CQueuePriority :: Insert( int iValue, float fPriority ) -{ - - if ( Full() ) - { - printf ( "Queue is full!\n" ); - return; - } - - m_heap[ m_cSize ].Priority = fPriority; - m_heap[ m_cSize ].Id = iValue; - m_cSize++; - Heap_SiftUp(); -} - -//========================================================= -// removes the smallest item from the priority queue -// -//========================================================= -int CQueuePriority :: Remove( float &fPriority ) -{ - int iReturn = m_heap[ 0 ].Id; - fPriority = m_heap[ 0 ].Priority; - - m_cSize--; - - m_heap[ 0 ] = m_heap[ m_cSize ]; - - Heap_SiftDown(0); - return iReturn; -} - -#define HEAP_LEFT_CHILD(x) (2*(x)+1) -#define HEAP_RIGHT_CHILD(x) (2*(x)+2) -#define HEAP_PARENT(x) (((x)-1)/2) - -void CQueuePriority::Heap_SiftDown(int iSubRoot) -{ - int parent = iSubRoot; - int child = HEAP_LEFT_CHILD(parent); - - struct tag_HEAP_NODE Ref = m_heap[ parent ]; - - while (child < m_cSize) - { - int rightchild = HEAP_RIGHT_CHILD(parent); - if (rightchild < m_cSize) - { - if ( m_heap[ rightchild ].Priority < m_heap[ child ].Priority ) - { - child = rightchild; - } - } - if ( Ref.Priority <= m_heap[ child ].Priority ) - break; - - m_heap[ parent ] = m_heap[ child ]; - parent = child; - child = HEAP_LEFT_CHILD(parent); - } - m_heap[ parent ] = Ref; -} - -void CQueuePriority::Heap_SiftUp(void) -{ - int child = m_cSize-1; - while (child) - { - int parent = HEAP_PARENT(child); - if ( m_heap[ parent ].Priority <= m_heap[ child ].Priority ) - break; - - struct tag_HEAP_NODE Tmp; - Tmp = m_heap[ child ]; - m_heap[ child ] = m_heap[ parent ]; - m_heap[ parent ] = Tmp; - - child = parent; - } -} - -//========================================================= -// CGraph - FLoadGraph - attempts to load a node graph from disk. -// if the current level is maps/snar.bsp, maps/graphs/snar.nod -// will be loaded. If file cannot be loaded, the node tree -// will be created and saved to disk. -//========================================================= -int CGraph :: FLoadGraph ( char *szMapName ) -{ - char szFilename[MAX_PATH]; - int iVersion; - int length; - byte *aMemFile; - byte *pMemFile; - - // make sure the directories have been made - char szDirName[MAX_PATH]; - GET_GAME_DIR( szDirName ); - strcat( szDirName, "/maps" ); - CreateDirectory( szDirName, NULL ); - strcat( szDirName, "/graphs" ); - CreateDirectory( szDirName, NULL ); - - strcpy ( szFilename, "maps/graphs/" ); - strcat ( szFilename, szMapName ); - strcat( szFilename, ".nod" ); - - pMemFile = aMemFile = LOAD_FILE_FOR_ME(szFilename, &length); - - if ( !aMemFile ) - { - return FALSE; - } - else - { - // Read the graph version number - // - length -= sizeof(int); - if (length < 0) goto ShortFile; - memcpy(&iVersion, pMemFile, sizeof(int)); - pMemFile += sizeof(int); - - if ( iVersion != GRAPH_VERSION ) - { - // This file was written by a different build of the dll! - // - ALERT ( at_aiconsole, "**ERROR** Graph version is %d, expected %d\n",iVersion, GRAPH_VERSION ); - goto ShortFile; - } - - // Read the graph class - // - length -= sizeof(CGraph); - if (length < 0) goto ShortFile; - memcpy(this, pMemFile, sizeof(CGraph)); - pMemFile += sizeof(CGraph); - - // Set the pointers to zero, just in case we run out of memory. - // - m_pNodes = NULL; - m_pLinkPool = NULL; - m_di = NULL; - m_pRouteInfo = NULL; - m_pHashLinks = NULL; - - - // Malloc for the nodes - // - m_pNodes = ( CNode * )calloc ( sizeof ( CNode ), m_cNodes ); - - if ( !m_pNodes ) - { - ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d nodes!\n", m_cNodes ); - goto NoMemory; - } - - // Read in all the nodes - // - length -= sizeof(CNode) * m_cNodes; - if (length < 0) goto ShortFile; - memcpy(m_pNodes, pMemFile, sizeof(CNode)*m_cNodes); - pMemFile += sizeof(CNode) * m_cNodes; - - - // Malloc for the link pool - // - m_pLinkPool = ( CLink * )calloc ( sizeof ( CLink ), m_cLinks ); - - if ( !m_pLinkPool ) - { - ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d link!\n", m_cLinks ); - goto NoMemory; - } - - // Read in all the links - // - length -= sizeof(CLink)*m_cLinks; - if (length < 0) goto ShortFile; - memcpy(m_pLinkPool, pMemFile, sizeof(CLink)*m_cLinks); - pMemFile += sizeof(CLink)*m_cLinks; - - // Malloc for the sorting info. - // - m_di = (DIST_INFO *)calloc( sizeof(DIST_INFO), m_cNodes ); - if ( !m_di ) - { - ALERT ( at_aiconsole, "***ERROR**\nCouldn't malloc %d entries sorting nodes!\n", m_cNodes ); - goto NoMemory; - } - - // Read it in. - // - length -= sizeof(DIST_INFO)*m_cNodes; - if (length < 0) goto ShortFile; - memcpy(m_di, pMemFile, sizeof(DIST_INFO)*m_cNodes); - pMemFile += sizeof(DIST_INFO)*m_cNodes; - - // Malloc for the routing info. - // - m_fRoutingComplete = FALSE; - m_pRouteInfo = (char *)calloc( sizeof(char), m_nRouteInfo ); - if ( !m_pRouteInfo ) - { - ALERT ( at_aiconsole, "***ERROR**\nCounldn't malloc %d route bytes!\n", m_nRouteInfo ); - goto NoMemory; - } - m_CheckedCounter = 0; - for (int i = 0; i < m_cNodes; i++) - { - m_di[i].m_CheckedEvent = 0; - } - - // Read in the route information. - // - length -= sizeof(char)*m_nRouteInfo; - if (length < 0) goto ShortFile; - memcpy(m_pRouteInfo, pMemFile, sizeof(char)*m_nRouteInfo); - pMemFile += sizeof(char)*m_nRouteInfo; - m_fRoutingComplete = TRUE;; - - // malloc for the hash links - // - m_pHashLinks = (short *)calloc(sizeof(short), m_nHashLinks); - if (!m_pHashLinks) - { - ALERT ( at_aiconsole, "***ERROR**\nCounldn't malloc %d hash link bytes!\n", m_nHashLinks ); - goto NoMemory; - } - - // Read in the hash link information - // - length -= sizeof(short)*m_nHashLinks; - if (length < 0) goto ShortFile; - memcpy(m_pHashLinks, pMemFile, sizeof(short)*m_nHashLinks); - pMemFile += sizeof(short)*m_nHashLinks; - - // Set the graph present flag, clear the pointers set flag - // - m_fGraphPresent = TRUE; - m_fGraphPointersSet = TRUE; // what if...? - - FREE_FILE(aMemFile); - - if (length != 0) - { - ALERT ( at_aiconsole, "***WARNING***:Node graph was longer than expected by %d bytes.!\n", length); - } - - return TRUE; - } - -ShortFile: -NoMemory: - FREE_FILE(aMemFile); - return FALSE; -} - -//========================================================= -// CGraph - FSaveGraph - It's not rocket science. -// this WILL overwrite existing files. -//========================================================= -int CGraph :: FSaveGraph ( char *szMapName ) -{ - - int iVersion = GRAPH_VERSION; - char szFilename[MAX_PATH]; - FILE *file; - - if ( !m_fGraphPresent || !m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available or built - ALERT ( at_aiconsole, "Graph not ready!\n" ); - return FALSE; - } - - // make sure directories have been made - GET_GAME_DIR( szFilename ); - strcat( szFilename, "/maps" ); - CreateDirectory( szFilename, NULL ); - strcat( szFilename, "/graphs" ); - CreateDirectory( szFilename, NULL ); - - strcat( szFilename, "/" ); - strcat( szFilename, szMapName ); - strcat( szFilename, ".nod" ); - - file = fopen ( szFilename, "wb" ); - - ALERT ( at_aiconsole, "Created: %s\n", szFilename ); - - if ( !file ) - {// couldn't create - ALERT ( at_aiconsole, "Couldn't Create: %s\n", szFilename ); - return FALSE; - } - else - { - // write the version - fwrite ( &iVersion, sizeof ( int ), 1, file ); - - // write the CGraph class - fwrite ( this, sizeof ( CGraph ), 1, file ); - - // write the nodes - fwrite ( m_pNodes, sizeof ( CNode ), m_cNodes, file ); - - // write the links - fwrite ( m_pLinkPool, sizeof ( CLink ), m_cLinks, file ); - - fwrite ( m_di, sizeof(DIST_INFO), m_cNodes, file ); - - // Write the route info. - // - if ( m_pRouteInfo && m_nRouteInfo ) - { - fwrite ( m_pRouteInfo, sizeof( char ), m_nRouteInfo, file ); - } - - if (m_pHashLinks && m_nHashLinks) - { - fwrite(m_pHashLinks, sizeof(short), m_nHashLinks, file); - } - fclose ( file ); - return TRUE; - } -} - -//========================================================= -// CGraph - FSetGraphPointers - Takes the modelnames of -// all of the brush ents that block connections in the node -// graph and resolves them into pointers to those entities. -// this is done after loading the graph from disk, whereupon -// the pointers are not valid. -//========================================================= -int CGraph :: FSetGraphPointers ( void ) -{ - int i; - edict_t *pentLinkEnt; - - for ( i = 0 ; i < m_cLinks ; i++ ) - {// go through all of the links - - if ( m_pLinkPool[ i ].m_pLinkEnt != NULL ) - { - char name[5]; - // when graphs are saved, any valid pointers are will be non-zero, signifying that we should - // reset those pointers upon reloading. Any pointers that were NULL when the graph was saved - // will be NULL when reloaded, and will ignored by this function. - - // m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes) - memcpy( name, m_pLinkPool[ i ].m_szLinkEntModelname, 4 ); - name[4] = 0; - pentLinkEnt = FIND_ENTITY_BY_STRING( NULL, "model", name ); - - if ( FNullEnt ( pentLinkEnt ) ) - { - // the ent isn't around anymore? Either there is a major problem, or it was removed from the world - // ( like a func_breakable that's been destroyed or something ). Make sure that LinkEnt is null. - ALERT ( at_aiconsole, "**Could not find model %s\n", name ); - m_pLinkPool[ i ].m_pLinkEnt = NULL; - } - else - { - m_pLinkPool[ i ].m_pLinkEnt = VARS( pentLinkEnt ); - - if ( !FBitSet( m_pLinkPool[ i ].m_pLinkEnt->flags, FL_GRAPHED ) ) - { - m_pLinkPool[ i ].m_pLinkEnt->flags += FL_GRAPHED; - } - } - } - } - - // the pointers are now set. - m_fGraphPointersSet = TRUE; - return TRUE; -} - -//========================================================= -// CGraph - CheckNODFile - this function checks the date of -// the BSP file that was just loaded and the date of the a -// ssociated .NOD file. If the NOD file is not present, or -// is older than the BSP file, we rebuild it. -// -// returns FALSE if the .NOD file doesn't qualify and needs -// to be rebuilt. -// -// !!!BUGBUG - the file times we get back are 20 hours ahead! -// since this happens consistantly, we can still correctly -// determine which of the 2 files is newer. This needs fixed, -// though. ( I now suspect that we are getting GMT back from -// these functions and must compensate for local time ) (sjb) -//========================================================= -int CGraph :: CheckNODFile ( char *szMapName ) -{ - int retValue; - - char szBspFilename[MAX_PATH]; - char szGraphFilename[MAX_PATH]; - - - strcpy ( szBspFilename, "maps/" ); - strcat ( szBspFilename, szMapName ); - strcat ( szBspFilename, ".bsp" ); - - strcpy ( szGraphFilename, "maps/graphs/" ); - strcat ( szGraphFilename, szMapName ); - strcat ( szGraphFilename, ".nod" ); - - retValue = TRUE; - - int iCompare; - if (COMPARE_FILE_TIME(szBspFilename, szGraphFilename, &iCompare)) - { - if ( iCompare > 0 ) - {// BSP file is newer. - ALERT ( at_aiconsole, ".NOD File will be updated\n\n" ); - retValue = FALSE; - } - } - else - { - retValue = FALSE; - } - - return retValue; -} - -#define ENTRY_STATE_EMPTY -1 - -struct tagNodePair -{ - short iSrc; - short iDest; -}; - -void CGraph::HashInsert(int iSrcNode, int iDestNode, int iKey) -{ - struct tagNodePair np; - - np.iSrc = iSrcNode; - np.iDest = iDestNode; - CRC32_t dwHash; - CRC32_INIT(&dwHash); - CRC32_PROCESS_BUFFER(&dwHash, &np, sizeof(np)); - dwHash = CRC32_FINAL(dwHash); - - int di = m_HashPrimes[dwHash&15]; - int i = (dwHash >> 4) % m_nHashLinks; - while (m_pHashLinks[i] != ENTRY_STATE_EMPTY) - { - i += di; - if (i >= m_nHashLinks) i -= m_nHashLinks; - } - m_pHashLinks[i] = iKey; -} - -void CGraph::HashSearch(int iSrcNode, int iDestNode, int &iKey) -{ - struct tagNodePair np; - - np.iSrc = iSrcNode; - np.iDest = iDestNode; - CRC32_t dwHash; - CRC32_INIT(&dwHash); - CRC32_PROCESS_BUFFER(&dwHash, &np, sizeof(np)); - dwHash = CRC32_FINAL(dwHash); - - int di = m_HashPrimes[dwHash&15]; - int i = (dwHash >> 4) % m_nHashLinks; - while (m_pHashLinks[i] != ENTRY_STATE_EMPTY) - { - CLink &link = Link(m_pHashLinks[i]); - if (iSrcNode == link.m_iSrcNode && iDestNode == link.m_iDestNode) - { - break; - } - else - { - i += di; - if (i >= m_nHashLinks) i -= m_nHashLinks; - } - } - iKey = m_pHashLinks[i]; -} - -#define NUMBER_OF_PRIMES 177 - -int Primes[NUMBER_OF_PRIMES] = -{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, -71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, -157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, -241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, -347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, -439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, -547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, -643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, -751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, -859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, -977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 0 }; - -void CGraph::HashChoosePrimes(int TableSize) -{ - int LargestPrime = TableSize/2; - if (LargestPrime > Primes[NUMBER_OF_PRIMES-2]) - { - LargestPrime = Primes[NUMBER_OF_PRIMES-2]; - } - int Spacing = LargestPrime/16; - - // Pick a set primes that are evenly spaced from (0 to LargestPrime) - // We divide this interval into 16 equal sized zones. We want to find - // one prime number that best represents that zone. - // - int iPrime; - int iZone; - for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing) - { - // Search for a prime number that is less than the target zone - // number given by iZone. - // - int Lower = Primes[0]; - for (int jPrime = 0; Primes[jPrime] != 0; jPrime++) - { - if (jPrime != 0 && TableSize % Primes[jPrime] == 0) continue; - int Upper = Primes[jPrime]; - if (Lower <= iZone && iZone <= Upper) - { - // Choose the closest lower prime number. - // - if (iZone - Lower <= Upper - iZone) - { - m_HashPrimes[iPrime++] = Lower; - } - else - { - m_HashPrimes[iPrime++] = Upper; - } - break; - } - Lower = Upper; - } - } - - // Alternate negative and positive numbers - // - for (iPrime = 0; iPrime < 16; iPrime += 2) - { - m_HashPrimes[iPrime] = TableSize-m_HashPrimes[iPrime]; - } - - // Shuffle the set of primes to reduce correlation with bits in - // hash key. - // - for (iPrime = 0; iPrime < 16-1; iPrime++) - { - int Pick = RANDOM_LONG(0, 15-iPrime); - int Temp = m_HashPrimes[Pick]; - m_HashPrimes[Pick] = m_HashPrimes[15-iPrime]; - m_HashPrimes[15-iPrime] = Temp; - } -} - -// Renumber nodes so that nodes that link together are together. -// -#define UNNUMBERED_NODE -1 -void CGraph::SortNodes(void) -{ - // We are using m_iPreviousNode to be the new node number. - // After assigning new node numbers to everything, we move - // things and patchup the links. - // - int iNodeCnt = 0; - int i; - m_pNodes[0].m_iPreviousNode = iNodeCnt++; - for (i = 1; i < m_cNodes; i++) - { - m_pNodes[i].m_iPreviousNode = UNNUMBERED_NODE; - } - - for (i = 0; i < m_cNodes; i++) - { - // Run through all of this node's neighbors - // - for (int j = 0 ; j < m_pNodes[i].m_cNumLinks; j++ ) - { - int iDestNode = INodeLink(i, j); - if (m_pNodes[iDestNode].m_iPreviousNode == UNNUMBERED_NODE) - { - m_pNodes[iDestNode].m_iPreviousNode = iNodeCnt++; - } - } - } - - // Assign remaining node numbers to unlinked nodes. - // - for (i = 0; i < m_cNodes; i++) - { - if (m_pNodes[i].m_iPreviousNode == UNNUMBERED_NODE) - { - m_pNodes[i].m_iPreviousNode = iNodeCnt++; - } - } - - // Alter links to reflect new node numbers. - // - for (i = 0; i < m_cLinks; i++) - { - m_pLinkPool[i].m_iSrcNode = m_pNodes[m_pLinkPool[i].m_iSrcNode].m_iPreviousNode; - m_pLinkPool[i].m_iDestNode = m_pNodes[m_pLinkPool[i].m_iDestNode].m_iPreviousNode; - } - - // Rearrange nodes to reflect new node numbering. - // - for (i = 0; i < m_cNodes; i++) - { - while (m_pNodes[i].m_iPreviousNode != i) - { - // Move current node off to where it should be, and bring - // that other node back into the current slot. - // - int iDestNode = m_pNodes[i].m_iPreviousNode; - CNode TempNode = m_pNodes[iDestNode]; - m_pNodes[iDestNode] = m_pNodes[i]; - m_pNodes[i] = TempNode; - } - } -} - -void CGraph::BuildLinkLookups(void) -{ - m_nHashLinks = 3*m_cLinks/2 + 3; - - HashChoosePrimes(m_nHashLinks); - m_pHashLinks = (short *)calloc(sizeof(short), m_nHashLinks); - if (!m_pHashLinks) - { - ALERT(at_aiconsole, "Couldn't allocated Link Lookup Table.\n"); - return; - } - int i; - for (i = 0; i < m_nHashLinks; i++) - { - m_pHashLinks[i] = ENTRY_STATE_EMPTY; - } - - for (i = 0; i < m_cLinks; i++) - { - CLink &link = Link(i); - HashInsert(link.m_iSrcNode, link.m_iDestNode, i); - } -#if 0 - for (i = 0; i < m_cLinks; i++) - { - CLink &link = Link(i); - int iKey; - HashSearch(link.m_iSrcNode, link.m_iDestNode, iKey); - if (iKey != i) - { - ALERT(at_aiconsole, "HashLinks don't match (%d versus %d)\n", i, iKey); - } - } -#endif -} - -void CGraph::BuildRegionTables(void) -{ - if (m_di) free(m_di); - - // Go ahead and setup for range searching the nodes for FindNearestNodes - // - m_di = (DIST_INFO *)calloc(sizeof(DIST_INFO), m_cNodes); - if (!m_di) - { - ALERT(at_aiconsole, "Couldn't allocated node ordering array.\n"); - return; - } - - // Calculate regions for all the nodes. - // - // - int i; - for (i = 0; i < 3; i++) - { - m_RegionMin[i] = 999999999.0; // just a big number out there; - m_RegionMax[i] = -999999999.0; // just a big number out there; - } - for (i = 0; i < m_cNodes; i++) - { - if (m_pNodes[i].m_vecOrigin.x < m_RegionMin[0]) - m_RegionMin[0] = m_pNodes[i].m_vecOrigin.x; - if (m_pNodes[i].m_vecOrigin.y < m_RegionMin[1]) - m_RegionMin[1] = m_pNodes[i].m_vecOrigin.y; - if (m_pNodes[i].m_vecOrigin.z < m_RegionMin[2]) - m_RegionMin[2] = m_pNodes[i].m_vecOrigin.z; - - if (m_pNodes[i].m_vecOrigin.x > m_RegionMax[0]) - m_RegionMax[0] = m_pNodes[i].m_vecOrigin.x; - if (m_pNodes[i].m_vecOrigin.y > m_RegionMax[1]) - m_RegionMax[1] = m_pNodes[i].m_vecOrigin.y; - if (m_pNodes[i].m_vecOrigin.z > m_RegionMax[2]) - m_RegionMax[2] = m_pNodes[i].m_vecOrigin.z; - } - for (i = 0; i < m_cNodes; i++) - { - m_pNodes[i].m_Region[0] = CALC_RANGE(m_pNodes[i].m_vecOrigin.x, m_RegionMin[0], m_RegionMax[0]); - m_pNodes[i].m_Region[1] = CALC_RANGE(m_pNodes[i].m_vecOrigin.y, m_RegionMin[1], m_RegionMax[1]); - m_pNodes[i].m_Region[2] = CALC_RANGE(m_pNodes[i].m_vecOrigin.z, m_RegionMin[2], m_RegionMax[2]); - } - - for (i = 0; i < 3; i++) - { - int j; - for (j = 0; j < NUM_RANGES; j++) - { - m_RangeStart[i][j] = 255; - m_RangeEnd[i][j] = 0; - } - for (j = 0; j < m_cNodes; j++) - { - m_di[j].m_SortedBy[i] = j; - } - - for (j = 0; j < m_cNodes - 1; j++) - { - int jNode = m_di[j].m_SortedBy[i]; - int jCodeX = m_pNodes[jNode].m_Region[0]; - int jCodeY = m_pNodes[jNode].m_Region[1]; - int jCodeZ = m_pNodes[jNode].m_Region[2]; - int jCode; - switch (i) - { - case 0: - jCode = (jCodeX << 16) + (jCodeY << 8) + jCodeZ; - break; - case 1: - jCode = (jCodeY << 16) + (jCodeZ << 8) + jCodeX; - break; - case 2: - jCode = (jCodeZ << 16) + (jCodeX << 8) + jCodeY; - break; - } - - for (int k = j+1; k < m_cNodes; k++) - { - int kNode = m_di[k].m_SortedBy[i]; - int kCodeX = m_pNodes[kNode].m_Region[0]; - int kCodeY = m_pNodes[kNode].m_Region[1]; - int kCodeZ = m_pNodes[kNode].m_Region[2]; - int kCode; - switch (i) - { - case 0: - kCode = (kCodeX << 16) + (kCodeY << 8) + kCodeZ; - break; - case 1: - kCode = (kCodeY << 16) + (kCodeZ << 8) + kCodeX; - break; - case 2: - kCode = (kCodeZ << 16) + (kCodeX << 8) + kCodeY; - break; - } - - if (kCode < jCode) - { - // Swap j and k entries. - // - int Tmp = m_di[j].m_SortedBy[i]; - m_di[j].m_SortedBy[i] = m_di[k].m_SortedBy[i]; - m_di[k].m_SortedBy[i] = Tmp; - } - } - } - } - - // Generate lookup tables. - // - for (i = 0; i < m_cNodes; i++) - { - int CodeX = m_pNodes[m_di[i].m_SortedBy[0]].m_Region[0]; - int CodeY = m_pNodes[m_di[i].m_SortedBy[1]].m_Region[1]; - int CodeZ = m_pNodes[m_di[i].m_SortedBy[2]].m_Region[2]; - - if (i < m_RangeStart[0][CodeX]) - { - m_RangeStart[0][CodeX] = i; - } - if (i < m_RangeStart[1][CodeY]) - { - m_RangeStart[1][CodeY] = i; - } - if (i < m_RangeStart[2][CodeZ]) - { - m_RangeStart[2][CodeZ] = i; - } - if (m_RangeEnd[0][CodeX] < i) - { - m_RangeEnd[0][CodeX] = i; - } - if (m_RangeEnd[1][CodeY] < i) - { - m_RangeEnd[1][CodeY] = i; - } - if (m_RangeEnd[2][CodeZ] < i) - { - m_RangeEnd[2][CodeZ] = i; - } - } - - // Initialize the cache. - // - memset(m_Cache, 0, sizeof(m_Cache)); -} - -void CGraph :: ComputeStaticRoutingTables( void ) -{ - int nRoutes = m_cNodes*m_cNodes; -#define FROM_TO(x,y) ((x)*m_cNodes+(y)) - short *Routes = new short[nRoutes]; - - int *pMyPath = new int[m_cNodes]; - unsigned short *BestNextNodes = new unsigned short[m_cNodes]; - char *pRoute = new char[m_cNodes*2]; - - - if (Routes && pMyPath && BestNextNodes && pRoute) - { - int nTotalCompressedSize = 0; - for (int iHull = 0; iHull < MAX_NODE_HULLS; iHull++) - { - for (int iCap = 0; iCap < 2; iCap++) - { - int iCapMask; - switch (iCap) - { - case 0: - iCapMask = 0; - break; - - case 1: - iCapMask = bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; - break; - } - - - // Initialize Routing table to uncalculated. - // - int iFrom; - for (iFrom = 0; iFrom < m_cNodes; iFrom++) - { - for (int iTo = 0; iTo < m_cNodes; iTo++) - { - Routes[FROM_TO(iFrom, iTo)] = -1; - } - } - - for (iFrom = 0; iFrom < m_cNodes; iFrom++) - { - for (int iTo = m_cNodes-1; iTo >= 0; iTo--) - { - if (Routes[FROM_TO(iFrom, iTo)] != -1) continue; - - int cPathSize = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); - - // Use the computed path to update the routing table. - // - if (cPathSize > 1) - { - for (int iNode = 0; iNode < cPathSize-1; iNode++) - { - int iStart = pMyPath[iNode]; - int iNext = pMyPath[iNode+1]; - for (int iNode1 = iNode+1; iNode1 < cPathSize; iNode1++) - { - int iEnd = pMyPath[iNode1]; - Routes[FROM_TO(iStart, iEnd)] = iNext; - } - } -#if 0 - // Well, at first glance, this should work, but actually it's safer - // to be told explictly that you can take a series of node in a - // particular direction. Some links don't appear to have links in - // the opposite direction. - // - for (iNode = cPathSize-1; iNode >= 1; iNode--) - { - int iStart = pMyPath[iNode]; - int iNext = pMyPath[iNode-1]; - for (int iNode1 = iNode-1; iNode1 >= 0; iNode1--) - { - int iEnd = pMyPath[iNode1]; - Routes[FROM_TO(iStart, iEnd)] = iNext; - } - } -#endif - } - else - { - Routes[FROM_TO(iFrom, iTo)] = iFrom; - Routes[FROM_TO(iTo, iFrom)] = iTo; - } - } - } - - for (iFrom = 0; iFrom < m_cNodes; iFrom++) - { - for (int iTo = 0; iTo < m_cNodes; iTo++) - { - BestNextNodes[iTo] = Routes[FROM_TO(iFrom, iTo)]; - } - - // Compress this node's routing table. - // - int iLastNode = 9999999; // just really big. - int cSequence = 0; - int cRepeats = 0; - int CompressedSize = 0; - char *p = pRoute; - for (int i = 0; i < m_cNodes; i++) - { - BOOL CanRepeat = ((BestNextNodes[i] == iLastNode) && cRepeats < 127); - BOOL CanSequence = (BestNextNodes[i] == i && cSequence < 128); - - if (cRepeats) - { - if (CanRepeat) - { - cRepeats++; - } - else - { - // Emit the repeat phrase. - // - CompressedSize += 2; // (count-1, iLastNode-i) - *p++ = cRepeats - 1; - int a = iLastNode - iFrom; - int b = iLastNode - iFrom + m_cNodes; - int c = iLastNode - iFrom - m_cNodes; - if (-128 <= a && a <= 127) - { - *p++ = a; - } - else if (-128 <= b && b <= 127) - { - *p++ = b; - } - else if (-128 <= c && c <= 127) - { - *p++ = c; - } - else - { - ALERT( at_aiconsole, "Nodes need sorting (%d,%d)!\n", iLastNode, iFrom); - } - cRepeats = 0; - - if (CanSequence) - { - // Start a sequence. - // - cSequence++; - } - else - { - // Start another repeat. - // - cRepeats++; - } - } - } - else if (cSequence) - { - if (CanSequence) - { - cSequence++; - } - else - { - // It may be advantageous to combine - // a single-entry sequence phrase with the - // next repeat phrase. - // - if (cSequence == 1 && CanRepeat) - { - // Combine with repeat phrase. - // - cRepeats = 2; - cSequence = 0; - } - else - { - // Emit the sequence phrase. - // - CompressedSize += 1; // (-count) - *p++ = -cSequence; - cSequence = 0; - - // Start a repeat sequence. - // - cRepeats++; - } - } - } - else - { - if (CanSequence) - { - // Start a sequence phrase. - // - cSequence++; - } - else - { - // Start a repeat sequence. - // - cRepeats++; - } - } - iLastNode = BestNextNodes[i]; - } - if (cRepeats) - { - // Emit the repeat phrase. - // - CompressedSize += 2; - *p++ = cRepeats - 1; -#if 0 - iLastNode = iFrom + *pRoute; - if (iLastNode >= m_cNodes) iLastNode -= m_cNodes; - else if (iLastNode < 0) iLastNode += m_cNodes; -#endif - int a = iLastNode - iFrom; - int b = iLastNode - iFrom + m_cNodes; - int c = iLastNode - iFrom - m_cNodes; - if (-128 <= a && a <= 127) - { - *p++ = a; - } - else if (-128 <= b && b <= 127) - { - *p++ = b; - } - else if (-128 <= c && c <= 127) - { - *p++ = c; - } - else - { - ALERT( at_aiconsole, "Nodes need sorting (%d,%d)!\n", iLastNode, iFrom); - } - } - if (cSequence) - { - // Emit the Sequence phrase. - // - CompressedSize += 1; - *p++ = -cSequence; - } - - // Go find a place to store this thing and point to it. - // - int nRoute = p - pRoute; - if (m_pRouteInfo) - { - int i; - for (i = 0; i < m_nRouteInfo - nRoute; i++) - { - if (memcmp(m_pRouteInfo + i, pRoute, nRoute) == 0) - { - break; - } - } - if (i < m_nRouteInfo - nRoute) - { - m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = i; - } - else - { - char *Tmp = (char *)calloc(sizeof(char), (m_nRouteInfo + nRoute)); - memcpy(Tmp, m_pRouteInfo, m_nRouteInfo); - free(m_pRouteInfo); - m_pRouteInfo = Tmp; - memcpy(m_pRouteInfo + m_nRouteInfo, pRoute, nRoute); - m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = m_nRouteInfo; - m_nRouteInfo += nRoute; - nTotalCompressedSize += CompressedSize; - } - } - else - { - m_nRouteInfo = nRoute; - m_pRouteInfo = (char *)calloc(sizeof(char), nRoute); - memcpy(m_pRouteInfo, pRoute, nRoute); - m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = 0; - nTotalCompressedSize += CompressedSize; - } - } - } - } - ALERT( at_aiconsole, "Size of Routes = %d\n", nTotalCompressedSize); - } - if (Routes) delete Routes; - if (BestNextNodes) delete BestNextNodes; - if (pRoute) delete pRoute; - if (pMyPath) delete pMyPath; - Routes = 0; - BestNextNodes = 0; - pRoute = 0; - pMyPath = 0; - -#if 0 - TestRoutingTables(); -#endif - m_fRoutingComplete = TRUE; -} - -// Test those routing tables. Doesn't really work, yet. -// -void CGraph :: TestRoutingTables( void ) -{ - int *pMyPath = new int[m_cNodes]; - int *pMyPath2 = new int[m_cNodes]; - if (pMyPath && pMyPath2) - { - for (int iHull = 0; iHull < MAX_NODE_HULLS; iHull++) - { - for (int iCap = 0; iCap < 2; iCap++) - { - int iCapMask; - switch (iCap) - { - case 0: - iCapMask = 0; - break; - - case 1: - iCapMask = bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; - break; - } - - for (int iFrom = 0; iFrom < m_cNodes; iFrom++) - { - for (int iTo = 0; iTo < m_cNodes; iTo++) - { - m_fRoutingComplete = FALSE; - int cPathSize1 = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); - m_fRoutingComplete = TRUE; - int cPathSize2 = FindShortestPath(pMyPath2, iFrom, iTo, iHull, iCapMask); - - // Unless we can look at the entire path, we can verify that it's correct. - // - if (cPathSize2 == MAX_PATH_SIZE) continue; - - // Compare distances. - // -#if 1 - float flDistance1 = 0.0; - int i; - for (i = 0; i < cPathSize1-1; i++) - { - // Find the link from pMyPath[i] to pMyPath[i+1] - // - if (pMyPath[i] == pMyPath[i+1]) continue; - int iVisitNode; - BOOL bFound = FALSE; - for (int iLink = 0; iLink < m_pNodes[pMyPath[i]].m_cNumLinks; iLink++) - { - iVisitNode = INodeLink ( pMyPath[i], iLink ); - if (iVisitNode == pMyPath[i+1]) - { - flDistance1 += m_pLinkPool[ m_pNodes[ pMyPath[i] ].m_iFirstLink + iLink].m_flWeight; - bFound = TRUE; - break; - } - } - if (!bFound) - { - ALERT(at_aiconsole, "No link.\n"); - } - } - - float flDistance2 = 0.0; - for (i = 0; i < cPathSize2-1; i++) - { - // Find the link from pMyPath2[i] to pMyPath2[i+1] - // - if (pMyPath2[i] == pMyPath2[i+1]) continue; - int iVisitNode; - BOOL bFound = FALSE; - for (int iLink = 0; iLink < m_pNodes[pMyPath2[i]].m_cNumLinks; iLink++) - { - iVisitNode = INodeLink ( pMyPath2[i], iLink ); - if (iVisitNode == pMyPath2[i+1]) - { - flDistance2 += m_pLinkPool[ m_pNodes[ pMyPath2[i] ].m_iFirstLink + iLink].m_flWeight; - bFound = TRUE; - break; - } - } - if (!bFound) - { - ALERT(at_aiconsole, "No link.\n"); - } - } - if (fabs(flDistance1 - flDistance2) > 0.10) - { -#else - if (cPathSize1 != cPathSize2 || memcmp(pMyPath, pMyPath2, sizeof(int)*cPathSize1) != 0) - { -#endif - ALERT(at_aiconsole, "Routing is inconsistent!!!\n"); - ALERT(at_aiconsole, "(%d to %d |%d/%d)1:", iFrom, iTo, iHull, iCap); - for (int i = 0; i < cPathSize1; i++) - { - ALERT(at_aiconsole, "%d ", pMyPath[i]); - } - ALERT(at_aiconsole, "\n(%d to %d |%d/%d)2:", iFrom, iTo, iHull, iCap); - for (i = 0; i < cPathSize2; i++) - { - ALERT(at_aiconsole, "%d ", pMyPath2[i]); - } - ALERT(at_aiconsole, "\n"); - m_fRoutingComplete = FALSE; - cPathSize1 = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); - m_fRoutingComplete = TRUE; - cPathSize2 = FindShortestPath(pMyPath2, iFrom, iTo, iHull, iCapMask); - goto EnoughSaid; - } - } - } - } - } - } - -EnoughSaid: - - if (pMyPath) delete pMyPath; - if (pMyPath2) delete pMyPath2; - pMyPath = 0; - pMyPath2 = 0; -} - - -//========================================================= -// CNodeViewer - Draws a graph of the shorted path from all nodes -// to current location (typically the player). It then draws -// as many connects as it can per frame, trying not to overflow the buffer -//========================================================= -void CNodeViewer::Spawn( ) -{ - /*CNodeViewer *pViewer = CreateClassPtr((CNodeViewer *)NULL); - pViewer->Spawn();*/ - - if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) - {// protect us in the case that the node graph isn't available or built - ALERT ( at_console, "Graph not ready!\n" ); - UTIL_Remove( this->edict() ); - return; - } - - - if (FClassnameIs( pev, "node_viewer_fly")) - { - m_iHull = NODE_FLY_HULL; - m_afNodeType = bits_NODE_AIR; - m_vecColor = Vector( 160, 100, 255 ); - } - else if (FClassnameIs( pev, "node_viewer_large")) - { - m_iHull = NODE_LARGE_HULL; - m_afNodeType = bits_NODE_LAND | bits_NODE_WATER; - m_vecColor = Vector( 100, 255, 160 ); - } - else - { - m_iHull = NODE_HUMAN_HULL; - m_afNodeType = bits_NODE_LAND | bits_NODE_WATER; - m_vecColor = Vector( 255, 160, 100 ); - } - - - m_iBaseNode = WorldGraph.FindNearestNode ( pev->origin, m_afNodeType ); - - if ( m_iBaseNode < 0 ) - { - ALERT( at_console, "No nearby node\n" ); - return; - } - - m_nVisited = 0; - - ALERT( at_aiconsole, "basenode %d\n", m_iBaseNode ); - - if (WorldGraph.m_cNodes < 128) - { - for (int i = 0; i < WorldGraph.m_cNodes; i++) - { - AddNode( i, WorldGraph.NextNodeInRoute( i, m_iBaseNode, m_iHull, 0 )); - } - } - else - { - // do a depth traversal - FindNodeConnections( m_iBaseNode ); - - int start = 0; - int end; - do { - end = m_nVisited; - // ALERT( at_console, "%d :", m_nVisited ); - for (end = m_nVisited; start < end; start++) - { - FindNodeConnections( m_aFrom[start] ); - FindNodeConnections( m_aTo[start] ); - } - } while (end != m_nVisited); - } - - ALERT( at_aiconsole, "%d nodes\n", m_nVisited ); - - m_iDraw = 0; - SetThink( &CNodeViewer::DrawThink ); - pev->nextthink = gpGlobals->time; - - pev->classname = MAKE_STRING( "node_viewer" ); -} - - -void CNodeViewer :: FindNodeConnections ( int iNode ) -{ - AddNode( iNode, WorldGraph.NextNodeInRoute( iNode, m_iBaseNode, m_iHull, 0 )); - for ( int i = 0 ; i < WorldGraph.m_pNodes[ iNode ].m_cNumLinks ; i++ ) - { - CLink *pToLink = &WorldGraph.NodeLink( iNode, i); - AddNode( pToLink->m_iDestNode, WorldGraph.NextNodeInRoute( pToLink->m_iDestNode, m_iBaseNode, m_iHull, 0 )); - } -} - -void CNodeViewer::AddNode( int iFrom, int iTo ) -{ - if (m_nVisited >= 128) - { - return; - } - else - { - if (iFrom == iTo) - return; - - for (int i = 0; i < m_nVisited; i++) - { - if (m_aFrom[i] == iFrom && m_aTo[i] == iTo) - return; - if (m_aFrom[i] == iTo && m_aTo[i] == iFrom) - return; - } - m_aFrom[m_nVisited] = iFrom; - m_aTo[m_nVisited] = iTo; - m_nVisited++; - } -} - - -void CNodeViewer :: DrawThink( void ) -{ - pev->nextthink = gpGlobals->time; - - for (int i = 0; i < 10; i++) - { - if (m_iDraw == m_nVisited) - { - UTIL_Remove( this->edict() ); - return; - } - - extern short g_sModelIndexLaser; - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMPOINTS ); - WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.x ); - WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.y ); - WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.z + NODE_HEIGHT ); - - WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.x ); - WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.y ); - WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.z + NODE_HEIGHT ); - WRITE_SHORT( g_sModelIndexLaser ); - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 250 ); // life - WRITE_BYTE( 40 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( m_vecColor.x ); // r, g, b - WRITE_BYTE( m_vecColor.y ); // r, g, b - WRITE_BYTE( m_vecColor.z ); // r, g, b - WRITE_BYTE( 128 ); // brightness - WRITE_BYTE( 0 ); // speed - MESSAGE_END(); - - m_iDraw++; - } -} - - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// nodes.cpp - AI node tree stuff. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "nodes.h" +#include "animation.h" +#include "doors.h" + +#if !defined ( _WIN32 ) +#include +#include +#include +#include // mkdir +#endif + +#define HULL_STEP_SIZE 16// how far the test hull moves on each step +#define NODE_HEIGHT 8 // how high to lift nodes off the ground after we drop them all (make stair/ramp mapping easier) + +// to help eliminate node clutter by level designers, this is used to cap how many other nodes +// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()". +#define MAX_NODE_INITIAL_LINKS 128 +//#define MAX_NODES 1024 // already defined in monster_plugin.h + +Vector VecBModelOrigin( entvars_t* pevBModel ); + +CGraph WorldGraph; + +#ifdef __linux__ +#include +#define CreateDirectory(p, n) mkdir(p, 0777) +#endif +//========================================================= +// CGraph - InitGraph - prepares the graph for use. Frees any +// memory currently in use by the world graph, NULLs +// all pointers, and zeros the node count. +//========================================================= +void CGraph :: InitGraph( void) +{ + + // Make the graph unavailable + // + m_fGraphPresent = FALSE; + m_fGraphPointersSet = FALSE; + m_fRoutingComplete = FALSE; + + // Free the link pool + // + if ( m_pLinkPool ) + { + free ( m_pLinkPool ); + m_pLinkPool = NULL; + } + + // Free the node info + // + if ( m_pNodes ) + { + free ( m_pNodes ); + m_pNodes = NULL; + } + + if ( m_di ) + { + free ( m_di ); + m_di = NULL; + } + + // Free the routing info. + // + if ( m_pRouteInfo ) + { + free ( m_pRouteInfo ); + m_pRouteInfo = NULL; + } + + if (m_pHashLinks) + { + free(m_pHashLinks); + m_pHashLinks = NULL; + } + + // Zero node and link counts + // + m_cNodes = 0; + m_cLinks = 0; + m_nRouteInfo = 0; + + m_iLastActiveIdleSearch = 0; + m_iLastCoverSearch = 0; +} + +//========================================================= +// CGraph - AllocNodes - temporary function that mallocs a +// reasonable number of nodes so we can build the path which +// will be saved to disk. +//========================================================= +int CGraph :: AllocNodes ( void ) +{ +// malloc all of the nodes + WorldGraph.m_pNodes = (CNode *)calloc ( sizeof ( CNode ), MAX_NODES ); + +// could not malloc space for all the nodes! + if ( !WorldGraph.m_pNodes ) + { + ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d nodes!\n", WorldGraph.m_cNodes ); + return FALSE; + } + + return TRUE; +} + +//========================================================= +// CGraph - LinkEntForLink - sometimes the ent that blocks +// a path is a usable door, in which case the monster just +// needs to face the door and fire it. In other cases, the +// monster needs to operate a button or lever to get the +// door to open. This function will return a pointer to the +// button if the monster needs to hit a button to open the +// door, or returns a pointer to the door if the monster +// need only use the door. +// +// pNode is the node the monster will be standing on when it +// will need to stop and trigger the ent. +//========================================================= +entvars_t* CGraph :: LinkEntForLink ( CLink *pLink, CNode *pNode ) +{ + edict_t *pentSearch; + edict_t *pentTrigger; + entvars_t *pevTrigger; + entvars_t *pevLinkEnt; + TraceResult tr; + + pevLinkEnt = pLink->m_pLinkEnt; + if ( !pevLinkEnt ) + return NULL; + + pentSearch = NULL;// start search at the top of the ent list. + + if ( FClassnameIs ( pevLinkEnt, "func_door" ) || FClassnameIs ( pevLinkEnt, "func_door_rotating" ) ) + { + + ///!!!UNDONE - check for TOGGLE or STAY open doors here. If a door is in the way, and is + // TOGGLE or STAY OPEN, even monsters that can't open doors can go that way. + + if ( ( pevLinkEnt->spawnflags & SF_DOOR_USE_ONLY ) ) + {// door is use only, so the door is all the monster has to worry about + return pevLinkEnt; + } + + while ( 1 ) + { + pentTrigger = FIND_ENTITY_BY_TARGET ( pentSearch, STRING( pevLinkEnt->targetname ) );// find the button or trigger + + if ( FNullEnt( pentTrigger ) ) + {// no trigger found + + // right now this is a problem among auto-open doors, or any door that opens through the use + // of a trigger brush. Trigger brushes have no models, and don't show up in searches. Just allow + // monsters to open these sorts of doors for now. + return pevLinkEnt; + } + + pentSearch = pentTrigger; + pevTrigger = VARS( pentTrigger ); + + if ( FClassnameIs(pevTrigger, "func_button") || FClassnameIs(pevTrigger, "func_rot_button" ) ) + {// only buttons are handled right now. + + // trace from the node to the trigger, make sure it's one we can see from the node. + // !!!HACKHACK Use bodyqueue here cause there are no ents we really wish to ignore! + UTIL_TraceLine ( pNode->m_vecOrigin, VecBModelOrigin( pevTrigger ), ignore_monsters, NULL, &tr ); + + + if ( VARS(tr.pHit) == pevTrigger ) + {// good to go! + return VARS( tr.pHit ); + } + } + } + } + else + { + ALERT ( at_aiconsole, "Unsupported PathEnt:\n'%s'\n", STRING ( pevLinkEnt->classname ) ); + return NULL; + } +} + +//========================================================= +// CGraph - HandleLinkEnt - a brush ent is between two +// nodes that would otherwise be able to see each other. +// Given the monster's capability, determine whether +// or not the monster can go this way. +//========================================================= +int CGraph :: HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType ) +{ + edict_t *pentWorld; + CMBaseEntity *pDoor; + TraceResult tr; + + if ( !m_fGraphPresent || !m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return FALSE; + } + + if ( FNullEnt ( pevLinkEnt ) ) + { + ALERT ( at_aiconsole, "dead path ent!\n" ); + return TRUE; + } + pentWorld = NULL; + +// func_door + if ( FClassnameIs( pevLinkEnt, "func_door" ) || FClassnameIs( pevLinkEnt, "func_door_rotating" ) ) + {// ent is a door. + + pDoor = ( CMBaseEntity::Instance( pevLinkEnt ) ); + + if ( ( pevLinkEnt->spawnflags & SF_DOOR_USE_ONLY ) ) + {// door is use only. + + if ( ( afCapMask & bits_CAP_OPEN_DOORS ) ) + {// let monster right through if he can open doors + return TRUE; + } + else + { + // monster should try for it if the door is open and looks as if it will stay that way + if ( pDoor->GetToggleState()== TS_AT_TOP && ( pevLinkEnt->spawnflags & SF_DOOR_NO_AUTO_RETURN ) ) + { + return TRUE; + } + + return FALSE; + } + } + else + {// door must be opened with a button or trigger field. + + // monster should try for it if the door is open and looks as if it will stay that way + if ( pDoor->GetToggleState() == TS_AT_TOP && ( pevLinkEnt->spawnflags & SF_DOOR_NO_AUTO_RETURN ) ) + { + return TRUE; + } + if ( ( afCapMask & bits_CAP_OPEN_DOORS ) ) + { + if ( !( pevLinkEnt->spawnflags & SF_DOOR_NOMONSTERS ) || queryType == NODEGRAPH_STATIC ) + return TRUE; + } + + return FALSE; + } + } +// func_breakable + else if ( FClassnameIs( pevLinkEnt, "func_breakable" ) && queryType == NODEGRAPH_STATIC ) + { + return TRUE; + } + else + { + ALERT ( at_aiconsole, "Unhandled Ent in Path %s\n", STRING( pevLinkEnt->classname ) ); + return FALSE; + } + + return FALSE; +} + +#if 0 +//========================================================= +// FindNearestLink - finds the connection (line) nearest +// the given point. Returns FALSE if fails, or TRUE if it +// has stuffed the index into the nearest link pool connection +// into the passed int pointer, and a BOOL telling whether or +// not the point is along the line into the passed BOOL pointer. +//========================================================= +int CGraph :: FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine ) +{ + int i, j;// loops + + int iNearestLink;// index into the link pool, this is the nearest node at any time. + float flMinDist;// the distance of of the nearest case so far + float flDistToLine;// the distance of the current test case + + BOOL fCurrentAlongLine; + BOOL fSuccess; + + //float flConstant;// line constant + Vector vecSpot1, vecSpot2; + Vector2D vec2Spot1, vec2Spot2, vec2TestPoint; + Vector2D vec2Normal;// line normal + Vector2D vec2Line; + + TraceResult tr; + + iNearestLink = -1;// prepare for failure + fSuccess = FALSE; + + flMinDist = 9999;// anything will be closer than this + +// go through all of the nodes, and each node's connections + int cSkip = 0;// how many links proper pairing allowed us to skip + int cChecked = 0;// how many links were checked + + for ( i = 0 ; i < m_cNodes ; i++ ) + { + vecSpot1 = m_pNodes[ i ].m_vecOrigin; + + if ( m_pNodes[ i ].m_cNumLinks <= 0 ) + {// this shouldn't happen! + ALERT ( at_aiconsole, "**Node %d has no links\n", i ); + continue; + } + + for ( j = 0 ; j < m_pNodes[ i ].m_cNumLinks ; j++ ) + { + /* + !!!This optimization only works when the node graph consists of properly linked pairs. + if ( INodeLink ( i, j ) <= i ) + { + // since we're going through the nodes in order, don't check + // any connections whose second node is lower in the list + // than the node we're currently working with. This eliminates + // redundant checks. + cSkip++; + continue; + } + */ + + vecSpot2 = PNodeLink ( i, j )->m_vecOrigin; + + // these values need a little attention now and then, or sometimes ramps cause trouble. + if ( fabs ( vecSpot1.z - vecTestPoint.z ) > 48 && fabs ( vecSpot2.z - vecTestPoint.z ) > 48 ) + { + // if both endpoints of the line are 32 units or more above or below the monster, + // the monster won't be able to get to them, so we do a bit of trivial rejection here. + // this may change if monsters are allowed to jump down. + // + // !!!LATER: some kind of clever X/Y hashing should be used here, too + continue; + } + +// now we have two endpoints for a line segment that we've not already checked. +// since all lines that make it this far are within -/+ 32 units of the test point's +// Z Plane, we can get away with doing the point->line check in 2d. + + cChecked++; + + vec2Spot1 = vecSpot1.Make2D(); + vec2Spot2 = vecSpot2.Make2D(); + vec2TestPoint = vecTestPoint.Make2D(); + + // get the line normal. + vec2Line = ( vec2Spot1 - vec2Spot2 ).Normalize(); + vec2Normal.x = -vec2Line.y; + vec2Normal.y = vec2Line.x; + + if ( DotProduct ( vec2Line, ( vec2TestPoint - vec2Spot1 ) ) > 0 ) + {// point outside of line + flDistToLine = ( vec2TestPoint - vec2Spot1 ).Length(); + fCurrentAlongLine = FALSE; + } + else if ( DotProduct ( vec2Line, ( vec2TestPoint - vec2Spot2 ) ) < 0 ) + {// point outside of line + flDistToLine = ( vec2TestPoint - vec2Spot2 ).Length(); + fCurrentAlongLine = FALSE; + } + else + {// point inside line + flDistToLine = fabs( DotProduct ( vec2TestPoint - vec2Spot2, vec2Normal ) ); + fCurrentAlongLine = TRUE; + } + + if ( flDistToLine < flMinDist ) + {// just found a line nearer than any other so far + + UTIL_TraceLine ( vecTestPoint, SourceNode( i, j ).m_vecOrigin, ignore_monsters, NULL, &tr ); + + if ( tr.flFraction != 1.0 ) + {// crap. can't see the first node of this link, try to see the other + + UTIL_TraceLine ( vecTestPoint, DestNode( i, j ).m_vecOrigin, ignore_monsters, NULL, &tr ); + if ( tr.flFraction != 1.0 ) + {// can't use this link, cause can't see either node! + continue; + } + + } + + fSuccess = TRUE;// we know there will be something to return. + flMinDist = flDistToLine; + iNearestLink = m_pNodes [ i ].m_iFirstLink + j; + *piNearestLink = m_pNodes[ i ].m_iFirstLink + j; + *pfAlongLine = fCurrentAlongLine; + } + } + } + +/* + if ( fSuccess ) + { + WRITE_BYTE(MSG_BROADCAST, SVC_TEMPENTITY); + WRITE_BYTE(MSG_BROADCAST, TE_SHOWLINE); + + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.x ); + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.y ); + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iSrcNode ].m_vecOrigin.z + NODE_HEIGHT); + + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.x ); + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.y ); + WRITE_COORD(MSG_BROADCAST, m_pNodes[ m_pLinkPool[ iNearestLink ].m_iDestNode ].m_vecOrigin.z + NODE_HEIGHT); + } +*/ + + ALERT ( at_aiconsole, "%d Checked\n", cChecked ); + return fSuccess; +} + +#endif + +int CGraph::HullIndex( const CMBaseEntity *pEntity ) +{ + if ( pEntity->pev->movetype == MOVETYPE_FLY) + return NODE_FLY_HULL; + + if ( pEntity->pev->mins == Vector( -12, -12, 0 ) ) + return NODE_SMALL_HULL; + else if ( pEntity->pev->mins == VEC_HUMAN_HULL_MIN ) + return NODE_HUMAN_HULL; + else if ( pEntity->pev->mins == Vector ( -32, -32, 0 ) ) + return NODE_LARGE_HULL; + +// ALERT ( at_aiconsole, "Unknown Hull Mins!\n" ); + return NODE_HUMAN_HULL; +} + + +int CGraph::NodeType( const CMBaseEntity *pEntity ) +{ + if ( pEntity->pev->movetype == MOVETYPE_FLY) + { + if (pEntity->pev->waterlevel != 0) + { + return bits_NODE_WATER; + } + else + { + return bits_NODE_AIR; + } + } + return bits_NODE_LAND; +} + + +// Sum up graph weights on the path from iStart to iDest to determine path length +float CGraph::PathLength( int iStart, int iDest, int iHull, int afCapMask ) +{ + float distance = 0; + int iNext; + + int iMaxLoop = m_cNodes; + + int iCurrentNode = iStart; + int iCap = CapIndex( afCapMask ); + + while (iCurrentNode != iDest) + { + if (iMaxLoop-- <= 0) + { + ALERT( at_console, "Route Failure\n" ); + return 0; + } + + iNext = NextNodeInRoute( iCurrentNode, iDest, iHull, iCap ); + if (iCurrentNode == iNext) + { + //ALERT(at_aiconsole, "SVD: Can't get there from here..\n"); + return 0; + } + + int iLink; + HashSearch(iCurrentNode, iNext, iLink); + if (iLink < 0) + { + ALERT(at_console, "HashLinks is broken from %d to %d.\n", iCurrentNode, iDest); + return 0; + } + CLink &link = Link(iLink); + distance += link.m_flWeight; + + iCurrentNode = iNext; + } + + return distance; +} + + +// Parse the routing table at iCurrentNode for the next node on the shortest path to iDest +int CGraph::NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap ) +{ + int iNext = iCurrentNode; + int nCount = iDest+1; + char *pRoute = m_pRouteInfo + m_pNodes[ iCurrentNode ].m_pNextBestNode[iHull][iCap]; + + // Until we decode the next best node + // + while (nCount > 0) + { + char ch = *pRoute++; + //ALERT(at_aiconsole, "C(%d)", ch); + if (ch < 0) + { + // Sequence phrase + // + ch = -ch; + if (nCount <= ch) + { + iNext = iDest; + nCount = 0; + //ALERT(at_aiconsole, "SEQ: iNext/iDest=%d\n", iNext); + } + else + { + //ALERT(at_aiconsole, "SEQ: nCount + ch (%d + %d)\n", nCount, ch); + nCount = nCount - ch; + } + } + else + { + //ALERT(at_aiconsole, "C(%d)", *pRoute); + + // Repeat phrase + // + if (nCount <= ch+1) + { + iNext = iCurrentNode + *pRoute; + if (iNext >= m_cNodes) iNext -= m_cNodes; + else if (iNext < 0) iNext += m_cNodes; + nCount = 0; + //ALERT(at_aiconsole, "REP: iNext=%d\n", iNext); + } + else + { + //ALERT(at_aiconsole, "REP: nCount - ch+1 (%d - %d+1)\n", nCount, ch); + nCount = nCount - ch - 1; + } + pRoute++; + } + } + + return iNext; +} + + +//========================================================= +// CGraph - FindShortestPath +// +// accepts a capability mask (afCapMask), and will only +// find a path usable by a monster with those capabilities +// returns the number of nodes copied into supplied array +//========================================================= +int CGraph :: FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask) +{ + int iVisitNode; + int iCurrentNode; + int iNumPathNodes; + int iHullMask; + + if ( !m_fGraphPresent || !m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available or built + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return FALSE; + } + + if ( iStart < 0 || iStart > m_cNodes ) + {// The start node is bad? + ALERT ( at_aiconsole, "Can't build a path, iStart is %d!\n", iStart ); + return FALSE; + } + + if (iStart == iDest) + { + piPath[0] = iStart; + piPath[1] = iDest; + return 2; + } + + // Is routing information present. + // + if (m_fRoutingComplete) + { + int iCap = CapIndex( afCapMask ); + + iNumPathNodes = 0; + piPath[iNumPathNodes++] = iStart; + iCurrentNode = iStart; + int iNext; + + //ALERT(at_aiconsole, "GOAL: %d to %d\n", iStart, iDest); + + // Until we arrive at the destination + // + while (iCurrentNode != iDest) + { + iNext = NextNodeInRoute( iCurrentNode, iDest, iHull, iCap ); + if (iCurrentNode == iNext) + { + //ALERT(at_aiconsole, "SVD: Can't get there from here..\n"); + return 0; + break; + } + if (iNumPathNodes >= MAX_PATH_SIZE) + { + //ALERT(at_aiconsole, "SVD: Don't return the entire path.\n"); + break; + } + piPath[iNumPathNodes++] = iNext; + iCurrentNode = iNext; + } + //ALERT( at_aiconsole, "SVD: Path with %d nodes.\n", iNumPathNodes); + } + else + { + CQueuePriority queue; + + switch( iHull ) + { + case NODE_SMALL_HULL: + iHullMask = bits_LINK_SMALL_HULL; + break; + case NODE_HUMAN_HULL: + iHullMask = bits_LINK_HUMAN_HULL; + break; + case NODE_LARGE_HULL: + iHullMask = bits_LINK_LARGE_HULL; + break; + case NODE_FLY_HULL: + iHullMask = bits_LINK_FLY_HULL; + break; + } + + // Mark all the nodes as unvisited. + // + int i; + for ( i = 0; i < m_cNodes; i++) + { + m_pNodes[ i ].m_flClosestSoFar = -1.0; + } + + m_pNodes[ iStart ].m_flClosestSoFar = 0.0; + m_pNodes[ iStart ].m_iPreviousNode = iStart;// tag this as the origin node + queue.Insert( iStart, 0.0 );// insert start node + + while ( !queue.Empty() ) + { + // now pull a node out of the queue + float flCurrentDistance; + iCurrentNode = queue.Remove(flCurrentDistance); + + // For straight-line weights, the following Shortcut works. For arbitrary weights, + // it doesn't. + // + if (iCurrentNode == iDest) break; + + CNode *pCurrentNode = &m_pNodes[ iCurrentNode ]; + + for ( i = 0 ; i < pCurrentNode->m_cNumLinks ; i++ ) + {// run through all of this node's neighbors + + iVisitNode = INodeLink ( iCurrentNode, i ); + if ( ( m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_afLinkInfo & iHullMask ) != iHullMask ) + {// monster is too large to walk this connection + //ALERT ( at_aiconsole, "fat ass %d/%d\n",m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_afLinkInfo, iMonsterHull ); + continue; + } + // check the connection from the current node to the node we're about to mark visited and push into the queue + if ( m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_pLinkEnt != NULL ) + {// there's a brush ent in the way! Don't mark this node or put it into the queue unless the monster can negotiate it + + if ( !HandleLinkEnt ( iCurrentNode, m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i ].m_pLinkEnt, afCapMask, NODEGRAPH_STATIC ) ) + {// monster should not try to go this way. + continue; + } + } + float flOurDistance = flCurrentDistance + m_pLinkPool[ m_pNodes[ iCurrentNode ].m_iFirstLink + i].m_flWeight; + if ( m_pNodes[ iVisitNode ].m_flClosestSoFar < -0.5 + || flOurDistance < m_pNodes[ iVisitNode ].m_flClosestSoFar - 0.001 ) + { + m_pNodes[iVisitNode].m_flClosestSoFar = flOurDistance; + m_pNodes[iVisitNode].m_iPreviousNode = iCurrentNode; + + queue.Insert ( iVisitNode, flOurDistance ); + } + } + } + if ( m_pNodes[iDest].m_flClosestSoFar < -0.5 ) + {// Destination is unreachable, no path found. + return 0; + } + + // the queue is not empty + + // now we must walk backwards through the m_iPreviousNode field, and count how many connections there are in the path + iCurrentNode = iDest; + iNumPathNodes = 1;// count the dest + + while ( iCurrentNode != iStart ) + { + iNumPathNodes++; + iCurrentNode = m_pNodes[ iCurrentNode ].m_iPreviousNode; + } + + iCurrentNode = iDest; + for ( i = iNumPathNodes - 1 ; i >= 0 ; i-- ) + { + piPath[ i ] = iCurrentNode; + iCurrentNode = m_pNodes [ iCurrentNode ].m_iPreviousNode; + } + } + +#if 0 + + if (m_fRoutingComplete) + { + // This will draw the entire path that was generated for the monster. + + for ( int i = 0 ; i < iNumPathNodes - 1 ; i++ ) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + + WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.x ); + WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.y ); + WRITE_COORD( m_pNodes[ piPath[ i ] ].m_vecOrigin.z + NODE_HEIGHT ); + + WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.x ); + WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.y ); + WRITE_COORD( m_pNodes[ piPath[ i + 1 ] ].m_vecOrigin.z + NODE_HEIGHT ); + MESSAGE_END(); + } + } + +#endif +#if 0 // MAZE map + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + + WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.x ); + WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.y ); + WRITE_COORD( m_pNodes[ 4 ].m_vecOrigin.z + NODE_HEIGHT ); + + WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.x ); + WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.y ); + WRITE_COORD( m_pNodes[ 9 ].m_vecOrigin.z + NODE_HEIGHT ); + MESSAGE_END(); +#endif + + return iNumPathNodes; +} + +inline ULONG Hash(void *p, int len) +{ + CRC32_t ulCrc; + CRC32_INIT(&ulCrc); + CRC32_PROCESS_BUFFER(&ulCrc, p, len); + return CRC32_FINAL(ulCrc); +} + +void inline CalcBounds(int &Lower, int &Upper, int Goal, int Best) +{ + int Temp = 2*Goal - Best; + if (Best > Goal) + { + Lower = max(0, Temp); + Upper = Best; + } + else + { + Upper = min(255, Temp); + Lower = Best; + } +} + +// Convert from [-8192,8192] to [0, 255] +// +inline int CALC_RANGE(int x, int lower, int upper) +{ + return NUM_RANGES*(x-lower)/((upper-lower+1)); +} + + +void inline UpdateRange(int &minValue, int &maxValue, int Goal, int Best) +{ + int Lower, Upper; + CalcBounds(Lower, Upper, Goal, Best); + if (Upper < maxValue) maxValue = Upper; + if (minValue < Lower) minValue = Lower; +} + +void CGraph :: CheckNode(Vector vecOrigin, int iNode) +{ + // Have we already seen this point before?. + // + if (m_di[iNode].m_CheckedEvent == m_CheckedCounter) return; + m_di[iNode].m_CheckedEvent = m_CheckedCounter; + + float flDist = ( vecOrigin - m_pNodes[ iNode ].m_vecOriginPeek ).Length(); + + if ( flDist < m_flShortest ) + { + TraceResult tr; + + // make sure that vecOrigin can trace to this node! + UTIL_TraceLine ( vecOrigin, m_pNodes[ iNode ].m_vecOriginPeek, ignore_monsters, 0, &tr ); + + if ( tr.flFraction == 1.0 ) + { + m_iNearest = iNode; + m_flShortest = flDist; + + UpdateRange(m_minX, m_maxX, CALC_RANGE(vecOrigin.x, m_RegionMin[0], m_RegionMax[0]), m_pNodes[iNode].m_Region[0]); + UpdateRange(m_minY, m_maxY, CALC_RANGE(vecOrigin.y, m_RegionMin[1], m_RegionMax[1]), m_pNodes[iNode].m_Region[1]); + UpdateRange(m_minZ, m_maxZ, CALC_RANGE(vecOrigin.z, m_RegionMin[2], m_RegionMax[2]), m_pNodes[iNode].m_Region[2]); + + // From maxCircle, calculate maximum bounds box. All points must be + // simultaneously inside all bounds of the box. + // + m_minBoxX = CALC_RANGE(vecOrigin.x - flDist, m_RegionMin[0], m_RegionMax[0]); + m_maxBoxX = CALC_RANGE(vecOrigin.x + flDist, m_RegionMin[0], m_RegionMax[0]); + m_minBoxY = CALC_RANGE(vecOrigin.y - flDist, m_RegionMin[1], m_RegionMax[1]); + m_maxBoxY = CALC_RANGE(vecOrigin.y + flDist, m_RegionMin[1], m_RegionMax[1]); + m_minBoxZ = CALC_RANGE(vecOrigin.z - flDist, m_RegionMin[2], m_RegionMax[2]); + m_maxBoxZ = CALC_RANGE(vecOrigin.z + flDist, m_RegionMin[2], m_RegionMax[2]); + } + } +} + +//========================================================= +// CGraph - FindNearestNode - returns the index of the node nearest +// the given vector -1 is failure (couldn't find a valid +// near node ) +//========================================================= +int CGraph :: FindNearestNode ( const Vector &vecOrigin, CMBaseEntity *pEntity ) +{ + return FindNearestNode( vecOrigin, NodeType( pEntity ) ); +} + +int CGraph :: FindNearestNode ( const Vector &vecOrigin, int afNodeTypes ) +{ + int i; + TraceResult tr; + + if ( !m_fGraphPresent || !m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return -1; + } + + // Check with the cache + // + ULONG iHash = (CACHE_SIZE-1) & Hash((void *)(const float *)vecOrigin, sizeof(vecOrigin)); + if (m_Cache[iHash].v == vecOrigin) + { + //ALERT(at_aiconsole, "Cache Hit.\n"); + return m_Cache[iHash].n; + } + else + { + //ALERT(at_aiconsole, "Cache Miss.\n"); + } + + // Mark all points as unchecked. + // + m_CheckedCounter++; + if (m_CheckedCounter == 0) + { + for (int i = 0; i < m_cNodes; i++) + { + m_di[i].m_CheckedEvent = 0; + } + m_CheckedCounter++; + } + + m_iNearest = -1; + m_flShortest = 999999.0; // just a big number. + + // If we can find a visible point, then let CalcBounds set the limits, but if + // we have no visible point at all to start with, then don't restrict the limits. + // +#if 1 + m_minX = 0; m_maxX = 255; + m_minY = 0; m_maxY = 255; + m_minZ = 0; m_maxZ = 255; + m_minBoxX = 0; m_maxBoxX = 255; + m_minBoxY = 0; m_maxBoxY = 255; + m_minBoxZ = 0; m_maxBoxZ = 255; +#else + m_minBoxX = CALC_RANGE(vecOrigin.x - flDist, m_RegionMin[0], m_RegionMax[0]); + m_maxBoxX = CALC_RANGE(vecOrigin.x + flDist, m_RegionMin[0], m_RegionMax[0]); + m_minBoxY = CALC_RANGE(vecOrigin.y - flDist, m_RegionMin[1], m_RegionMax[1]); + m_maxBoxY = CALC_RANGE(vecOrigin.y + flDist, m_RegionMin[1], m_RegionMax[1]); + m_minBoxZ = CALC_RANGE(vecOrigin.z - flDist, m_RegionMin[2], m_RegionMax[2]); + m_maxBoxZ = CALC_RANGE(vecOrigin.z + flDist, m_RegionMin[2], m_RegionMax[2]) + CalcBounds(m_minX, m_maxX, CALC_RANGE(vecOrigin.x, m_RegionMin[0], m_RegionMax[0]), m_pNodes[m_iNearest].m_Region[0]); + CalcBounds(m_minY, m_maxY, CALC_RANGE(vecOrigin.y, m_RegionMin[1], m_RegionMax[1]), m_pNodes[m_iNearest].m_Region[1]); + CalcBounds(m_minZ, m_maxZ, CALC_RANGE(vecOrigin.z, m_RegionMin[2], m_RegionMax[2]), m_pNodes[m_iNearest].m_Region[2]); +#endif + + int halfX = (m_minX+m_maxX)/2; + int halfY = (m_minY+m_maxY)/2; + int halfZ = (m_minZ+m_maxZ)/2; + + int j; + + for (i = halfX; i >= m_minX; i--) + { + for (j = m_RangeStart[0][i]; j <= m_RangeEnd[0][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[0]].m_afNodeInfo & afNodeTypes)) continue; + + int rgY = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[1]; + if (rgY > m_maxBoxY) break; + if (rgY < m_minBoxY) continue; + + int rgZ = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[2]; + if (rgZ < m_minBoxZ) continue; + if (rgZ > m_maxBoxZ) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[0]); + } + } + + for (i = max(m_minY,halfY+1); i <= m_maxY; i++) + { + for (j = m_RangeStart[1][i]; j <= m_RangeEnd[1][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[1]].m_afNodeInfo & afNodeTypes)) continue; + + int rgZ = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[2]; + if (rgZ > m_maxBoxZ) break; + if (rgZ < m_minBoxZ) continue; + int rgX = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[0]; + if (rgX < m_minBoxX) continue; + if (rgX > m_maxBoxX) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[1]); + } + } + + for (i = min(m_maxZ,halfZ); i >= m_minZ; i--) + { + for (j = m_RangeStart[2][i]; j <= m_RangeEnd[2][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[2]].m_afNodeInfo & afNodeTypes)) continue; + + int rgX = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[0]; + if (rgX > m_maxBoxX) break; + if (rgX < m_minBoxX) continue; + int rgY = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[1]; + if (rgY < m_minBoxY) continue; + if (rgY > m_maxBoxY) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[2]); + } + } + + for (i = max(m_minX,halfX+1); i <= m_maxX; i++) + { + for (j = m_RangeStart[0][i]; j <= m_RangeEnd[0][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[0]].m_afNodeInfo & afNodeTypes)) continue; + + int rgY = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[1]; + if (rgY > m_maxBoxY) break; + if (rgY < m_minBoxY) continue; + + int rgZ = m_pNodes[m_di[j].m_SortedBy[0]].m_Region[2]; + if (rgZ < m_minBoxZ) continue; + if (rgZ > m_maxBoxZ) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[0]); + } + } + + for (i = min(m_maxY,halfY); i >= m_minY; i--) + { + for (j = m_RangeStart[1][i]; j <= m_RangeEnd[1][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[1]].m_afNodeInfo & afNodeTypes)) continue; + + int rgZ = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[2]; + if (rgZ > m_maxBoxZ) break; + if (rgZ < m_minBoxZ) continue; + int rgX = m_pNodes[m_di[j].m_SortedBy[1]].m_Region[0]; + if (rgX < m_minBoxX) continue; + if (rgX > m_maxBoxX) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[1]); + } + } + + for (i = max(m_minZ,halfZ+1); i <= m_maxZ; i++) + { + for (j = m_RangeStart[2][i]; j <= m_RangeEnd[2][i]; j++) + { + if (!(m_pNodes[m_di[j].m_SortedBy[2]].m_afNodeInfo & afNodeTypes)) continue; + + int rgX = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[0]; + if (rgX > m_maxBoxX) break; + if (rgX < m_minBoxX) continue; + int rgY = m_pNodes[m_di[j].m_SortedBy[2]].m_Region[1]; + if (rgY < m_minBoxY) continue; + if (rgY > m_maxBoxY) continue; + CheckNode(vecOrigin, m_di[j].m_SortedBy[2]); + } + } + +#if 0 + // Verify our answers. + // + int iNearestCheck = -1; + m_flShortest = 8192;// find nodes within this radius + + for ( i = 0 ; i < m_cNodes ; i++ ) + { + float flDist = ( vecOrigin - m_pNodes[ i ].m_vecOriginPeek ).Length(); + + if ( flDist < m_flShortest ) + { + // make sure that vecOrigin can trace to this node! + UTIL_TraceLine ( vecOrigin, m_pNodes[ i ].m_vecOriginPeek, ignore_monsters, 0, &tr ); + + if ( tr.flFraction == 1.0 ) + { + iNearestCheck = i; + m_flShortest = flDist; + } + } + } + + if (iNearestCheck != m_iNearest) + { + ALERT( at_aiconsole, "NOT closest %d(%f,%f,%f) %d(%f,%f,%f).\n", + iNearestCheck, + m_pNodes[iNearestCheck].m_vecOriginPeek.x, + m_pNodes[iNearestCheck].m_vecOriginPeek.y, + m_pNodes[iNearestCheck].m_vecOriginPeek.z, + m_iNearest, + (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.x), + (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.y), + (m_iNearest == -1?0.0:m_pNodes[m_iNearest].m_vecOriginPeek.z)); + } + if (m_iNearest == -1) + { + ALERT(at_aiconsole, "All that work for nothing.\n"); + } +#endif + m_Cache[iHash].v = vecOrigin; + m_Cache[iHash].n = m_iNearest; + return m_iNearest; +} + +//========================================================= +// CGraph - ShowNodeConnections - draws a line from the given node +// to all connected nodes +//========================================================= +void CGraph :: ShowNodeConnections ( int iNode ) +{ + Vector vecSpot; + CNode *pNode; + CNode *pLinkNode; + int i; + + if ( !m_fGraphPresent || !m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available or built + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return; + } + + if ( iNode < 0 ) + { + ALERT( at_aiconsole, "Can't show connections for node %d\n", iNode ); + return; + } + + pNode = &m_pNodes[ iNode ]; + + UTIL_ParticleEffect( pNode->m_vecOrigin, g_vecZero, 255, 20 );// show node position + + if ( pNode->m_cNumLinks <= 0 ) + {// no connections! + ALERT ( at_aiconsole, "**No Connections!\n" ); + } + + for ( i = 0 ; i < pNode->m_cNumLinks ; i++ ) + { + + pLinkNode = &Node( NodeLink( iNode, i).m_iDestNode ); + vecSpot = pLinkNode->m_vecOrigin; + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + + WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.x ); + WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.y ); + WRITE_COORD( m_pNodes[ iNode ].m_vecOrigin.z + NODE_HEIGHT ); + + WRITE_COORD( vecSpot.x ); + WRITE_COORD( vecSpot.y ); + WRITE_COORD( vecSpot.z + NODE_HEIGHT ); + MESSAGE_END(); + + } +} + +//========================================================= +// CGraph - LinkVisibleNodes - the first, most basic +// function of node graph creation, this connects every +// node to every other node that it can see. Expects a +// pointer to an empty connection pool and a file pointer +// to write progress to. Returns the total number of initial +// links. +// +// If there's a problem with this process, the index +// of the offending node will be written to piBadNode +//========================================================= +int CGraph :: LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode ) +{ + int i,j,z; + edict_t *pTraceEnt; + int cTotalLinks, cLinksThisNode, cMaxInitialLinks; + TraceResult tr; + + // !!!BUGBUG - this function returns 0 if there is a problem in the middle of connecting the graph + // it also returns 0 if none of the nodes in a level can see each other. piBadNode is ALWAYS read + // by BuildNodeGraph() if this function returns a 0, so make sure that it doesn't get some random + // number back. + *piBadNode = 0; + + + if ( m_cNodes <= 0 ) + { + ALERT ( at_aiconsole, "No Nodes!\n" ); + return FALSE; + } + + // if the file pointer is bad, don't blow up, just don't write the + // file. + if ( !file ) + { + ALERT ( at_aiconsole, "**LinkVisibleNodes:\ncan't write to file." ); + } + else + { + fprintf ( file, "----------------------------------------------------------------------------\n" ); + fprintf ( file, "LinkVisibleNodes - Initial Connections\n" ); + fprintf ( file, "----------------------------------------------------------------------------\n" ); + } + + cTotalLinks = 0;// start with no connections + + // to keep track of the maximum number of initial links any node had so far. + // this lets us keep an eye on MAX_NODE_INITIAL_LINKS to ensure that we are + // being generous enough. + cMaxInitialLinks = 0; + + for ( i = 0 ; i < m_cNodes ; i++ ) + { + cLinksThisNode = 0;// reset this count for each node. + + if ( file ) + { + fprintf ( file, "Node #%4d:\n\n", i ); + } + + for ( z = 0 ; z < MAX_NODE_INITIAL_LINKS ; z++ ) + {// clear out the important fields in the link pool for this node + pLinkPool [ cTotalLinks + z ].m_iSrcNode = i;// so each link knows which node it originates from + pLinkPool [ cTotalLinks + z ].m_iDestNode = 0; + pLinkPool [ cTotalLinks + z ].m_pLinkEnt = NULL; + } + + m_pNodes [ i ].m_iFirstLink = cTotalLinks; + + // now build a list of every other node that this node can see + for ( j = 0 ; j < m_cNodes ; j++ ) + { + if ( j == i ) + {// don't connect to self! + continue; + } + +#if 0 + + if ( (m_pNodes[ i ].m_afNodeInfo & bits_NODE_WATER) != (m_pNodes[ j ].m_afNodeInfo & bits_NODE_WATER) ) + { + // don't connect water nodes to air nodes or land nodes. It just wouldn't be prudent at this juncture. + continue; + } +#else + if ( (m_pNodes[ i ].m_afNodeInfo & bits_NODE_GROUP_REALM) != (m_pNodes[ j ].m_afNodeInfo & bits_NODE_GROUP_REALM) ) + { + // don't connect air nodes to water nodes to land nodes. It just wouldn't be prudent at this juncture. + continue; + } +#endif + + tr.pHit = NULL;// clear every time so we don't get stuck with last trace's hit ent + pTraceEnt = 0; + + UTIL_TraceLine ( m_pNodes[ i ].m_vecOrigin, + m_pNodes[ j ].m_vecOrigin, + ignore_monsters, + NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about + &tr ); + + + if ( tr.fStartSolid ) + continue; + + if ( tr.flFraction != 1.0 ) + {// trace hit a brush ent, trace backwards to make sure that this ent is the only thing in the way. + + pTraceEnt = tr.pHit;// store the ent that the trace hit, for comparison + + UTIL_TraceLine ( m_pNodes[ j ].m_vecOrigin, + m_pNodes[ i ].m_vecOrigin, + ignore_monsters, + NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about + &tr ); + + +// there is a solid_bsp ent in the way of these two nodes, so we must record several things about in order to keep +// track of it in the pathfinding code, as well as through save and restore of the node graph. ANY data that is manipulated +// as part of the process of adding a LINKENT to a connection here must also be done in CGraph::SetGraphPointers, where reloaded +// graphs are prepared for use. + if ( tr.pHit == pTraceEnt && !FClassnameIs( tr.pHit, "worldspawn" ) ) + { + // get a pointer + pLinkPool [ cTotalLinks ].m_pLinkEnt = VARS( tr.pHit ); + + // record the modelname, so that we can save/load node trees + memcpy( pLinkPool [ cTotalLinks ].m_szLinkEntModelname, STRING( VARS(tr.pHit)->model ), 4 ); + + // set the flag for this ent that indicates that it is attached to the world graph + // if this ent is removed from the world, it must also be removed from the connections + // that it formerly blocked. + if ( !FBitSet( VARS( tr.pHit )->flags, FL_GRAPHED ) ) + { + VARS( tr.pHit )->flags += FL_GRAPHED; + } + } + else + {// even if the ent wasn't there, these nodes couldn't be connected. Skip. + continue; + } + } + + if ( file ) + { + fprintf ( file, "%4d", j ); + + if ( !FNullEnt( pLinkPool[ cTotalLinks ].m_pLinkEnt ) ) + {// record info about the ent in the way, if any. + fprintf ( file, " Entity on connection: %s, name: %s Model: %s", STRING( VARS( pTraceEnt )->classname ), STRING ( VARS( pTraceEnt )->targetname ), STRING ( VARS(tr.pHit)->model ) ); + } + + fprintf ( file, "\n", j ); + } + + pLinkPool [ cTotalLinks ].m_iDestNode = j; + cLinksThisNode++; + cTotalLinks++; + + // If we hit this, either a level designer is placing too many nodes in the same area, or + // we need to allow for a larger initial link pool. + if ( cLinksThisNode == MAX_NODE_INITIAL_LINKS ) + { + ALERT ( at_aiconsole, "**LinkVisibleNodes:\nNode %d has NodeLinks > MAX_NODE_INITIAL_LINKS", i ); + fprintf ( file, "** NODE %d HAS NodeLinks > MAX_NODE_INITIAL_LINKS **\n", i ); + *piBadNode = i; + return FALSE; + } + else if ( cTotalLinks > MAX_NODE_INITIAL_LINKS * m_cNodes ) + {// this is paranoia + ALERT ( at_aiconsole, "**LinkVisibleNodes:\nTotalLinks > MAX_NODE_INITIAL_LINKS * NUMNODES" ); + *piBadNode = i; + return FALSE; + } + + if ( cLinksThisNode == 0 ) + { + fprintf ( file, "**NO INITIAL LINKS**\n" ); + } + + // record the connection info in the link pool + WorldGraph.m_pNodes [ i ].m_cNumLinks = cLinksThisNode; + + // keep track of the most initial links ANY node had, so we can figure out + // if we have a large enough default link pool + if ( cLinksThisNode > cMaxInitialLinks ) + { + cMaxInitialLinks = cLinksThisNode; + } + } + + + if ( file ) + { + fprintf ( file, "----------------------------------------------------------------------------\n" ); + } + } + + fprintf ( file, "\n%4d Total Initial Connections - %4d Maximum connections for a single node.\n", cTotalLinks, cMaxInitialLinks ); + fprintf ( file, "----------------------------------------------------------------------------\n\n\n" ); + + return cTotalLinks; +} + +//========================================================= +// CGraph - RejectInlineLinks - expects a pointer to a link +// pool, and a pointer to and already-open file ( if you +// want status reports written to disk ). RETURNS the number +// of connections that were rejected +//========================================================= +int CGraph :: RejectInlineLinks ( CLink *pLinkPool, FILE *file ) +{ + int i,j,k; + + int cRejectedLinks; + + BOOL fRestartLoop;// have to restart the J loop if we eliminate a link. + + CNode *pSrcNode; + CNode *pCheckNode;// the node we are testing for (one of pSrcNode's connections) + CNode *pTestNode;// the node we are checking against ( also one of pSrcNode's connections) + + float flDistToTestNode, flDistToCheckNode; + + Vector2D vec2DirToTestNode, vec2DirToCheckNode; + + if ( file ) + { + fprintf ( file, "----------------------------------------------------------------------------\n" ); + fprintf ( file, "InLine Rejection:\n" ); + fprintf ( file, "----------------------------------------------------------------------------\n" ); + } + + cRejectedLinks = 0; + + for ( i = 0 ; i < m_cNodes ; i++ ) + { + pSrcNode = &m_pNodes[ i ]; + + if ( file ) + { + fprintf ( file, "Node %3d:\n", i ); + } + + for ( j = 0 ; j < pSrcNode->m_cNumLinks ; j++ ) + { + pCheckNode = &m_pNodes[ pLinkPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode ]; + + vec2DirToCheckNode = ( pCheckNode->m_vecOrigin - pSrcNode->m_vecOrigin ).Make2D(); + flDistToCheckNode = vec2DirToCheckNode.Length(); + vec2DirToCheckNode = vec2DirToCheckNode.Normalize(); + + pLinkPool[ pSrcNode->m_iFirstLink + j ].m_flWeight = flDistToCheckNode; + + fRestartLoop = FALSE; + for ( k = 0 ; k < pSrcNode->m_cNumLinks && !fRestartLoop ; k++ ) + { + if ( k == j ) + {// don't check against same node + continue; + } + + pTestNode = &m_pNodes [ pLinkPool[ pSrcNode->m_iFirstLink + k ].m_iDestNode ]; + + vec2DirToTestNode = ( pTestNode->m_vecOrigin - pSrcNode->m_vecOrigin ).Make2D(); + + flDistToTestNode = vec2DirToTestNode.Length(); + vec2DirToTestNode = vec2DirToTestNode.Normalize(); + + if ( DotProduct ( vec2DirToCheckNode, vec2DirToTestNode ) >= 0.998 ) + { + // there's a chance that TestNode intersects the line to CheckNode. If so, we should disconnect the link to CheckNode. + if ( flDistToTestNode < flDistToCheckNode ) + { + if ( file ) + { + fprintf ( file, "REJECTED NODE %3d through Node %3d, Dot = %8f\n", pLinkPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode, pLinkPool[ pSrcNode->m_iFirstLink + k ].m_iDestNode, DotProduct ( vec2DirToCheckNode, vec2DirToTestNode ) ); + } + + pLinkPool[ pSrcNode->m_iFirstLink + j ] = pLinkPool[ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; + pSrcNode->m_cNumLinks--; + j--; + + cRejectedLinks++;// keeping track of how many links are cut, so that we can return that value. + + fRestartLoop = TRUE; + } + } + } + } + + if ( file ) + { + fprintf ( file, "----------------------------------------------------------------------------\n\n" ); + } + } + + return cRejectedLinks; +} + +//========================================================= +// TestHull is a modelless clip hull that verifies reachable +// nodes by walking from every node to each of it's connections +//========================================================= +class CTestHull : public CMBaseMonster +{ + +public: + void Spawn( entvars_t *pevMasterNode ); + virtual int ObjectCaps( void ) { return CMBaseMonster :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + void EXPORT CallBuildNodeGraph ( void ); + void BuildNodeGraph ( void ); + void EXPORT ShowBadNode ( void ); + void EXPORT DropDelay ( void ); + void EXPORT PathFind ( void ); + + Vector vecBadNodeOrigin; +}; + +//========================================================= +// CTestHull::Spawn +//========================================================= +void CTestHull :: Spawn( entvars_t *pevMasterNode ) +{ + SET_MODEL(ENT(pev), "models/player.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + pev->effects = 0; + pev->health = 50; + pev->yaw_speed = 8; + + if ( WorldGraph.m_fGraphPresent ) + {// graph loaded from disk, so we don't need the test hull + SetThink ( &CTestHull::SUB_Remove ); + pev->nextthink = gpGlobals->time; + } + else + { + SetThink ( &CTestHull::DropDelay ); + pev->nextthink = gpGlobals->time + 1; + } + + // Make this invisible + // UNDONE: Shouldn't we just use EF_NODRAW? This doesn't need to go to the client. + pev->rendermode = kRenderTransTexture; + pev->renderamt = 0; + + pev->classname = MAKE_STRING("testhull"); +} + +//========================================================= +// TestHull::DropDelay - spawns TestHull on top of +// the 0th node and drops it to the ground. +//========================================================= +void CTestHull::DropDelay ( void ) +{ + //UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding..." ); + + UTIL_SetOrigin ( VARS(pev), WorldGraph.m_pNodes[ 0 ].m_vecOrigin ); + + SetThink ( &CTestHull::CallBuildNodeGraph ); + + pev->nextthink = gpGlobals->time + 2; // think called earlier, so add extra second. -Giegue +} + +//========================================================= +// nodes start out as ents in the world. As they are spawned, +// the node info is recorded then the ents are discarded. +//========================================================= +void CNodeEnt :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "hinttype")) + { + m_sHintType = (short)atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + + if (FStrEq(pkvd->szKeyName, "activity")) + { + m_sHintActivity = (short)atoi( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else + CMBaseEntity::KeyValue( pkvd ); +} + +//========================================================= +//========================================================= +void CNodeEnt :: Spawn( void ) +{ + pev->movetype = MOVETYPE_NONE; + pev->solid = SOLID_NOT;// always solid_not + + if ( WorldGraph.m_fGraphPresent ) + {// graph loaded from disk, so discard all these node ents as soon as they spawn + REMOVE_ENTITY( edict() ); + return; + } + + if ( WorldGraph.m_cNodes == 0 ) + { + // this is the first node to spawn, spawn the test hull entity that builds and walks the node tree + CTestHull *pHull = CreateClassPtr((CTestHull *)NULL); + pHull->Spawn( pev ); + } + + if ( WorldGraph.m_cNodes >= MAX_NODES ) + { + ALERT ( at_aiconsole, "cNodes > MAX_NODES\n" ); + return; + } + + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_vecOriginPeek = + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_vecOrigin = pev->origin; + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_flHintYaw = pev->angles.y; + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_sHintType = m_sHintType; + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_sHintActivity = m_sHintActivity; + + if (FClassnameIs( pev, "info_node_air" )) + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_afNodeInfo = bits_NODE_AIR; + else + WorldGraph.m_pNodes[ WorldGraph.m_cNodes ].m_afNodeInfo = 0; + + WorldGraph.m_cNodes++; + + REMOVE_ENTITY( edict() ); +} + +//========================================================= +// CTestHull - ShowBadNode - makes a bad node fizzle. When +// there's a problem with node graph generation, the test +// hull will be placed up the bad node's location and will generate +// particles +//========================================================= +void CTestHull :: ShowBadNode( void ) +{ + pev->movetype = MOVETYPE_FLY; + pev->angles.y = pev->angles.y + 4; + + UTIL_MakeVectors ( pev->angles ); + + UTIL_ParticleEffect ( pev->origin, g_vecZero, 255, 25 ); + UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * 64, g_vecZero, 255, 25 ); + UTIL_ParticleEffect ( pev->origin - gpGlobals->v_forward * 64, g_vecZero, 255, 25 ); + UTIL_ParticleEffect ( pev->origin + gpGlobals->v_right * 64, g_vecZero, 255, 25 ); + UTIL_ParticleEffect ( pev->origin - gpGlobals->v_right * 64, g_vecZero, 255, 25 ); + + pev->nextthink = gpGlobals->time + 0.1; +} + +extern BOOL gTouchDisabled; +void CTestHull::CallBuildNodeGraph( void ) +{ + // TOUCH HACK -- Don't allow this entity to call anyone's "touch" function + gTouchDisabled = TRUE; + BuildNodeGraph(); + gTouchDisabled = FALSE; + // Undo TOUCH HACK +} + +//========================================================= +// BuildNodeGraph - think function called by the empty walk +// hull that is spawned by the first node to spawn. This +// function links all nodes that can see each other, then +// eliminates all inline links, then uses a monster-sized +// hull that walks between each node and each of its links +// to ensure that a monster can actually fit through the space +//========================================================= +void CTestHull :: BuildNodeGraph( void ) +{ + TraceResult tr; + FILE *file; + + char szNrpFilename [MAX_PATH];// text node report filename + + CLink *pTempPool; // temporary link pool + + CNode *pSrcNode;// node we're currently working with + CNode *pDestNode;// the other node in comparison operations + + BOOL fSkipRemainingHulls;//if smallest hull can't fit, don't check any others + BOOL fPairsValid;// are all links in the graph evenly paired? + + int i, j, hull; + + int iBadNode;// this is the node that caused graph generation to fail + + int cMaxInitialLinks = 0; + int cMaxValidLinks = 0; + + int iPoolIndex = 0; + int cPoolLinks;// number of links in the pool. + + Vector vecDirToCheckNode; + Vector vecDirToTestNode; + Vector vecStepCheckDir; + Vector vecTraceSpot; + Vector vecSpot; + + Vector2D vec2DirToCheckNode; + Vector2D vec2DirToTestNode; + Vector2D vec2StepCheckDir; + Vector2D vec2TraceSpot; + Vector2D vec2Spot; + + float flYaw;// use this stuff to walk the hull between nodes + float flDist; + int step; + + SetThink ( &CTestHull::SUB_Remove );// no matter what happens, the hull gets rid of itself. + pev->nextthink = gpGlobals->time; + +// malloc a swollen temporary connection pool that we trim down after we know exactly how many connections there are. + pTempPool = (CLink *)calloc ( sizeof ( CLink ) , ( WorldGraph.m_cNodes * MAX_NODE_INITIAL_LINKS ) ); + if ( !pTempPool ) + { + ALERT ( at_aiconsole, "**Could not malloc TempPool!\n" ); + return; + } + + + // make sure directories have been made + GET_GAME_DIR( szNrpFilename ); + strcat( szNrpFilename, "/maps" ); + CreateDirectory( szNrpFilename, NULL ); + strcat( szNrpFilename, "/graphs" ); + CreateDirectory( szNrpFilename, NULL ); + + strcat( szNrpFilename, "/" ); + strcat( szNrpFilename, STRING( gpGlobals->mapname ) ); + strcat( szNrpFilename, ".nrp" ); + + file = fopen ( szNrpFilename, "w+" ); + + if ( !file ) + {// file error + ALERT ( at_aiconsole, "Couldn't create %s!\n", szNrpFilename ); + + if ( pTempPool ) + { + free ( pTempPool ); + } + + return; + } + + fprintf( file, "Node Graph Report for map: %s.bsp\n", STRING(gpGlobals->mapname) ); + fprintf ( file, "%d Total Nodes\n\n", WorldGraph.m_cNodes ); + + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + {// print all node numbers and their locations to the file. + WorldGraph.m_pNodes[ i ].m_cNumLinks = 0; + WorldGraph.m_pNodes[ i ].m_iFirstLink = 0; + memset(WorldGraph.m_pNodes[ i ].m_pNextBestNode, 0, sizeof(WorldGraph.m_pNodes[ i ].m_pNextBestNode)); + + fprintf ( file, "Node# %4d\n", i ); + fprintf ( file, "Location %4d,%4d,%4d\n",(int)WorldGraph.m_pNodes[ i ].m_vecOrigin.x, (int)WorldGraph.m_pNodes[ i ].m_vecOrigin.y, (int)WorldGraph.m_pNodes[ i ].m_vecOrigin.z ); + fprintf ( file, "HintType: %4d\n", WorldGraph.m_pNodes[ i ].m_sHintType ); + fprintf ( file, "HintActivity: %4d\n", WorldGraph.m_pNodes[ i ].m_sHintActivity ); + fprintf ( file, "HintYaw: %4f\n", WorldGraph.m_pNodes[ i ].m_flHintYaw ); + fprintf ( file, "-------------------------------------------------------------------------------\n" ); + } + fprintf ( file, "\n\n" ); + + + // Automatically recognize WATER nodes and drop the LAND nodes to the floor. + // + for ( i = 0; i < WorldGraph.m_cNodes; i++) + { + if (WorldGraph.m_pNodes[ i ].m_afNodeInfo & bits_NODE_AIR) + { + // do nothing + } + else if (UTIL_PointContents(WorldGraph.m_pNodes[ i ].m_vecOrigin) == CONTENTS_WATER) + { + WorldGraph.m_pNodes[ i ].m_afNodeInfo |= bits_NODE_WATER; + } + else + { + WorldGraph.m_pNodes[ i ].m_afNodeInfo |= bits_NODE_LAND; + + // trace to the ground, then pop up 8 units and place node there to make it + // easier for them to connect (think stairs, chairs, and bumps in the floor). + // After the routing is done, push them back down. + // + TraceResult tr; + + UTIL_TraceLine ( WorldGraph.m_pNodes[i].m_vecOrigin, + WorldGraph.m_pNodes[i].m_vecOrigin - Vector ( 0, 0, 384 ), + ignore_monsters, + NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about + &tr ); + + // This trace is ONLY used if we hit an entity flagged with FL_WORLDBRUSH + TraceResult trEnt; + UTIL_TraceLine ( WorldGraph.m_pNodes[i].m_vecOrigin, + WorldGraph.m_pNodes[i].m_vecOrigin - Vector ( 0, 0, 384 ), + dont_ignore_monsters, + NULL,//!!!HACKHACK no real ent to supply here, using a global we don't care about + &trEnt ); + + + // Did we hit something closer than the floor? + if ( trEnt.flFraction < tr.flFraction ) + { + // If it was a world brush entity, copy the node location + if ( trEnt.pHit && (trEnt.pHit->v.flags & FL_WORLDBRUSH) ) + tr.vecEndPos = trEnt.vecEndPos; + } + + WorldGraph.m_pNodes[i].m_vecOriginPeek.z = + WorldGraph.m_pNodes[i].m_vecOrigin.z = tr.vecEndPos.z + NODE_HEIGHT; + } + } + + cPoolLinks = WorldGraph.LinkVisibleNodes( pTempPool, file, &iBadNode ); + + if ( !cPoolLinks ) + { + ALERT ( at_aiconsole, "**ConnectVisibleNodes FAILED!\n" ); + + SetThink ( &CTestHull::ShowBadNode );// send the hull off to show the offending node. + //pev->solid = SOLID_NOT; + pev->origin = WorldGraph.m_pNodes[ iBadNode ].m_vecOrigin; + + if ( pTempPool ) + { + free ( pTempPool ); + } + + if ( file ) + {// close the file + fclose ( file ); + } + + return; + } + +// send the walkhull to all of this node's connections now. We'll do this here since +// so much of it relies on being able to control the test hull. + fprintf ( file, "----------------------------------------------------------------------------\n" ); + fprintf ( file, "Walk Rejection:\n"); + + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + pSrcNode = &WorldGraph.m_pNodes[ i ]; + + fprintf ( file, "-------------------------------------------------------------------------------\n"); + fprintf ( file, "Node %4d:\n\n", i ); + + for ( j = 0 ; j < pSrcNode->m_cNumLinks ; j++ ) + { + // assume that all hulls can walk this link, then eliminate the ones that can't. + pTempPool [ pSrcNode->m_iFirstLink + j ].m_afLinkInfo = bits_LINK_SMALL_HULL | bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL | bits_LINK_FLY_HULL; + + + // do a check for each hull size. + + // if we can't fit a tiny hull through a connection, no other hulls with fit either, so we + // should just fall out of the loop. Do so by setting the SkipRemainingHulls flag. + fSkipRemainingHulls = FALSE; + for ( hull = 0 ; hull < MAX_NODE_HULLS; hull++ ) + { + if (fSkipRemainingHulls && (hull == NODE_HUMAN_HULL || hull == NODE_LARGE_HULL)) // skip the remaining walk hulls + continue; + + switch ( hull ) + { + case NODE_SMALL_HULL: + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 24)); + break; + case NODE_HUMAN_HULL: + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); + break; + case NODE_LARGE_HULL: + UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); + break; + case NODE_FLY_HULL: + UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 64)); + // UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); + break; + } + + UTIL_SetOrigin ( pev, pSrcNode->m_vecOrigin );// place the hull on the node + + if ( !FBitSet ( pev->flags, FL_ONGROUND ) ) + { + ALERT ( at_aiconsole, "OFFGROUND!\n" ); + } + + // now build a yaw that points to the dest node, and get the distance. + if ( j < 0 ) + { + ALERT ( at_aiconsole, "**** j = %d ****\n", j ); + if ( pTempPool ) + { + free ( pTempPool ); + } + + if ( file ) + {// close the file + fclose ( file ); + } + return; + } + + pDestNode = &WorldGraph.m_pNodes [ pTempPool[ pSrcNode->m_iFirstLink + j ].m_iDestNode ]; + + vecSpot = pDestNode->m_vecOrigin; + //vecSpot.z = pev->origin.z; + + if (hull < NODE_FLY_HULL) + { + int SaveFlags = pev->flags; + int MoveMode = WALKMOVE_WORLDONLY; + if (pSrcNode->m_afNodeInfo & bits_NODE_WATER) + { + pev->flags |= FL_SWIM; + MoveMode = WALKMOVE_NORMAL; + } + + flYaw = UTIL_VecToYaw ( pDestNode->m_vecOrigin - pev->origin ); + + flDist = ( vecSpot - pev->origin ).Length2D(); + + int fWalkFailed = FALSE; + + // in this loop we take tiny steps from the current node to the nodes that it links to, one at a time. + // pev->angles.y = flYaw; + for ( step = 0 ; step < flDist && !fWalkFailed ; step += HULL_STEP_SIZE ) + { + float stepSize = HULL_STEP_SIZE; + + if ( (step + stepSize) >= (flDist-1) ) + stepSize = (flDist - step) - 1; + + if ( !WALK_MOVE( ENT(pev), flYaw, stepSize, MoveMode ) ) + {// can't take the next step + + fWalkFailed = TRUE; + break; + } + } + + if (!fWalkFailed && (pev->origin - vecSpot).Length() > 64) + { + // ALERT( at_console, "bogus walk\n"); + // we thought we + fWalkFailed = TRUE; + } + + if (fWalkFailed) + { + + //pTempPool[ pSrcNode->m_iFirstLink + j ] = pTempPool [ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; + + // now me must eliminate the hull that couldn't walk this connection + switch ( hull ) + { + case NODE_SMALL_HULL: // if this hull can't fit, nothing can, so drop the connection + fprintf ( file, "NODE_SMALL_HULL step %f\n", step ); + pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_SMALL_HULL | bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL); + fSkipRemainingHulls = TRUE;// don't bother checking larger hulls + break; + case NODE_HUMAN_HULL: + fprintf ( file, "NODE_HUMAN_HULL step %f\n", step ); + pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~(bits_LINK_HUMAN_HULL | bits_LINK_LARGE_HULL); + fSkipRemainingHulls = TRUE;// don't bother checking larger hulls + break; + case NODE_LARGE_HULL: + fprintf ( file, "NODE_LARGE_HULL step %f\n", step ); + pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_LARGE_HULL; + break; + } + } + pev->flags = SaveFlags; + } + else + { + TraceResult tr; + + UTIL_TraceHull( pSrcNode->m_vecOrigin + Vector( 0, 0, 32 ), pDestNode->m_vecOriginPeek + Vector( 0, 0, 32 ), ignore_monsters, large_hull, ENT( pev ), &tr ); + if (tr.fStartSolid || tr.flFraction < 1.0) + { + pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo &= ~bits_LINK_FLY_HULL; + } + } + } + + if (pTempPool[ pSrcNode->m_iFirstLink + j ].m_afLinkInfo == 0) + { + fprintf ( file, "Rejected Node %3d - Unreachable by ", pTempPool [ pSrcNode->m_iFirstLink + j ].m_iDestNode ); + pTempPool[ pSrcNode->m_iFirstLink + j ] = pTempPool [ pSrcNode->m_iFirstLink + ( pSrcNode->m_cNumLinks - 1 ) ]; + fprintf ( file, "Any Hull\n" ); + + pSrcNode->m_cNumLinks--; + cPoolLinks--;// we just removed a link, so decrement the total number of links in the pool. + j--; + } + + } + } + fprintf ( file, "-------------------------------------------------------------------------------\n\n\n"); + + cPoolLinks -= WorldGraph.RejectInlineLinks ( pTempPool, file ); + +// now malloc a pool just large enough to hold the links that are actually used + WorldGraph.m_pLinkPool = (CLink *) calloc ( sizeof ( CLink ), cPoolLinks ); + + if ( !WorldGraph.m_pLinkPool ) + {// couldn't make the link pool! + ALERT ( at_aiconsole, "Couldn't malloc LinkPool!\n" ); + if ( pTempPool ) + { + free ( pTempPool ); + } + if ( file ) + {// close the file + fclose ( file ); + } + + return; + } + WorldGraph.m_cLinks = cPoolLinks; + +//copy only the used portions of the TempPool into the graph's link pool + int iFinalPoolIndex = 0; + int iOldFirstLink; + + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + iOldFirstLink = WorldGraph.m_pNodes[ i ].m_iFirstLink;// store this, because we have to re-assign it before entering the copy loop + + WorldGraph.m_pNodes[ i ].m_iFirstLink = iFinalPoolIndex; + + for ( j = 0 ; j < WorldGraph.m_pNodes[ i ].m_cNumLinks ; j++ ) + { + WorldGraph.m_pLinkPool[ iFinalPoolIndex++ ] = pTempPool[ iOldFirstLink + j ]; + } + } + + + // Node sorting numbers linked nodes close to each other + // + WorldGraph.SortNodes(); + + // This is used for HashSearch + // + WorldGraph.BuildLinkLookups(); + + fPairsValid = TRUE; // assume that the connection pairs are all valid to start + + fprintf ( file, "\n\n-------------------------------------------------------------------------------\n"); + fprintf ( file, "Link Pairings:\n"); + +// link integrity check. The idea here is that if Node A links to Node B, node B should +// link to node A. If not, we have a situation that prevents us from using a basic +// optimization in the FindNearestLink function. + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + for ( j = 0 ; j < WorldGraph.m_pNodes[ i ].m_cNumLinks ; j++ ) + { + int iLink; + WorldGraph.HashSearch(WorldGraph.INodeLink(i,j), i, iLink); + if (iLink < 0) + { + fPairsValid = FALSE;// unmatched link pair. + fprintf ( file, "WARNING: Node %3d does not connect back to Node %3d\n", WorldGraph.INodeLink(i, j), i); + } + } + } + + // !!!LATER - if all connections are properly paired, when can enable an optimization in the pathfinding code + // (in the find nearest line function) + if ( fPairsValid ) + { + fprintf ( file, "\nAll Connections are Paired!\n"); + } + + fprintf ( file, "-------------------------------------------------------------------------------\n"); + fprintf ( file, "\n\n-------------------------------------------------------------------------------\n"); + fprintf ( file, "Total Number of Connections in Pool: %d\n", cPoolLinks ); + fprintf ( file, "-------------------------------------------------------------------------------\n"); + fprintf ( file, "Connection Pool: %d bytes\n", sizeof ( CLink ) * cPoolLinks ); + fprintf ( file, "-------------------------------------------------------------------------------\n"); + + + ALERT ( at_aiconsole, "%d Nodes, %d Connections\n", WorldGraph.m_cNodes, cPoolLinks ); + + // This is used for FindNearestNode + // + WorldGraph.BuildRegionTables(); + + + // Push all of the LAND nodes down to the ground now. Leave the water and air nodes alone. + // + for ( i = 0 ; i < WorldGraph.m_cNodes ; i++ ) + { + if ((WorldGraph.m_pNodes[ i ].m_afNodeInfo & bits_NODE_LAND)) + { + WorldGraph.m_pNodes[ i ].m_vecOrigin.z -= NODE_HEIGHT; + } + } + + + if ( pTempPool ) + {// free the temp pool + free ( pTempPool ); + } + + if ( file ) + { + fclose ( file ); + } + + // We now have some graphing capabilities. + // + WorldGraph.m_fGraphPresent = TRUE;//graph is in memory. + WorldGraph.m_fGraphPointersSet = TRUE;// since the graph was generated, the pointers are ready + WorldGraph.m_fRoutingComplete = FALSE; // Optimal routes aren't computed, yet. + + // Compute and compress the routing information. + // + WorldGraph.ComputeStaticRoutingTables(); + +// save the node graph for this level + WorldGraph.FSaveGraph( (char *)STRING( gpGlobals->mapname ) ); + ALERT( at_console, "Done.\n"); +} + + +//========================================================= +// returns a hardcoded path. +//========================================================= +void CTestHull :: PathFind ( void ) +{ + int iPath[ 50 ]; + int iPathSize; + int i; + CNode *pNode, *pNextNode; + + if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return; + } + + iPathSize = WorldGraph.FindShortestPath ( iPath, 0, 19, 0, 0 ); // UNDONE use hull constant + + if ( !iPathSize ) + { + ALERT ( at_aiconsole, "No Path!\n" ); + return; + } + + ALERT ( at_aiconsole, "%d\n", iPathSize ); + + pNode = &WorldGraph.m_pNodes[ iPath [ 0 ] ]; + + for ( i = 0 ; i < iPathSize - 1 ; i++ ) + { + + pNextNode = &WorldGraph.m_pNodes[ iPath [ i + 1 ] ]; + + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SHOWLINE); + + WRITE_COORD( pNode->m_vecOrigin.x ); + WRITE_COORD( pNode->m_vecOrigin.y ); + WRITE_COORD( pNode->m_vecOrigin.z + NODE_HEIGHT ); + + WRITE_COORD( pNextNode->m_vecOrigin.x); + WRITE_COORD( pNextNode->m_vecOrigin.y); + WRITE_COORD( pNextNode->m_vecOrigin.z + NODE_HEIGHT); + MESSAGE_END(); + + pNode = pNextNode; + } + +} + + +//========================================================= +// CStack Constructor +//========================================================= +CStack :: CStack( void ) +{ + m_level = 0; +} + +//========================================================= +// pushes a value onto the stack +//========================================================= +void CStack :: Push( int value ) +{ + if ( m_level >= MAX_STACK_NODES ) + { + printf("Error!\n"); + return; + } + m_stack[m_level] = value; + m_level++; +} + +//========================================================= +// pops a value off of the stack +//========================================================= +int CStack :: Pop( void ) +{ + if ( m_level <= 0 ) + return -1; + + m_level--; + return m_stack[ m_level ]; +} + +//========================================================= +// returns the value on the top of the stack +//========================================================= +int CStack :: Top ( void ) +{ + return m_stack[ m_level - 1 ]; +} + +//========================================================= +// copies every element on the stack into an array LIFO +//========================================================= +void CStack :: CopyToArray ( int *piArray ) +{ + int i; + + for ( i = 0 ; i < m_level ; i++ ) + { + piArray[ i ] = m_stack[ i ]; + } +} + +//========================================================= +// CQueue constructor +//========================================================= +CQueue :: CQueue( void ) +{ + m_cSize = 0; + m_head = 0; + m_tail = -1; +} + +//========================================================= +// inserts a value into the queue +//========================================================= +void CQueue :: Insert ( int iValue, float fPriority ) +{ + + if ( Full() ) + { + printf ( "Queue is full!\n" ); + return; + } + + m_tail++; + + if ( m_tail == MAX_STACK_NODES ) + {//wrap around + m_tail = 0; + } + + m_queue[ m_tail ].Id = iValue; + m_queue[ m_tail ].Priority = fPriority; + m_cSize++; +} + +//========================================================= +// removes a value from the queue (FIFO) +//========================================================= +int CQueue :: Remove ( float &fPriority ) +{ + if ( m_head == MAX_STACK_NODES ) + {// wrap + m_head = 0; + } + + m_cSize--; + fPriority = m_queue[ m_head ].Priority; + return m_queue[ m_head++ ].Id; +} + +//========================================================= +// CQueue constructor +//========================================================= +CQueuePriority :: CQueuePriority( void ) +{ + m_cSize = 0; +} + +//========================================================= +// inserts a value into the priority queue +//========================================================= +void CQueuePriority :: Insert( int iValue, float fPriority ) +{ + + if ( Full() ) + { + printf ( "Queue is full!\n" ); + return; + } + + m_heap[ m_cSize ].Priority = fPriority; + m_heap[ m_cSize ].Id = iValue; + m_cSize++; + Heap_SiftUp(); +} + +//========================================================= +// removes the smallest item from the priority queue +// +//========================================================= +int CQueuePriority :: Remove( float &fPriority ) +{ + int iReturn = m_heap[ 0 ].Id; + fPriority = m_heap[ 0 ].Priority; + + m_cSize--; + + m_heap[ 0 ] = m_heap[ m_cSize ]; + + Heap_SiftDown(0); + return iReturn; +} + +#define HEAP_LEFT_CHILD(x) (2*(x)+1) +#define HEAP_RIGHT_CHILD(x) (2*(x)+2) +#define HEAP_PARENT(x) (((x)-1)/2) + +void CQueuePriority::Heap_SiftDown(int iSubRoot) +{ + int parent = iSubRoot; + int child = HEAP_LEFT_CHILD(parent); + + struct tag_HEAP_NODE Ref = m_heap[ parent ]; + + while (child < m_cSize) + { + int rightchild = HEAP_RIGHT_CHILD(parent); + if (rightchild < m_cSize) + { + if ( m_heap[ rightchild ].Priority < m_heap[ child ].Priority ) + { + child = rightchild; + } + } + if ( Ref.Priority <= m_heap[ child ].Priority ) + break; + + m_heap[ parent ] = m_heap[ child ]; + parent = child; + child = HEAP_LEFT_CHILD(parent); + } + m_heap[ parent ] = Ref; +} + +void CQueuePriority::Heap_SiftUp(void) +{ + int child = m_cSize-1; + while (child) + { + int parent = HEAP_PARENT(child); + if ( m_heap[ parent ].Priority <= m_heap[ child ].Priority ) + break; + + struct tag_HEAP_NODE Tmp; + Tmp = m_heap[ child ]; + m_heap[ child ] = m_heap[ parent ]; + m_heap[ parent ] = Tmp; + + child = parent; + } +} + +//========================================================= +// CGraph - FLoadGraph - attempts to load a node graph from disk. +// if the current level is maps/snar.bsp, maps/graphs/snar.nod +// will be loaded. If file cannot be loaded, the node tree +// will be created and saved to disk. +//========================================================= +int CGraph :: FLoadGraph ( char *szMapName ) +{ + char szFilename[MAX_PATH]; + int iVersion; + int length; + byte *aMemFile; + byte *pMemFile; + + // make sure the directories have been made + char szDirName[MAX_PATH]; + GET_GAME_DIR( szDirName ); + strcat( szDirName, "/maps" ); + CreateDirectory( szDirName, NULL ); + strcat( szDirName, "/graphs" ); + CreateDirectory( szDirName, NULL ); + + strcpy ( szFilename, "maps/graphs/" ); + strcat ( szFilename, szMapName ); + strcat( szFilename, ".nod" ); + + pMemFile = aMemFile = LOAD_FILE_FOR_ME(szFilename, &length); + + if ( !aMemFile ) + { + return FALSE; + } + else + { + // Read the graph version number + // + length -= sizeof(int); + if (length < 0) goto ShortFile; + memcpy(&iVersion, pMemFile, sizeof(int)); + pMemFile += sizeof(int); + + if ( iVersion != GRAPH_VERSION ) + { + // This file was written by a different build of the dll! + // + ALERT ( at_aiconsole, "**ERROR** Graph version is %d, expected %d\n",iVersion, GRAPH_VERSION ); + goto ShortFile; + } + + // Read the graph class + // + length -= sizeof(CGraph); + if (length < 0) goto ShortFile; + memcpy(this, pMemFile, sizeof(CGraph)); + pMemFile += sizeof(CGraph); + + // Set the pointers to zero, just in case we run out of memory. + // + m_pNodes = NULL; + m_pLinkPool = NULL; + m_di = NULL; + m_pRouteInfo = NULL; + m_pHashLinks = NULL; + + + // Malloc for the nodes + // + m_pNodes = ( CNode * )calloc ( sizeof ( CNode ), m_cNodes ); + + if ( !m_pNodes ) + { + ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d nodes!\n", m_cNodes ); + goto NoMemory; + } + + // Read in all the nodes + // + length -= sizeof(CNode) * m_cNodes; + if (length < 0) goto ShortFile; + memcpy(m_pNodes, pMemFile, sizeof(CNode)*m_cNodes); + pMemFile += sizeof(CNode) * m_cNodes; + + + // Malloc for the link pool + // + m_pLinkPool = ( CLink * )calloc ( sizeof ( CLink ), m_cLinks ); + + if ( !m_pLinkPool ) + { + ALERT ( at_aiconsole, "**ERROR**\nCouldn't malloc %d link!\n", m_cLinks ); + goto NoMemory; + } + + // Read in all the links + // + length -= sizeof(CLink)*m_cLinks; + if (length < 0) goto ShortFile; + memcpy(m_pLinkPool, pMemFile, sizeof(CLink)*m_cLinks); + pMemFile += sizeof(CLink)*m_cLinks; + + // Malloc for the sorting info. + // + m_di = (DIST_INFO *)calloc( sizeof(DIST_INFO), m_cNodes ); + if ( !m_di ) + { + ALERT ( at_aiconsole, "***ERROR**\nCouldn't malloc %d entries sorting nodes!\n", m_cNodes ); + goto NoMemory; + } + + // Read it in. + // + length -= sizeof(DIST_INFO)*m_cNodes; + if (length < 0) goto ShortFile; + memcpy(m_di, pMemFile, sizeof(DIST_INFO)*m_cNodes); + pMemFile += sizeof(DIST_INFO)*m_cNodes; + + // Malloc for the routing info. + // + m_fRoutingComplete = FALSE; + m_pRouteInfo = (char *)calloc( sizeof(char), m_nRouteInfo ); + if ( !m_pRouteInfo ) + { + ALERT ( at_aiconsole, "***ERROR**\nCounldn't malloc %d route bytes!\n", m_nRouteInfo ); + goto NoMemory; + } + m_CheckedCounter = 0; + for (int i = 0; i < m_cNodes; i++) + { + m_di[i].m_CheckedEvent = 0; + } + + // Read in the route information. + // + length -= sizeof(char)*m_nRouteInfo; + if (length < 0) goto ShortFile; + memcpy(m_pRouteInfo, pMemFile, sizeof(char)*m_nRouteInfo); + pMemFile += sizeof(char)*m_nRouteInfo; + m_fRoutingComplete = TRUE;; + + // malloc for the hash links + // + m_pHashLinks = (short *)calloc(sizeof(short), m_nHashLinks); + if (!m_pHashLinks) + { + ALERT ( at_aiconsole, "***ERROR**\nCounldn't malloc %d hash link bytes!\n", m_nHashLinks ); + goto NoMemory; + } + + // Read in the hash link information + // + length -= sizeof(short)*m_nHashLinks; + if (length < 0) goto ShortFile; + memcpy(m_pHashLinks, pMemFile, sizeof(short)*m_nHashLinks); + pMemFile += sizeof(short)*m_nHashLinks; + + // Set the graph present flag, clear the pointers set flag + // + m_fGraphPresent = TRUE; + m_fGraphPointersSet = TRUE; // what if...? + + FREE_FILE(aMemFile); + + if (length != 0) + { + ALERT ( at_aiconsole, "***WARNING***:Node graph was longer than expected by %d bytes.!\n", length); + } + + return TRUE; + } + +ShortFile: +NoMemory: + FREE_FILE(aMemFile); + return FALSE; +} + +//========================================================= +// CGraph - FSaveGraph - It's not rocket science. +// this WILL overwrite existing files. +//========================================================= +int CGraph :: FSaveGraph ( char *szMapName ) +{ + + int iVersion = GRAPH_VERSION; + char szFilename[MAX_PATH]; + FILE *file; + + if ( !m_fGraphPresent || !m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available or built + ALERT ( at_aiconsole, "Graph not ready!\n" ); + return FALSE; + } + + // make sure directories have been made + GET_GAME_DIR( szFilename ); + strcat( szFilename, "/maps" ); + CreateDirectory( szFilename, NULL ); + strcat( szFilename, "/graphs" ); + CreateDirectory( szFilename, NULL ); + + strcat( szFilename, "/" ); + strcat( szFilename, szMapName ); + strcat( szFilename, ".nod" ); + + file = fopen ( szFilename, "wb" ); + + ALERT ( at_aiconsole, "Created: %s\n", szFilename ); + + if ( !file ) + {// couldn't create + ALERT ( at_aiconsole, "Couldn't Create: %s\n", szFilename ); + return FALSE; + } + else + { + // write the version + fwrite ( &iVersion, sizeof ( int ), 1, file ); + + // write the CGraph class + fwrite ( this, sizeof ( CGraph ), 1, file ); + + // write the nodes + fwrite ( m_pNodes, sizeof ( CNode ), m_cNodes, file ); + + // write the links + fwrite ( m_pLinkPool, sizeof ( CLink ), m_cLinks, file ); + + fwrite ( m_di, sizeof(DIST_INFO), m_cNodes, file ); + + // Write the route info. + // + if ( m_pRouteInfo && m_nRouteInfo ) + { + fwrite ( m_pRouteInfo, sizeof( char ), m_nRouteInfo, file ); + } + + if (m_pHashLinks && m_nHashLinks) + { + fwrite(m_pHashLinks, sizeof(short), m_nHashLinks, file); + } + fclose ( file ); + return TRUE; + } +} + +//========================================================= +// CGraph - FSetGraphPointers - Takes the modelnames of +// all of the brush ents that block connections in the node +// graph and resolves them into pointers to those entities. +// this is done after loading the graph from disk, whereupon +// the pointers are not valid. +//========================================================= +int CGraph :: FSetGraphPointers ( void ) +{ + int i; + edict_t *pentLinkEnt; + + for ( i = 0 ; i < m_cLinks ; i++ ) + {// go through all of the links + + if ( m_pLinkPool[ i ].m_pLinkEnt != NULL ) + { + char name[5]; + // when graphs are saved, any valid pointers are will be non-zero, signifying that we should + // reset those pointers upon reloading. Any pointers that were NULL when the graph was saved + // will be NULL when reloaded, and will ignored by this function. + + // m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes) + memcpy( name, m_pLinkPool[ i ].m_szLinkEntModelname, 4 ); + name[4] = 0; + pentLinkEnt = FIND_ENTITY_BY_STRING( NULL, "model", name ); + + if ( FNullEnt ( pentLinkEnt ) ) + { + // the ent isn't around anymore? Either there is a major problem, or it was removed from the world + // ( like a func_breakable that's been destroyed or something ). Make sure that LinkEnt is null. + ALERT ( at_aiconsole, "**Could not find model %s\n", name ); + m_pLinkPool[ i ].m_pLinkEnt = NULL; + } + else + { + m_pLinkPool[ i ].m_pLinkEnt = VARS( pentLinkEnt ); + + if ( !FBitSet( m_pLinkPool[ i ].m_pLinkEnt->flags, FL_GRAPHED ) ) + { + m_pLinkPool[ i ].m_pLinkEnt->flags += FL_GRAPHED; + } + } + } + } + + // the pointers are now set. + m_fGraphPointersSet = TRUE; + return TRUE; +} + +//========================================================= +// CGraph - CheckNODFile - this function checks the date of +// the BSP file that was just loaded and the date of the a +// ssociated .NOD file. If the NOD file is not present, or +// is older than the BSP file, we rebuild it. +// +// returns FALSE if the .NOD file doesn't qualify and needs +// to be rebuilt. +// +// !!!BUGBUG - the file times we get back are 20 hours ahead! +// since this happens consistantly, we can still correctly +// determine which of the 2 files is newer. This needs fixed, +// though. ( I now suspect that we are getting GMT back from +// these functions and must compensate for local time ) (sjb) +//========================================================= +int CGraph :: CheckNODFile ( char *szMapName ) +{ + int retValue; + + char szBspFilename[MAX_PATH]; + char szGraphFilename[MAX_PATH]; + + + strcpy ( szBspFilename, "maps/" ); + strcat ( szBspFilename, szMapName ); + strcat ( szBspFilename, ".bsp" ); + + strcpy ( szGraphFilename, "maps/graphs/" ); + strcat ( szGraphFilename, szMapName ); + strcat ( szGraphFilename, ".nod" ); + + retValue = TRUE; + + int iCompare; + if (COMPARE_FILE_TIME(szBspFilename, szGraphFilename, &iCompare)) + { + if ( iCompare > 0 ) + {// BSP file is newer. + ALERT ( at_aiconsole, ".NOD File will be updated\n\n" ); + retValue = FALSE; + } + } + else + { + retValue = FALSE; + } + + return retValue; +} + +#define ENTRY_STATE_EMPTY -1 + +struct tagNodePair +{ + short iSrc; + short iDest; +}; + +void CGraph::HashInsert(int iSrcNode, int iDestNode, int iKey) +{ + struct tagNodePair np; + + np.iSrc = iSrcNode; + np.iDest = iDestNode; + CRC32_t dwHash; + CRC32_INIT(&dwHash); + CRC32_PROCESS_BUFFER(&dwHash, &np, sizeof(np)); + dwHash = CRC32_FINAL(dwHash); + + int di = m_HashPrimes[dwHash&15]; + int i = (dwHash >> 4) % m_nHashLinks; + while (m_pHashLinks[i] != ENTRY_STATE_EMPTY) + { + i += di; + if (i >= m_nHashLinks) i -= m_nHashLinks; + } + m_pHashLinks[i] = iKey; +} + +void CGraph::HashSearch(int iSrcNode, int iDestNode, int &iKey) +{ + struct tagNodePair np; + + np.iSrc = iSrcNode; + np.iDest = iDestNode; + CRC32_t dwHash; + CRC32_INIT(&dwHash); + CRC32_PROCESS_BUFFER(&dwHash, &np, sizeof(np)); + dwHash = CRC32_FINAL(dwHash); + + int di = m_HashPrimes[dwHash&15]; + int i = (dwHash >> 4) % m_nHashLinks; + while (m_pHashLinks[i] != ENTRY_STATE_EMPTY) + { + CLink &link = Link(m_pHashLinks[i]); + if (iSrcNode == link.m_iSrcNode && iDestNode == link.m_iDestNode) + { + break; + } + else + { + i += di; + if (i >= m_nHashLinks) i -= m_nHashLinks; + } + } + iKey = m_pHashLinks[i]; +} + +#define NUMBER_OF_PRIMES 177 + +int Primes[NUMBER_OF_PRIMES] = +{ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, +71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, +157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, +241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, +347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, +439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, +547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, +643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, +751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, +859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, +977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 0 }; + +void CGraph::HashChoosePrimes(int TableSize) +{ + int LargestPrime = TableSize/2; + if (LargestPrime > Primes[NUMBER_OF_PRIMES-2]) + { + LargestPrime = Primes[NUMBER_OF_PRIMES-2]; + } + int Spacing = LargestPrime/16; + + // Pick a set primes that are evenly spaced from (0 to LargestPrime) + // We divide this interval into 16 equal sized zones. We want to find + // one prime number that best represents that zone. + // + int iPrime; + int iZone; + for (iZone = 1, iPrime = 0; iPrime < 16; iZone += Spacing) + { + // Search for a prime number that is less than the target zone + // number given by iZone. + // + int Lower = Primes[0]; + for (int jPrime = 0; Primes[jPrime] != 0; jPrime++) + { + if (jPrime != 0 && TableSize % Primes[jPrime] == 0) continue; + int Upper = Primes[jPrime]; + if (Lower <= iZone && iZone <= Upper) + { + // Choose the closest lower prime number. + // + if (iZone - Lower <= Upper - iZone) + { + m_HashPrimes[iPrime++] = Lower; + } + else + { + m_HashPrimes[iPrime++] = Upper; + } + break; + } + Lower = Upper; + } + } + + // Alternate negative and positive numbers + // + for (iPrime = 0; iPrime < 16; iPrime += 2) + { + m_HashPrimes[iPrime] = TableSize-m_HashPrimes[iPrime]; + } + + // Shuffle the set of primes to reduce correlation with bits in + // hash key. + // + for (iPrime = 0; iPrime < 16-1; iPrime++) + { + int Pick = RANDOM_LONG(0, 15-iPrime); + int Temp = m_HashPrimes[Pick]; + m_HashPrimes[Pick] = m_HashPrimes[15-iPrime]; + m_HashPrimes[15-iPrime] = Temp; + } +} + +// Renumber nodes so that nodes that link together are together. +// +#define UNNUMBERED_NODE -1 +void CGraph::SortNodes(void) +{ + // We are using m_iPreviousNode to be the new node number. + // After assigning new node numbers to everything, we move + // things and patchup the links. + // + int iNodeCnt = 0; + int i; + m_pNodes[0].m_iPreviousNode = iNodeCnt++; + for (i = 1; i < m_cNodes; i++) + { + m_pNodes[i].m_iPreviousNode = UNNUMBERED_NODE; + } + + for (i = 0; i < m_cNodes; i++) + { + // Run through all of this node's neighbors + // + for (int j = 0 ; j < m_pNodes[i].m_cNumLinks; j++ ) + { + int iDestNode = INodeLink(i, j); + if (m_pNodes[iDestNode].m_iPreviousNode == UNNUMBERED_NODE) + { + m_pNodes[iDestNode].m_iPreviousNode = iNodeCnt++; + } + } + } + + // Assign remaining node numbers to unlinked nodes. + // + for (i = 0; i < m_cNodes; i++) + { + if (m_pNodes[i].m_iPreviousNode == UNNUMBERED_NODE) + { + m_pNodes[i].m_iPreviousNode = iNodeCnt++; + } + } + + // Alter links to reflect new node numbers. + // + for (i = 0; i < m_cLinks; i++) + { + m_pLinkPool[i].m_iSrcNode = m_pNodes[m_pLinkPool[i].m_iSrcNode].m_iPreviousNode; + m_pLinkPool[i].m_iDestNode = m_pNodes[m_pLinkPool[i].m_iDestNode].m_iPreviousNode; + } + + // Rearrange nodes to reflect new node numbering. + // + for (i = 0; i < m_cNodes; i++) + { + while (m_pNodes[i].m_iPreviousNode != i) + { + // Move current node off to where it should be, and bring + // that other node back into the current slot. + // + int iDestNode = m_pNodes[i].m_iPreviousNode; + CNode TempNode = m_pNodes[iDestNode]; + m_pNodes[iDestNode] = m_pNodes[i]; + m_pNodes[i] = TempNode; + } + } +} + +void CGraph::BuildLinkLookups(void) +{ + m_nHashLinks = 3*m_cLinks/2 + 3; + + HashChoosePrimes(m_nHashLinks); + m_pHashLinks = (short *)calloc(sizeof(short), m_nHashLinks); + if (!m_pHashLinks) + { + ALERT(at_aiconsole, "Couldn't allocated Link Lookup Table.\n"); + return; + } + int i; + for (i = 0; i < m_nHashLinks; i++) + { + m_pHashLinks[i] = ENTRY_STATE_EMPTY; + } + + for (i = 0; i < m_cLinks; i++) + { + CLink &link = Link(i); + HashInsert(link.m_iSrcNode, link.m_iDestNode, i); + } +#if 0 + for (i = 0; i < m_cLinks; i++) + { + CLink &link = Link(i); + int iKey; + HashSearch(link.m_iSrcNode, link.m_iDestNode, iKey); + if (iKey != i) + { + ALERT(at_aiconsole, "HashLinks don't match (%d versus %d)\n", i, iKey); + } + } +#endif +} + +void CGraph::BuildRegionTables(void) +{ + if (m_di) free(m_di); + + // Go ahead and setup for range searching the nodes for FindNearestNodes + // + m_di = (DIST_INFO *)calloc(sizeof(DIST_INFO), m_cNodes); + if (!m_di) + { + ALERT(at_aiconsole, "Couldn't allocated node ordering array.\n"); + return; + } + + // Calculate regions for all the nodes. + // + // + int i; + for (i = 0; i < 3; i++) + { + m_RegionMin[i] = 999999999.0; // just a big number out there; + m_RegionMax[i] = -999999999.0; // just a big number out there; + } + for (i = 0; i < m_cNodes; i++) + { + if (m_pNodes[i].m_vecOrigin.x < m_RegionMin[0]) + m_RegionMin[0] = m_pNodes[i].m_vecOrigin.x; + if (m_pNodes[i].m_vecOrigin.y < m_RegionMin[1]) + m_RegionMin[1] = m_pNodes[i].m_vecOrigin.y; + if (m_pNodes[i].m_vecOrigin.z < m_RegionMin[2]) + m_RegionMin[2] = m_pNodes[i].m_vecOrigin.z; + + if (m_pNodes[i].m_vecOrigin.x > m_RegionMax[0]) + m_RegionMax[0] = m_pNodes[i].m_vecOrigin.x; + if (m_pNodes[i].m_vecOrigin.y > m_RegionMax[1]) + m_RegionMax[1] = m_pNodes[i].m_vecOrigin.y; + if (m_pNodes[i].m_vecOrigin.z > m_RegionMax[2]) + m_RegionMax[2] = m_pNodes[i].m_vecOrigin.z; + } + for (i = 0; i < m_cNodes; i++) + { + m_pNodes[i].m_Region[0] = CALC_RANGE(m_pNodes[i].m_vecOrigin.x, m_RegionMin[0], m_RegionMax[0]); + m_pNodes[i].m_Region[1] = CALC_RANGE(m_pNodes[i].m_vecOrigin.y, m_RegionMin[1], m_RegionMax[1]); + m_pNodes[i].m_Region[2] = CALC_RANGE(m_pNodes[i].m_vecOrigin.z, m_RegionMin[2], m_RegionMax[2]); + } + + for (i = 0; i < 3; i++) + { + int j; + for (j = 0; j < NUM_RANGES; j++) + { + m_RangeStart[i][j] = 255; + m_RangeEnd[i][j] = 0; + } + for (j = 0; j < m_cNodes; j++) + { + m_di[j].m_SortedBy[i] = j; + } + + for (j = 0; j < m_cNodes - 1; j++) + { + int jNode = m_di[j].m_SortedBy[i]; + int jCodeX = m_pNodes[jNode].m_Region[0]; + int jCodeY = m_pNodes[jNode].m_Region[1]; + int jCodeZ = m_pNodes[jNode].m_Region[2]; + int jCode; + switch (i) + { + case 0: + jCode = (jCodeX << 16) + (jCodeY << 8) + jCodeZ; + break; + case 1: + jCode = (jCodeY << 16) + (jCodeZ << 8) + jCodeX; + break; + case 2: + jCode = (jCodeZ << 16) + (jCodeX << 8) + jCodeY; + break; + } + + for (int k = j+1; k < m_cNodes; k++) + { + int kNode = m_di[k].m_SortedBy[i]; + int kCodeX = m_pNodes[kNode].m_Region[0]; + int kCodeY = m_pNodes[kNode].m_Region[1]; + int kCodeZ = m_pNodes[kNode].m_Region[2]; + int kCode; + switch (i) + { + case 0: + kCode = (kCodeX << 16) + (kCodeY << 8) + kCodeZ; + break; + case 1: + kCode = (kCodeY << 16) + (kCodeZ << 8) + kCodeX; + break; + case 2: + kCode = (kCodeZ << 16) + (kCodeX << 8) + kCodeY; + break; + } + + if (kCode < jCode) + { + // Swap j and k entries. + // + int Tmp = m_di[j].m_SortedBy[i]; + m_di[j].m_SortedBy[i] = m_di[k].m_SortedBy[i]; + m_di[k].m_SortedBy[i] = Tmp; + } + } + } + } + + // Generate lookup tables. + // + for (i = 0; i < m_cNodes; i++) + { + int CodeX = m_pNodes[m_di[i].m_SortedBy[0]].m_Region[0]; + int CodeY = m_pNodes[m_di[i].m_SortedBy[1]].m_Region[1]; + int CodeZ = m_pNodes[m_di[i].m_SortedBy[2]].m_Region[2]; + + if (i < m_RangeStart[0][CodeX]) + { + m_RangeStart[0][CodeX] = i; + } + if (i < m_RangeStart[1][CodeY]) + { + m_RangeStart[1][CodeY] = i; + } + if (i < m_RangeStart[2][CodeZ]) + { + m_RangeStart[2][CodeZ] = i; + } + if (m_RangeEnd[0][CodeX] < i) + { + m_RangeEnd[0][CodeX] = i; + } + if (m_RangeEnd[1][CodeY] < i) + { + m_RangeEnd[1][CodeY] = i; + } + if (m_RangeEnd[2][CodeZ] < i) + { + m_RangeEnd[2][CodeZ] = i; + } + } + + // Initialize the cache. + // + memset(m_Cache, 0, sizeof(m_Cache)); +} + +void CGraph :: ComputeStaticRoutingTables( void ) +{ + int nRoutes = m_cNodes*m_cNodes; +#define FROM_TO(x,y) ((x)*m_cNodes+(y)) + short *Routes = new short[nRoutes]; + + int *pMyPath = new int[m_cNodes]; + unsigned short *BestNextNodes = new unsigned short[m_cNodes]; + char *pRoute = new char[m_cNodes*2]; + + + if (Routes && pMyPath && BestNextNodes && pRoute) + { + int nTotalCompressedSize = 0; + for (int iHull = 0; iHull < MAX_NODE_HULLS; iHull++) + { + for (int iCap = 0; iCap < 2; iCap++) + { + int iCapMask; + switch (iCap) + { + case 0: + iCapMask = 0; + break; + + case 1: + iCapMask = bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; + break; + } + + + // Initialize Routing table to uncalculated. + // + int iFrom; + for (iFrom = 0; iFrom < m_cNodes; iFrom++) + { + for (int iTo = 0; iTo < m_cNodes; iTo++) + { + Routes[FROM_TO(iFrom, iTo)] = -1; + } + } + + for (iFrom = 0; iFrom < m_cNodes; iFrom++) + { + for (int iTo = m_cNodes-1; iTo >= 0; iTo--) + { + if (Routes[FROM_TO(iFrom, iTo)] != -1) continue; + + int cPathSize = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); + + // Use the computed path to update the routing table. + // + if (cPathSize > 1) + { + for (int iNode = 0; iNode < cPathSize-1; iNode++) + { + int iStart = pMyPath[iNode]; + int iNext = pMyPath[iNode+1]; + for (int iNode1 = iNode+1; iNode1 < cPathSize; iNode1++) + { + int iEnd = pMyPath[iNode1]; + Routes[FROM_TO(iStart, iEnd)] = iNext; + } + } +#if 0 + // Well, at first glance, this should work, but actually it's safer + // to be told explictly that you can take a series of node in a + // particular direction. Some links don't appear to have links in + // the opposite direction. + // + for (iNode = cPathSize-1; iNode >= 1; iNode--) + { + int iStart = pMyPath[iNode]; + int iNext = pMyPath[iNode-1]; + for (int iNode1 = iNode-1; iNode1 >= 0; iNode1--) + { + int iEnd = pMyPath[iNode1]; + Routes[FROM_TO(iStart, iEnd)] = iNext; + } + } +#endif + } + else + { + Routes[FROM_TO(iFrom, iTo)] = iFrom; + Routes[FROM_TO(iTo, iFrom)] = iTo; + } + } + } + + for (iFrom = 0; iFrom < m_cNodes; iFrom++) + { + for (int iTo = 0; iTo < m_cNodes; iTo++) + { + BestNextNodes[iTo] = Routes[FROM_TO(iFrom, iTo)]; + } + + // Compress this node's routing table. + // + int iLastNode = 9999999; // just really big. + int cSequence = 0; + int cRepeats = 0; + int CompressedSize = 0; + char *p = pRoute; + for (int i = 0; i < m_cNodes; i++) + { + BOOL CanRepeat = ((BestNextNodes[i] == iLastNode) && cRepeats < 127); + BOOL CanSequence = (BestNextNodes[i] == i && cSequence < 128); + + if (cRepeats) + { + if (CanRepeat) + { + cRepeats++; + } + else + { + // Emit the repeat phrase. + // + CompressedSize += 2; // (count-1, iLastNode-i) + *p++ = cRepeats - 1; + int a = iLastNode - iFrom; + int b = iLastNode - iFrom + m_cNodes; + int c = iLastNode - iFrom - m_cNodes; + if (-128 <= a && a <= 127) + { + *p++ = a; + } + else if (-128 <= b && b <= 127) + { + *p++ = b; + } + else if (-128 <= c && c <= 127) + { + *p++ = c; + } + else + { + ALERT( at_aiconsole, "Nodes need sorting (%d,%d)!\n", iLastNode, iFrom); + } + cRepeats = 0; + + if (CanSequence) + { + // Start a sequence. + // + cSequence++; + } + else + { + // Start another repeat. + // + cRepeats++; + } + } + } + else if (cSequence) + { + if (CanSequence) + { + cSequence++; + } + else + { + // It may be advantageous to combine + // a single-entry sequence phrase with the + // next repeat phrase. + // + if (cSequence == 1 && CanRepeat) + { + // Combine with repeat phrase. + // + cRepeats = 2; + cSequence = 0; + } + else + { + // Emit the sequence phrase. + // + CompressedSize += 1; // (-count) + *p++ = -cSequence; + cSequence = 0; + + // Start a repeat sequence. + // + cRepeats++; + } + } + } + else + { + if (CanSequence) + { + // Start a sequence phrase. + // + cSequence++; + } + else + { + // Start a repeat sequence. + // + cRepeats++; + } + } + iLastNode = BestNextNodes[i]; + } + if (cRepeats) + { + // Emit the repeat phrase. + // + CompressedSize += 2; + *p++ = cRepeats - 1; +#if 0 + iLastNode = iFrom + *pRoute; + if (iLastNode >= m_cNodes) iLastNode -= m_cNodes; + else if (iLastNode < 0) iLastNode += m_cNodes; +#endif + int a = iLastNode - iFrom; + int b = iLastNode - iFrom + m_cNodes; + int c = iLastNode - iFrom - m_cNodes; + if (-128 <= a && a <= 127) + { + *p++ = a; + } + else if (-128 <= b && b <= 127) + { + *p++ = b; + } + else if (-128 <= c && c <= 127) + { + *p++ = c; + } + else + { + ALERT( at_aiconsole, "Nodes need sorting (%d,%d)!\n", iLastNode, iFrom); + } + } + if (cSequence) + { + // Emit the Sequence phrase. + // + CompressedSize += 1; + *p++ = -cSequence; + } + + // Go find a place to store this thing and point to it. + // + int nRoute = p - pRoute; + if (m_pRouteInfo) + { + int i; + for (i = 0; i < m_nRouteInfo - nRoute; i++) + { + if (memcmp(m_pRouteInfo + i, pRoute, nRoute) == 0) + { + break; + } + } + if (i < m_nRouteInfo - nRoute) + { + m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = i; + } + else + { + char *Tmp = (char *)calloc(sizeof(char), (m_nRouteInfo + nRoute)); + memcpy(Tmp, m_pRouteInfo, m_nRouteInfo); + free(m_pRouteInfo); + m_pRouteInfo = Tmp; + memcpy(m_pRouteInfo + m_nRouteInfo, pRoute, nRoute); + m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = m_nRouteInfo; + m_nRouteInfo += nRoute; + nTotalCompressedSize += CompressedSize; + } + } + else + { + m_nRouteInfo = nRoute; + m_pRouteInfo = (char *)calloc(sizeof(char), nRoute); + memcpy(m_pRouteInfo, pRoute, nRoute); + m_pNodes[ iFrom ].m_pNextBestNode[iHull][iCap] = 0; + nTotalCompressedSize += CompressedSize; + } + } + } + } + ALERT( at_aiconsole, "Size of Routes = %d\n", nTotalCompressedSize); + } + if (Routes) delete Routes; + if (BestNextNodes) delete BestNextNodes; + if (pRoute) delete pRoute; + if (pMyPath) delete pMyPath; + Routes = 0; + BestNextNodes = 0; + pRoute = 0; + pMyPath = 0; + +#if 0 + TestRoutingTables(); +#endif + m_fRoutingComplete = TRUE; +} + +// Test those routing tables. Doesn't really work, yet. +// +void CGraph :: TestRoutingTables( void ) +{ + int *pMyPath = new int[m_cNodes]; + int *pMyPath2 = new int[m_cNodes]; + if (pMyPath && pMyPath2) + { + for (int iHull = 0; iHull < MAX_NODE_HULLS; iHull++) + { + for (int iCap = 0; iCap < 2; iCap++) + { + int iCapMask; + switch (iCap) + { + case 0: + iCapMask = 0; + break; + + case 1: + iCapMask = bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; + break; + } + + for (int iFrom = 0; iFrom < m_cNodes; iFrom++) + { + for (int iTo = 0; iTo < m_cNodes; iTo++) + { + m_fRoutingComplete = FALSE; + int cPathSize1 = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); + m_fRoutingComplete = TRUE; + int cPathSize2 = FindShortestPath(pMyPath2, iFrom, iTo, iHull, iCapMask); + + // Unless we can look at the entire path, we can verify that it's correct. + // + if (cPathSize2 == MAX_PATH_SIZE) continue; + + // Compare distances. + // +#if 1 + float flDistance1 = 0.0; + int i; + for (i = 0; i < cPathSize1-1; i++) + { + // Find the link from pMyPath[i] to pMyPath[i+1] + // + if (pMyPath[i] == pMyPath[i+1]) continue; + int iVisitNode; + BOOL bFound = FALSE; + for (int iLink = 0; iLink < m_pNodes[pMyPath[i]].m_cNumLinks; iLink++) + { + iVisitNode = INodeLink ( pMyPath[i], iLink ); + if (iVisitNode == pMyPath[i+1]) + { + flDistance1 += m_pLinkPool[ m_pNodes[ pMyPath[i] ].m_iFirstLink + iLink].m_flWeight; + bFound = TRUE; + break; + } + } + if (!bFound) + { + ALERT(at_aiconsole, "No link.\n"); + } + } + + float flDistance2 = 0.0; + for (i = 0; i < cPathSize2-1; i++) + { + // Find the link from pMyPath2[i] to pMyPath2[i+1] + // + if (pMyPath2[i] == pMyPath2[i+1]) continue; + int iVisitNode; + BOOL bFound = FALSE; + for (int iLink = 0; iLink < m_pNodes[pMyPath2[i]].m_cNumLinks; iLink++) + { + iVisitNode = INodeLink ( pMyPath2[i], iLink ); + if (iVisitNode == pMyPath2[i+1]) + { + flDistance2 += m_pLinkPool[ m_pNodes[ pMyPath2[i] ].m_iFirstLink + iLink].m_flWeight; + bFound = TRUE; + break; + } + } + if (!bFound) + { + ALERT(at_aiconsole, "No link.\n"); + } + } + if (fabs(flDistance1 - flDistance2) > 0.10) + { +#else + if (cPathSize1 != cPathSize2 || memcmp(pMyPath, pMyPath2, sizeof(int)*cPathSize1) != 0) + { +#endif + ALERT(at_aiconsole, "Routing is inconsistent!!!\n"); + ALERT(at_aiconsole, "(%d to %d |%d/%d)1:", iFrom, iTo, iHull, iCap); + for (int i = 0; i < cPathSize1; i++) + { + ALERT(at_aiconsole, "%d ", pMyPath[i]); + } + ALERT(at_aiconsole, "\n(%d to %d |%d/%d)2:", iFrom, iTo, iHull, iCap); + for (i = 0; i < cPathSize2; i++) + { + ALERT(at_aiconsole, "%d ", pMyPath2[i]); + } + ALERT(at_aiconsole, "\n"); + m_fRoutingComplete = FALSE; + cPathSize1 = FindShortestPath(pMyPath, iFrom, iTo, iHull, iCapMask); + m_fRoutingComplete = TRUE; + cPathSize2 = FindShortestPath(pMyPath2, iFrom, iTo, iHull, iCapMask); + goto EnoughSaid; + } + } + } + } + } + } + +EnoughSaid: + + if (pMyPath) delete pMyPath; + if (pMyPath2) delete pMyPath2; + pMyPath = 0; + pMyPath2 = 0; +} + + +//========================================================= +// CNodeViewer - Draws a graph of the shorted path from all nodes +// to current location (typically the player). It then draws +// as many connects as it can per frame, trying not to overflow the buffer +//========================================================= +void CNodeViewer::Spawn( ) +{ + /*CNodeViewer *pViewer = CreateClassPtr((CNodeViewer *)NULL); + pViewer->Spawn();*/ + + if ( !WorldGraph.m_fGraphPresent || !WorldGraph.m_fGraphPointersSet ) + {// protect us in the case that the node graph isn't available or built + ALERT ( at_console, "Graph not ready!\n" ); + UTIL_Remove( this->edict() ); + return; + } + + + if (FClassnameIs( pev, "node_viewer_fly")) + { + m_iHull = NODE_FLY_HULL; + m_afNodeType = bits_NODE_AIR; + m_vecColor = Vector( 160, 100, 255 ); + } + else if (FClassnameIs( pev, "node_viewer_large")) + { + m_iHull = NODE_LARGE_HULL; + m_afNodeType = bits_NODE_LAND | bits_NODE_WATER; + m_vecColor = Vector( 100, 255, 160 ); + } + else + { + m_iHull = NODE_HUMAN_HULL; + m_afNodeType = bits_NODE_LAND | bits_NODE_WATER; + m_vecColor = Vector( 255, 160, 100 ); + } + + + m_iBaseNode = WorldGraph.FindNearestNode ( pev->origin, m_afNodeType ); + + if ( m_iBaseNode < 0 ) + { + ALERT( at_console, "No nearby node\n" ); + return; + } + + m_nVisited = 0; + + ALERT( at_aiconsole, "basenode %d\n", m_iBaseNode ); + + if (WorldGraph.m_cNodes < 128) + { + for (int i = 0; i < WorldGraph.m_cNodes; i++) + { + AddNode( i, WorldGraph.NextNodeInRoute( i, m_iBaseNode, m_iHull, 0 )); + } + } + else + { + // do a depth traversal + FindNodeConnections( m_iBaseNode ); + + int start = 0; + int end; + do { + end = m_nVisited; + // ALERT( at_console, "%d :", m_nVisited ); + for (end = m_nVisited; start < end; start++) + { + FindNodeConnections( m_aFrom[start] ); + FindNodeConnections( m_aTo[start] ); + } + } while (end != m_nVisited); + } + + ALERT( at_aiconsole, "%d nodes\n", m_nVisited ); + + m_iDraw = 0; + SetThink( &CNodeViewer::DrawThink ); + pev->nextthink = gpGlobals->time; + + pev->classname = MAKE_STRING( "node_viewer" ); +} + + +void CNodeViewer :: FindNodeConnections ( int iNode ) +{ + AddNode( iNode, WorldGraph.NextNodeInRoute( iNode, m_iBaseNode, m_iHull, 0 )); + for ( int i = 0 ; i < WorldGraph.m_pNodes[ iNode ].m_cNumLinks ; i++ ) + { + CLink *pToLink = &WorldGraph.NodeLink( iNode, i); + AddNode( pToLink->m_iDestNode, WorldGraph.NextNodeInRoute( pToLink->m_iDestNode, m_iBaseNode, m_iHull, 0 )); + } +} + +void CNodeViewer::AddNode( int iFrom, int iTo ) +{ + if (m_nVisited >= 128) + { + return; + } + else + { + if (iFrom == iTo) + return; + + for (int i = 0; i < m_nVisited; i++) + { + if (m_aFrom[i] == iFrom && m_aTo[i] == iTo) + return; + if (m_aFrom[i] == iTo && m_aTo[i] == iFrom) + return; + } + m_aFrom[m_nVisited] = iFrom; + m_aTo[m_nVisited] = iTo; + m_nVisited++; + } +} + + +void CNodeViewer :: DrawThink( void ) +{ + pev->nextthink = gpGlobals->time; + + for (int i = 0; i < 10; i++) + { + if (m_iDraw == m_nVisited) + { + UTIL_Remove( this->edict() ); + return; + } + + extern short g_sModelIndexLaser; + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMPOINTS ); + WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.x ); + WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.y ); + WRITE_COORD( WorldGraph.m_pNodes[ m_aFrom[m_iDraw] ].m_vecOrigin.z + NODE_HEIGHT ); + + WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.x ); + WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.y ); + WRITE_COORD( WorldGraph.m_pNodes[ m_aTo[m_iDraw] ].m_vecOrigin.z + NODE_HEIGHT ); + WRITE_SHORT( g_sModelIndexLaser ); + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 0 ); // framerate + WRITE_BYTE( 250 ); // life + WRITE_BYTE( 40 ); // width + WRITE_BYTE( 0 ); // noise + WRITE_BYTE( m_vecColor.x ); // r, g, b + WRITE_BYTE( m_vecColor.y ); // r, g, b + WRITE_BYTE( m_vecColor.z ); // r, g, b + WRITE_BYTE( 128 ); // brightness + WRITE_BYTE( 0 ); // speed + MESSAGE_END(); + + m_iDraw++; + } +} + + diff --git a/src/dlls/nodes.h b/src/dlls/nodes.h index 930afd2..2064d0a 100644 --- a/src/dlls/nodes.h +++ b/src/dlls/nodes.h @@ -1,397 +1,397 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// nodes.h -//========================================================= - -//========================================================= -// DEFINE -//========================================================= -#define MAX_STACK_NODES 100 -#define NO_NODE -1 -#define MAX_NODE_HULLS 4 - -#define bits_NODE_LAND ( 1 << 0 ) // Land node, so nudge if necessary. -#define bits_NODE_AIR ( 1 << 1 ) // Air node, don't nudge. -#define bits_NODE_WATER ( 1 << 2 ) // Water node, don't nudge. -#define bits_NODE_GROUP_REALM (bits_NODE_LAND | bits_NODE_AIR | bits_NODE_WATER) - -//========================================================= -// Instance of a node. -//========================================================= -class CNode -{ -public: - Vector m_vecOrigin;// location of this node in space - Vector m_vecOriginPeek; // location of this node (LAND nodes are NODE_HEIGHT higher). - BYTE m_Region[3]; // Which of 256 regions do each of the coordinate belong? - int m_afNodeInfo;// bits that tell us more about this location - - int m_cNumLinks; // how many links this node has - int m_iFirstLink;// index of this node's first link in the link pool. - - // Where to start looking in the compressed routing table (offset into m_pRouteInfo). - // (4 hull sizes -- smallest to largest + fly/swim), and secondly, door capability. - // - int m_pNextBestNode[MAX_NODE_HULLS][2]; - - // Used in finding the shortest path. m_fClosestSoFar is -1 if not visited. - // Then it is the distance to the source. If another path uses this node - // and has a closer distance, then m_iPreviousNode is also updated. - // - float m_flClosestSoFar; // Used in finding the shortest path. - int m_iPreviousNode; - - short m_sHintType;// there is something interesting in the world at this node's position - short m_sHintActivity;// there is something interesting in the world at this node's position - float m_flHintYaw;// monster on this node should face this yaw to face the hint. -}; - -//========================================================= -// CLink - A link between 2 nodes -//========================================================= -#define bits_LINK_SMALL_HULL ( 1 << 0 )// headcrab box can fit through this connection -#define bits_LINK_HUMAN_HULL ( 1 << 1 )// player box can fit through this connection -#define bits_LINK_LARGE_HULL ( 1 << 2 )// big box can fit through this connection -#define bits_LINK_FLY_HULL ( 1 << 3 )// a flying big box can fit through this connection -#define bits_LINK_DISABLED ( 1 << 4 )// link is not valid when the set - -#define NODE_SMALL_HULL 0 -#define NODE_HUMAN_HULL 1 -#define NODE_LARGE_HULL 2 -#define NODE_FLY_HULL 3 - -class CLink -{ -public: - int m_iSrcNode;// the node that 'owns' this link ( keeps us from having to make reverse lookups ) - int m_iDestNode;// the node on the other end of the link. - - entvars_t *m_pLinkEnt;// the entity that blocks this connection (doors, etc) - - // m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes) - char m_szLinkEntModelname[ 4 ];// the unique name of the brush model that blocks the connection (this is kept for save/restore) - - int m_afLinkInfo;// information about this link - float m_flWeight;// length of the link line segment -}; - - -typedef struct -{ - int m_SortedBy[3]; - int m_CheckedEvent; -} DIST_INFO; - -typedef struct -{ - Vector v; - short n; // Nearest node or -1 if no node found. -} CACHE_ENTRY; - -//========================================================= -// CGraph -//========================================================= -#define GRAPH_VERSION (int)16// !!!increment this whever graph/node/link classes change, to obsolesce older disk files. -class CGraph -{ -public: - -// the graph has two flags, and should not be accessed unless both flags are TRUE! - BOOL m_fGraphPresent;// is the graph in memory? - BOOL m_fGraphPointersSet;// are the entity pointers for the graph all set? - BOOL m_fRoutingComplete; // are the optimal routes computed, yet? - - CNode *m_pNodes;// pointer to the memory block that contains all node info - CLink *m_pLinkPool;// big list of all node connections - char *m_pRouteInfo; // compressed routing information the nodes use. - - int m_cNodes;// total number of nodes - int m_cLinks;// total number of links - int m_nRouteInfo; // size of m_pRouteInfo in bytes. - - // Tables for making nearest node lookup faster. SortedBy provided nodes in a - // order of a particular coordinate. Instead of doing a binary search, RangeStart - // and RangeEnd let you get to the part of SortedBy that you are interested in. - // - // Once you have a point of interest, the only way you'll find a closer point is - // if at least one of the coordinates is closer than the ones you have now. So we - // search each range. After the search is exhausted, we know we have the closest - // node. - // -#define CACHE_SIZE 128 -#define NUM_RANGES 256 - DIST_INFO *m_di; // This is m_cNodes long, but the entries don't correspond to CNode entries. - int m_RangeStart[3][NUM_RANGES]; - int m_RangeEnd[3][NUM_RANGES]; - float m_flShortest; - int m_iNearest; - int m_minX, m_minY, m_minZ, m_maxX, m_maxY, m_maxZ; - int m_minBoxX, m_minBoxY, m_minBoxZ, m_maxBoxX, m_maxBoxY, m_maxBoxZ; - int m_CheckedCounter; - float m_RegionMin[3], m_RegionMax[3]; // The range of nodes. - CACHE_ENTRY m_Cache[CACHE_SIZE]; - - - int m_HashPrimes[16]; - short *m_pHashLinks; - int m_nHashLinks; - - - // kinda sleazy. In order to allow variety in active idles for monster groups in a room with more than one node, - // we keep track of the last node we searched from and store it here. Subsequent searches by other monsters will pick - // up where the last search stopped. - int m_iLastActiveIdleSearch; - - // another such system used to track the search for cover nodes, helps greatly with two monsters trying to get to the same node. - int m_iLastCoverSearch; - - // functions to create the graph - int LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode ); - int RejectInlineLinks ( CLink *pLinkPool, FILE *file ); - int FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask); - int FindNearestNode ( const Vector &vecOrigin, CMBaseEntity *pEntity ); - int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes ); - //int FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine ); - float PathLength( int iStart, int iDest, int iHull, int afCapMask ); - int NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap ); - - enum NODEQUERY { NODEGRAPH_DYNAMIC, NODEGRAPH_STATIC }; - // A static query means we're asking about the possiblity of handling this entity at ANY time - // A dynamic query means we're asking about it RIGHT NOW. So we should query the current state - int HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType ); - entvars_t* LinkEntForLink ( CLink *pLink, CNode *pNode ); - void ShowNodeConnections ( int iNode ); - void InitGraph( void ); - int AllocNodes ( void ); - - int CheckNODFile(char *szMapName); - int FLoadGraph(char *szMapName); - int FSaveGraph(char *szMapName); - int FSetGraphPointers(void); - void CheckNode(Vector vecOrigin, int iNode); - - void BuildRegionTables(void); - void ComputeStaticRoutingTables(void); - void TestRoutingTables(void); - - void HashInsert(int iSrcNode, int iDestNode, int iKey); - void HashSearch(int iSrcNode, int iDestNode, int &iKey); - void HashChoosePrimes(int TableSize); - void BuildLinkLookups(void); - - void SortNodes(void); - - int HullIndex( const CMBaseEntity *pEntity ); // what hull the monster uses - int NodeType( const CMBaseEntity *pEntity ); // what node type the monster uses - inline int CapIndex( int afCapMask ) - { - if (afCapMask & (bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE)) - return 1; - return 0; - } - - - inline CNode &Node( int i ) - { -#ifdef _DEBUG - if ( !m_pNodes || i < 0 || i > m_cNodes ) - ALERT( at_error, "Bad Node!\n" ); -#endif - return m_pNodes[i]; - } - - inline CLink &Link( int i ) - { -#ifdef _DEBUG - if ( !m_pLinkPool || i < 0 || i > m_cLinks ) - ALERT( at_error, "Bad link!\n" ); -#endif - return m_pLinkPool[i]; - } - - inline CLink &NodeLink( int iNode, int iLink ) - { - return Link( Node( iNode ).m_iFirstLink + iLink ); - } - - inline CLink &NodeLink( const CNode &node, int iLink ) - { - return Link( node.m_iFirstLink + iLink ); - } - - inline int INodeLink ( int iNode, int iLink ) - { - return NodeLink( iNode, iLink ).m_iDestNode; - } - -#if 0 - inline CNode &SourceNode( int iNode, int iLink ) - { - return Node( NodeLink( iNode, iLink ).m_iSrcNode ); - } - - inline CNode &DestNode( int iNode, int iLink ) - { - return Node( NodeLink( iNode, iLink ).m_iDestNode ); - } - - inline CNode *PNodeLink ( int iNode, int iLink ) - { - return &DestNode( iNode, iLink ); - } -#endif -}; - -//========================================================= -// Nodes start out as ents in the level. The node graph -// is built, then these ents are discarded. -//========================================================= -class CNodeEnt : public CMBaseEntity -{ - void Spawn( void ); - void KeyValue( KeyValueData *pkvd ); - virtual int ObjectCaps( void ) { return CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } - - short m_sHintType; - short m_sHintActivity; -}; - - -//========================================================= -// Node viewer -//========================================================= -class CNodeViewer : public CMBaseEntity -{ -public: - void Spawn( void ); - - int m_iBaseNode; - int m_iDraw; - int m_nVisited; - int m_aFrom[128]; - int m_aTo[128]; - int m_iHull; - int m_afNodeType; - Vector m_vecColor; - - void FindNodeConnections( int iNode ); - void AddNode( int iFrom, int iTo ); - void EXPORT DrawThink( void ); - -}; - -//========================================================= -// CStack - last in, first out. -//========================================================= -class CStack -{ -public: - CStack( void ); - void Push( int value ); - int Pop( void ); - int Top( void ); - int Empty( void ) { return m_level==0; } - int Size( void ) { return m_level; } - void CopyToArray ( int *piArray ); - -private: - int m_stack[ MAX_STACK_NODES ]; - int m_level; -}; - - -//========================================================= -// CQueue - first in, first out. -//========================================================= -class CQueue -{ -public: - - CQueue( void );// constructor - inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); } - inline int Empty ( void ) { return ( m_cSize == 0 ); } - //inline int Tail ( void ) { return ( m_queue[ m_tail ] ); } - inline int Size ( void ) { return ( m_cSize ); } - void Insert( int, float ); - int Remove( float & ); - -private: - int m_cSize; - struct tag_QUEUE_NODE - { - int Id; - float Priority; - } m_queue[ MAX_STACK_NODES ]; - int m_head; - int m_tail; -}; - -//========================================================= -// CQueuePriority - Priority queue (smallest item out first). -// -//========================================================= -class CQueuePriority -{ -public: - - CQueuePriority( void );// constructor - inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); } - inline int Empty ( void ) { return ( m_cSize == 0 ); } - //inline int Tail ( float & ) { return ( m_queue[ m_tail ].Id ); } - inline int Size ( void ) { return ( m_cSize ); } - void Insert( int, float ); - int Remove( float &); - -private: - int m_cSize; - struct tag_HEAP_NODE - { - int Id; - float Priority; - } m_heap[ MAX_STACK_NODES ]; - void Heap_SiftDown(int); - void Heap_SiftUp(void); - -}; - -//========================================================= -// hints - these MUST coincide with the HINTS listed under -// info_node in the FGD file! -//========================================================= -enum -{ - HINT_NONE = 0, - HINT_WORLD_DOOR, - HINT_WORLD_WINDOW, - HINT_WORLD_BUTTON, - HINT_WORLD_MACHINERY, - HINT_WORLD_LEDGE, - HINT_WORLD_LIGHT_SOURCE, - HINT_WORLD_HEAT_SOURCE, - HINT_WORLD_BLINKING_LIGHT, - HINT_WORLD_BRIGHT_COLORS, - HINT_WORLD_HUMAN_BLOOD, - HINT_WORLD_ALIEN_BLOOD, - - HINT_TACTICAL_EXIT = 100, - HINT_TACTICAL_VANTAGE, - HINT_TACTICAL_AMBUSH, - - HINT_STUKA_PERCH = 300, - HINT_STUKA_LANDING, -}; - -extern CGraph WorldGraph; +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// nodes.h +//========================================================= + +//========================================================= +// DEFINE +//========================================================= +#define MAX_STACK_NODES 100 +#define NO_NODE -1 +#define MAX_NODE_HULLS 4 + +#define bits_NODE_LAND ( 1 << 0 ) // Land node, so nudge if necessary. +#define bits_NODE_AIR ( 1 << 1 ) // Air node, don't nudge. +#define bits_NODE_WATER ( 1 << 2 ) // Water node, don't nudge. +#define bits_NODE_GROUP_REALM (bits_NODE_LAND | bits_NODE_AIR | bits_NODE_WATER) + +//========================================================= +// Instance of a node. +//========================================================= +class CNode +{ +public: + Vector m_vecOrigin;// location of this node in space + Vector m_vecOriginPeek; // location of this node (LAND nodes are NODE_HEIGHT higher). + BYTE m_Region[3]; // Which of 256 regions do each of the coordinate belong? + int m_afNodeInfo;// bits that tell us more about this location + + int m_cNumLinks; // how many links this node has + int m_iFirstLink;// index of this node's first link in the link pool. + + // Where to start looking in the compressed routing table (offset into m_pRouteInfo). + // (4 hull sizes -- smallest to largest + fly/swim), and secondly, door capability. + // + int m_pNextBestNode[MAX_NODE_HULLS][2]; + + // Used in finding the shortest path. m_fClosestSoFar is -1 if not visited. + // Then it is the distance to the source. If another path uses this node + // and has a closer distance, then m_iPreviousNode is also updated. + // + float m_flClosestSoFar; // Used in finding the shortest path. + int m_iPreviousNode; + + short m_sHintType;// there is something interesting in the world at this node's position + short m_sHintActivity;// there is something interesting in the world at this node's position + float m_flHintYaw;// monster on this node should face this yaw to face the hint. +}; + +//========================================================= +// CLink - A link between 2 nodes +//========================================================= +#define bits_LINK_SMALL_HULL ( 1 << 0 )// headcrab box can fit through this connection +#define bits_LINK_HUMAN_HULL ( 1 << 1 )// player box can fit through this connection +#define bits_LINK_LARGE_HULL ( 1 << 2 )// big box can fit through this connection +#define bits_LINK_FLY_HULL ( 1 << 3 )// a flying big box can fit through this connection +#define bits_LINK_DISABLED ( 1 << 4 )// link is not valid when the set + +#define NODE_SMALL_HULL 0 +#define NODE_HUMAN_HULL 1 +#define NODE_LARGE_HULL 2 +#define NODE_FLY_HULL 3 + +class CLink +{ +public: + int m_iSrcNode;// the node that 'owns' this link ( keeps us from having to make reverse lookups ) + int m_iDestNode;// the node on the other end of the link. + + entvars_t *m_pLinkEnt;// the entity that blocks this connection (doors, etc) + + // m_szLinkEntModelname is not necessarily NULL terminated (so we can store it in a more alignment-friendly 4 bytes) + char m_szLinkEntModelname[ 4 ];// the unique name of the brush model that blocks the connection (this is kept for save/restore) + + int m_afLinkInfo;// information about this link + float m_flWeight;// length of the link line segment +}; + + +typedef struct +{ + int m_SortedBy[3]; + int m_CheckedEvent; +} DIST_INFO; + +typedef struct +{ + Vector v; + short n; // Nearest node or -1 if no node found. +} CACHE_ENTRY; + +//========================================================= +// CGraph +//========================================================= +#define GRAPH_VERSION (int)16// !!!increment this whever graph/node/link classes change, to obsolesce older disk files. +class CGraph +{ +public: + +// the graph has two flags, and should not be accessed unless both flags are TRUE! + BOOL m_fGraphPresent;// is the graph in memory? + BOOL m_fGraphPointersSet;// are the entity pointers for the graph all set? + BOOL m_fRoutingComplete; // are the optimal routes computed, yet? + + CNode *m_pNodes;// pointer to the memory block that contains all node info + CLink *m_pLinkPool;// big list of all node connections + char *m_pRouteInfo; // compressed routing information the nodes use. + + int m_cNodes;// total number of nodes + int m_cLinks;// total number of links + int m_nRouteInfo; // size of m_pRouteInfo in bytes. + + // Tables for making nearest node lookup faster. SortedBy provided nodes in a + // order of a particular coordinate. Instead of doing a binary search, RangeStart + // and RangeEnd let you get to the part of SortedBy that you are interested in. + // + // Once you have a point of interest, the only way you'll find a closer point is + // if at least one of the coordinates is closer than the ones you have now. So we + // search each range. After the search is exhausted, we know we have the closest + // node. + // +#define CACHE_SIZE 128 +#define NUM_RANGES 256 + DIST_INFO *m_di; // This is m_cNodes long, but the entries don't correspond to CNode entries. + int m_RangeStart[3][NUM_RANGES]; + int m_RangeEnd[3][NUM_RANGES]; + float m_flShortest; + int m_iNearest; + int m_minX, m_minY, m_minZ, m_maxX, m_maxY, m_maxZ; + int m_minBoxX, m_minBoxY, m_minBoxZ, m_maxBoxX, m_maxBoxY, m_maxBoxZ; + int m_CheckedCounter; + float m_RegionMin[3], m_RegionMax[3]; // The range of nodes. + CACHE_ENTRY m_Cache[CACHE_SIZE]; + + + int m_HashPrimes[16]; + short *m_pHashLinks; + int m_nHashLinks; + + + // kinda sleazy. In order to allow variety in active idles for monster groups in a room with more than one node, + // we keep track of the last node we searched from and store it here. Subsequent searches by other monsters will pick + // up where the last search stopped. + int m_iLastActiveIdleSearch; + + // another such system used to track the search for cover nodes, helps greatly with two monsters trying to get to the same node. + int m_iLastCoverSearch; + + // functions to create the graph + int LinkVisibleNodes ( CLink *pLinkPool, FILE *file, int *piBadNode ); + int RejectInlineLinks ( CLink *pLinkPool, FILE *file ); + int FindShortestPath ( int *piPath, int iStart, int iDest, int iHull, int afCapMask); + int FindNearestNode ( const Vector &vecOrigin, CMBaseEntity *pEntity ); + int FindNearestNode ( const Vector &vecOrigin, int afNodeTypes ); + //int FindNearestLink ( const Vector &vecTestPoint, int *piNearestLink, BOOL *pfAlongLine ); + float PathLength( int iStart, int iDest, int iHull, int afCapMask ); + int NextNodeInRoute( int iCurrentNode, int iDest, int iHull, int iCap ); + + enum NODEQUERY { NODEGRAPH_DYNAMIC, NODEGRAPH_STATIC }; + // A static query means we're asking about the possiblity of handling this entity at ANY time + // A dynamic query means we're asking about it RIGHT NOW. So we should query the current state + int HandleLinkEnt ( int iNode, entvars_t *pevLinkEnt, int afCapMask, NODEQUERY queryType ); + entvars_t* LinkEntForLink ( CLink *pLink, CNode *pNode ); + void ShowNodeConnections ( int iNode ); + void InitGraph( void ); + int AllocNodes ( void ); + + int CheckNODFile(char *szMapName); + int FLoadGraph(char *szMapName); + int FSaveGraph(char *szMapName); + int FSetGraphPointers(void); + void CheckNode(Vector vecOrigin, int iNode); + + void BuildRegionTables(void); + void ComputeStaticRoutingTables(void); + void TestRoutingTables(void); + + void HashInsert(int iSrcNode, int iDestNode, int iKey); + void HashSearch(int iSrcNode, int iDestNode, int &iKey); + void HashChoosePrimes(int TableSize); + void BuildLinkLookups(void); + + void SortNodes(void); + + int HullIndex( const CMBaseEntity *pEntity ); // what hull the monster uses + int NodeType( const CMBaseEntity *pEntity ); // what node type the monster uses + inline int CapIndex( int afCapMask ) + { + if (afCapMask & (bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE)) + return 1; + return 0; + } + + + inline CNode &Node( int i ) + { +#ifdef _DEBUG + if ( !m_pNodes || i < 0 || i > m_cNodes ) + ALERT( at_error, "Bad Node!\n" ); +#endif + return m_pNodes[i]; + } + + inline CLink &Link( int i ) + { +#ifdef _DEBUG + if ( !m_pLinkPool || i < 0 || i > m_cLinks ) + ALERT( at_error, "Bad link!\n" ); +#endif + return m_pLinkPool[i]; + } + + inline CLink &NodeLink( int iNode, int iLink ) + { + return Link( Node( iNode ).m_iFirstLink + iLink ); + } + + inline CLink &NodeLink( const CNode &node, int iLink ) + { + return Link( node.m_iFirstLink + iLink ); + } + + inline int INodeLink ( int iNode, int iLink ) + { + return NodeLink( iNode, iLink ).m_iDestNode; + } + +#if 0 + inline CNode &SourceNode( int iNode, int iLink ) + { + return Node( NodeLink( iNode, iLink ).m_iSrcNode ); + } + + inline CNode &DestNode( int iNode, int iLink ) + { + return Node( NodeLink( iNode, iLink ).m_iDestNode ); + } + + inline CNode *PNodeLink ( int iNode, int iLink ) + { + return &DestNode( iNode, iLink ); + } +#endif +}; + +//========================================================= +// Nodes start out as ents in the level. The node graph +// is built, then these ents are discarded. +//========================================================= +class CNodeEnt : public CMBaseEntity +{ + void Spawn( void ); + void KeyValue( KeyValueData *pkvd ); + virtual int ObjectCaps( void ) { return CMBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + short m_sHintType; + short m_sHintActivity; +}; + + +//========================================================= +// Node viewer +//========================================================= +class CNodeViewer : public CMBaseEntity +{ +public: + void Spawn( void ); + + int m_iBaseNode; + int m_iDraw; + int m_nVisited; + int m_aFrom[128]; + int m_aTo[128]; + int m_iHull; + int m_afNodeType; + Vector m_vecColor; + + void FindNodeConnections( int iNode ); + void AddNode( int iFrom, int iTo ); + void EXPORT DrawThink( void ); + +}; + +//========================================================= +// CStack - last in, first out. +//========================================================= +class CStack +{ +public: + CStack( void ); + void Push( int value ); + int Pop( void ); + int Top( void ); + int Empty( void ) { return m_level==0; } + int Size( void ) { return m_level; } + void CopyToArray ( int *piArray ); + +private: + int m_stack[ MAX_STACK_NODES ]; + int m_level; +}; + + +//========================================================= +// CQueue - first in, first out. +//========================================================= +class CQueue +{ +public: + + CQueue( void );// constructor + inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); } + inline int Empty ( void ) { return ( m_cSize == 0 ); } + //inline int Tail ( void ) { return ( m_queue[ m_tail ] ); } + inline int Size ( void ) { return ( m_cSize ); } + void Insert( int, float ); + int Remove( float & ); + +private: + int m_cSize; + struct tag_QUEUE_NODE + { + int Id; + float Priority; + } m_queue[ MAX_STACK_NODES ]; + int m_head; + int m_tail; +}; + +//========================================================= +// CQueuePriority - Priority queue (smallest item out first). +// +//========================================================= +class CQueuePriority +{ +public: + + CQueuePriority( void );// constructor + inline int Full ( void ) { return ( m_cSize == MAX_STACK_NODES ); } + inline int Empty ( void ) { return ( m_cSize == 0 ); } + //inline int Tail ( float & ) { return ( m_queue[ m_tail ].Id ); } + inline int Size ( void ) { return ( m_cSize ); } + void Insert( int, float ); + int Remove( float &); + +private: + int m_cSize; + struct tag_HEAP_NODE + { + int Id; + float Priority; + } m_heap[ MAX_STACK_NODES ]; + void Heap_SiftDown(int); + void Heap_SiftUp(void); + +}; + +//========================================================= +// hints - these MUST coincide with the HINTS listed under +// info_node in the FGD file! +//========================================================= +enum +{ + HINT_NONE = 0, + HINT_WORLD_DOOR, + HINT_WORLD_WINDOW, + HINT_WORLD_BUTTON, + HINT_WORLD_MACHINERY, + HINT_WORLD_LEDGE, + HINT_WORLD_LIGHT_SOURCE, + HINT_WORLD_HEAT_SOURCE, + HINT_WORLD_BLINKING_LIGHT, + HINT_WORLD_BRIGHT_COLORS, + HINT_WORLD_HUMAN_BLOOD, + HINT_WORLD_ALIEN_BLOOD, + + HINT_TACTICAL_EXIT = 100, + HINT_TACTICAL_VANTAGE, + HINT_TACTICAL_AMBUSH, + + HINT_STUKA_PERCH = 300, + HINT_STUKA_LANDING, +}; + +extern CGraph WorldGraph; diff --git a/src/dlls/otis.cpp b/src/dlls/otis.cpp index df81f42..5de9f58 100644 --- a/src/dlls/otis.cpp +++ b/src/dlls/otis.cpp @@ -1,329 +1,329 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// monster template -//========================================================= -// UNDONE: Holster weapon? - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "defaultai.h" -#include "weapons.h" - -#define NUM_OTIS_HEADS 2 // heads available for otis model - -#define GUN_GROUP 1 -#define HEAD_GROUP 2 - -#define HEAD_HAIR 0 -#define HEAD_BALD 1 - -#define GUN_NONE 0 -#define GUN_EAGLE 1 -#define GUN_DONUT 2 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -// first flag is Otis dying for scripted sequences? -#define OTIS_AE_DRAW ( 2 ) -#define OTIS_AE_SHOOT ( 3 ) -#define OTIS_AE_HOLSTER ( 4 ) - -#define OTIS_BODY_GUNHOLSTERED 0 -#define OTIS_BODY_GUNDRAWN 1 -#define OTIS_BODY_DONUT 2 - -//========================================================= -// ALertSound - otis says "Freeze!" -//========================================================= -void CMOtis::AlertSound(void) -{ - if (m_hEnemy != 0) - { - if (FOkToSpeak()) - { - PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE); - } - } -} - -//========================================================= -// BarneyFirePistol - shoots one round from the pistol at -// the enemy otis is facing. -//========================================================= -void CMOtis::BarneyFirePistol(void) -{ - Vector vecShootOrigin; - - UTIL_MakeVectors(pev->angles); - vecShootOrigin = pev->origin + Vector(0, 0, 55); - Vector vecShootDir = ShootAtEnemy(vecShootOrigin); - - Vector angDir = UTIL_VecToAngles(vecShootDir); - SetBlending(0, angDir.x); - pev->effects = EF_MUZZLEFLASH; - - FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_357); - - int pitchShift = RANDOM_LONG(0, 20); - - // Only shift about half the time - if (pitchShift > 10) - pitchShift = 0; - else - pitchShift -= 5; - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift); - - // UNDONE: Reload? - m_cAmmoLoaded--;// take away a bullet! -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -// -// Returns number of events handled, 0 if none. -//========================================================= -void CMOtis::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch (pEvent->event) - { - case OTIS_AE_SHOOT: - BarneyFirePistol(); - break; - - case OTIS_AE_DRAW: - // otis' bodygroup switches here so he can pull gun from holster - // pev->body = OTIS_BODY_GUNDRAWN; - SetBodygroup( GUN_GROUP, GUN_EAGLE ); - m_fGunDrawn = TRUE; - break; - - case OTIS_AE_HOLSTER: - // change bodygroup to replace gun in holster - // pev->body = OTIS_BODY_GUNHOLSTERED; - SetBodygroup( GUN_GROUP, GUN_NONE ); - m_fGunDrawn = FALSE; - break; - - default: - CMBarney::HandleAnimEvent(pEvent); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMOtis::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/otis.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->health = gSkillData.otisHealth; - pev->view_ofs = Vector(0, 0, 50);// position of the eyes relative to monster's origin. - m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello - m_MonsterState = MONSTERSTATE_NONE; - - pev->body = 0; // gun in holster - m_fGunDrawn = FALSE; - - m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - - // Make sure hands are white. - pev->skin = 0; - - // Select a random head. - if (head == -1) - { - SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1)); - } - else - { - SetBodygroup(HEAD_GROUP, head); - } - - if (bodystate == -1) - { - SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut - } - else - { - SetBodygroup(GUN_GROUP, bodystate); - } - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_otis" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Otis" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMOtis::Precache() -{ - PRECACHE_MODEL("models/otis.mdl"); - - PRECACHE_SOUND("weapons/desert_eagle_fire.wav"); - - PRECACHE_SOUND("barney/ba_pain1.wav"); - PRECACHE_SOUND("barney/ba_pain2.wav"); - PRECACHE_SOUND("barney/ba_pain3.wav"); - - PRECACHE_SOUND("barney/ba_die1.wav"); - PRECACHE_SOUND("barney/ba_die2.wav"); - PRECACHE_SOUND("barney/ba_die3.wav"); - - // every new otis must call this, otherwise - // when a level is loaded, nobody will talk (time is reset to 0) - TalkInit(); - CMTalkMonster::Precache(); -} - -// Init talk data -void CMOtis::TalkInit() -{ - CMTalkMonster::TalkInit(); - - // scientists speach group names (group names are in sentences.txt) - - m_szGrp[TLK_ANSWER] = "OT_ANSWER"; - m_szGrp[TLK_QUESTION] = "OT_QUESTION"; - m_szGrp[TLK_IDLE] = "OT_IDLE"; - m_szGrp[TLK_STARE] = "OT_STARE"; - m_szGrp[TLK_USE] = "OT_OK"; - m_szGrp[TLK_UNUSE] = "OT_WAIT"; - m_szGrp[TLK_STOP] = "OT_STOP"; - - m_szGrp[TLK_NOSHOOT] = "OT_SCARED"; - m_szGrp[TLK_HELLO] = "OT_HELLO"; - - m_szGrp[TLK_PLHURT1] = "!OT_CUREA"; - m_szGrp[TLK_PLHURT2] = "!OT_CUREB"; - m_szGrp[TLK_PLHURT3] = "!OT_CUREC"; - - m_szGrp[TLK_PHELLO] = NULL; - m_szGrp[TLK_PIDLE] = NULL; - m_szGrp[TLK_PQUESTION] = NULL; - - m_szGrp[TLK_SMELL] = "OT_SMELL"; - - m_szGrp[TLK_WOUND] = "OT_WOUND"; - m_szGrp[TLK_MORTAL] = "OT_MORTAL"; - - // get voice for head - just one otis voice for now - m_voicePitch = 100; -} - - -int CMOtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - // make sure friends talk about it if player hurts talkmonsters... - int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); - if (!IsAlive() || pev->deadflag == DEAD_DYING) - return ret; - - if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT)) - { - // This is a heurstic to determine if the player intended to harm me - // If I have an enemy, we can't establish intent (may just be crossfire) - if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) ) - { - Remember( bits_MEMORY_PROVOKED ); - } - } - - return ret; -} - -void CMOtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - switch (ptr->iHitgroup) - { - case HITGROUP_CHEST: - case HITGROUP_STOMACH: - if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) - { - flDamage = flDamage / 2; - } - break; - case 10: // Otis wears no helmet, so do not prevent taking headshot damage. - // always a head shot - ptr->iHitgroup = HITGROUP_HEAD; - break; - default: - break; - } - CMTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); -} - - -void CMOtis::Killed(entvars_t *pevAttacker, int iGib) -{ - if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED) - { - // drop the gun! - SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED); - } - - CMTalkMonster::Killed(pevAttacker, iGib); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - -//========================================================= -// GetSchedule - Decides which type of schedule best suits -// the monster's current state and conditions. Then calls -// monster's member function to get a pointer to a schedule -// of the proper type. -//========================================================= -Schedule_t *CMOtis::GetSchedule(void) -{ - if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak()) - { - PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM); - } - - return CMBarney::GetSchedule(); -} - -void CMOtis::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "head")) - { - head = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBarney::KeyValue(pkvd); -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// monster template +//========================================================= +// UNDONE: Holster weapon? + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "defaultai.h" +#include "weapons.h" + +#define NUM_OTIS_HEADS 2 // heads available for otis model + +#define GUN_GROUP 1 +#define HEAD_GROUP 2 + +#define HEAD_HAIR 0 +#define HEAD_BALD 1 + +#define GUN_NONE 0 +#define GUN_EAGLE 1 +#define GUN_DONUT 2 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +// first flag is Otis dying for scripted sequences? +#define OTIS_AE_DRAW ( 2 ) +#define OTIS_AE_SHOOT ( 3 ) +#define OTIS_AE_HOLSTER ( 4 ) + +#define OTIS_BODY_GUNHOLSTERED 0 +#define OTIS_BODY_GUNDRAWN 1 +#define OTIS_BODY_DONUT 2 + +//========================================================= +// ALertSound - otis says "Freeze!" +//========================================================= +void CMOtis::AlertSound(void) +{ + if (m_hEnemy != 0) + { + if (FOkToSpeak()) + { + PlaySentence("OT_ATTACK", RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE); + } + } +} + +//========================================================= +// BarneyFirePistol - shoots one round from the pistol at +// the enemy otis is facing. +//========================================================= +void CMOtis::BarneyFirePistol(void) +{ + Vector vecShootOrigin; + + UTIL_MakeVectors(pev->angles); + vecShootOrigin = pev->origin + Vector(0, 0, 55); + Vector vecShootDir = ShootAtEnemy(vecShootOrigin); + + Vector angDir = UTIL_VecToAngles(vecShootDir); + SetBlending(0, angDir.x); + pev->effects = EF_MUZZLEFLASH; + + FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, BULLET_MONSTER_357); + + int pitchShift = RANDOM_LONG(0, 20); + + // Only shift about half the time + if (pitchShift > 10) + pitchShift = 0; + else + pitchShift -= 5; + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/desert_eagle_fire.wav", 1, ATTN_NORM, 0, 100 + pitchShift); + + // UNDONE: Reload? + m_cAmmoLoaded--;// take away a bullet! +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +// +// Returns number of events handled, 0 if none. +//========================================================= +void CMOtis::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case OTIS_AE_SHOOT: + BarneyFirePistol(); + break; + + case OTIS_AE_DRAW: + // otis' bodygroup switches here so he can pull gun from holster + // pev->body = OTIS_BODY_GUNDRAWN; + SetBodygroup( GUN_GROUP, GUN_EAGLE ); + m_fGunDrawn = TRUE; + break; + + case OTIS_AE_HOLSTER: + // change bodygroup to replace gun in holster + // pev->body = OTIS_BODY_GUNHOLSTERED; + SetBodygroup( GUN_GROUP, GUN_NONE ); + m_fGunDrawn = FALSE; + break; + + default: + CMBarney::HandleAnimEvent(pEvent); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMOtis::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/otis.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.otisHealth; + pev->view_ofs = Vector(0, 0, 50);// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + + pev->body = 0; // gun in holster + m_fGunDrawn = FALSE; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + // Make sure hands are white. + pev->skin = 0; + + // Select a random head. + if (head == -1) + { + SetBodygroup(HEAD_GROUP, RANDOM_LONG(0, NUM_OTIS_HEADS - 1)); + } + else + { + SetBodygroup(HEAD_GROUP, head); + } + + if (bodystate == -1) + { + SetBodygroup(GUN_GROUP, RANDOM_LONG(OTIS_BODY_GUNHOLSTERED, OTIS_BODY_GUNDRAWN)); // don't random donut + } + else + { + SetBodygroup(GUN_GROUP, bodystate); + } + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_otis" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Otis" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMOtis::Precache() +{ + PRECACHE_MODEL("models/otis.mdl"); + + PRECACHE_SOUND("weapons/desert_eagle_fire.wav"); + + PRECACHE_SOUND("barney/ba_pain1.wav"); + PRECACHE_SOUND("barney/ba_pain2.wav"); + PRECACHE_SOUND("barney/ba_pain3.wav"); + + PRECACHE_SOUND("barney/ba_die1.wav"); + PRECACHE_SOUND("barney/ba_die2.wav"); + PRECACHE_SOUND("barney/ba_die3.wav"); + + // every new otis must call this, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + TalkInit(); + CMTalkMonster::Precache(); +} + +// Init talk data +void CMOtis::TalkInit() +{ + CMTalkMonster::TalkInit(); + + // scientists speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = "OT_ANSWER"; + m_szGrp[TLK_QUESTION] = "OT_QUESTION"; + m_szGrp[TLK_IDLE] = "OT_IDLE"; + m_szGrp[TLK_STARE] = "OT_STARE"; + m_szGrp[TLK_USE] = "OT_OK"; + m_szGrp[TLK_UNUSE] = "OT_WAIT"; + m_szGrp[TLK_STOP] = "OT_STOP"; + + m_szGrp[TLK_NOSHOOT] = "OT_SCARED"; + m_szGrp[TLK_HELLO] = "OT_HELLO"; + + m_szGrp[TLK_PLHURT1] = "!OT_CUREA"; + m_szGrp[TLK_PLHURT2] = "!OT_CUREB"; + m_szGrp[TLK_PLHURT3] = "!OT_CUREC"; + + m_szGrp[TLK_PHELLO] = NULL; + m_szGrp[TLK_PIDLE] = NULL; + m_szGrp[TLK_PQUESTION] = NULL; + + m_szGrp[TLK_SMELL] = "OT_SMELL"; + + m_szGrp[TLK_WOUND] = "OT_WOUND"; + m_szGrp[TLK_MORTAL] = "OT_MORTAL"; + + // get voice for head - just one otis voice for now + m_voicePitch = 100; +} + + +int CMOtis::TakeDamage(entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + // make sure friends talk about it if player hurts talkmonsters... + int ret = CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); + if (!IsAlive() || pev->deadflag == DEAD_DYING) + return ret; + + if (m_MonsterState != MONSTERSTATE_PRONE && (pevAttacker->flags & FL_CLIENT)) + { + // This is a heurstic to determine if the player intended to harm me + // If I have an enemy, we can't establish intent (may just be crossfire) + if ( ( m_hEnemy != NULL ) && UTIL_IsPlayer(m_hEnemy) ) + { + Remember( bits_MEMORY_PROVOKED ); + } + } + + return ret; +} + +void CMOtis::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + switch (ptr->iHitgroup) + { + case HITGROUP_CHEST: + case HITGROUP_STOMACH: + if (bitsDamageType & (DMG_BULLET | DMG_SLASH | DMG_BLAST)) + { + flDamage = flDamage / 2; + } + break; + case 10: // Otis wears no helmet, so do not prevent taking headshot damage. + // always a head shot + ptr->iHitgroup = HITGROUP_HEAD; + break; + default: + break; + } + CMTalkMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); +} + + +void CMOtis::Killed(entvars_t *pevAttacker, int iGib) +{ + if (GetBodygroup(GUN_GROUP) != OTIS_BODY_GUNHOLSTERED) + { + // drop the gun! + SetBodygroup(GUN_GROUP, OTIS_BODY_GUNHOLSTERED); + } + + CMTalkMonster::Killed(pevAttacker, iGib); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + +//========================================================= +// GetSchedule - Decides which type of schedule best suits +// the monster's current state and conditions. Then calls +// monster's member function to get a pointer to a schedule +// of the proper type. +//========================================================= +Schedule_t *CMOtis::GetSchedule(void) +{ + if (HasConditions(bits_COND_ENEMY_DEAD) && FOkToSpeak()) + { + PlaySentence("OT_KILL", 4, VOL_NORM, ATTN_NORM); + } + + return CMBarney::GetSchedule(); +} + +void CMOtis::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "head")) + { + head = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBarney::KeyValue(pkvd); +} diff --git a/src/dlls/pitdrone.cpp b/src/dlls/pitdrone.cpp index f7e8ea0..4bc4009 100644 --- a/src/dlls/pitdrone.cpp +++ b/src/dlls/pitdrone.cpp @@ -1,1064 +1,1064 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "nodes.h" -#include "effects.h" -#include "decals.h" - -/* - * In Opposing Force pitdrone spawned via monstermaker did not have spikes - * That's probably a bug, because number of spikes is set in level editor, - * so spawned pitdrones always had 0 spikes. - * Having no spikes after spawn also prevented spike reloading. - * Those who want to keep original Opposing Force behavior can set this constant to zero. - */ -#define FEATURE_PITDRONE_SPAWN_WITH_SPIKES 1 - -// Disable this feature if you don't want to include spike_trail.spr in your mod -#define FEATURE_PITDRONE_SPIKE_TRAIL 1 - -#if FEATURE_PITDRONE_SPIKE_TRAIL -int iSpikeTrail; -#endif -int iPitdroneSpitSprite; - -void CPitdroneSpike::Spawn(void) -{ - pev->movetype = MOVETYPE_FLY; - pev->classname = MAKE_STRING("pitdronespike"); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), "models/pit_drone_spike.mdl"); - pev->frame = 0; - pev->scale = 0.5; - - UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4)); -} - -void CPitdroneSpike::SpikeTouch(edict_t *pOther) -{ - int iPitch; - - // splat sound - iPitch = RANDOM_FLOAT(115, 125); - - if (!pOther->v.takedamage) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hit1.wav", 1, ATTN_NORM, 0, iPitch); - - SetThink(&CMBaseEntity::SUB_Remove); - pev->nextthink = gpGlobals->time; - - if (FClassnameIs(pOther, "worldspawn")) - { - // if what we hit is static architecture, can stay around for a while. - Vector vecDir = pev->velocity.Normalize(); - UTIL_SetOrigin(pev, pev->origin - vecDir * 12); - pev->angles = UTIL_VecToAngles(vecDir); - pev->solid = SOLID_NOT; - pev->movetype = MOVETYPE_FLY; - pev->velocity = Vector(0, 0, 0); - pev->avelocity.z = 0; - pev->angles.z = RANDOM_LONG(0, 360); - pev->nextthink = gpGlobals->time + 10.0; - } - } - else - { - entvars_t *pevOwner = VARS(pev->owner); - - if ( UTIL_IsPlayer( pOther ) ) - UTIL_TakeDamage( pOther, pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB ); - else if ( pOther->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage( pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB ); - } - - if (RANDOM_LONG(0,1)) - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM, 0, iPitch); - else - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM, 0, iPitch); - - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time; - } -} - -void CPitdroneSpike::StartTrail() -{ -#if FEATURE_PITDRONE_SPIKE_TRAIL - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT( entindex() ); - WRITE_SHORT( iSpikeTrail ); // model - WRITE_BYTE(2); // life - WRITE_BYTE(1); // width - WRITE_BYTE(197); // r - WRITE_BYTE(194); // g - WRITE_BYTE(11); // b - WRITE_BYTE(192); //brigtness - MESSAGE_END(); -#endif - SetTouch(&CPitdroneSpike::SpikeTouch); -} - -edict_t *CPitdroneSpike::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles) -{ - CPitdroneSpike *pSpit = CreateClassPtr( (CPitdroneSpike *)NULL ); - - if (pSpit == NULL) - return NULL; - - pSpit->Spawn(); - - UTIL_SetOrigin( pSpit->pev, vecStart ); - pSpit->pev->velocity = vecVelocity; - pSpit->pev->angles = vecAngles; - pSpit->pev->owner = ENT( pevOwner ); - - pSpit->SetThink(&CPitdroneSpike::StartTrail); - pSpit->pev->nextthink = gpGlobals->time + 0.1; - - return pSpit->edict(); -} - -// -// PitDrone, main part. -// -#define HORNGROUP 1 -#define PITDRONE_HORNS0 0 -#define PITDRONE_HORNS1 1 -#define PITDRONE_HORNS2 2 -#define PITDRONE_HORNS3 3 -#define PITDRONE_HORNS4 4 -#define PITDRONE_HORNS5 5 -#define PITDRONE_HORNS6 6 -#define PITDRONE_SPRINT_DIST 255 -#define PITDRONE_FLINCH_DELAY 2 // at most one flinch every n secs -#define PITDRONE_MAX_HORNS 6 -#define PITDRONE_GIB_COUNT 7 - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_PDRONE_HURTHOP = LAST_COMMON_SCHEDULE + 1, - SCHED_PDRONE_SMELLFOOD, - SCHED_PDRONE_EAT, - SCHED_PDRONE_SNIFF_AND_EAT, - SCHED_PDRONE_COVER_AND_RELOAD -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_PDRONE_HOPTURN = LAST_COMMON_TASK + 1 -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define PIT_DRONE_AE_SPIT ( 1 ) -// not sure what it is. It happens twice when pitdrone uses two claws at the same time -// once before 'throw' event and once after -#define PIT_DRONE_AE_ATTACK ( 2 ) -#define PIT_DRONE_AE_SLASH ( 4 ) -#define PIT_DRONE_AE_HOP ( 5 ) -#define PIT_DRONE_AE_THROW ( 6 ) -#define PIT_DRONE_AE_RELOAD ( 7 ) - -void CMPitdrone::KeyValue(KeyValueData *pkvd) -{ - if (FStrEq(pkvd->szKeyName, "initammo")) - { - m_iInitialAmmo = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBaseMonster::KeyValue(pkvd); -} - -//========================================================= -// IgnoreConditions -//========================================================= -int CMPitdrone::IgnoreConditions(void) -{ - int iIgnore = CMBaseMonster::IgnoreConditions(); - - if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2)) - { - if (m_flNextFlinch >= gpGlobals->time) - iIgnore |= (bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE); - } - - if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) - { - if (m_flNextFlinch < gpGlobals->time) - m_flNextFlinch = gpGlobals->time + PITDRONE_FLINCH_DELAY; - } - - return iIgnore; - -} - -const char *CMPitdrone::pAttackMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char *CMPitdrone::pIdleSounds[] = -{ - "pitdrone/pit_drone_idle1.wav", - "pitdrone/pit_drone_idle2.wav", - "pitdrone/pit_drone_idle3.wav", - -}; - -const char *CMPitdrone::pAlertSounds[] = -{ - "pitdrone/pit_drone_alert1.wav", - "pitdrone/pit_drone_alert2.wav", - "pitdrone/pit_drone_alert3.wav", -}; - -const char *CMPitdrone::pPainSounds[] = -{ - "pitdrone/pit_drone_pain1.wav", - "pitdrone/pit_drone_pain2.wav", - "pitdrone/pit_drone_pain3.wav", - "pitdrone/pit_drone_pain4.wav", -}; - -const char *CMPitdrone::pDieSounds[] = -{ - "pitdrone/pit_drone_die1.wav", - "pitdrone/pit_drone_die2.wav", - "pitdrone/pit_drone_die3.wav", -}; - -//========================================================= -// TakeDamage - overridden for pitdrone so we can keep track -// of how much time has passed since it was last injured -//========================================================= -int CMPitdrone::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - float flDist; - Vector vecApex; - - // if the pitdrone is running, has an enemy, was hurt by the enemy, and isn't too close to the enemy, - // it will swerve. (whew). - if (m_hEnemy != 0 && IsMoving() && pevAttacker == VARS(m_hEnemy)) - { - flDist = (pev->origin - m_hEnemy->v.origin).Length2D(); - - if (flDist > PITDRONE_SPRINT_DIST) - { - flDist = (pev->origin - m_Route[m_iRouteIndex].vecLocation).Length2D();// reusing flDist. - - if (FTriangulate(pev->origin, m_Route[m_iRouteIndex].vecLocation, flDist * 0.5, m_hEnemy, &vecApex)) - { - InsertWaypoint(vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY); - } - } - } - - m_flLastHurtTime = gpGlobals->time; - - return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - -//========================================================= -// CheckMeleeAttack1 - attack with both claws at the same time -//========================================================= -BOOL CMPitdrone::CheckMeleeAttack1(float flDot, float flDist) -{ - // Give a better chance for MeleeAttack2 - if (RANDOM_LONG(0,2) == 0) { - return CMBaseMonster::CheckMeleeAttack1(flDot, flDist); - } - return FALSE; -} - -//========================================================= -// CheckRangeAttack1 - spike attack -//========================================================= -BOOL CMPitdrone::CheckRangeAttack1(float flDot, float flDist) -{ - if (m_cAmmoLoaded <= 0) - { - return FALSE; - } - if (IsMoving() && flDist >= 512) - { - // pitdone will far too far behind if he stops running to spit at this distance from the enemy. - return FALSE; - } - - if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextSpitTime) - { - - if (IsMoving()) - { - // don't spit again for a long time, resume chasing enemy. - m_flNextSpitTime = gpGlobals->time + 5; - } - else - { - // not moving, so spit again pretty soon. - m_flNextSpitTime = gpGlobals->time + 1; - } - - return TRUE; - } - - return FALSE; - -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMPitdrone::SetYawSpeed(void) -{ - int ys; - - ys = 0; - - switch (m_Activity) - { - case ACT_WALK: ys = 120; break; - case ACT_RUN: ys = 120; break; - case ACT_IDLE: ys = 120; break; - case ACT_RANGE_ATTACK1: ys = 120; break; - default: - ys = 120; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. In the base class implementation, -// monsters care about all sounds, but no scents. -//========================================================= -int CMPitdrone::ISoundMask( void ) -{ - return 0; -} - -void CMPitdrone::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch (pEvent->event) - { - case PIT_DRONE_AE_ATTACK: - break; - case PIT_DRONE_AE_THROW: - { - // SOUND HERE (in the pitdrone model) - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.pitdroneDmgWhip, DMG_SLASH ); - - if( pHurt ) - { - // croonchy bite sound - const int iPitch = RANDOM_FLOAT( 110, 120 ); - switch( RANDOM_LONG( 0, 1 ) ) - { - case 0: - EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 0.7, ATTN_NORM, 0, iPitch ); - break; - case 1: - EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 0.7, ATTN_NORM, 0, iPitch ); - break; - } - - // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. - UTIL_ScreenShake( pHurt->v.origin, 15.0, 1.5, 0.7, 2 ); - - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 200; - } - else - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, pAttackMissSounds[RANDOM_LONG(0, ARRAYSIZE(pAttackMissSounds) - 1)], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5, 5)); - } - break; - - case PIT_DRONE_AE_SLASH: - { - /* The same event is reused for both right and left claw attacks. - * Pitdrone always starts the attack with the right claw so we use shouldAttackWithLeftClaw to check which claw is used now. - */ - // SOUND HERE (in the pitdrone model) - edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.pitdroneDmgBite, DMG_SLASH); - if (pHurt) - { - if (pHurt->v.flags & (FL_MONSTER | FL_CLIENT)) - { - pHurt->v.punchangle.z = shouldAttackWithLeftClaw ? 18 : -18; - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * ( shouldAttackWithLeftClaw ? 100 : -100 ); - } - } - else // Play a random attack miss sound - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, pAttackMissSounds[RANDOM_LONG(0, ARRAYSIZE(pAttackMissSounds) - 1)], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5, 5)); - shouldAttackWithLeftClaw = !shouldAttackWithLeftClaw; - } - break; - - case PIT_DRONE_AE_RELOAD: - { - if (m_iInitialAmmo >= 0) - m_cAmmoLoaded = PITDRONE_MAX_HORNS; - else - m_cAmmoLoaded = 0; - BodyChange(m_cAmmoLoaded); - ClearConditions(bits_COND_NO_AMMO_LOADED); - } - break; - - case PIT_DRONE_AE_HOP: - { - float flGravity = g_psv_gravity->value; - - // throw the squid up into the air on this frame. - if( FBitSet( pev->flags, FL_ONGROUND ) ) - { - pev->flags -= FL_ONGROUND; - } - - // jump into air for 0.8 (24/30) seconds - pev->velocity.z += ( 0.625 * flGravity ) * 0.5; - } - break; - - case PIT_DRONE_AE_SPIT: - { - m_cAmmoLoaded--; - BodyChange(m_cAmmoLoaded); - - Vector vecSpitOffset; - Vector vecSpitDir; - - UTIL_MakeAimVectors(pev->angles); - - // !!!HACKHACK - the spot at which the spit originates (in front of the mouth) was measured in 3ds and hardcoded here. - // we should be able to read the position of bones at runtime for this info. - vecSpitOffset = (gpGlobals->v_forward * 15 + gpGlobals->v_up * 36); - vecSpitOffset = (pev->origin + vecSpitOffset); - //vecSpitDir = ((m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs) - vecSpitOffset).Normalize(); - Vector vecEnemyPosition; - if (m_hEnemy != 0) - vecEnemyPosition = UTIL_BodyTarget(m_hEnemy, pev->origin); - else - vecEnemyPosition = m_vecEnemyLKP; - vecSpitDir = (vecEnemyPosition - vecSpitOffset).Normalize(); - - vecSpitDir.x += RANDOM_FLOAT(-0.01, 0.01); - vecSpitDir.y += RANDOM_FLOAT(-0.01, 0.01); - vecSpitDir.z += RANDOM_FLOAT(-0.01, 0); - - // SOUND HERE! (in the pitdrone model) - - CPitdroneSpike::Shoot(pev, vecSpitOffset, vecSpitDir * 900, UTIL_VecToAngles(vecSpitDir)); - - // spew the spittle temporary ents. - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOffset ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( vecSpitOffset.x ); // pos - WRITE_COORD( vecSpitOffset.y ); - WRITE_COORD( vecSpitOffset.z ); - WRITE_COORD( vecSpitDir.x ); // dir - WRITE_COORD( vecSpitDir.y ); - WRITE_COORD( vecSpitDir.z ); - WRITE_SHORT( iPitdroneSpitSprite ); // model - WRITE_BYTE( 15 ); // count - WRITE_BYTE( 210 ); // speed - WRITE_BYTE( 25 ); // noise ( client will divide by 100 ) - MESSAGE_END(); - } - break; - - - default: - CMBaseMonster::HandleAnimEvent(pEvent); - } -} - -int CMPitdrone::Classify(void) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_RACEX_PITDRONE; -} - -void CMPitdrone::BodyChange(float horns) -{ - if (horns <= 0) - SetBodygroup(HORNGROUP, PITDRONE_HORNS0); - // pev->body = PITDRONE_HORNS0; - - if (horns == 1) - SetBodygroup(HORNGROUP, PITDRONE_HORNS6); - // pev->body = PITDRONE_HORNS6; - - if (horns == 2) - SetBodygroup(HORNGROUP, PITDRONE_HORNS5); - // pev->body = PITDRONE_HORNS5; - - if (horns == 3) - SetBodygroup(HORNGROUP, PITDRONE_HORNS4); - // pev->body = PITDRONE_HORNS4; - - if (horns == 4) - SetBodygroup(HORNGROUP, PITDRONE_HORNS3); - // pev->body = PITDRONE_HORNS3; - - if (horns == 5) - SetBodygroup(HORNGROUP, PITDRONE_HORNS2); - // pev->body = PITDRONE_HORNS2; - - if (horns >= 6) - SetBodygroup(HORNGROUP, PITDRONE_HORNS1); - // pev->body = PITDRONE_HORNS1; - - return; -} -//========================================================= -// Spawn -//========================================================= -void CMPitdrone::Spawn() -{ - Precache(); - - SET_MODEL( ENT(pev), "models/pit_drone.mdl" ); - UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 48)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.pitdroneHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_flNextSpitTime = gpGlobals->time; - - if (m_iInitialAmmo >= 0) - { - m_cAmmoLoaded = min(m_iInitialAmmo, PITDRONE_MAX_HORNS); -#if FEATURE_PITDRONE_SPAWN_WITH_SPIKES - if (!m_cAmmoLoaded) { - m_cAmmoLoaded = PITDRONE_MAX_HORNS; - } -#endif - } - BodyChange(m_cAmmoLoaded); - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_pitdrone" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Pit Drone" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMPitdrone::Precache() -{ - PRECACHE_MODEL("models/pit_drone.mdl"); - PRECACHE_MODEL("models/pit_drone_gibs.mdl"); - iPitdroneSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle. - - PRECACHE_SOUND_ARRAY(pAttackMissSounds); - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pDieSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pAlertSounds); - - PRECACHE_SOUND("bullchicken/bc_bite2.wav"); - PRECACHE_SOUND("bullchicken/bc_bite3.wav"); - - PRECACHE_SOUND("pitdrone/pit_drone_melee_attack1.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_melee_attack2.wav"); - - PRECACHE_SOUND("pitdrone/pit_drone_attack_spike1.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_attack_spike2.wav"); - - PRECACHE_SOUND("pitdrone/pit_drone_communicate1.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_communicate2.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_communicate3.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_communicate4.wav"); - - PRECACHE_SOUND("pitdrone/pit_drone_eat.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_hunt1.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_hunt2.wav"); - PRECACHE_SOUND("pitdrone/pit_drone_hunt3.wav"); - - PRECACHE_SOUND("weapons/xbow_hitbod1.wav"); - PRECACHE_SOUND("weapons/xbow_hitbod2.wav"); - PRECACHE_SOUND("weapons/xbow_hit1.wav"); - - //UTIL_PrecacheOther("pitdronespike"); - PRECACHE_MODEL("models/pit_drone_spike.mdl");// spit projectile - PRECACHE_SOUND("weapons/xbow_hitbod1.wav"); - PRECACHE_SOUND("weapons/xbow_hitbod2.wav"); - PRECACHE_SOUND("weapons/xbow_hit1.wav"); -#if FEATURE_PITDRONE_SPIKE_TRAIL - iSpikeTrail = PRECACHE_MODEL("sprites/spike_trail.spr"); -#endif -} - -//========================================================= -// IdleSound -//========================================================= -#define PITDRONE_ATTN_IDLE (float)1.5 -void CMPitdrone::IdleSound(void) -{ - EMIT_SOUND(ENT(pev), CHAN_VOICE, pIdleSounds[RANDOM_LONG(0, ARRAYSIZE(pIdleSounds)-1)], 1, PITDRONE_ATTN_IDLE); -} - -//========================================================= -// PainSound -//========================================================= -void CMPitdrone::PainSound(void) -{ - int iPitch = RANDOM_LONG(85, 120); - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0, ARRAYSIZE(pPainSounds)-1)], 1, ATTN_NORM, 0, iPitch); -} - -//========================================================= -// AlertSound -//========================================================= -void CMPitdrone::AlertSound(void) -{ - int iPitch = RANDOM_LONG(140, 160); - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0, ARRAYSIZE(pAlertSounds)-1)], 1, ATTN_NORM, 0, iPitch); -} -//========================================================= -// DeathSound -//========================================================= -void CMPitdrone::DeathSound(void) -{ - EMIT_SOUND(ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0, ARRAYSIZE(pDieSounds)-1)], 1, ATTN_NORM); -} - -void CMPitdrone::RunAI(void) -{ - // first, do base class stuff - CMBaseMonster::RunAI(); - - if (m_hEnemy != 0 && m_Activity == ACT_RUN) - { - // chasing enemy. Sprint for last bit - if ((pev->origin - m_hEnemy->v.origin).Length2D() < PITDRONE_SPRINT_DIST) - { - pev->framerate = 1.25; - } - } -} - -void CMPitdrone::CheckAmmo( void ) -{ - if( m_cAmmoLoaded <= 0 && m_iInitialAmmo >= 0 ) - { - SetConditions( bits_COND_NO_AMMO_LOADED ); - } -} - -void CMPitdrone::GibMonster() -{ - EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); - - if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly - { - CMGib::SpawnRandomGibs( pev, PITDRONE_GIB_COUNT, "models/pit_drone_gibs.mdl", 0 ); // Throw alien gibs - } - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time; -} - -//======================================================== -// AI Schedules Specific to this monster -//========================================================= - -// primary range attack -Task_t tlPDroneRangeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slPDroneRangeAttack1[] = -{ - { - tlPDroneRangeAttack1, - ARRAYSIZE(tlPDroneRangeAttack1), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "PDrone Range Attack1" - }, -}; - -// Chase enemy schedule -Task_t tlPDroneChaseEnemy1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty PitDrone oscillation. - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slPDroneChaseEnemy[] = -{ - { - tlPDroneChaseEnemy1, - ARRAYSIZE(tlPDroneChaseEnemy1), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_SMELL_FOOD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_TASK_FAILED | - bits_COND_HEAR_SOUND, - - 0, - "PDrone Chase Enemy" - }, -}; - -Task_t tlPDroneHurtHop[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SOUND_WAKE, (float)0 }, - { TASK_PDRONE_HOPTURN, (float)0 }, - { TASK_FACE_ENEMY, (float)0 },// in case squid didn't turn all the way in the air. -}; - -Schedule_t slPDroneHurtHop[] = -{ - { - tlPDroneHurtHop, - ARRAYSIZE( tlPDroneHurtHop ), - 0, - 0, - "PDroneHurtHop" - } -}; - - -// PitDrone walks to something tasty and eats it. -Task_t tlPDroneEat[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_EAT, (float)10 },// this is in case the PitDrone can't get to the food - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_EAT, (float)50 }, - { TASK_GET_PATH_TO_LASTPOSITION, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slPDroneEat[] = -{ - { - tlPDroneEat, - ARRAYSIZE(tlPDroneEat), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY, - - // even though HEAR_SOUND/SMELL FOOD doesn't break this schedule, we need this mask - // here or the monster won't detect these sounds at ALL while running this schedule. - 0, - "PDroneEat" - } -}; - -// this is a bit different than just Eat. We use this schedule when the food is far away, occluded, or behind -// the PitDrone. This schedule plays a sniff animation before going to the source of food. -Task_t tlPDroneSniffAndEat[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_EAT, (float)10 },// this is in case the PitDrone can't get to the food - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, - { TASK_EAT, (float)50 }, - { TASK_GET_PATH_TO_LASTPOSITION, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_CLEAR_LASTPOSITION, (float)0 }, -}; - -Schedule_t slPDroneSniffAndEat[] = -{ - { - tlPDroneSniffAndEat, - ARRAYSIZE(tlPDroneSniffAndEat), - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_NEW_ENEMY, - - // even though HEAR_SOUND/SMELL FOOD doesn't break this schedule, we need this mask - // here or the monster won't detect these sounds at ALL while running this schedule. - 0, - "PDroneSniffAndEat" - } -}; - -Task_t tlPDroneHideReload[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, -}; - -Schedule_t slPDroneHideReload[] = -{ - { - tlPDroneHideReload, - ARRAYSIZE( tlPDroneHideReload ), - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "PDroneHideReload" - } -}; - -DEFINE_CUSTOM_SCHEDULES(CMPitdrone) -{ - slPDroneRangeAttack1, - slPDroneChaseEnemy, - slPDroneHurtHop, - slPDroneEat, - slPDroneSniffAndEat, - slPDroneHideReload -}; - -IMPLEMENT_CUSTOM_SCHEDULES(CMPitdrone, CMBaseMonster) - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMPitdrone::GetSchedule(void) -{ - switch (m_MonsterState) - { - case MONSTERSTATE_ALERT: - { - if( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - return GetScheduleOfType( SCHED_PDRONE_HURTHOP ); - } - - if (HasConditions(bits_COND_SMELL_FOOD)) - { - // Just go (try) get it. - return GetScheduleOfType(SCHED_PDRONE_EAT); - } - - break; - } - case MONSTERSTATE_COMBAT: - { - // dead enemy - if (HasConditions(bits_COND_ENEMY_DEAD)) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster::GetSchedule(); - } - - if (HasConditions(bits_COND_NEW_ENEMY)) - { - return GetScheduleOfType(SCHED_WAKE_ANGRY); - } - - if (HasConditions(bits_COND_SMELL_FOOD)) - { - // Just go (try) get it. - return GetScheduleOfType(SCHED_PDRONE_EAT); - } - - if( HasConditions( bits_COND_NO_AMMO_LOADED ) && (m_iInitialAmmo >= 0) ) - { - return GetScheduleOfType( SCHED_PDRONE_COVER_AND_RELOAD ); - } - - if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) - { - return GetScheduleOfType(SCHED_RANGE_ATTACK1); - } - - if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) - { - return GetScheduleOfType(SCHED_MELEE_ATTACK1); - } - - if (HasConditions(bits_COND_CAN_MELEE_ATTACK2)) - { - return GetScheduleOfType(SCHED_MELEE_ATTACK2); - } - - return GetScheduleOfType(SCHED_CHASE_ENEMY); - - break; - } - } - - return CMBaseMonster::GetSchedule(); -} - -//========================================================= -// GetScheduleOfType -//========================================================= -Schedule_t* CMPitdrone::GetScheduleOfType(int Type) -{ - switch (Type) - { - case SCHED_RANGE_ATTACK1: - return &slPDroneRangeAttack1[0]; - break; - case SCHED_PDRONE_HURTHOP: - return &slPDroneHurtHop[0]; - break; - case SCHED_PDRONE_EAT: - return &slPDroneEat[0]; - break; - case SCHED_PDRONE_SNIFF_AND_EAT: - return &slPDroneSniffAndEat[0]; - break; - case SCHED_CHASE_ENEMY: - return &slPDroneChaseEnemy[0]; - break; - case SCHED_PDRONE_COVER_AND_RELOAD: - return &slPDroneHideReload[0]; - break; - } - - return CMBaseMonster::GetScheduleOfType(Type); -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. OVERRIDDEN for PitDrone because it needs to -// know explicitly when the last attempt to chase the enemy -// failed, since that impacts its attack choices. -//========================================================= -void CMPitdrone::StartTask(Task_t *pTask) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch (pTask->iTask) - { - case TASK_PDRONE_HOPTURN: - { - SetActivity( ACT_HOP ); - MakeIdealYaw( m_vecEnemyLKP ); - break; - } - case TASK_GET_PATH_TO_ENEMY: - { - if (BuildRoute(m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy)) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - else - { - ALERT(at_aiconsole, "GetPathToEnemy failed!!\n"); - TaskFail(); - } - break; - } - default: - { - CMBaseMonster::StartTask(pTask); - break; - } - } -} - -//========================================================= -// RunTask -//========================================================= -void CMPitdrone::RunTask(Task_t *pTask) -{ - switch( pTask->iTask ) - { - case TASK_PDRONE_HOPTURN: - { - MakeIdealYaw( m_vecEnemyLKP ); - ChangeYaw( pev->yaw_speed ); - - if( m_fSequenceFinished ) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - break; - } - default: - { - CMBaseMonster::RunTask( pTask ); - break; - } - } -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "nodes.h" +#include "effects.h" +#include "decals.h" + +/* + * In Opposing Force pitdrone spawned via monstermaker did not have spikes + * That's probably a bug, because number of spikes is set in level editor, + * so spawned pitdrones always had 0 spikes. + * Having no spikes after spawn also prevented spike reloading. + * Those who want to keep original Opposing Force behavior can set this constant to zero. + */ +#define FEATURE_PITDRONE_SPAWN_WITH_SPIKES 1 + +// Disable this feature if you don't want to include spike_trail.spr in your mod +#define FEATURE_PITDRONE_SPIKE_TRAIL 1 + +#if FEATURE_PITDRONE_SPIKE_TRAIL +int iSpikeTrail; +#endif +int iPitdroneSpitSprite; + +void CPitdroneSpike::Spawn(void) +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING("pitdronespike"); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAlpha; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), "models/pit_drone_spike.mdl"); + pev->frame = 0; + pev->scale = 0.5; + + UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4)); +} + +void CPitdroneSpike::SpikeTouch(edict_t *pOther) +{ + int iPitch; + + // splat sound + iPitch = RANDOM_FLOAT(115, 125); + + if (!pOther->v.takedamage) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hit1.wav", 1, ATTN_NORM, 0, iPitch); + + SetThink(&CMBaseEntity::SUB_Remove); + pev->nextthink = gpGlobals->time; + + if (FClassnameIs(pOther, "worldspawn")) + { + // if what we hit is static architecture, can stay around for a while. + Vector vecDir = pev->velocity.Normalize(); + UTIL_SetOrigin(pev, pev->origin - vecDir * 12); + pev->angles = UTIL_VecToAngles(vecDir); + pev->solid = SOLID_NOT; + pev->movetype = MOVETYPE_FLY; + pev->velocity = Vector(0, 0, 0); + pev->avelocity.z = 0; + pev->angles.z = RANDOM_LONG(0, 360); + pev->nextthink = gpGlobals->time + 10.0; + } + } + else + { + entvars_t *pevOwner = VARS(pev->owner); + + if ( UTIL_IsPlayer( pOther ) ) + UTIL_TakeDamage( pOther, pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB ); + else if ( pOther->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage( pev, pevOwner, gSkillData.pitdroneDmgSpit, DMG_GENERIC | DMG_NEVERGIB ); + } + + if (RANDOM_LONG(0,1)) + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM, 0, iPitch); + else + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM, 0, iPitch); + + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; + } +} + +void CPitdroneSpike::StartTrail() +{ +#if FEATURE_PITDRONE_SPIKE_TRAIL + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT( entindex() ); + WRITE_SHORT( iSpikeTrail ); // model + WRITE_BYTE(2); // life + WRITE_BYTE(1); // width + WRITE_BYTE(197); // r + WRITE_BYTE(194); // g + WRITE_BYTE(11); // b + WRITE_BYTE(192); //brigtness + MESSAGE_END(); +#endif + SetTouch(&CPitdroneSpike::SpikeTouch); +} + +edict_t *CPitdroneSpike::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, Vector vecAngles) +{ + CPitdroneSpike *pSpit = CreateClassPtr( (CPitdroneSpike *)NULL ); + + if (pSpit == NULL) + return NULL; + + pSpit->Spawn(); + + UTIL_SetOrigin( pSpit->pev, vecStart ); + pSpit->pev->velocity = vecVelocity; + pSpit->pev->angles = vecAngles; + pSpit->pev->owner = ENT( pevOwner ); + + pSpit->SetThink(&CPitdroneSpike::StartTrail); + pSpit->pev->nextthink = gpGlobals->time + 0.1; + + return pSpit->edict(); +} + +// +// PitDrone, main part. +// +#define HORNGROUP 1 +#define PITDRONE_HORNS0 0 +#define PITDRONE_HORNS1 1 +#define PITDRONE_HORNS2 2 +#define PITDRONE_HORNS3 3 +#define PITDRONE_HORNS4 4 +#define PITDRONE_HORNS5 5 +#define PITDRONE_HORNS6 6 +#define PITDRONE_SPRINT_DIST 255 +#define PITDRONE_FLINCH_DELAY 2 // at most one flinch every n secs +#define PITDRONE_MAX_HORNS 6 +#define PITDRONE_GIB_COUNT 7 + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_PDRONE_HURTHOP = LAST_COMMON_SCHEDULE + 1, + SCHED_PDRONE_SMELLFOOD, + SCHED_PDRONE_EAT, + SCHED_PDRONE_SNIFF_AND_EAT, + SCHED_PDRONE_COVER_AND_RELOAD +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_PDRONE_HOPTURN = LAST_COMMON_TASK + 1 +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define PIT_DRONE_AE_SPIT ( 1 ) +// not sure what it is. It happens twice when pitdrone uses two claws at the same time +// once before 'throw' event and once after +#define PIT_DRONE_AE_ATTACK ( 2 ) +#define PIT_DRONE_AE_SLASH ( 4 ) +#define PIT_DRONE_AE_HOP ( 5 ) +#define PIT_DRONE_AE_THROW ( 6 ) +#define PIT_DRONE_AE_RELOAD ( 7 ) + +void CMPitdrone::KeyValue(KeyValueData *pkvd) +{ + if (FStrEq(pkvd->szKeyName, "initammo")) + { + m_iInitialAmmo = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseMonster::KeyValue(pkvd); +} + +//========================================================= +// IgnoreConditions +//========================================================= +int CMPitdrone::IgnoreConditions(void) +{ + int iIgnore = CMBaseMonster::IgnoreConditions(); + + if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK2)) + { + if (m_flNextFlinch >= gpGlobals->time) + iIgnore |= (bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE); + } + + if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) + { + if (m_flNextFlinch < gpGlobals->time) + m_flNextFlinch = gpGlobals->time + PITDRONE_FLINCH_DELAY; + } + + return iIgnore; + +} + +const char *CMPitdrone::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CMPitdrone::pIdleSounds[] = +{ + "pitdrone/pit_drone_idle1.wav", + "pitdrone/pit_drone_idle2.wav", + "pitdrone/pit_drone_idle3.wav", + +}; + +const char *CMPitdrone::pAlertSounds[] = +{ + "pitdrone/pit_drone_alert1.wav", + "pitdrone/pit_drone_alert2.wav", + "pitdrone/pit_drone_alert3.wav", +}; + +const char *CMPitdrone::pPainSounds[] = +{ + "pitdrone/pit_drone_pain1.wav", + "pitdrone/pit_drone_pain2.wav", + "pitdrone/pit_drone_pain3.wav", + "pitdrone/pit_drone_pain4.wav", +}; + +const char *CMPitdrone::pDieSounds[] = +{ + "pitdrone/pit_drone_die1.wav", + "pitdrone/pit_drone_die2.wav", + "pitdrone/pit_drone_die3.wav", +}; + +//========================================================= +// TakeDamage - overridden for pitdrone so we can keep track +// of how much time has passed since it was last injured +//========================================================= +int CMPitdrone::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + float flDist; + Vector vecApex; + + // if the pitdrone is running, has an enemy, was hurt by the enemy, and isn't too close to the enemy, + // it will swerve. (whew). + if (m_hEnemy != 0 && IsMoving() && pevAttacker == VARS(m_hEnemy)) + { + flDist = (pev->origin - m_hEnemy->v.origin).Length2D(); + + if (flDist > PITDRONE_SPRINT_DIST) + { + flDist = (pev->origin - m_Route[m_iRouteIndex].vecLocation).Length2D();// reusing flDist. + + if (FTriangulate(pev->origin, m_Route[m_iRouteIndex].vecLocation, flDist * 0.5, m_hEnemy, &vecApex)) + { + InsertWaypoint(vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY); + } + } + } + + m_flLastHurtTime = gpGlobals->time; + + return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + +//========================================================= +// CheckMeleeAttack1 - attack with both claws at the same time +//========================================================= +BOOL CMPitdrone::CheckMeleeAttack1(float flDot, float flDist) +{ + // Give a better chance for MeleeAttack2 + if (RANDOM_LONG(0,2) == 0) { + return CMBaseMonster::CheckMeleeAttack1(flDot, flDist); + } + return FALSE; +} + +//========================================================= +// CheckRangeAttack1 - spike attack +//========================================================= +BOOL CMPitdrone::CheckRangeAttack1(float flDot, float flDist) +{ + if (m_cAmmoLoaded <= 0) + { + return FALSE; + } + if (IsMoving() && flDist >= 512) + { + // pitdone will far too far behind if he stops running to spit at this distance from the enemy. + return FALSE; + } + + if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextSpitTime) + { + + if (IsMoving()) + { + // don't spit again for a long time, resume chasing enemy. + m_flNextSpitTime = gpGlobals->time + 5; + } + else + { + // not moving, so spit again pretty soon. + m_flNextSpitTime = gpGlobals->time + 1; + } + + return TRUE; + } + + return FALSE; + +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMPitdrone::SetYawSpeed(void) +{ + int ys; + + ys = 0; + + switch (m_Activity) + { + case ACT_WALK: ys = 120; break; + case ACT_RUN: ys = 120; break; + case ACT_IDLE: ys = 120; break; + case ACT_RANGE_ATTACK1: ys = 120; break; + default: + ys = 120; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. In the base class implementation, +// monsters care about all sounds, but no scents. +//========================================================= +int CMPitdrone::ISoundMask( void ) +{ + return 0; +} + +void CMPitdrone::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case PIT_DRONE_AE_ATTACK: + break; + case PIT_DRONE_AE_THROW: + { + // SOUND HERE (in the pitdrone model) + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.pitdroneDmgWhip, DMG_SLASH ); + + if( pHurt ) + { + // croonchy bite sound + const int iPitch = RANDOM_FLOAT( 110, 120 ); + switch( RANDOM_LONG( 0, 1 ) ) + { + case 0: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_bite2.wav", 0.7, ATTN_NORM, 0, iPitch ); + break; + case 1: + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "bullchicken/bc_bite3.wav", 0.7, ATTN_NORM, 0, iPitch ); + break; + } + + // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels. + UTIL_ScreenShake( pHurt->v.origin, 15.0, 1.5, 0.7, 2 ); + + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 200; + } + else + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, pAttackMissSounds[RANDOM_LONG(0, ARRAYSIZE(pAttackMissSounds) - 1)], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5, 5)); + } + break; + + case PIT_DRONE_AE_SLASH: + { + /* The same event is reused for both right and left claw attacks. + * Pitdrone always starts the attack with the right claw so we use shouldAttackWithLeftClaw to check which claw is used now. + */ + // SOUND HERE (in the pitdrone model) + edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.pitdroneDmgBite, DMG_SLASH); + if (pHurt) + { + if (pHurt->v.flags & (FL_MONSTER | FL_CLIENT)) + { + pHurt->v.punchangle.z = shouldAttackWithLeftClaw ? 18 : -18; + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * ( shouldAttackWithLeftClaw ? 100 : -100 ); + } + } + else // Play a random attack miss sound + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, pAttackMissSounds[RANDOM_LONG(0, ARRAYSIZE(pAttackMissSounds) - 1)], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5, 5)); + shouldAttackWithLeftClaw = !shouldAttackWithLeftClaw; + } + break; + + case PIT_DRONE_AE_RELOAD: + { + if (m_iInitialAmmo >= 0) + m_cAmmoLoaded = PITDRONE_MAX_HORNS; + else + m_cAmmoLoaded = 0; + BodyChange(m_cAmmoLoaded); + ClearConditions(bits_COND_NO_AMMO_LOADED); + } + break; + + case PIT_DRONE_AE_HOP: + { + float flGravity = g_psv_gravity->value; + + // throw the squid up into the air on this frame. + if( FBitSet( pev->flags, FL_ONGROUND ) ) + { + pev->flags -= FL_ONGROUND; + } + + // jump into air for 0.8 (24/30) seconds + pev->velocity.z += ( 0.625 * flGravity ) * 0.5; + } + break; + + case PIT_DRONE_AE_SPIT: + { + m_cAmmoLoaded--; + BodyChange(m_cAmmoLoaded); + + Vector vecSpitOffset; + Vector vecSpitDir; + + UTIL_MakeAimVectors(pev->angles); + + // !!!HACKHACK - the spot at which the spit originates (in front of the mouth) was measured in 3ds and hardcoded here. + // we should be able to read the position of bones at runtime for this info. + vecSpitOffset = (gpGlobals->v_forward * 15 + gpGlobals->v_up * 36); + vecSpitOffset = (pev->origin + vecSpitOffset); + //vecSpitDir = ((m_hEnemy->pev->origin + m_hEnemy->pev->view_ofs) - vecSpitOffset).Normalize(); + Vector vecEnemyPosition; + if (m_hEnemy != 0) + vecEnemyPosition = UTIL_BodyTarget(m_hEnemy, pev->origin); + else + vecEnemyPosition = m_vecEnemyLKP; + vecSpitDir = (vecEnemyPosition - vecSpitOffset).Normalize(); + + vecSpitDir.x += RANDOM_FLOAT(-0.01, 0.01); + vecSpitDir.y += RANDOM_FLOAT(-0.01, 0.01); + vecSpitDir.z += RANDOM_FLOAT(-0.01, 0); + + // SOUND HERE! (in the pitdrone model) + + CPitdroneSpike::Shoot(pev, vecSpitOffset, vecSpitDir * 900, UTIL_VecToAngles(vecSpitDir)); + + // spew the spittle temporary ents. + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOffset ); + WRITE_BYTE( TE_SPRITE_SPRAY ); + WRITE_COORD( vecSpitOffset.x ); // pos + WRITE_COORD( vecSpitOffset.y ); + WRITE_COORD( vecSpitOffset.z ); + WRITE_COORD( vecSpitDir.x ); // dir + WRITE_COORD( vecSpitDir.y ); + WRITE_COORD( vecSpitDir.z ); + WRITE_SHORT( iPitdroneSpitSprite ); // model + WRITE_BYTE( 15 ); // count + WRITE_BYTE( 210 ); // speed + WRITE_BYTE( 25 ); // noise ( client will divide by 100 ) + MESSAGE_END(); + } + break; + + + default: + CMBaseMonster::HandleAnimEvent(pEvent); + } +} + +int CMPitdrone::Classify(void) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_RACEX_PITDRONE; +} + +void CMPitdrone::BodyChange(float horns) +{ + if (horns <= 0) + SetBodygroup(HORNGROUP, PITDRONE_HORNS0); + // pev->body = PITDRONE_HORNS0; + + if (horns == 1) + SetBodygroup(HORNGROUP, PITDRONE_HORNS6); + // pev->body = PITDRONE_HORNS6; + + if (horns == 2) + SetBodygroup(HORNGROUP, PITDRONE_HORNS5); + // pev->body = PITDRONE_HORNS5; + + if (horns == 3) + SetBodygroup(HORNGROUP, PITDRONE_HORNS4); + // pev->body = PITDRONE_HORNS4; + + if (horns == 4) + SetBodygroup(HORNGROUP, PITDRONE_HORNS3); + // pev->body = PITDRONE_HORNS3; + + if (horns == 5) + SetBodygroup(HORNGROUP, PITDRONE_HORNS2); + // pev->body = PITDRONE_HORNS2; + + if (horns >= 6) + SetBodygroup(HORNGROUP, PITDRONE_HORNS1); + // pev->body = PITDRONE_HORNS1; + + return; +} +//========================================================= +// Spawn +//========================================================= +void CMPitdrone::Spawn() +{ + Precache(); + + SET_MODEL( ENT(pev), "models/pit_drone.mdl" ); + UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 48)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.pitdroneHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextSpitTime = gpGlobals->time; + + if (m_iInitialAmmo >= 0) + { + m_cAmmoLoaded = min(m_iInitialAmmo, PITDRONE_MAX_HORNS); +#if FEATURE_PITDRONE_SPAWN_WITH_SPIKES + if (!m_cAmmoLoaded) { + m_cAmmoLoaded = PITDRONE_MAX_HORNS; + } +#endif + } + BodyChange(m_cAmmoLoaded); + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_pitdrone" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Pit Drone" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMPitdrone::Precache() +{ + PRECACHE_MODEL("models/pit_drone.mdl"); + PRECACHE_MODEL("models/pit_drone_gibs.mdl"); + iPitdroneSpitSprite = PRECACHE_MODEL("sprites/tinyspit.spr");// client side spittle. + + PRECACHE_SOUND_ARRAY(pAttackMissSounds); + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pDieSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pAlertSounds); + + PRECACHE_SOUND("bullchicken/bc_bite2.wav"); + PRECACHE_SOUND("bullchicken/bc_bite3.wav"); + + PRECACHE_SOUND("pitdrone/pit_drone_melee_attack1.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_melee_attack2.wav"); + + PRECACHE_SOUND("pitdrone/pit_drone_attack_spike1.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_attack_spike2.wav"); + + PRECACHE_SOUND("pitdrone/pit_drone_communicate1.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_communicate2.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_communicate3.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_communicate4.wav"); + + PRECACHE_SOUND("pitdrone/pit_drone_eat.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_hunt1.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_hunt2.wav"); + PRECACHE_SOUND("pitdrone/pit_drone_hunt3.wav"); + + PRECACHE_SOUND("weapons/xbow_hitbod1.wav"); + PRECACHE_SOUND("weapons/xbow_hitbod2.wav"); + PRECACHE_SOUND("weapons/xbow_hit1.wav"); + + //UTIL_PrecacheOther("pitdronespike"); + PRECACHE_MODEL("models/pit_drone_spike.mdl");// spit projectile + PRECACHE_SOUND("weapons/xbow_hitbod1.wav"); + PRECACHE_SOUND("weapons/xbow_hitbod2.wav"); + PRECACHE_SOUND("weapons/xbow_hit1.wav"); +#if FEATURE_PITDRONE_SPIKE_TRAIL + iSpikeTrail = PRECACHE_MODEL("sprites/spike_trail.spr"); +#endif +} + +//========================================================= +// IdleSound +//========================================================= +#define PITDRONE_ATTN_IDLE (float)1.5 +void CMPitdrone::IdleSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, pIdleSounds[RANDOM_LONG(0, ARRAYSIZE(pIdleSounds)-1)], 1, PITDRONE_ATTN_IDLE); +} + +//========================================================= +// PainSound +//========================================================= +void CMPitdrone::PainSound(void) +{ + int iPitch = RANDOM_LONG(85, 120); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, pPainSounds[RANDOM_LONG(0, ARRAYSIZE(pPainSounds)-1)], 1, ATTN_NORM, 0, iPitch); +} + +//========================================================= +// AlertSound +//========================================================= +void CMPitdrone::AlertSound(void) +{ + int iPitch = RANDOM_LONG(140, 160); + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, pAlertSounds[RANDOM_LONG(0, ARRAYSIZE(pAlertSounds)-1)], 1, ATTN_NORM, 0, iPitch); +} +//========================================================= +// DeathSound +//========================================================= +void CMPitdrone::DeathSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, pDieSounds[RANDOM_LONG(0, ARRAYSIZE(pDieSounds)-1)], 1, ATTN_NORM); +} + +void CMPitdrone::RunAI(void) +{ + // first, do base class stuff + CMBaseMonster::RunAI(); + + if (m_hEnemy != 0 && m_Activity == ACT_RUN) + { + // chasing enemy. Sprint for last bit + if ((pev->origin - m_hEnemy->v.origin).Length2D() < PITDRONE_SPRINT_DIST) + { + pev->framerate = 1.25; + } + } +} + +void CMPitdrone::CheckAmmo( void ) +{ + if( m_cAmmoLoaded <= 0 && m_iInitialAmmo >= 0 ) + { + SetConditions( bits_COND_NO_AMMO_LOADED ); + } +} + +void CMPitdrone::GibMonster() +{ + EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); + + if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly + { + CMGib::SpawnRandomGibs( pev, PITDRONE_GIB_COUNT, "models/pit_drone_gibs.mdl", 0 ); // Throw alien gibs + } + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +//======================================================== +// AI Schedules Specific to this monster +//========================================================= + +// primary range attack +Task_t tlPDroneRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slPDroneRangeAttack1[] = +{ + { + tlPDroneRangeAttack1, + ARRAYSIZE(tlPDroneRangeAttack1), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "PDrone Range Attack1" + }, +}; + +// Chase enemy schedule +Task_t tlPDroneChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RANGE_ATTACK1 },// !!!OEM - this will stop nasty PitDrone oscillation. + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slPDroneChaseEnemy[] = +{ + { + tlPDroneChaseEnemy1, + ARRAYSIZE(tlPDroneChaseEnemy1), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_SMELL_FOOD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED | + bits_COND_HEAR_SOUND, + + 0, + "PDrone Chase Enemy" + }, +}; + +Task_t tlPDroneHurtHop[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SOUND_WAKE, (float)0 }, + { TASK_PDRONE_HOPTURN, (float)0 }, + { TASK_FACE_ENEMY, (float)0 },// in case squid didn't turn all the way in the air. +}; + +Schedule_t slPDroneHurtHop[] = +{ + { + tlPDroneHurtHop, + ARRAYSIZE( tlPDroneHurtHop ), + 0, + 0, + "PDroneHurtHop" + } +}; + + +// PitDrone walks to something tasty and eats it. +Task_t tlPDroneEat[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_EAT, (float)10 },// this is in case the PitDrone can't get to the food + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_EAT, (float)50 }, + { TASK_GET_PATH_TO_LASTPOSITION, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slPDroneEat[] = +{ + { + tlPDroneEat, + ARRAYSIZE(tlPDroneEat), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY, + + // even though HEAR_SOUND/SMELL FOOD doesn't break this schedule, we need this mask + // here or the monster won't detect these sounds at ALL while running this schedule. + 0, + "PDroneEat" + } +}; + +// this is a bit different than just Eat. We use this schedule when the food is far away, occluded, or behind +// the PitDrone. This schedule plays a sniff animation before going to the source of food. +Task_t tlPDroneSniffAndEat[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_EAT, (float)10 },// this is in case the PitDrone can't get to the food + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_GET_PATH_TO_BESTSCENT, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_PLAY_SEQUENCE, (float)ACT_EAT }, + { TASK_EAT, (float)50 }, + { TASK_GET_PATH_TO_LASTPOSITION, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_CLEAR_LASTPOSITION, (float)0 }, +}; + +Schedule_t slPDroneSniffAndEat[] = +{ + { + tlPDroneSniffAndEat, + ARRAYSIZE(tlPDroneSniffAndEat), + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_NEW_ENEMY, + + // even though HEAR_SOUND/SMELL FOOD doesn't break this schedule, we need this mask + // here or the monster won't detect these sounds at ALL while running this schedule. + 0, + "PDroneSniffAndEat" + } +}; + +Task_t tlPDroneHideReload[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_RELOAD }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_REMEMBER, (float)bits_MEMORY_INCOVER }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_RELOAD }, +}; + +Schedule_t slPDroneHideReload[] = +{ + { + tlPDroneHideReload, + ARRAYSIZE( tlPDroneHideReload ), + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "PDroneHideReload" + } +}; + +DEFINE_CUSTOM_SCHEDULES(CMPitdrone) +{ + slPDroneRangeAttack1, + slPDroneChaseEnemy, + slPDroneHurtHop, + slPDroneEat, + slPDroneSniffAndEat, + slPDroneHideReload +}; + +IMPLEMENT_CUSTOM_SCHEDULES(CMPitdrone, CMBaseMonster) + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMPitdrone::GetSchedule(void) +{ + switch (m_MonsterState) + { + case MONSTERSTATE_ALERT: + { + if( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + return GetScheduleOfType( SCHED_PDRONE_HURTHOP ); + } + + if (HasConditions(bits_COND_SMELL_FOOD)) + { + // Just go (try) get it. + return GetScheduleOfType(SCHED_PDRONE_EAT); + } + + break; + } + case MONSTERSTATE_COMBAT: + { + // dead enemy + if (HasConditions(bits_COND_ENEMY_DEAD)) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster::GetSchedule(); + } + + if (HasConditions(bits_COND_NEW_ENEMY)) + { + return GetScheduleOfType(SCHED_WAKE_ANGRY); + } + + if (HasConditions(bits_COND_SMELL_FOOD)) + { + // Just go (try) get it. + return GetScheduleOfType(SCHED_PDRONE_EAT); + } + + if( HasConditions( bits_COND_NO_AMMO_LOADED ) && (m_iInitialAmmo >= 0) ) + { + return GetScheduleOfType( SCHED_PDRONE_COVER_AND_RELOAD ); + } + + if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) + { + return GetScheduleOfType(SCHED_RANGE_ATTACK1); + } + + if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) + { + return GetScheduleOfType(SCHED_MELEE_ATTACK1); + } + + if (HasConditions(bits_COND_CAN_MELEE_ATTACK2)) + { + return GetScheduleOfType(SCHED_MELEE_ATTACK2); + } + + return GetScheduleOfType(SCHED_CHASE_ENEMY); + + break; + } + } + + return CMBaseMonster::GetSchedule(); +} + +//========================================================= +// GetScheduleOfType +//========================================================= +Schedule_t* CMPitdrone::GetScheduleOfType(int Type) +{ + switch (Type) + { + case SCHED_RANGE_ATTACK1: + return &slPDroneRangeAttack1[0]; + break; + case SCHED_PDRONE_HURTHOP: + return &slPDroneHurtHop[0]; + break; + case SCHED_PDRONE_EAT: + return &slPDroneEat[0]; + break; + case SCHED_PDRONE_SNIFF_AND_EAT: + return &slPDroneSniffAndEat[0]; + break; + case SCHED_CHASE_ENEMY: + return &slPDroneChaseEnemy[0]; + break; + case SCHED_PDRONE_COVER_AND_RELOAD: + return &slPDroneHideReload[0]; + break; + } + + return CMBaseMonster::GetScheduleOfType(Type); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. OVERRIDDEN for PitDrone because it needs to +// know explicitly when the last attempt to chase the enemy +// failed, since that impacts its attack choices. +//========================================================= +void CMPitdrone::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch (pTask->iTask) + { + case TASK_PDRONE_HOPTURN: + { + SetActivity( ACT_HOP ); + MakeIdealYaw( m_vecEnemyLKP ); + break; + } + case TASK_GET_PATH_TO_ENEMY: + { + if (BuildRoute(m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy)) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + else + { + ALERT(at_aiconsole, "GetPathToEnemy failed!!\n"); + TaskFail(); + } + break; + } + default: + { + CMBaseMonster::StartTask(pTask); + break; + } + } +} + +//========================================================= +// RunTask +//========================================================= +void CMPitdrone::RunTask(Task_t *pTask) +{ + switch( pTask->iTask ) + { + case TASK_PDRONE_HOPTURN: + { + MakeIdealYaw( m_vecEnemyLKP ); + ChangeYaw( pev->yaw_speed ); + + if( m_fSequenceFinished ) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + break; + } + default: + { + CMBaseMonster::RunTask( pTask ); + break; + } + } +} diff --git a/src/dlls/plane.h b/src/dlls/plane.h index 10baeee..f2d0030 100644 --- a/src/dlls/plane.h +++ b/src/dlls/plane.h @@ -1,43 +1,43 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef PLANE_H -#define PLANE_H - -//========================================================= -// Plane -//========================================================= -class CPlane -{ -public: - CPlane ( void ); - - //========================================================= - // InitializePlane - Takes a normal for the plane and a - // point on the plane and - //========================================================= - void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); - - //========================================================= - // PointInFront - determines whether the given vector is - // in front of the plane. - //========================================================= - BOOL PointInFront ( const Vector &vecPoint ); - - Vector m_vecNormal; - float m_flDist; - BOOL m_fInitialized; -}; - -#endif // PLANE_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#ifndef PLANE_H +#define PLANE_H + +//========================================================= +// Plane +//========================================================= +class CPlane +{ +public: + CPlane ( void ); + + //========================================================= + // InitializePlane - Takes a normal for the plane and a + // point on the plane and + //========================================================= + void InitializePlane ( const Vector &vecNormal, const Vector &vecPoint ); + + //========================================================= + // PointInFront - determines whether the given vector is + // in front of the plane. + //========================================================= + BOOL PointInFront ( const Vector &vecPoint ); + + Vector m_vecNormal; + float m_flDist; + BOOL m_fInitialized; +}; + +#endif // PLANE_H diff --git a/src/dlls/schedule.h b/src/dlls/schedule.h index 0c09441..7d9ff17 100644 --- a/src/dlls/schedule.h +++ b/src/dlls/schedule.h @@ -1,290 +1,290 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// Scheduling -//========================================================= - -#ifndef SCHEDULE_H -#define SCHEDULE_H - -#define TASKSTATUS_NEW 0 // Just started -#define TASKSTATUS_RUNNING 1 // Running task & movement -#define TASKSTATUS_RUNNING_MOVEMENT 2 // Just running movement -#define TASKSTATUS_RUNNING_TASK 3 // Just running task -#define TASKSTATUS_COMPLETE 4 // Completed, get next task - - -//========================================================= -// These are the schedule types -//========================================================= -typedef enum -{ - SCHED_NONE = 0, - SCHED_IDLE_STAND, - SCHED_IDLE_WALK, - SCHED_WAKE_ANGRY, - SCHED_WAKE_CALLED, - SCHED_ALERT_FACE, - SCHED_ALERT_SMALL_FLINCH, - SCHED_ALERT_BIG_FLINCH, - SCHED_ALERT_STAND, - SCHED_INVESTIGATE_SOUND, - SCHED_COMBAT_FACE, - SCHED_COMBAT_STAND, - SCHED_CHASE_ENEMY, - SCHED_CHASE_ENEMY_FAILED, - SCHED_VICTORY_DANCE, - SCHED_TARGET_FACE, - SCHED_TARGET_CHASE, - SCHED_SMALL_FLINCH, - SCHED_TAKE_COVER_FROM_ENEMY, - SCHED_TAKE_COVER_FROM_BEST_SOUND, - SCHED_TAKE_COVER_FROM_ORIGIN, - SCHED_COWER, // usually a last resort! - SCHED_MELEE_ATTACK1, - SCHED_MELEE_ATTACK2, - SCHED_RANGE_ATTACK1, - SCHED_RANGE_ATTACK2, - SCHED_SPECIAL_ATTACK1, - SCHED_SPECIAL_ATTACK2, - SCHED_STANDOFF, - SCHED_ARM_WEAPON, - SCHED_RELOAD, - SCHED_GUARD, - SCHED_AMBUSH, - SCHED_DIE, - SCHED_WAIT_TRIGGER, - SCHED_FOLLOW, - SCHED_SLEEP, - SCHED_WAKE, - SCHED_BARNACLE_VICTIM_GRAB, - SCHED_BARNACLE_VICTIM_CHOMP, - SCHED_AISCRIPT, - SCHED_FAIL, - - LAST_COMMON_SCHEDULE // Leave this at the bottom -} SCHEDULE_TYPE; - -//========================================================= -// These are the shared tasks -//========================================================= -typedef enum -{ - TASK_INVALID = 0, - TASK_WAIT, - TASK_WAIT_FACE_ENEMY, - TASK_WAIT_PVS, - TASK_SUGGEST_STATE, - TASK_WALK_TO_TARGET, - TASK_RUN_TO_TARGET, - TASK_MOVE_TO_TARGET_RANGE, - TASK_GET_PATH_TO_ENEMY, - TASK_GET_PATH_TO_ENEMY_LKP, - TASK_GET_PATH_TO_ENEMY_CORPSE, - TASK_GET_PATH_TO_LEADER, - TASK_GET_PATH_TO_SPOT, - TASK_GET_PATH_TO_TARGET, - TASK_GET_PATH_TO_HINTNODE, - TASK_GET_PATH_TO_LASTPOSITION, - TASK_GET_PATH_TO_BESTSOUND, - TASK_GET_PATH_TO_BESTSCENT, - TASK_RUN_PATH, - TASK_WALK_PATH, - TASK_STRAFE_PATH, - TASK_CLEAR_MOVE_WAIT, - TASK_STORE_LASTPOSITION, - TASK_CLEAR_LASTPOSITION, - TASK_PLAY_ACTIVE_IDLE, - TASK_FIND_HINTNODE, - TASK_CLEAR_HINTNODE, - TASK_SMALL_FLINCH, - TASK_FACE_IDEAL, - TASK_FACE_ROUTE, - TASK_FACE_ENEMY, - TASK_FACE_HINTNODE, - TASK_FACE_TARGET, - TASK_FACE_LASTPOSITION, - TASK_RANGE_ATTACK1, - TASK_RANGE_ATTACK2, - TASK_MELEE_ATTACK1, - TASK_MELEE_ATTACK2, - TASK_RELOAD, - TASK_RANGE_ATTACK1_NOTURN, - TASK_RANGE_ATTACK2_NOTURN, - TASK_MELEE_ATTACK1_NOTURN, - TASK_MELEE_ATTACK2_NOTURN, - TASK_RELOAD_NOTURN, - TASK_SPECIAL_ATTACK1, - TASK_SPECIAL_ATTACK2, - TASK_CROUCH, - TASK_STAND, - TASK_GUARD, - TASK_STEP_LEFT, - TASK_STEP_RIGHT, - TASK_STEP_FORWARD, - TASK_STEP_BACK, - TASK_DODGE_LEFT, - TASK_DODGE_RIGHT, - TASK_SOUND_ANGRY, - TASK_SOUND_DEATH, - TASK_SET_ACTIVITY, - TASK_SET_SCHEDULE, - TASK_SET_FAIL_SCHEDULE, - TASK_CLEAR_FAIL_SCHEDULE, - TASK_PLAY_SEQUENCE, - TASK_PLAY_SEQUENCE_FACE_ENEMY, - TASK_PLAY_SEQUENCE_FACE_TARGET, - TASK_SOUND_IDLE, - TASK_SOUND_WAKE, - TASK_SOUND_PAIN, - TASK_SOUND_DIE, - TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover - TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover - TASK_FIND_LATERAL_COVER_FROM_ENEMY, - TASK_FIND_NODE_COVER_FROM_ENEMY, - TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover. - TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover. - TASK_FIND_COVER_FROM_ORIGIN, - TASK_EAT, - TASK_DIE, - TASK_WAIT_FOR_SCRIPT, - TASK_PLAY_SCRIPT, - TASK_ENABLE_SCRIPT, - TASK_PLANT_ON_SCRIPT, - TASK_FACE_SCRIPT, - TASK_WAIT_RANDOM, - TASK_WAIT_INDEFINITE, - TASK_STOP_MOVING, - TASK_TURN_LEFT, - TASK_TURN_RIGHT, - TASK_REMEMBER, - TASK_FORGET, - TASK_WAIT_FOR_MOVEMENT, // wait until MovementIsComplete() - LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb) -} SHARED_TASKS; - - -// These go in the flData member of the TASK_WALK_TO_TARGET, TASK_RUN_TO_TARGET -enum -{ - TARGET_MOVE_NORMAL = 0, - TARGET_MOVE_SCRIPTED = 1, -}; - - -// A goal should be used for a task that requires several schedules to complete. -// The goal index should indicate which schedule (ordinally) the monster is running. -// That way, when tasks fail, the AI can make decisions based on the context of the -// current goal and sequence rather than just the current schedule. -enum -{ - GOAL_ATTACK_ENEMY, - GOAL_MOVE, - GOAL_TAKE_COVER, - GOAL_MOVE_TARGET, - GOAL_EAT, -}; - -// an array of tasks is a task list -// an array of schedules is a schedule list -struct Task_t -{ - - int iTask; - float flData; -}; - -struct Schedule_t -{ - - Task_t *pTasklist; - int cTasks; - int iInterruptMask;// a bit mask of conditions that can interrupt this schedule - - // a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the - // event that the schedule is broken by COND_HEAR_SOUND - int iSoundMask; - const char *pName; -}; - -// an array of waypoints makes up the monster's route. -// !!!LATER- this declaration doesn't belong in this file. -struct WayPoint_t -{ - Vector vecLocation; - int iType; -}; - -// these MoveFlag values are assigned to a WayPoint's TYPE in order to demonstrate the -// type of movement the monster should use to get there. -#define bits_MF_TO_TARGETENT ( 1 << 0 ) // local move to targetent. -#define bits_MF_TO_ENEMY ( 1 << 1 ) // local move to enemy -#define bits_MF_TO_COVER ( 1 << 2 ) // local move to a hiding place -#define bits_MF_TO_DETOUR ( 1 << 3 ) // local move to detour point. -#define bits_MF_TO_PATHCORNER ( 1 << 4 ) // local move to a path corner -#define bits_MF_TO_NODE ( 1 << 5 ) // local move to a node -#define bits_MF_TO_LOCATION ( 1 << 6 ) // local move to an arbitrary point -#define bits_MF_IS_GOAL ( 1 << 7 ) // this waypoint is the goal of the whole move. -#define bits_MF_DONT_SIMPLIFY ( 1 << 8 ) // Don't let the route code simplify this waypoint - -// If you define any flags that aren't _TO_ flags, add them here so we can mask -// them off when doing compares. -#define bits_MF_NOT_TO_MASK (bits_MF_IS_GOAL | bits_MF_DONT_SIMPLIFY) - -#define MOVEGOAL_NONE (0) -#define MOVEGOAL_TARGETENT (bits_MF_TO_TARGETENT) -#define MOVEGOAL_ENEMY (bits_MF_TO_ENEMY) -#define MOVEGOAL_PATHCORNER (bits_MF_TO_PATHCORNER) -#define MOVEGOAL_LOCATION (bits_MF_TO_LOCATION) -#define MOVEGOAL_NODE (bits_MF_TO_NODE) - -// these bits represent conditions that may befall the monster, of which some are allowed -// to interrupt certain schedules. -#define bits_COND_NO_AMMO_LOADED ( 1 << 0 ) // weapon needs to be reloaded! -#define bits_COND_SEE_HATE ( 1 << 1 ) // see something that you hate -#define bits_COND_SEE_FEAR ( 1 << 2 ) // see something that you are afraid of -#define bits_COND_SEE_DISLIKE ( 1 << 3 ) // see something that you dislike -#define bits_COND_SEE_ENEMY ( 1 << 4 ) // target entity is in full view. -#define bits_COND_ENEMY_OCCLUDED ( 1 << 5 ) // target entity occluded by the world -#define bits_COND_SMELL_FOOD ( 1 << 6 ) -#define bits_COND_ENEMY_TOOFAR ( 1 << 7 ) -#define bits_COND_LIGHT_DAMAGE ( 1 << 8 ) // hurt a little -#define bits_COND_HEAVY_DAMAGE ( 1 << 9 ) // hurt a lot -#define bits_COND_CAN_RANGE_ATTACK1 ( 1 << 10) -#define bits_COND_CAN_MELEE_ATTACK1 ( 1 << 11) -#define bits_COND_CAN_RANGE_ATTACK2 ( 1 << 12) -#define bits_COND_CAN_MELEE_ATTACK2 ( 1 << 13) -// #define bits_COND_CAN_RANGE_ATTACK3 ( 1 << 14) -#define bits_COND_PROVOKED ( 1 << 15) -#define bits_COND_NEW_ENEMY ( 1 << 16) -#define bits_COND_HEAR_SOUND ( 1 << 17) // there is an interesting sound -#define bits_COND_SMELL ( 1 << 18) // there is an interesting scent -#define bits_COND_ENEMY_FACING_ME ( 1 << 19) // enemy is facing me -#define bits_COND_ENEMY_DEAD ( 1 << 20) // enemy was killed. If you get this in combat, try to find another enemy. If you get it in alert, victory dance. -#define bits_COND_SEE_CLIENT ( 1 << 21) // see a client -#define bits_COND_SEE_NEMESIS ( 1 << 22) // see my nemesis - -#define bits_COND_SPECIAL1 ( 1 << 28) // Defined by individual monster -#define bits_COND_SPECIAL2 ( 1 << 29) // Defined by individual monster - -#define bits_COND_TASK_FAILED ( 1 << 30) -#define bits_COND_SCHEDULE_DONE ( 1 << 31) - - -#define bits_COND_ALL_SPECIAL (bits_COND_SPECIAL1 | bits_COND_SPECIAL2) - -#define bits_COND_CAN_ATTACK (bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2) - -#endif // SCHEDULE_H +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Scheduling +//========================================================= + +#ifndef SCHEDULE_H +#define SCHEDULE_H + +#define TASKSTATUS_NEW 0 // Just started +#define TASKSTATUS_RUNNING 1 // Running task & movement +#define TASKSTATUS_RUNNING_MOVEMENT 2 // Just running movement +#define TASKSTATUS_RUNNING_TASK 3 // Just running task +#define TASKSTATUS_COMPLETE 4 // Completed, get next task + + +//========================================================= +// These are the schedule types +//========================================================= +typedef enum +{ + SCHED_NONE = 0, + SCHED_IDLE_STAND, + SCHED_IDLE_WALK, + SCHED_WAKE_ANGRY, + SCHED_WAKE_CALLED, + SCHED_ALERT_FACE, + SCHED_ALERT_SMALL_FLINCH, + SCHED_ALERT_BIG_FLINCH, + SCHED_ALERT_STAND, + SCHED_INVESTIGATE_SOUND, + SCHED_COMBAT_FACE, + SCHED_COMBAT_STAND, + SCHED_CHASE_ENEMY, + SCHED_CHASE_ENEMY_FAILED, + SCHED_VICTORY_DANCE, + SCHED_TARGET_FACE, + SCHED_TARGET_CHASE, + SCHED_SMALL_FLINCH, + SCHED_TAKE_COVER_FROM_ENEMY, + SCHED_TAKE_COVER_FROM_BEST_SOUND, + SCHED_TAKE_COVER_FROM_ORIGIN, + SCHED_COWER, // usually a last resort! + SCHED_MELEE_ATTACK1, + SCHED_MELEE_ATTACK2, + SCHED_RANGE_ATTACK1, + SCHED_RANGE_ATTACK2, + SCHED_SPECIAL_ATTACK1, + SCHED_SPECIAL_ATTACK2, + SCHED_STANDOFF, + SCHED_ARM_WEAPON, + SCHED_RELOAD, + SCHED_GUARD, + SCHED_AMBUSH, + SCHED_DIE, + SCHED_WAIT_TRIGGER, + SCHED_FOLLOW, + SCHED_SLEEP, + SCHED_WAKE, + SCHED_BARNACLE_VICTIM_GRAB, + SCHED_BARNACLE_VICTIM_CHOMP, + SCHED_AISCRIPT, + SCHED_FAIL, + + LAST_COMMON_SCHEDULE // Leave this at the bottom +} SCHEDULE_TYPE; + +//========================================================= +// These are the shared tasks +//========================================================= +typedef enum +{ + TASK_INVALID = 0, + TASK_WAIT, + TASK_WAIT_FACE_ENEMY, + TASK_WAIT_PVS, + TASK_SUGGEST_STATE, + TASK_WALK_TO_TARGET, + TASK_RUN_TO_TARGET, + TASK_MOVE_TO_TARGET_RANGE, + TASK_GET_PATH_TO_ENEMY, + TASK_GET_PATH_TO_ENEMY_LKP, + TASK_GET_PATH_TO_ENEMY_CORPSE, + TASK_GET_PATH_TO_LEADER, + TASK_GET_PATH_TO_SPOT, + TASK_GET_PATH_TO_TARGET, + TASK_GET_PATH_TO_HINTNODE, + TASK_GET_PATH_TO_LASTPOSITION, + TASK_GET_PATH_TO_BESTSOUND, + TASK_GET_PATH_TO_BESTSCENT, + TASK_RUN_PATH, + TASK_WALK_PATH, + TASK_STRAFE_PATH, + TASK_CLEAR_MOVE_WAIT, + TASK_STORE_LASTPOSITION, + TASK_CLEAR_LASTPOSITION, + TASK_PLAY_ACTIVE_IDLE, + TASK_FIND_HINTNODE, + TASK_CLEAR_HINTNODE, + TASK_SMALL_FLINCH, + TASK_FACE_IDEAL, + TASK_FACE_ROUTE, + TASK_FACE_ENEMY, + TASK_FACE_HINTNODE, + TASK_FACE_TARGET, + TASK_FACE_LASTPOSITION, + TASK_RANGE_ATTACK1, + TASK_RANGE_ATTACK2, + TASK_MELEE_ATTACK1, + TASK_MELEE_ATTACK2, + TASK_RELOAD, + TASK_RANGE_ATTACK1_NOTURN, + TASK_RANGE_ATTACK2_NOTURN, + TASK_MELEE_ATTACK1_NOTURN, + TASK_MELEE_ATTACK2_NOTURN, + TASK_RELOAD_NOTURN, + TASK_SPECIAL_ATTACK1, + TASK_SPECIAL_ATTACK2, + TASK_CROUCH, + TASK_STAND, + TASK_GUARD, + TASK_STEP_LEFT, + TASK_STEP_RIGHT, + TASK_STEP_FORWARD, + TASK_STEP_BACK, + TASK_DODGE_LEFT, + TASK_DODGE_RIGHT, + TASK_SOUND_ANGRY, + TASK_SOUND_DEATH, + TASK_SET_ACTIVITY, + TASK_SET_SCHEDULE, + TASK_SET_FAIL_SCHEDULE, + TASK_CLEAR_FAIL_SCHEDULE, + TASK_PLAY_SEQUENCE, + TASK_PLAY_SEQUENCE_FACE_ENEMY, + TASK_PLAY_SEQUENCE_FACE_TARGET, + TASK_SOUND_IDLE, + TASK_SOUND_WAKE, + TASK_SOUND_PAIN, + TASK_SOUND_DIE, + TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover + TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover + TASK_FIND_LATERAL_COVER_FROM_ENEMY, + TASK_FIND_NODE_COVER_FROM_ENEMY, + TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover. + TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover. + TASK_FIND_COVER_FROM_ORIGIN, + TASK_EAT, + TASK_DIE, + TASK_WAIT_FOR_SCRIPT, + TASK_PLAY_SCRIPT, + TASK_ENABLE_SCRIPT, + TASK_PLANT_ON_SCRIPT, + TASK_FACE_SCRIPT, + TASK_WAIT_RANDOM, + TASK_WAIT_INDEFINITE, + TASK_STOP_MOVING, + TASK_TURN_LEFT, + TASK_TURN_RIGHT, + TASK_REMEMBER, + TASK_FORGET, + TASK_WAIT_FOR_MOVEMENT, // wait until MovementIsComplete() + LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb) +} SHARED_TASKS; + + +// These go in the flData member of the TASK_WALK_TO_TARGET, TASK_RUN_TO_TARGET +enum +{ + TARGET_MOVE_NORMAL = 0, + TARGET_MOVE_SCRIPTED = 1, +}; + + +// A goal should be used for a task that requires several schedules to complete. +// The goal index should indicate which schedule (ordinally) the monster is running. +// That way, when tasks fail, the AI can make decisions based on the context of the +// current goal and sequence rather than just the current schedule. +enum +{ + GOAL_ATTACK_ENEMY, + GOAL_MOVE, + GOAL_TAKE_COVER, + GOAL_MOVE_TARGET, + GOAL_EAT, +}; + +// an array of tasks is a task list +// an array of schedules is a schedule list +struct Task_t +{ + + int iTask; + float flData; +}; + +struct Schedule_t +{ + + Task_t *pTasklist; + int cTasks; + int iInterruptMask;// a bit mask of conditions that can interrupt this schedule + + // a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the + // event that the schedule is broken by COND_HEAR_SOUND + int iSoundMask; + const char *pName; +}; + +// an array of waypoints makes up the monster's route. +// !!!LATER- this declaration doesn't belong in this file. +struct WayPoint_t +{ + Vector vecLocation; + int iType; +}; + +// these MoveFlag values are assigned to a WayPoint's TYPE in order to demonstrate the +// type of movement the monster should use to get there. +#define bits_MF_TO_TARGETENT ( 1 << 0 ) // local move to targetent. +#define bits_MF_TO_ENEMY ( 1 << 1 ) // local move to enemy +#define bits_MF_TO_COVER ( 1 << 2 ) // local move to a hiding place +#define bits_MF_TO_DETOUR ( 1 << 3 ) // local move to detour point. +#define bits_MF_TO_PATHCORNER ( 1 << 4 ) // local move to a path corner +#define bits_MF_TO_NODE ( 1 << 5 ) // local move to a node +#define bits_MF_TO_LOCATION ( 1 << 6 ) // local move to an arbitrary point +#define bits_MF_IS_GOAL ( 1 << 7 ) // this waypoint is the goal of the whole move. +#define bits_MF_DONT_SIMPLIFY ( 1 << 8 ) // Don't let the route code simplify this waypoint + +// If you define any flags that aren't _TO_ flags, add them here so we can mask +// them off when doing compares. +#define bits_MF_NOT_TO_MASK (bits_MF_IS_GOAL | bits_MF_DONT_SIMPLIFY) + +#define MOVEGOAL_NONE (0) +#define MOVEGOAL_TARGETENT (bits_MF_TO_TARGETENT) +#define MOVEGOAL_ENEMY (bits_MF_TO_ENEMY) +#define MOVEGOAL_PATHCORNER (bits_MF_TO_PATHCORNER) +#define MOVEGOAL_LOCATION (bits_MF_TO_LOCATION) +#define MOVEGOAL_NODE (bits_MF_TO_NODE) + +// these bits represent conditions that may befall the monster, of which some are allowed +// to interrupt certain schedules. +#define bits_COND_NO_AMMO_LOADED ( 1 << 0 ) // weapon needs to be reloaded! +#define bits_COND_SEE_HATE ( 1 << 1 ) // see something that you hate +#define bits_COND_SEE_FEAR ( 1 << 2 ) // see something that you are afraid of +#define bits_COND_SEE_DISLIKE ( 1 << 3 ) // see something that you dislike +#define bits_COND_SEE_ENEMY ( 1 << 4 ) // target entity is in full view. +#define bits_COND_ENEMY_OCCLUDED ( 1 << 5 ) // target entity occluded by the world +#define bits_COND_SMELL_FOOD ( 1 << 6 ) +#define bits_COND_ENEMY_TOOFAR ( 1 << 7 ) +#define bits_COND_LIGHT_DAMAGE ( 1 << 8 ) // hurt a little +#define bits_COND_HEAVY_DAMAGE ( 1 << 9 ) // hurt a lot +#define bits_COND_CAN_RANGE_ATTACK1 ( 1 << 10) +#define bits_COND_CAN_MELEE_ATTACK1 ( 1 << 11) +#define bits_COND_CAN_RANGE_ATTACK2 ( 1 << 12) +#define bits_COND_CAN_MELEE_ATTACK2 ( 1 << 13) +// #define bits_COND_CAN_RANGE_ATTACK3 ( 1 << 14) +#define bits_COND_PROVOKED ( 1 << 15) +#define bits_COND_NEW_ENEMY ( 1 << 16) +#define bits_COND_HEAR_SOUND ( 1 << 17) // there is an interesting sound +#define bits_COND_SMELL ( 1 << 18) // there is an interesting scent +#define bits_COND_ENEMY_FACING_ME ( 1 << 19) // enemy is facing me +#define bits_COND_ENEMY_DEAD ( 1 << 20) // enemy was killed. If you get this in combat, try to find another enemy. If you get it in alert, victory dance. +#define bits_COND_SEE_CLIENT ( 1 << 21) // see a client +#define bits_COND_SEE_NEMESIS ( 1 << 22) // see my nemesis + +#define bits_COND_SPECIAL1 ( 1 << 28) // Defined by individual monster +#define bits_COND_SPECIAL2 ( 1 << 29) // Defined by individual monster + +#define bits_COND_TASK_FAILED ( 1 << 30) +#define bits_COND_SCHEDULE_DONE ( 1 << 31) + + +#define bits_COND_ALL_SPECIAL (bits_COND_SPECIAL1 | bits_COND_SPECIAL2) + +#define bits_COND_CAN_ATTACK (bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1 | bits_COND_CAN_RANGE_ATTACK2 | bits_COND_CAN_MELEE_ATTACK2) + +#endif // SCHEDULE_H diff --git a/src/dlls/scientist.cpp b/src/dlls/scientist.cpp index a28d72f..3398bb5 100644 --- a/src/dlls/scientist.cpp +++ b/src/dlls/scientist.cpp @@ -1,1025 +1,1025 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// human scientist (passive lab worker) -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "defaultai.h" -#include "animation.h" - - -#define NUM_SCIENTIST_HEADS 4 // four heads available for scientist model -enum { HEAD_GLASSES = 0, HEAD_EINSTEIN = 1, HEAD_LUTHER = 2, HEAD_SLICK = 3 }; - -enum -{ - SCHED_HIDE = LAST_TALKMONSTER_SCHEDULE + 1, - SCHED_FEAR, - SCHED_PANIC, - SCHED_STARTLE, - SCHED_TARGET_CHASE_SCARED, - SCHED_TARGET_FACE_SCARED, -}; - -enum -{ - TASK_SAY_HEAL = LAST_TALKMONSTER_TASK + 1, - TASK_HEAL, - TASK_SAY_FEAR, - TASK_RUN_PATH_SCARED, - TASK_SCREAM, - TASK_RANDOM_SCREAM, - TASK_MOVE_TO_TARGET_RANGE_SCARED, -}; - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define SCIENTIST_AE_HEAL ( 1 ) -#define SCIENTIST_AE_NEEDLEON ( 2 ) -#define SCIENTIST_AE_NEEDLEOFF ( 3 ) - -//======================================================= -// Scientist -//======================================================= - - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= -Task_t tlFollow[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CANT_FOLLOW }, // If you fail, bail out of follow - { TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client) -// { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, -}; - -Schedule_t slFollow[] = -{ - { - tlFollow, - ARRAYSIZE ( tlFollow ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "Follow" - }, -}; - -Task_t tlFollowScared[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_CHASE },// If you fail, follow normally - { TASK_MOVE_TO_TARGET_RANGE_SCARED,(float)128 }, // Move within 128 of target ent (client) -// { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE_SCARED }, -}; - -Schedule_t slFollowScared[] = -{ - { - tlFollowScared, - ARRAYSIZE ( tlFollowScared ), - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "FollowScared" - }, -}; - -Task_t tlFaceTargetScared[] = -{ - { TASK_FACE_TARGET, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, - { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE_SCARED }, -}; - -Schedule_t slFaceTargetScared[] = -{ - { - tlFaceTargetScared, - ARRAYSIZE ( tlFaceTargetScared ), - bits_COND_HEAR_SOUND | - bits_COND_NEW_ENEMY, - 0, - "FaceTargetScared" - }, -}; - -Task_t tlStopFollowing[] = -{ - { TASK_CANT_FOLLOW, (float)0 }, -}; - -Schedule_t slStopFollowing[] = -{ - { - tlStopFollowing, - ARRAYSIZE ( tlStopFollowing ), - 0, - 0, - "StopFollowing" - }, -}; - - -Task_t tlHeal[] = -{ - { TASK_MOVE_TO_TARGET_RANGE,(float)50 }, // Move within 60 of target ent (client) - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_CHASE }, // If you fail, catch up with that guy! (change this to put syringe away and then chase) - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SAY_HEAL, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_ARM }, // Whip out the needle - { TASK_HEAL, (float)0 }, // Put it in the player - { TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_DISARM }, // Put away the needle -}; - -Schedule_t slHeal[] = -{ - { - tlHeal, - ARRAYSIZE ( tlHeal ), - 0, // Don't interrupt or he'll end up running around with a needle all the time - 0, - "Heal" - }, -}; - - -Task_t tlFaceTarget[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_TARGET, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE }, -}; - -Schedule_t slFaceTarget[] = -{ - { - tlFaceTarget, - ARRAYSIZE ( tlFaceTarget ), - bits_COND_CLIENT_PUSH | - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND, - 0, - "FaceTarget" - }, -}; - - -Task_t tlSciPanic[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SCREAM, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_EXCITED }, // This is really fear-stricken excitement - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slSciPanic[] = -{ - { - tlSciPanic, - ARRAYSIZE ( tlSciPanic ), - 0, - 0, - "SciPanic" - }, -}; - - -Task_t tlIdleSciStand[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds. - { TASK_TLK_HEADRESET, (float)0 }, // reset head position -}; - -Schedule_t slIdleSciStand[] = -{ - { - tlIdleSciStand, - ARRAYSIZE ( tlIdleSciStand ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL | - bits_COND_CLIENT_PUSH | - bits_COND_PROVOKED, - 0, - "IdleSciStand" - - }, -}; - - -Task_t tlScientistCover[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! - { TASK_STOP_MOVING, (float)0 }, - { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, - { TASK_RUN_PATH_SCARED, (float)0 }, - { TASK_TURN_LEFT, (float)179 }, - { TASK_SET_SCHEDULE, (float)SCHED_HIDE }, -}; - -Schedule_t slScientistCover[] = -{ - { - tlScientistCover, - ARRAYSIZE ( tlScientistCover ), - bits_COND_NEW_ENEMY, - 0, - "ScientistCover" - }, -}; - - - -Task_t tlScientistHide[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, - { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, // FIXME: This looks lame - { TASK_WAIT_RANDOM, (float)10.0 }, -}; - -Schedule_t slScientistHide[] = -{ - { - tlScientistHide, - ARRAYSIZE ( tlScientistHide ), - bits_COND_NEW_ENEMY | - bits_COND_HEAR_SOUND | - bits_COND_SEE_ENEMY | - bits_COND_SEE_HATE | - bits_COND_SEE_FEAR | - bits_COND_SEE_DISLIKE, - 0, - "ScientistHide" - }, -}; - - -Task_t tlScientistStartle[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! - { TASK_RANDOM_SCREAM, (float)0.3 }, // Scream 30% of the time - { TASK_STOP_MOVING, (float)0 }, - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, - { TASK_RANDOM_SCREAM, (float)0.1 }, // Scream again 10% of the time - { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCHIDLE }, - { TASK_WAIT_RANDOM, (float)1.0 }, -}; - -Schedule_t slScientistStartle[] = -{ - { - tlScientistStartle, - ARRAYSIZE ( tlScientistStartle ), - bits_COND_NEW_ENEMY | - bits_COND_SEE_ENEMY | - bits_COND_SEE_HATE | - bits_COND_SEE_FEAR | - bits_COND_SEE_DISLIKE, - 0, - "ScientistStartle" - }, -}; - - - -Task_t tlFear[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_SAY_FEAR, (float)0 }, -// { TASK_PLAY_SEQUENCE, (float)ACT_FEAR_DISPLAY }, -}; - -Schedule_t slFear[] = -{ - { - tlFear, - ARRAYSIZE ( tlFear ), - bits_COND_NEW_ENEMY, - 0, - "Fear" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMScientist ) -{ - slFollow, - slFaceTarget, - slIdleSciStand, - slFear, - slScientistCover, - slScientistHide, - slScientistStartle, - slHeal, - slStopFollowing, - slSciPanic, - slFollowScared, - slFaceTargetScared, -}; - - -IMPLEMENT_CUSTOM_SCHEDULES( CMScientist, CMTalkMonster ); - - -void CMScientist::DeclineFollowing( void ) -{ - Talk( 10 ); - m_hTalkTarget = m_hEnemy; - PlaySentence( "SC_POK", 2, VOL_NORM, ATTN_NORM ); -} - - -void CMScientist :: Scream( void ) -{ - if ( FOkToSpeak() ) - { - Talk( 10 ); - m_hTalkTarget = m_hEnemy; - PlaySentence( "SC_SCREAM", RANDOM_FLOAT(3, 6), VOL_NORM, ATTN_NORM ); - } -} - - -Activity CMScientist::GetStoppedActivity( void ) -{ - if ( m_hEnemy != NULL ) - return ACT_EXCITED; - return CMTalkMonster::GetStoppedActivity(); -} - - -void CMScientist :: StartTask( Task_t *pTask ) -{ - switch( pTask->iTask ) - { - case TASK_SAY_HEAL: -// if ( FOkToSpeak() ) - Talk( 2 ); - m_hTalkTarget = m_hTargetEnt; - PlaySentence( "SC_HEAL", 2, VOL_NORM, ATTN_IDLE ); - - TaskComplete(); - break; - - case TASK_SCREAM: - Scream(); - TaskComplete(); - break; - - case TASK_RANDOM_SCREAM: - if ( RANDOM_FLOAT( 0, 1 ) < pTask->flData ) - Scream(); - TaskComplete(); - break; - - case TASK_SAY_FEAR: - if ( FOkToSpeak() ) - { - Talk( 2 ); - m_hTalkTarget = m_hEnemy; - if ( UTIL_IsPlayer(m_hEnemy) ) - PlaySentence( "SC_PLFEAR", 5, VOL_NORM, ATTN_NORM ); - else - PlaySentence( "SC_FEAR", 5, VOL_NORM, ATTN_NORM ); - } - TaskComplete(); - break; - - case TASK_HEAL: - m_IdealActivity = ACT_MELEE_ATTACK1; - break; - - case TASK_RUN_PATH_SCARED: - m_movementActivity = ACT_RUN_SCARED; - break; - - case TASK_MOVE_TO_TARGET_RANGE_SCARED: - { - if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) - TaskComplete(); - else - { - m_vecMoveGoal = m_hTargetEnt->v.origin; - if ( !MoveToTarget( ACT_WALK_SCARED, 0.5 ) ) - TaskFail(); - } - } - break; - - default: - CMTalkMonster::StartTask( pTask ); - break; - } -} - -void CMScientist :: RunTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_RUN_PATH_SCARED: - if ( MovementIsComplete() ) - TaskComplete(); - if ( RANDOM_LONG(0,31) < 8 ) - Scream(); - break; - - case TASK_MOVE_TO_TARGET_RANGE_SCARED: - { - if ( RANDOM_LONG(0,63)< 8 ) - Scream(); - - if ( m_hEnemy == NULL ) - { - TaskFail(); - } - else - { - float distance; - - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - // Re-evaluate when you think your finished, or the target has moved too far - if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->v.origin).Length() > pTask->flData * 0.5 ) - { - m_vecMoveGoal = m_hTargetEnt->v.origin; - distance = ( m_vecMoveGoal - pev->origin ).Length2D(); - FRefreshRoute(); - } - - // Set the appropriate activity based on an overlapping range - // overlap the range to prevent oscillation - if ( distance < pTask->flData ) - { - TaskComplete(); - RouteClear(); // Stop moving - } - else if ( distance < 190 && m_movementActivity != ACT_WALK_SCARED ) - m_movementActivity = ACT_WALK_SCARED; - else if ( distance >= 270 && m_movementActivity != ACT_RUN_SCARED ) - m_movementActivity = ACT_RUN_SCARED; - } - } - break; - - case TASK_HEAL: - if ( m_fSequenceFinished ) - { - TaskComplete(); - } - else - { - if ( TargetDistance() > 90 ) - TaskComplete(); - pev->ideal_yaw = UTIL_VecToYaw( m_hTargetEnt->v.origin - pev->origin ); - ChangeYaw( pev->yaw_speed ); - } - break; - default: - CMTalkMonster::RunTask( pTask ); - break; - } -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMScientist :: Classify ( void ) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_HUMAN_PASSIVE; -} - - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMScientist :: SetYawSpeed ( void ) -{ - int ys; - - ys = 90; - - switch ( m_Activity ) - { - case ACT_IDLE: - ys = 120; - break; - case ACT_WALK: - ys = 180; - break; - case ACT_RUN: - ys = 150; - break; - case ACT_TURN_LEFT: - case ACT_TURN_RIGHT: - ys = 120; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMScientist :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case SCIENTIST_AE_HEAL: // Heal my target (if within range) - Heal(); - break; - case SCIENTIST_AE_NEEDLEON: - { - int oldBody = pev->body; - pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 1; - } - break; - case SCIENTIST_AE_NEEDLEOFF: - { - int oldBody = pev->body; - pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 0; - } - break; - - default: - CMTalkMonster::HandleAnimEvent( pEvent ); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMScientist :: Spawn( void ) -{ - Precache( ); - - // every new scientist must call this, otherwise - // when a level is loaded, nobody will talk (time is reset to 0) - TalkInit(); - - SET_MODEL(ENT(pev), "models/scientist.mdl"); - UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_RED; - pev->health = gSkillData.scientistHealth; - pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. - m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so scientists will notice player and say hello - m_MonsterState = MONSTERSTATE_NONE; - -// m_flDistTooFar = 256.0; - - m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; - - // White hands - pev->skin = 0; - - pev->body = RANDOM_LONG(0, NUM_SCIENTIST_HEADS-1); // pick a head, any head - - // Luther is black, make his hands black - if ( pev->body == HEAD_LUTHER ) - pev->skin = 1; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_scientist" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Scientist" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMScientist :: Precache( void ) -{ - PRECACHE_MODEL("models/scientist.mdl"); - PRECACHE_SOUND("scientist/sci_pain1.wav"); - PRECACHE_SOUND("scientist/sci_pain2.wav"); - PRECACHE_SOUND("scientist/sci_pain3.wav"); - PRECACHE_SOUND("scientist/sci_pain4.wav"); - PRECACHE_SOUND("scientist/sci_pain5.wav"); - - CMTalkMonster::Precache(); -} - -// Init talk data -void CMScientist :: TalkInit() -{ - CMTalkMonster::TalkInit(); - - // scientist will try to talk to friends in this order: - - m_szFriends[0] = "monster_scientist"; - m_szFriends[1] = "monster_sitting_scientist"; - m_szFriends[2] = "monster_barney"; - - // scientists speach group names (group names are in sentences.txt) - - m_szGrp[TLK_ANSWER] = "SC_ANSWER"; - m_szGrp[TLK_QUESTION] = "SC_QUESTION"; - m_szGrp[TLK_IDLE] = "SC_IDLE"; - m_szGrp[TLK_STARE] = "SC_STARE"; - m_szGrp[TLK_USE] = "SC_OK"; - m_szGrp[TLK_UNUSE] = "SC_WAIT"; - m_szGrp[TLK_STOP] = "SC_STOP"; - m_szGrp[TLK_NOSHOOT] = "SC_SCARED"; - m_szGrp[TLK_HELLO] = "SC_HELLO"; - - m_szGrp[TLK_PLHURT1] = "!SC_CUREA"; - m_szGrp[TLK_PLHURT2] = "!SC_CUREB"; - m_szGrp[TLK_PLHURT3] = "!SC_CUREC"; - - m_szGrp[TLK_PHELLO] = "SC_PHELLO"; - m_szGrp[TLK_PIDLE] = "SC_PIDLE"; - m_szGrp[TLK_PQUESTION] = "SC_PQUEST"; - m_szGrp[TLK_SMELL] = "SC_SMELL"; - - m_szGrp[TLK_WOUND] = "SC_WOUND"; - m_szGrp[TLK_MORTAL] = "SC_MORTAL"; - - // get voice for head - switch (pev->body % NUM_SCIENTIST_HEADS) - { - default: - case HEAD_GLASSES: m_voicePitch = 105; break; //glasses - case HEAD_EINSTEIN: m_voicePitch = 100; break; //einstein - case HEAD_LUTHER: m_voicePitch = 95; break; //luther - case HEAD_SLICK: m_voicePitch = 100; break;//slick - } -} - -int CMScientist :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - - if ( pevInflictor && pevInflictor->flags & FL_CLIENT ) - { - Remember( bits_MEMORY_PROVOKED ); - StopFollowing( TRUE ); - } - - // make sure friends talk about it if player hurts scientist... - return CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - - -//========================================================= -// ISoundMask - returns a bit mask indicating which types -// of sounds this monster regards. In the base class implementation, -// monsters care about all sounds, but no scents. -//========================================================= -int CMScientist :: ISoundMask ( void ) -{ - return 0; -} - -//========================================================= -// PainSound -//========================================================= -void CMScientist :: PainSound ( void ) -{ - if (gpGlobals->time < m_painTime ) - return; - - m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); - - switch (RANDOM_LONG(0,4)) - { - case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 3: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain4.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - case 4: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain5.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CMScientist :: DeathSound ( void ) -{ - PainSound(); -} - - -void CMScientist::Killed( entvars_t *pevAttacker, int iGib ) -{ - SetUse( NULL ); - CMTalkMonster::Killed( pevAttacker, iGib ); -} - - -void CMScientist :: SetActivity ( Activity newActivity ) -{ - int iSequence; - - iSequence = LookupActivity ( newActivity ); - - // Set to the desired anim, or default anim if the desired is not present - if ( iSequence == ACTIVITY_NOT_AVAILABLE ) - newActivity = ACT_IDLE; - CMTalkMonster::SetActivity( newActivity ); -} - - -Schedule_t* CMScientist :: GetScheduleOfType ( int Type ) -{ - Schedule_t *psched; - - switch( Type ) - { - // Hook these to make a looping schedule - case SCHED_TARGET_FACE: - // call base class default so that scientist will talk - // when 'used' - psched = CMTalkMonster::GetScheduleOfType(Type); - - if (psched == slIdleStand) - return slFaceTarget; // override this for different target face behavior - else - return psched; - - case SCHED_TARGET_CHASE: - return slFollow; - - case SCHED_CANT_FOLLOW: - return slStopFollowing; - - case SCHED_PANIC: - return slSciPanic; - - case SCHED_TARGET_CHASE_SCARED: - return slFollowScared; - - case SCHED_TARGET_FACE_SCARED: - return slFaceTargetScared; - - case SCHED_IDLE_STAND: - // call base class default so that scientist will talk - // when standing during idle - psched = CMTalkMonster::GetScheduleOfType(Type); - - if (psched == slIdleStand) - return slIdleSciStand; - else - return psched; - - case SCHED_HIDE: - return slScientistHide; - - case SCHED_STARTLE: - return slScientistStartle; - - case SCHED_FEAR: - return slFear; - } - - return CMTalkMonster::GetScheduleOfType( Type ); -} - -Schedule_t *CMScientist :: GetSchedule ( void ) -{ - // so we don't keep calling through the EHANDLE stuff - edict_t *pEnemy = m_hEnemy; - int relationship; - - switch( m_MonsterState ) - { - case MONSTERSTATE_ALERT: - case MONSTERSTATE_IDLE: - if ( ( pEnemy ) && !UTIL_IsPlayer(pEnemy) ) - { - if ( HasConditions( bits_COND_SEE_ENEMY ) ) - m_fearTime = gpGlobals->time; - else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert - { - m_hEnemy = NULL; - pEnemy = NULL; - } - } - - if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) - { - // flinch if hurt - return GetScheduleOfType( SCHED_SMALL_FLINCH ); - } - - // Nothing scary, just me and the player - if ( pEnemy != NULL ) - { - relationship = R_NO; - - if (UTIL_IsPlayer(pEnemy)) - relationship = R_AL; // allies - else if (pEnemy->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEnemy)); - relationship = IRelationship( pMonster ); - } - - // UNDONE: Model fear properly, fix R_FR and add multiple levels of fear - if ( relationship != R_DL && relationship != R_HT ) - { - // If I'm already close enough to my target - if ( TargetDistance() <= 128 ) // uses m_hTargetEnt - { - if ( CanHeal() ) // Heal opportunistically - return slHeal; - if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move - return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); - } -//jlb - m_hEnemy = NULL; -//jlb - - return GetScheduleOfType( SCHED_TARGET_FACE ); // Just face and follow. - } - else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared - { - if ( HasConditions( bits_COND_NEW_ENEMY ) ) // I just saw something new and scary, react - return GetScheduleOfType( SCHED_FEAR ); // React to something scary - return GetScheduleOfType( SCHED_TARGET_FACE_SCARED ); // face and follow, but I'm scared! - } - } - - if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move - return GetScheduleOfType( SCHED_MOVE_AWAY ); - - // try to say something about smells - TrySmellTalk(); - break; - - case MONSTERSTATE_COMBAT: - if ( HasConditions( bits_COND_NEW_ENEMY ) ) - return slFear; // Point and scream! - if ( HasConditions( bits_COND_SEE_ENEMY ) ) - return slScientistCover; // Take Cover - - if ( HasConditions( bits_COND_HEAR_SOUND ) ) - return slTakeCoverFromBestSound; // Cower and panic from the scary sound! - - return slScientistCover; // Run & Cower - break; - } - - return CMTalkMonster::GetSchedule(); -} - -MONSTERSTATE CMScientist :: GetIdealState ( void ) -{ - switch ( m_MonsterState ) - { - case MONSTERSTATE_ALERT: - case MONSTERSTATE_IDLE: - if ( HasConditions( bits_COND_NEW_ENEMY ) ) - { - if ( IsFollowing() ) - { - int relationship = R_NO; - - if (UTIL_IsPlayer(m_hEnemy)) - relationship = R_AL; // allies - else if (m_hEnemy->v.euser4 != NULL) - { - edict_t *pEdict = m_hEnemy; - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); - relationship = IRelationship( pMonster ); - } - - if ( relationship != R_FR || relationship != R_HT && !HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - // Don't go to combat if you're following the player - m_IdealMonsterState = MONSTERSTATE_ALERT; - return m_IdealMonsterState; - } - StopFollowing( TRUE ); - } - } - else if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) - { - // Stop following if you take damage - if ( IsFollowing() ) - StopFollowing( TRUE ); - } - break; - - case MONSTERSTATE_COMBAT: - { - edict_t *pEnemy = m_hEnemy; - if ( pEnemy != NULL ) - { - if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert - { - // Strip enemy when going to alert - m_IdealMonsterState = MONSTERSTATE_ALERT; - m_hEnemy = NULL; - return m_IdealMonsterState; - } - // Follow if only scared a little - if ( m_hTargetEnt != NULL ) - { - m_IdealMonsterState = MONSTERSTATE_ALERT; - return m_IdealMonsterState; - } - - if ( HasConditions ( bits_COND_SEE_ENEMY ) ) - { - m_fearTime = gpGlobals->time; - m_IdealMonsterState = MONSTERSTATE_COMBAT; - return m_IdealMonsterState; - } - - } - } - break; - } - - return CMTalkMonster::GetIdealState(); -} - - -BOOL CMScientist::CanHeal( void ) -{ - if ( (m_healTime > gpGlobals->time) || (m_hTargetEnt == NULL) || (m_hTargetEnt->v.health > (m_hTargetEnt->v.max_health * 0.5)) ) - return FALSE; - - return TRUE; -} - -void CMScientist::Heal( void ) -{ - if ( !CanHeal() ) - return; - - Vector target = m_hTargetEnt->v.origin - pev->origin; - if ( target.Length() > 100 ) - return; - - if (UTIL_IsPlayer(m_hTargetEnt)) - UTIL_TakeHealth( m_hTargetEnt, gSkillData.scientistHeal, DMG_GENERIC ); - else if (m_hTargetEnt->v.euser4 != NULL) - { - edict_t *pEdict = m_hTargetEnt; - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); - pMonster->TakeHealth( gSkillData.scientistHeal, DMG_GENERIC ); - } - - // Don't heal again for 1 minute - m_healTime = gpGlobals->time + 60; -} - -int CMScientist::FriendNumber( int arrayNumber ) -{ - static int array[3] = { 1, 2, 0 }; - if ( arrayNumber < 3 ) - return array[ arrayNumber ]; - return arrayNumber; -} - -BOOL CMScientist :: DisregardEnemy( edict_t *pEnemy ) -{ - return !UTIL_IsAlive(pEnemy) || (gpGlobals->time - m_fearTime) > 15; -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// human scientist (passive lab worker) +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "defaultai.h" +#include "animation.h" + + +#define NUM_SCIENTIST_HEADS 4 // four heads available for scientist model +enum { HEAD_GLASSES = 0, HEAD_EINSTEIN = 1, HEAD_LUTHER = 2, HEAD_SLICK = 3 }; + +enum +{ + SCHED_HIDE = LAST_TALKMONSTER_SCHEDULE + 1, + SCHED_FEAR, + SCHED_PANIC, + SCHED_STARTLE, + SCHED_TARGET_CHASE_SCARED, + SCHED_TARGET_FACE_SCARED, +}; + +enum +{ + TASK_SAY_HEAL = LAST_TALKMONSTER_TASK + 1, + TASK_HEAL, + TASK_SAY_FEAR, + TASK_RUN_PATH_SCARED, + TASK_SCREAM, + TASK_RANDOM_SCREAM, + TASK_MOVE_TO_TARGET_RANGE_SCARED, +}; + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define SCIENTIST_AE_HEAL ( 1 ) +#define SCIENTIST_AE_NEEDLEON ( 2 ) +#define SCIENTIST_AE_NEEDLEOFF ( 3 ) + +//======================================================= +// Scientist +//======================================================= + + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= +Task_t tlFollow[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CANT_FOLLOW }, // If you fail, bail out of follow + { TASK_MOVE_TO_TARGET_RANGE,(float)128 }, // Move within 128 of target ent (client) +// { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, +}; + +Schedule_t slFollow[] = +{ + { + tlFollow, + ARRAYSIZE ( tlFollow ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "Follow" + }, +}; + +Task_t tlFollowScared[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_CHASE },// If you fail, follow normally + { TASK_MOVE_TO_TARGET_RANGE_SCARED,(float)128 }, // Move within 128 of target ent (client) +// { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE_SCARED }, +}; + +Schedule_t slFollowScared[] = +{ + { + tlFollowScared, + ARRAYSIZE ( tlFollowScared ), + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "FollowScared" + }, +}; + +Task_t tlFaceTargetScared[] = +{ + { TASK_FACE_TARGET, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, + { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE_SCARED }, +}; + +Schedule_t slFaceTargetScared[] = +{ + { + tlFaceTargetScared, + ARRAYSIZE ( tlFaceTargetScared ), + bits_COND_HEAR_SOUND | + bits_COND_NEW_ENEMY, + 0, + "FaceTargetScared" + }, +}; + +Task_t tlStopFollowing[] = +{ + { TASK_CANT_FOLLOW, (float)0 }, +}; + +Schedule_t slStopFollowing[] = +{ + { + tlStopFollowing, + ARRAYSIZE ( tlStopFollowing ), + 0, + 0, + "StopFollowing" + }, +}; + + +Task_t tlHeal[] = +{ + { TASK_MOVE_TO_TARGET_RANGE,(float)50 }, // Move within 60 of target ent (client) + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_CHASE }, // If you fail, catch up with that guy! (change this to put syringe away and then chase) + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SAY_HEAL, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_ARM }, // Whip out the needle + { TASK_HEAL, (float)0 }, // Put it in the player + { TASK_PLAY_SEQUENCE_FACE_TARGET, (float)ACT_DISARM }, // Put away the needle +}; + +Schedule_t slHeal[] = +{ + { + tlHeal, + ARRAYSIZE ( tlHeal ), + 0, // Don't interrupt or he'll end up running around with a needle all the time + 0, + "Heal" + }, +}; + + +Task_t tlFaceTarget[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_TARGET, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_SET_SCHEDULE, (float)SCHED_TARGET_CHASE }, +}; + +Schedule_t slFaceTarget[] = +{ + { + tlFaceTarget, + ARRAYSIZE ( tlFaceTarget ), + bits_COND_CLIENT_PUSH | + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND, + 0, + "FaceTarget" + }, +}; + + +Task_t tlSciPanic[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SCREAM, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_EXCITED }, // This is really fear-stricken excitement + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slSciPanic[] = +{ + { + tlSciPanic, + ARRAYSIZE ( tlSciPanic ), + 0, + 0, + "SciPanic" + }, +}; + + +Task_t tlIdleSciStand[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_WAIT, (float)2 }, // repick IDLESTAND every two seconds. + { TASK_TLK_HEADRESET, (float)0 }, // reset head position +}; + +Schedule_t slIdleSciStand[] = +{ + { + tlIdleSciStand, + ARRAYSIZE ( tlIdleSciStand ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL | + bits_COND_CLIENT_PUSH | + bits_COND_PROVOKED, + 0, + "IdleSciStand" + + }, +}; + + +Task_t tlScientistCover[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! + { TASK_STOP_MOVING, (float)0 }, + { TASK_FIND_COVER_FROM_ENEMY, (float)0 }, + { TASK_RUN_PATH_SCARED, (float)0 }, + { TASK_TURN_LEFT, (float)179 }, + { TASK_SET_SCHEDULE, (float)SCHED_HIDE }, +}; + +Schedule_t slScientistCover[] = +{ + { + tlScientistCover, + ARRAYSIZE ( tlScientistCover ), + bits_COND_NEW_ENEMY, + 0, + "ScientistCover" + }, +}; + + + +Task_t tlScientistHide[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_CROUCH }, + { TASK_SET_ACTIVITY, (float)ACT_CROUCHIDLE }, // FIXME: This looks lame + { TASK_WAIT_RANDOM, (float)10.0 }, +}; + +Schedule_t slScientistHide[] = +{ + { + tlScientistHide, + ARRAYSIZE ( tlScientistHide ), + bits_COND_NEW_ENEMY | + bits_COND_HEAR_SOUND | + bits_COND_SEE_ENEMY | + bits_COND_SEE_HATE | + bits_COND_SEE_FEAR | + bits_COND_SEE_DISLIKE, + 0, + "ScientistHide" + }, +}; + + +Task_t tlScientistStartle[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_PANIC }, // If you fail, just panic! + { TASK_RANDOM_SCREAM, (float)0.3 }, // Scream 30% of the time + { TASK_STOP_MOVING, (float)0 }, + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCH }, + { TASK_RANDOM_SCREAM, (float)0.1 }, // Scream again 10% of the time + { TASK_PLAY_SEQUENCE_FACE_ENEMY, (float)ACT_CROUCHIDLE }, + { TASK_WAIT_RANDOM, (float)1.0 }, +}; + +Schedule_t slScientistStartle[] = +{ + { + tlScientistStartle, + ARRAYSIZE ( tlScientistStartle ), + bits_COND_NEW_ENEMY | + bits_COND_SEE_ENEMY | + bits_COND_SEE_HATE | + bits_COND_SEE_FEAR | + bits_COND_SEE_DISLIKE, + 0, + "ScientistStartle" + }, +}; + + + +Task_t tlFear[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_SAY_FEAR, (float)0 }, +// { TASK_PLAY_SEQUENCE, (float)ACT_FEAR_DISPLAY }, +}; + +Schedule_t slFear[] = +{ + { + tlFear, + ARRAYSIZE ( tlFear ), + bits_COND_NEW_ENEMY, + 0, + "Fear" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMScientist ) +{ + slFollow, + slFaceTarget, + slIdleSciStand, + slFear, + slScientistCover, + slScientistHide, + slScientistStartle, + slHeal, + slStopFollowing, + slSciPanic, + slFollowScared, + slFaceTargetScared, +}; + + +IMPLEMENT_CUSTOM_SCHEDULES( CMScientist, CMTalkMonster ); + + +void CMScientist::DeclineFollowing( void ) +{ + Talk( 10 ); + m_hTalkTarget = m_hEnemy; + PlaySentence( "SC_POK", 2, VOL_NORM, ATTN_NORM ); +} + + +void CMScientist :: Scream( void ) +{ + if ( FOkToSpeak() ) + { + Talk( 10 ); + m_hTalkTarget = m_hEnemy; + PlaySentence( "SC_SCREAM", RANDOM_FLOAT(3, 6), VOL_NORM, ATTN_NORM ); + } +} + + +Activity CMScientist::GetStoppedActivity( void ) +{ + if ( m_hEnemy != NULL ) + return ACT_EXCITED; + return CMTalkMonster::GetStoppedActivity(); +} + + +void CMScientist :: StartTask( Task_t *pTask ) +{ + switch( pTask->iTask ) + { + case TASK_SAY_HEAL: +// if ( FOkToSpeak() ) + Talk( 2 ); + m_hTalkTarget = m_hTargetEnt; + PlaySentence( "SC_HEAL", 2, VOL_NORM, ATTN_IDLE ); + + TaskComplete(); + break; + + case TASK_SCREAM: + Scream(); + TaskComplete(); + break; + + case TASK_RANDOM_SCREAM: + if ( RANDOM_FLOAT( 0, 1 ) < pTask->flData ) + Scream(); + TaskComplete(); + break; + + case TASK_SAY_FEAR: + if ( FOkToSpeak() ) + { + Talk( 2 ); + m_hTalkTarget = m_hEnemy; + if ( UTIL_IsPlayer(m_hEnemy) ) + PlaySentence( "SC_PLFEAR", 5, VOL_NORM, ATTN_NORM ); + else + PlaySentence( "SC_FEAR", 5, VOL_NORM, ATTN_NORM ); + } + TaskComplete(); + break; + + case TASK_HEAL: + m_IdealActivity = ACT_MELEE_ATTACK1; + break; + + case TASK_RUN_PATH_SCARED: + m_movementActivity = ACT_RUN_SCARED; + break; + + case TASK_MOVE_TO_TARGET_RANGE_SCARED: + { + if ( (m_hTargetEnt->v.origin - pev->origin).Length() < 1 ) + TaskComplete(); + else + { + m_vecMoveGoal = m_hTargetEnt->v.origin; + if ( !MoveToTarget( ACT_WALK_SCARED, 0.5 ) ) + TaskFail(); + } + } + break; + + default: + CMTalkMonster::StartTask( pTask ); + break; + } +} + +void CMScientist :: RunTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_RUN_PATH_SCARED: + if ( MovementIsComplete() ) + TaskComplete(); + if ( RANDOM_LONG(0,31) < 8 ) + Scream(); + break; + + case TASK_MOVE_TO_TARGET_RANGE_SCARED: + { + if ( RANDOM_LONG(0,63)< 8 ) + Scream(); + + if ( m_hEnemy == NULL ) + { + TaskFail(); + } + else + { + float distance; + + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + // Re-evaluate when you think your finished, or the target has moved too far + if ( (distance < pTask->flData) || (m_vecMoveGoal - m_hTargetEnt->v.origin).Length() > pTask->flData * 0.5 ) + { + m_vecMoveGoal = m_hTargetEnt->v.origin; + distance = ( m_vecMoveGoal - pev->origin ).Length2D(); + FRefreshRoute(); + } + + // Set the appropriate activity based on an overlapping range + // overlap the range to prevent oscillation + if ( distance < pTask->flData ) + { + TaskComplete(); + RouteClear(); // Stop moving + } + else if ( distance < 190 && m_movementActivity != ACT_WALK_SCARED ) + m_movementActivity = ACT_WALK_SCARED; + else if ( distance >= 270 && m_movementActivity != ACT_RUN_SCARED ) + m_movementActivity = ACT_RUN_SCARED; + } + } + break; + + case TASK_HEAL: + if ( m_fSequenceFinished ) + { + TaskComplete(); + } + else + { + if ( TargetDistance() > 90 ) + TaskComplete(); + pev->ideal_yaw = UTIL_VecToYaw( m_hTargetEnt->v.origin - pev->origin ); + ChangeYaw( pev->yaw_speed ); + } + break; + default: + CMTalkMonster::RunTask( pTask ); + break; + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMScientist :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_HUMAN_PASSIVE; +} + + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMScientist :: SetYawSpeed ( void ) +{ + int ys; + + ys = 90; + + switch ( m_Activity ) + { + case ACT_IDLE: + ys = 120; + break; + case ACT_WALK: + ys = 180; + break; + case ACT_RUN: + ys = 150; + break; + case ACT_TURN_LEFT: + case ACT_TURN_RIGHT: + ys = 120; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMScientist :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case SCIENTIST_AE_HEAL: // Heal my target (if within range) + Heal(); + break; + case SCIENTIST_AE_NEEDLEON: + { + int oldBody = pev->body; + pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 1; + } + break; + case SCIENTIST_AE_NEEDLEOFF: + { + int oldBody = pev->body; + pev->body = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 0; + } + break; + + default: + CMTalkMonster::HandleAnimEvent( pEvent ); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMScientist :: Spawn( void ) +{ + Precache( ); + + // every new scientist must call this, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + TalkInit(); + + SET_MODEL(ENT(pev), "models/scientist.mdl"); + UTIL_SetSize(pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_RED; + pev->health = gSkillData.scientistHealth; + pev->view_ofs = Vector ( 0, 0, 50 );// position of the eyes relative to monster's origin. + m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so scientists will notice player and say hello + m_MonsterState = MONSTERSTATE_NONE; + +// m_flDistTooFar = 256.0; + + m_afCapability = bits_CAP_HEAR | bits_CAP_TURN_HEAD | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE; + + // White hands + pev->skin = 0; + + pev->body = RANDOM_LONG(0, NUM_SCIENTIST_HEADS-1); // pick a head, any head + + // Luther is black, make his hands black + if ( pev->body == HEAD_LUTHER ) + pev->skin = 1; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_scientist" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Scientist" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMScientist :: Precache( void ) +{ + PRECACHE_MODEL("models/scientist.mdl"); + PRECACHE_SOUND("scientist/sci_pain1.wav"); + PRECACHE_SOUND("scientist/sci_pain2.wav"); + PRECACHE_SOUND("scientist/sci_pain3.wav"); + PRECACHE_SOUND("scientist/sci_pain4.wav"); + PRECACHE_SOUND("scientist/sci_pain5.wav"); + + CMTalkMonster::Precache(); +} + +// Init talk data +void CMScientist :: TalkInit() +{ + CMTalkMonster::TalkInit(); + + // scientist will try to talk to friends in this order: + + m_szFriends[0] = "monster_scientist"; + m_szFriends[1] = "monster_sitting_scientist"; + m_szFriends[2] = "monster_barney"; + + // scientists speach group names (group names are in sentences.txt) + + m_szGrp[TLK_ANSWER] = "SC_ANSWER"; + m_szGrp[TLK_QUESTION] = "SC_QUESTION"; + m_szGrp[TLK_IDLE] = "SC_IDLE"; + m_szGrp[TLK_STARE] = "SC_STARE"; + m_szGrp[TLK_USE] = "SC_OK"; + m_szGrp[TLK_UNUSE] = "SC_WAIT"; + m_szGrp[TLK_STOP] = "SC_STOP"; + m_szGrp[TLK_NOSHOOT] = "SC_SCARED"; + m_szGrp[TLK_HELLO] = "SC_HELLO"; + + m_szGrp[TLK_PLHURT1] = "!SC_CUREA"; + m_szGrp[TLK_PLHURT2] = "!SC_CUREB"; + m_szGrp[TLK_PLHURT3] = "!SC_CUREC"; + + m_szGrp[TLK_PHELLO] = "SC_PHELLO"; + m_szGrp[TLK_PIDLE] = "SC_PIDLE"; + m_szGrp[TLK_PQUESTION] = "SC_PQUEST"; + m_szGrp[TLK_SMELL] = "SC_SMELL"; + + m_szGrp[TLK_WOUND] = "SC_WOUND"; + m_szGrp[TLK_MORTAL] = "SC_MORTAL"; + + // get voice for head + switch (pev->body % NUM_SCIENTIST_HEADS) + { + default: + case HEAD_GLASSES: m_voicePitch = 105; break; //glasses + case HEAD_EINSTEIN: m_voicePitch = 100; break; //einstein + case HEAD_LUTHER: m_voicePitch = 95; break; //luther + case HEAD_SLICK: m_voicePitch = 100; break;//slick + } +} + +int CMScientist :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + + if ( pevInflictor && pevInflictor->flags & FL_CLIENT ) + { + Remember( bits_MEMORY_PROVOKED ); + StopFollowing( TRUE ); + } + + // make sure friends talk about it if player hurts scientist... + return CMTalkMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + + +//========================================================= +// ISoundMask - returns a bit mask indicating which types +// of sounds this monster regards. In the base class implementation, +// monsters care about all sounds, but no scents. +//========================================================= +int CMScientist :: ISoundMask ( void ) +{ + return 0; +} + +//========================================================= +// PainSound +//========================================================= +void CMScientist :: PainSound ( void ) +{ + if (gpGlobals->time < m_painTime ) + return; + + m_painTime = gpGlobals->time + RANDOM_FLOAT(0.5, 0.75); + + switch (RANDOM_LONG(0,4)) + { + case 0: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain1.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 1: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain2.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 2: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain3.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 3: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain4.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + case 4: EMIT_SOUND_DYN( ENT(pev), CHAN_VOICE, "scientist/sci_pain5.wav", 1, ATTN_NORM, 0, GetVoicePitch()); break; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CMScientist :: DeathSound ( void ) +{ + PainSound(); +} + + +void CMScientist::Killed( entvars_t *pevAttacker, int iGib ) +{ + SetUse( NULL ); + CMTalkMonster::Killed( pevAttacker, iGib ); +} + + +void CMScientist :: SetActivity ( Activity newActivity ) +{ + int iSequence; + + iSequence = LookupActivity ( newActivity ); + + // Set to the desired anim, or default anim if the desired is not present + if ( iSequence == ACTIVITY_NOT_AVAILABLE ) + newActivity = ACT_IDLE; + CMTalkMonster::SetActivity( newActivity ); +} + + +Schedule_t* CMScientist :: GetScheduleOfType ( int Type ) +{ + Schedule_t *psched; + + switch( Type ) + { + // Hook these to make a looping schedule + case SCHED_TARGET_FACE: + // call base class default so that scientist will talk + // when 'used' + psched = CMTalkMonster::GetScheduleOfType(Type); + + if (psched == slIdleStand) + return slFaceTarget; // override this for different target face behavior + else + return psched; + + case SCHED_TARGET_CHASE: + return slFollow; + + case SCHED_CANT_FOLLOW: + return slStopFollowing; + + case SCHED_PANIC: + return slSciPanic; + + case SCHED_TARGET_CHASE_SCARED: + return slFollowScared; + + case SCHED_TARGET_FACE_SCARED: + return slFaceTargetScared; + + case SCHED_IDLE_STAND: + // call base class default so that scientist will talk + // when standing during idle + psched = CMTalkMonster::GetScheduleOfType(Type); + + if (psched == slIdleStand) + return slIdleSciStand; + else + return psched; + + case SCHED_HIDE: + return slScientistHide; + + case SCHED_STARTLE: + return slScientistStartle; + + case SCHED_FEAR: + return slFear; + } + + return CMTalkMonster::GetScheduleOfType( Type ); +} + +Schedule_t *CMScientist :: GetSchedule ( void ) +{ + // so we don't keep calling through the EHANDLE stuff + edict_t *pEnemy = m_hEnemy; + int relationship; + + switch( m_MonsterState ) + { + case MONSTERSTATE_ALERT: + case MONSTERSTATE_IDLE: + if ( ( pEnemy ) && !UTIL_IsPlayer(pEnemy) ) + { + if ( HasConditions( bits_COND_SEE_ENEMY ) ) + m_fearTime = gpGlobals->time; + else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert + { + m_hEnemy = NULL; + pEnemy = NULL; + } + } + + if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE)) + { + // flinch if hurt + return GetScheduleOfType( SCHED_SMALL_FLINCH ); + } + + // Nothing scary, just me and the player + if ( pEnemy != NULL ) + { + relationship = R_NO; + + if (UTIL_IsPlayer(pEnemy)) + relationship = R_AL; // allies + else if (pEnemy->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEnemy)); + relationship = IRelationship( pMonster ); + } + + // UNDONE: Model fear properly, fix R_FR and add multiple levels of fear + if ( relationship != R_DL && relationship != R_HT ) + { + // If I'm already close enough to my target + if ( TargetDistance() <= 128 ) // uses m_hTargetEnt + { + if ( CanHeal() ) // Heal opportunistically + return slHeal; + if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move + return GetScheduleOfType( SCHED_MOVE_AWAY_FOLLOW ); + } +//jlb + m_hEnemy = NULL; +//jlb + + return GetScheduleOfType( SCHED_TARGET_FACE ); // Just face and follow. + } + else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared + { + if ( HasConditions( bits_COND_NEW_ENEMY ) ) // I just saw something new and scary, react + return GetScheduleOfType( SCHED_FEAR ); // React to something scary + return GetScheduleOfType( SCHED_TARGET_FACE_SCARED ); // face and follow, but I'm scared! + } + } + + if ( HasConditions( bits_COND_CLIENT_PUSH ) ) // Player wants me to move + return GetScheduleOfType( SCHED_MOVE_AWAY ); + + // try to say something about smells + TrySmellTalk(); + break; + + case MONSTERSTATE_COMBAT: + if ( HasConditions( bits_COND_NEW_ENEMY ) ) + return slFear; // Point and scream! + if ( HasConditions( bits_COND_SEE_ENEMY ) ) + return slScientistCover; // Take Cover + + if ( HasConditions( bits_COND_HEAR_SOUND ) ) + return slTakeCoverFromBestSound; // Cower and panic from the scary sound! + + return slScientistCover; // Run & Cower + break; + } + + return CMTalkMonster::GetSchedule(); +} + +MONSTERSTATE CMScientist :: GetIdealState ( void ) +{ + switch ( m_MonsterState ) + { + case MONSTERSTATE_ALERT: + case MONSTERSTATE_IDLE: + if ( HasConditions( bits_COND_NEW_ENEMY ) ) + { + if ( IsFollowing() ) + { + int relationship = R_NO; + + if (UTIL_IsPlayer(m_hEnemy)) + relationship = R_AL; // allies + else if (m_hEnemy->v.euser4 != NULL) + { + edict_t *pEdict = m_hEnemy; + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); + relationship = IRelationship( pMonster ); + } + + if ( relationship != R_FR || relationship != R_HT && !HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + // Don't go to combat if you're following the player + m_IdealMonsterState = MONSTERSTATE_ALERT; + return m_IdealMonsterState; + } + StopFollowing( TRUE ); + } + } + else if ( HasConditions( bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE ) ) + { + // Stop following if you take damage + if ( IsFollowing() ) + StopFollowing( TRUE ); + } + break; + + case MONSTERSTATE_COMBAT: + { + edict_t *pEnemy = m_hEnemy; + if ( pEnemy != NULL ) + { + if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert + { + // Strip enemy when going to alert + m_IdealMonsterState = MONSTERSTATE_ALERT; + m_hEnemy = NULL; + return m_IdealMonsterState; + } + // Follow if only scared a little + if ( m_hTargetEnt != NULL ) + { + m_IdealMonsterState = MONSTERSTATE_ALERT; + return m_IdealMonsterState; + } + + if ( HasConditions ( bits_COND_SEE_ENEMY ) ) + { + m_fearTime = gpGlobals->time; + m_IdealMonsterState = MONSTERSTATE_COMBAT; + return m_IdealMonsterState; + } + + } + } + break; + } + + return CMTalkMonster::GetIdealState(); +} + + +BOOL CMScientist::CanHeal( void ) +{ + if ( (m_healTime > gpGlobals->time) || (m_hTargetEnt == NULL) || (m_hTargetEnt->v.health > (m_hTargetEnt->v.max_health * 0.5)) ) + return FALSE; + + return TRUE; +} + +void CMScientist::Heal( void ) +{ + if ( !CanHeal() ) + return; + + Vector target = m_hTargetEnt->v.origin - pev->origin; + if ( target.Length() > 100 ) + return; + + if (UTIL_IsPlayer(m_hTargetEnt)) + UTIL_TakeHealth( m_hTargetEnt, gSkillData.scientistHeal, DMG_GENERIC ); + else if (m_hTargetEnt->v.euser4 != NULL) + { + edict_t *pEdict = m_hTargetEnt; + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEdict)); + pMonster->TakeHealth( gSkillData.scientistHeal, DMG_GENERIC ); + } + + // Don't heal again for 1 minute + m_healTime = gpGlobals->time + 60; +} + +int CMScientist::FriendNumber( int arrayNumber ) +{ + static int array[3] = { 1, 2, 0 }; + if ( arrayNumber < 3 ) + return array[ arrayNumber ]; + return arrayNumber; +} + +BOOL CMScientist :: DisregardEnemy( edict_t *pEnemy ) +{ + return !UTIL_IsAlive(pEnemy) || (gpGlobals->time - m_fearTime) > 15; +} + diff --git a/src/dlls/shock.cpp b/src/dlls/shock.cpp index 9080874..a7991d5 100644 --- a/src/dlls/shock.cpp +++ b/src/dlls/shock.cpp @@ -1,233 +1,233 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// shock - projectile shot from shockrifles. -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "effects.h" -#include "decals.h" -#include "weapons.h" -#include "customentity.h" -#include "shock.h" - - -void CMShock::Spawn() -{ - Precache(); - - pev->movetype = MOVETYPE_FLY; - pev->solid = SOLID_BBOX; - pev->classname = MAKE_STRING("shock_beam"); - SET_MODEL(ENT(pev), "models/shock_effect.mdl"); - UTIL_SetOrigin(pev, pev->origin); - pev->dmg = gSkillData.monDmgShockroach; - UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4)); - - CreateEffects(); - SetThink( &CMShock::FlyThink ); - pev->nextthink = gpGlobals->time; -} - -void CMShock::Precache() -{ - PRECACHE_MODEL("sprites/flare3.spr"); - PRECACHE_MODEL("sprites/lgtning.spr"); - PRECACHE_MODEL("models/shock_effect.mdl"); - PRECACHE_SOUND("weapons/shock_impact.wav"); -} - -void CMShock::FlyThink() -{ - if (pev->waterlevel == 3) - { - entvars_t *pevOwner = VARS(pev->owner); - EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM); - RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB ); - ClearEffects(); - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time; - } - else - { - pev->nextthink = gpGlobals->time + 0.05; - } -} - -edict_t *CMShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity) -{ - CMShock *pShock = CreateClassPtr((CMShock *)NULL); - - if (pShock == NULL) - return NULL; - - UTIL_SetOrigin(pShock->pev, vecStart); - pShock->Spawn(); - - pShock->pev->velocity = vecVelocity; - pShock->pev->owner = ENT(pevOwner); - pShock->pev->angles = angles; - - pShock->pev->nextthink = gpGlobals->time; - - return pShock->edict(); -} - -void CMShock::Touch(edict_t *pOther) -{ - // Do not collide with the owner. - if (pOther == pev->owner) - return; - - TraceResult tr = UTIL_GetGlobalTrace( ); - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD(pev->origin.x); // X - WRITE_COORD(pev->origin.y); // Y - WRITE_COORD(pev->origin.z); // Z - WRITE_BYTE( 8 ); // radius * 0.1 - WRITE_BYTE( 0 ); // r - WRITE_BYTE( 255 ); // g - WRITE_BYTE( 255 ); // b - WRITE_BYTE( 10 ); // time * 10 - WRITE_BYTE( 10 ); // decay * 0.1 - MESSAGE_END( ); - - ClearEffects(); - if (!pOther->v.takedamage) - { - // make a splat on the wall - const int baseDecal = DECAL_SCORCH1; - UTIL_DecalTrace(&tr, baseDecal + RANDOM_LONG(0, 1)); - - int iContents = UTIL_PointContents(pev->origin); - - // Create sparks - if (iContents != CONTENTS_WATER) - { - UTIL_Sparks(tr.vecEndPos); - } - } - else - { - int damageType = DMG_SHOCK; - ClearMultiDamage(); - entvars_t *pevOwner = VARS(pev->owner); - entvars_t *pevAttacker = pevOwner ? pevOwner : pev; - - if ( UTIL_IsPlayer( pOther ) ) - UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType ); - else if ( pOther->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType ); - } - - ApplyMultiDamage(pev, pevAttacker); - } - - // splat sound - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM); - - pev->modelindex = 0; - pev->solid = SOLID_NOT; - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time + 0.01; // let the sound play -} - -void CMShock::CreateEffects() -{ - m_pSprite = CMSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE ); - m_pSprite->SetAttachment( edict(), 0 ); - m_pSprite->pev->scale = 0.35; - m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation ); - //m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY; - //m_pSprite->pev->flags |= FL_SKIPLOCALHOST; - - m_pBeam = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - - if (m_pBeam) - { - m_pBeam->EntsInit( entindex(), entindex() ); - m_pBeam->SetStartAttachment( 1 ); - m_pBeam->SetEndAttachment( 2 ); - m_pBeam->SetBrightness( 180 ); - m_pBeam->SetScrollRate( 10 ); - m_pBeam->SetNoise( 0 ); - m_pBeam->SetFlags( BEAM_FSHADEOUT ); - m_pBeam->SetColor( 0, 255, 255 ); - //m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY; - m_pBeam->RelinkBeam(); - } - else - { - ALERT(at_console, "Could not create shockbeam beam!\n"); - } - - m_pNoise = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); - - if (m_pNoise) - { - m_pNoise->EntsInit( entindex(), entindex() ); - m_pNoise->SetStartAttachment( 1 ); - m_pNoise->SetEndAttachment( 2 ); - m_pNoise->SetBrightness( 180 ); - m_pNoise->SetScrollRate( 30 ); - m_pNoise->SetNoise( 30 ); - m_pNoise->SetFlags( BEAM_FSHADEOUT ); - m_pNoise->SetColor( 255, 255, 173 ); - //m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY; - m_pNoise->RelinkBeam(); - } - else - { - ALERT(at_console, "Could not create shockbeam noise!\n"); - } -} - -void CMShock::ClearEffects() -{ - if (m_pBeam) - { - UTIL_Remove( m_pBeam->edict() ); - m_pBeam = NULL; - } - - if (m_pNoise) - { - UTIL_Remove( m_pNoise->edict() ); - m_pNoise = NULL; - } - - if (m_pSprite) - { - UTIL_Remove( m_pSprite->edict() ); - m_pSprite = NULL; - } -} - -void CMShock::UpdateOnRemove() -{ - CMBaseAnimating::UpdateOnRemove(); - ClearEffects(); -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// shock - projectile shot from shockrifles. +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "effects.h" +#include "decals.h" +#include "weapons.h" +#include "customentity.h" +#include "shock.h" + + +void CMShock::Spawn() +{ + Precache(); + + pev->movetype = MOVETYPE_FLY; + pev->solid = SOLID_BBOX; + pev->classname = MAKE_STRING("shock_beam"); + SET_MODEL(ENT(pev), "models/shock_effect.mdl"); + UTIL_SetOrigin(pev, pev->origin); + pev->dmg = gSkillData.monDmgShockroach; + UTIL_SetSize(pev, Vector(-4, -4, -4), Vector(4, 4, 4)); + + CreateEffects(); + SetThink( &CMShock::FlyThink ); + pev->nextthink = gpGlobals->time; +} + +void CMShock::Precache() +{ + PRECACHE_MODEL("sprites/flare3.spr"); + PRECACHE_MODEL("sprites/lgtning.spr"); + PRECACHE_MODEL("models/shock_effect.mdl"); + PRECACHE_SOUND("weapons/shock_impact.wav"); +} + +void CMShock::FlyThink() +{ + if (pev->waterlevel == 3) + { + entvars_t *pevOwner = VARS(pev->owner); + EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM); + RadiusDamage(pev->origin, pev, pevOwner ? pevOwner : pev, pev->dmg * 3, 144, CLASS_NONE, DMG_SHOCK | DMG_ALWAYSGIB ); + ClearEffects(); + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; + } + else + { + pev->nextthink = gpGlobals->time + 0.05; + } +} + +edict_t *CMShock::Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity) +{ + CMShock *pShock = CreateClassPtr((CMShock *)NULL); + + if (pShock == NULL) + return NULL; + + UTIL_SetOrigin(pShock->pev, vecStart); + pShock->Spawn(); + + pShock->pev->velocity = vecVelocity; + pShock->pev->owner = ENT(pevOwner); + pShock->pev->angles = angles; + + pShock->pev->nextthink = gpGlobals->time; + + return pShock->edict(); +} + +void CMShock::Touch(edict_t *pOther) +{ + // Do not collide with the owner. + if (pOther == pev->owner) + return; + + TraceResult tr = UTIL_GetGlobalTrace( ); + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD(pev->origin.x); // X + WRITE_COORD(pev->origin.y); // Y + WRITE_COORD(pev->origin.z); // Z + WRITE_BYTE( 8 ); // radius * 0.1 + WRITE_BYTE( 0 ); // r + WRITE_BYTE( 255 ); // g + WRITE_BYTE( 255 ); // b + WRITE_BYTE( 10 ); // time * 10 + WRITE_BYTE( 10 ); // decay * 0.1 + MESSAGE_END( ); + + ClearEffects(); + if (!pOther->v.takedamage) + { + // make a splat on the wall + const int baseDecal = DECAL_SCORCH1; + UTIL_DecalTrace(&tr, baseDecal + RANDOM_LONG(0, 1)); + + int iContents = UTIL_PointContents(pev->origin); + + // Create sparks + if (iContents != CONTENTS_WATER) + { + UTIL_Sparks(tr.vecEndPos); + } + } + else + { + int damageType = DMG_SHOCK; + ClearMultiDamage(); + entvars_t *pevOwner = VARS(pev->owner); + entvars_t *pevAttacker = pevOwner ? pevOwner : pev; + + if ( UTIL_IsPlayer( pOther ) ) + UTIL_TraceAttack( pOther, pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType ); + else if ( pOther->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TraceAttack( pevAttacker, pev->dmg, pev->velocity.Normalize(), &tr, damageType ); + } + + ApplyMultiDamage(pev, pevAttacker); + } + + // splat sound + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_impact.wav", VOL_NORM, ATTN_NORM); + + pev->modelindex = 0; + pev->solid = SOLID_NOT; + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time + 0.01; // let the sound play +} + +void CMShock::CreateEffects() +{ + m_pSprite = CMSprite::SpriteCreate( "sprites/flare3.spr", pev->origin, FALSE ); + m_pSprite->SetAttachment( edict(), 0 ); + m_pSprite->pev->scale = 0.35; + m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 170, kRenderFxNoDissipation ); + //m_pSprite->pev->spawnflags |= SF_SPRITE_TEMPORARY; + //m_pSprite->pev->flags |= FL_SKIPLOCALHOST; + + m_pBeam = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + + if (m_pBeam) + { + m_pBeam->EntsInit( entindex(), entindex() ); + m_pBeam->SetStartAttachment( 1 ); + m_pBeam->SetEndAttachment( 2 ); + m_pBeam->SetBrightness( 180 ); + m_pBeam->SetScrollRate( 10 ); + m_pBeam->SetNoise( 0 ); + m_pBeam->SetFlags( BEAM_FSHADEOUT ); + m_pBeam->SetColor( 0, 255, 255 ); + //m_pBeam->pev->spawnflags = SF_BEAM_TEMPORARY; + m_pBeam->RelinkBeam(); + } + else + { + ALERT(at_console, "Could not create shockbeam beam!\n"); + } + + m_pNoise = CMBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + + if (m_pNoise) + { + m_pNoise->EntsInit( entindex(), entindex() ); + m_pNoise->SetStartAttachment( 1 ); + m_pNoise->SetEndAttachment( 2 ); + m_pNoise->SetBrightness( 180 ); + m_pNoise->SetScrollRate( 30 ); + m_pNoise->SetNoise( 30 ); + m_pNoise->SetFlags( BEAM_FSHADEOUT ); + m_pNoise->SetColor( 255, 255, 173 ); + //m_pNoise->pev->spawnflags = SF_BEAM_TEMPORARY; + m_pNoise->RelinkBeam(); + } + else + { + ALERT(at_console, "Could not create shockbeam noise!\n"); + } +} + +void CMShock::ClearEffects() +{ + if (m_pBeam) + { + UTIL_Remove( m_pBeam->edict() ); + m_pBeam = NULL; + } + + if (m_pNoise) + { + UTIL_Remove( m_pNoise->edict() ); + m_pNoise = NULL; + } + + if (m_pSprite) + { + UTIL_Remove( m_pSprite->edict() ); + m_pSprite = NULL; + } +} + +void CMShock::UpdateOnRemove() +{ + CMBaseAnimating::UpdateOnRemove(); + ClearEffects(); +} diff --git a/src/dlls/shock.h b/src/dlls/shock.h index f38661a..c5433f4 100644 --- a/src/dlls/shock.h +++ b/src/dlls/shock.h @@ -1,25 +1,25 @@ -#ifndef SHOCKBEAM_H -#define SHOCKBEAM_H - -//========================================================= -// Shockrifle projectile -//========================================================= -class CMShock : public CMBaseAnimating -{ -public: - void Spawn(void); - void Precache(void); - - static edict_t *Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity); - void Touch(edict_t *pOther); - void EXPORT FlyThink(); - - void CreateEffects(); - void ClearEffects(); - void UpdateOnRemove(); - - CMBeam *m_pBeam; - CMBeam *m_pNoise; - CMSprite *m_pSprite; -}; -#endif +#ifndef SHOCKBEAM_H +#define SHOCKBEAM_H + +//========================================================= +// Shockrifle projectile +//========================================================= +class CMShock : public CMBaseAnimating +{ +public: + void Spawn(void); + void Precache(void); + + static edict_t *Shoot(entvars_t *pevOwner, const Vector angles, const Vector vecStart, const Vector vecVelocity); + void Touch(edict_t *pOther); + void EXPORT FlyThink(); + + void CreateEffects(); + void ClearEffects(); + void UpdateOnRemove(); + + CMBeam *m_pBeam; + CMBeam *m_pNoise; + CMSprite *m_pSprite; +}; +#endif diff --git a/src/dlls/shockroach.cpp b/src/dlls/shockroach.cpp index 3bd3ae7..baf7125 100644 --- a/src/dlls/shockroach.cpp +++ b/src/dlls/shockroach.cpp @@ -1,223 +1,223 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// shockroach.cpp -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "weapons.h" - -const char *CMShockRoach::pIdleSounds[] = -{ - "shockroach/shock_idle1.wav", - "shockroach/shock_idle2.wav", - "shockroach/shock_idle3.wav", -}; -const char *CMShockRoach::pAlertSounds[] = -{ - "shockroach/shock_angry.wav", -}; -const char *CMShockRoach::pPainSounds[] = -{ - "shockroach/shock_flinch.wav", -}; -const char *CMShockRoach::pAttackSounds[] = -{ - "shockroach/shock_jump1.wav", - "shockroach/shock_jump2.wav", -}; - -const char *CMShockRoach::pDeathSounds[] = -{ - "shockroach/shock_die.wav", -}; - -const char *CMShockRoach::pBiteSounds[] = -{ - "shockroach/shock_bite.wav", -}; - - -//========================================================= -// Spawn -//========================================================= -void CMShockRoach::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/w_shock_rifle.mdl"); - UTIL_SetOrigin(pev, pev->origin); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_FLY; - m_bloodColor = BLOOD_COLOR_GREEN; - - pev->effects = 0; - pev->health = gSkillData.roachHealth; - pev->view_ofs = Vector(0, 0, 20);// position of the eyes relative to monster's origin. - pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_fRoachSolid = 0; - m_flBirthTime = gpGlobals->time; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_shockroach" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Shock Roach" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMShockRoach::Precache() -{ - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pAlertSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pAttackSounds); - PRECACHE_SOUND_ARRAY(pDeathSounds); - PRECACHE_SOUND_ARRAY(pBiteSounds); - - PRECACHE_SOUND("shockroach/shock_walk.wav"); - - PRECACHE_MODEL("models/w_shock_rifle.mdl"); -} - -//========================================================= -// LeapTouch - this is the headcrab's touch function when it -// is in the air -//========================================================= -void CMShockRoach::LeapTouch(edict_t *pOther) -{ - if (!pOther->v.takedamage) - { - return; - } - - // Don't hit if back on ground - if (!FBitSet(pev->flags, FL_ONGROUND)) - { - EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); - - if (UTIL_IsPlayer(pOther)) - UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); - } - } - - SetTouch(NULL); -} -//========================================================= -// PrescheduleThink -//========================================================= -void CMShockRoach::MonsterThink(void) -{ - float lifeTime = (gpGlobals->time - m_flBirthTime); - if (lifeTime >= 0.2) - { - pev->movetype = MOVETYPE_STEP; - } - if (!m_fRoachSolid && lifeTime >= 2.0 ) { - m_fRoachSolid = TRUE; - UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 4)); - } - if (lifeTime >= gSkillData.roachLifespan) - { - pev->health = -1; - Killed(pev, 0); - return; - } - - CMHeadCrab::MonsterThink(); -} - -//========================================================= -// IdleSound -//========================================================= -void CMShockRoach::IdleSound(void) -{ - EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); -} - -//========================================================= -// AlertSound -//========================================================= -void CMShockRoach::AlertSound(void) -{ - EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); -} - -//========================================================= -// AlertSound -//========================================================= -void CMShockRoach::PainSound(void) -{ - EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); -} - -//========================================================= -// DeathSound -//========================================================= -void CMShockRoach::DeathSound(void) -{ - EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); -} - - -void CMShockRoach::StartTask(Task_t *pTask) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch (pTask->iTask) - { - case TASK_RANGE_ATTACK1: - { - m_IdealActivity = ACT_RANGE_ATTACK1; - SetTouch(&CMShockRoach::LeapTouch); - break; - } - default: - CMHeadCrab::StartTask(pTask); - } -} - -int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - if ( gpGlobals->time - m_flBirthTime < 2.0 ) - flDamage = 0.0; - // Skip headcrab's TakeDamage to avoid unwanted immunity to acid. - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CMShockRoach::AttackSound() -{ - EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// shockroach.cpp +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "weapons.h" + +const char *CMShockRoach::pIdleSounds[] = +{ + "shockroach/shock_idle1.wav", + "shockroach/shock_idle2.wav", + "shockroach/shock_idle3.wav", +}; +const char *CMShockRoach::pAlertSounds[] = +{ + "shockroach/shock_angry.wav", +}; +const char *CMShockRoach::pPainSounds[] = +{ + "shockroach/shock_flinch.wav", +}; +const char *CMShockRoach::pAttackSounds[] = +{ + "shockroach/shock_jump1.wav", + "shockroach/shock_jump2.wav", +}; + +const char *CMShockRoach::pDeathSounds[] = +{ + "shockroach/shock_die.wav", +}; + +const char *CMShockRoach::pBiteSounds[] = +{ + "shockroach/shock_bite.wav", +}; + + +//========================================================= +// Spawn +//========================================================= +void CMShockRoach::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/w_shock_rifle.mdl"); + UTIL_SetOrigin(pev, pev->origin); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_FLY; + m_bloodColor = BLOOD_COLOR_GREEN; + + pev->effects = 0; + pev->health = gSkillData.roachHealth; + pev->view_ofs = Vector(0, 0, 20);// position of the eyes relative to monster's origin. + pev->yaw_speed = 5;//!!! should we put this in the monster's changeanim function since turn rates may vary with state/anim? + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_fRoachSolid = 0; + m_flBirthTime = gpGlobals->time; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_shockroach" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Shock Roach" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMShockRoach::Precache() +{ + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pAlertSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pAttackSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + PRECACHE_SOUND_ARRAY(pBiteSounds); + + PRECACHE_SOUND("shockroach/shock_walk.wav"); + + PRECACHE_MODEL("models/w_shock_rifle.mdl"); +} + +//========================================================= +// LeapTouch - this is the headcrab's touch function when it +// is in the air +//========================================================= +void CMShockRoach::LeapTouch(edict_t *pOther) +{ + if (!pOther->v.takedamage) + { + return; + } + + // Don't hit if back on ground + if (!FBitSet(pev->flags, FL_ONGROUND)) + { + EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pBiteSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); + + if (UTIL_IsPlayer(pOther)) + UTIL_TakeDamage( pOther, pev, pev, GetDamageAmount(), DMG_SLASH ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage( pev, pev, GetDamageAmount(), DMG_SLASH ); + } + } + + SetTouch(NULL); +} +//========================================================= +// PrescheduleThink +//========================================================= +void CMShockRoach::MonsterThink(void) +{ + float lifeTime = (gpGlobals->time - m_flBirthTime); + if (lifeTime >= 0.2) + { + pev->movetype = MOVETYPE_STEP; + } + if (!m_fRoachSolid && lifeTime >= 2.0 ) { + m_fRoachSolid = TRUE; + UTIL_SetSize(pev, Vector(-12, -12, 0), Vector(12, 12, 4)); + } + if (lifeTime >= gSkillData.roachLifespan) + { + pev->health = -1; + Killed(pev, 0); + return; + } + + CMHeadCrab::MonsterThink(); +} + +//========================================================= +// IdleSound +//========================================================= +void CMShockRoach::IdleSound(void) +{ + EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); +} + +//========================================================= +// AlertSound +//========================================================= +void CMShockRoach::AlertSound(void) +{ + EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); +} + +//========================================================= +// AlertSound +//========================================================= +void CMShockRoach::PainSound(void) +{ + EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); +} + +//========================================================= +// DeathSound +//========================================================= +void CMShockRoach::DeathSound(void) +{ + EMIT_SOUND_DYN(edict(), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); +} + + +void CMShockRoach::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch (pTask->iTask) + { + case TASK_RANGE_ATTACK1: + { + m_IdealActivity = ACT_RANGE_ATTACK1; + SetTouch(&CMShockRoach::LeapTouch); + break; + } + default: + CMHeadCrab::StartTask(pTask); + } +} + +int CMShockRoach::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + if ( gpGlobals->time - m_flBirthTime < 2.0 ) + flDamage = 0.0; + // Skip headcrab's TakeDamage to avoid unwanted immunity to acid. + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CMShockRoach::AttackSound() +{ + EMIT_SOUND_DYN(edict(), CHAN_WEAPON, RANDOM_SOUND_ARRAY(pAttackSounds), GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch()); +} diff --git a/src/dlls/skill.cpp b/src/dlls/skill.cpp index 1fee87a..3dbb759 100644 --- a/src/dlls/skill.cpp +++ b/src/dlls/skill.cpp @@ -1,337 +1,337 @@ - -#include -#include -#include - -#ifndef __linux__ -#include -#else -#include -#endif - -#include "extdll.h" -#include "dllapi.h" -#include "meta_api.h" -#include "skill.h" - -extern cvar_t *dllapi_log; - -skilldata_t gSkillData; - -struct skill_cfg_t -{ - char *name; - float *value; -}; - -skill_cfg_t skill_cfg[] = { - {"sk_agrunt_health", &gSkillData.agruntHealth}, - {"sk_agrunt_dmg_punch", &gSkillData.agruntDmgPunch}, - {"sk_apache_health", &gSkillData.apacheHealth}, - {"sk_barney_health", &gSkillData.barneyHealth}, - {"sk_bigmomma_health_factor", &gSkillData.bigmommaHealthFactor}, - {"sk_bigmomma_dmg_slash", &gSkillData.bigmommaDmgSlash}, - {"sk_bigmomma_dmg_blast", &gSkillData.bigmommaDmgBlast}, - {"sk_bigmomma_radius_blast", &gSkillData.bigmommaRadiusBlast}, - {"sk_bullsquid_health", &gSkillData.bullsquidHealth}, - {"sk_bullsquid_dmg_bite", &gSkillData.bullsquidDmgBite}, - {"sk_bullsquid_dmg_whip", &gSkillData.bullsquidDmgWhip}, - {"sk_bullsquid_dmg_spit", &gSkillData.bullsquidDmgSpit}, - {"sk_gargantua_health", &gSkillData.gargantuaHealth}, - {"sk_gargantua_armor", &gSkillData.gargantuaArmor}, - {"sk_gargantua_dmg_slash", &gSkillData.gargantuaDmgSlash}, - {"sk_gargantua_dmg_fire", &gSkillData.gargantuaDmgFire}, - {"sk_gargantua_dmg_stomp", &gSkillData.gargantuaDmgStomp}, - {"sk_hassassin_health", &gSkillData.hassassinHealth}, - {"sk_headcrab_health", &gSkillData.headcrabHealth}, - {"sk_headcrab_dmg_bite", &gSkillData.headcrabDmgBite}, - {"sk_hgrunt_health", &gSkillData.hgruntHealth}, - {"sk_hgrunt_kick", &gSkillData.hgruntDmgKick}, - {"sk_hgrunt_pellets", &gSkillData.hgruntShotgunPellets}, - {"sk_hgrunt_gspeed", &gSkillData.hgruntGrenadeSpeed}, - {"sk_houndeye_health", &gSkillData.houndeyeHealth}, - {"sk_houndeye_dmg_blast", &gSkillData.houndeyeDmgBlast}, - {"sk_islave_health", &gSkillData.slaveHealth}, - {"sk_islave_dmg_claw", &gSkillData.slaveDmgClaw}, - {"sk_islave_dmg_clawrake", &gSkillData.slaveDmgClawrake}, - {"sk_islave_dmg_zap", &gSkillData.slaveDmgZap}, - {"sk_ichthyosaur_health", &gSkillData.ichthyosaurHealth}, - {"sk_ichthyosaur_shake", &gSkillData.ichthyosaurDmgShake}, - {"sk_controller_health", &gSkillData.controllerHealth}, - {"sk_controller_dmgzap", &gSkillData.controllerDmgZap}, - {"sk_controller_speedball", &gSkillData.controllerSpeedBall}, - {"sk_controller_dmgball", &gSkillData.controllerDmgBall}, - {"sk_scientist_health", &gSkillData.scientistHealth}, - {"sk_scientist_heal", &gSkillData.scientistHeal}, - {"sk_snark_health", &gSkillData.snarkHealth}, - {"sk_snark_dmg_bite", &gSkillData.snarkDmgBite}, - {"sk_snark_dmg_pop", &gSkillData.snarkDmgPop}, - {"sk_zombie_health", &gSkillData.zombieHealth}, - {"sk_zombie_dmg_one_slash", &gSkillData.zombieDmgOneSlash}, - {"sk_zombie_dmg_both_slash", &gSkillData.zombieDmgBothSlash}, - {"sk_turret_health", &gSkillData.turretHealth}, - {"sk_miniturret_health", &gSkillData.miniturretHealth}, - {"sk_sentry_health", &gSkillData.sentryHealth}, - {"sk_gonome_health", &gSkillData.gonomeHealth}, - {"sk_gonome_dmg_guts", &gSkillData.gonomeDmgGuts}, - {"sk_gonome_dmg_one_slash", &gSkillData.gonomeDmgOneSlash}, - {"sk_gonome_dmg_one_bite", &gSkillData.gonomeDmgOneBite}, - {"sk_massassin_health", &gSkillData.massnHealth}, - {"sk_massassin_kick", &gSkillData.massnDmgKick}, - {"sk_otis_health", &gSkillData.otisHealth}, - {"sk_pitdrone_health", &gSkillData.pitdroneHealth}, - {"sk_pitdrone_dmg_spit", &gSkillData.pitdroneDmgSpit}, - {"sk_pitdrone_dmg_whip", &gSkillData.pitdroneDmgWhip}, - {"sk_pitdrone_dmg_bite", &gSkillData.pitdroneDmgBite}, - {"sk_shockroach_health", &gSkillData.roachHealth}, - {"sk_shockroach_lifespan", &gSkillData.roachLifespan}, - {"sk_shocktrooper_health", &gSkillData.strooperHealth}, - {"sk_shocktrooper_kick", &gSkillData.strooperDmgKick}, - {"sk_shocktrooper_maxcharge", &gSkillData.strooperMaxCharge}, - {"sk_shocktrooper_rchgspeed", &gSkillData.strooperRchgSpeed}, - {"sk_voltigore_health", &gSkillData.voltigoreHealth}, - {"sk_voltigore_dmg_beam", &gSkillData.voltigoreDmgBeam}, - {"sk_voltigore_dmg_punch", &gSkillData.voltigoreDmgPunch}, - {"sk_babyvoltigore_health", &gSkillData.babyVoltigoreHealth}, - {"sk_babyvoltigore_dmg_punch", &gSkillData.babyVoltigoreDmgPunch}, - {"sk_babygarg_health", &gSkillData.babygargHealth}, - {"sk_babygarg_dmg_slash", &gSkillData.babygargDmgSlash}, - {"sk_babygarg_dmg_fire", &gSkillData.babygargDmgFire}, - {"sk_babygarg_dmg_stomp", &gSkillData.babygargDmgStomp}, - {"sk_12mm_bullet", &gSkillData.monDmg9MM}, - {"sk_9mmAR_bullet", &gSkillData.monDmgMP5}, - {"sk_9mm_bullet", &gSkillData.monDmg12MM}, - {"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade}, - {"sk_762_bullet", &gSkillData.monDmg762}, - {"sk_357_bullet", &gSkillData.monDmg357}, - {"sk_hornet_dmg", &gSkillData.monDmgHornet}, - {"sk_shock_dmg", &gSkillData.monDmgShockroach}, - {"sk_spore_dmg", &gSkillData.monDmgSpore}, - {"", NULL} -}; - -bool get_input(FILE *fp, char *input); - - -void scan_monster_skill(FILE *fp) -{ - char input[1024]; - int index, len, pos; - bool found; - - while (get_input(fp, input)) - { - index = 0; - found = FALSE; - - while (skill_cfg[index].name[0]) - { - len = strlen(skill_cfg[index].name); - if (strncmp(input, skill_cfg[index].name, len) == 0) - { - found = TRUE; - pos = len; - sscanf(&input[pos], "%f", skill_cfg[index].value); - - if (dllapi_log->value) - LOG_MESSAGE(PLID, "skill setting %s set to %f", - skill_cfg[index].name, *skill_cfg[index].value); - - break; - } - index++; - } - - if (!found) - { - //META_CONS("[MONSTER] ERROR: unknown monster_skill.cfg item: %s", input); - LOG_MESSAGE(PLID, "ERROR: unknown monster_skill.cfg item: %s", input); - } - } -} - - -void monster_skill_init(void) -{ - char game_dir[256]; - char filename[256]; - FILE *fp = NULL; - - // Alien Grunt - gSkillData.agruntHealth = 90.0f; - gSkillData.agruntDmgPunch = 20.0f; - - // Apache - gSkillData.apacheHealth = 250.0f; - - // Barney - gSkillData.barneyHealth = 35.0f; - - // Big momma - gSkillData.bigmommaHealthFactor = 1.5f; - gSkillData.bigmommaDmgSlash = 60.0f; - gSkillData.bigmommaDmgBlast = 120.0f; - gSkillData.bigmommaRadiusBlast = 250.0f; - - // Bullsquid - gSkillData.bullsquidHealth = 40.0f; - gSkillData.bullsquidDmgBite = 25.0f; - gSkillData.bullsquidDmgWhip = 35.0f; - gSkillData.bullsquidDmgSpit = 10.0f; - - // Gargantua - gSkillData.gargantuaHealth = 800.0f; - gSkillData.gargantuaArmor = 0.85f; - gSkillData.gargantuaDmgSlash = 30.0f; - gSkillData.gargantuaDmgFire = 5.0f; - gSkillData.gargantuaDmgStomp = 100.0f; - - // Hassassin (Female Assassin) - gSkillData.hassassinHealth = 50.0f; - - // Headcrab - gSkillData.headcrabHealth = 10.0f; - gSkillData.headcrabDmgBite = 10.0f; - - // Hgrunt (Human Grunt) - gSkillData.hgruntHealth = 50.0f; - gSkillData.hgruntDmgKick = 10.0f; - gSkillData.hgruntShotgunPellets = 5.0f; - gSkillData.hgruntGrenadeSpeed = 600.0f; - - // Houndeye - gSkillData.houndeyeHealth = 20.0f; - gSkillData.houndeyeDmgBlast = 15.0f; - - // Alien Slave - gSkillData.slaveHealth = 30.0f; - gSkillData.slaveDmgClaw = 10.0f; - gSkillData.slaveDmgClawrake = 25.0f; - gSkillData.slaveDmgZap = 10.0f; - - // Icthyosaur - gSkillData.ichthyosaurHealth = 200.0f; - gSkillData.ichthyosaurDmgShake = 35.0f; - - // Controller - gSkillData.controllerHealth = 60.0f; - gSkillData.controllerDmgZap = 25.0f; - gSkillData.controllerSpeedBall = 800.0f; - gSkillData.controllerDmgBall = 4.0f; - - // Scientist - gSkillData.scientistHealth = 20.0f; - gSkillData.scientistHeal = 25.0f; - - // Snark - gSkillData.snarkHealth = 2.0f; - gSkillData.snarkDmgBite = 10.0f; - gSkillData.snarkDmgPop = 5.0f; - - // Zombie - gSkillData.zombieHealth = 50.0f; - gSkillData.zombieDmgOneSlash = 20.0f; - gSkillData.zombieDmgBothSlash = 40.0f; - - // Turret - gSkillData.turretHealth = 50.0f; - - // Mini-Turret - gSkillData.miniturretHealth = 40.0f; - - // Sentry - gSkillData.sentryHealth = 40.0f; - - // Gonome - gSkillData.gonomeHealth = 85.0f; - gSkillData.gonomeDmgGuts = 10.0f; - gSkillData.gonomeDmgOneSlash = 20.0f; - gSkillData.gonomeDmgOneBite = 14.0f; - - // Male Assassin - gSkillData.massnHealth = 50.0f; - gSkillData.massnDmgKick = 25.0f; - - // Otis - gSkillData.otisHealth = 35.0f; - - // Pit Drone - gSkillData.pitdroneHealth = 40.0f; - gSkillData.pitdroneDmgSpit = 10.0f; - gSkillData.pitdroneDmgWhip = 35.0f; - gSkillData.pitdroneDmgBite = 25.0f; - - // Shock Roach - gSkillData.roachHealth = 10.0f; - gSkillData.roachLifespan = 10.0f; - - // Shock Trooper - gSkillData.strooperHealth = 50.0f; - gSkillData.strooperDmgKick = 10.0f; - gSkillData.strooperMaxCharge = 8.0f; - gSkillData.strooperRchgSpeed = 1.0f; - - // Voltigore - gSkillData.voltigoreHealth = 320.0f; - gSkillData.voltigoreDmgBeam = 50.0f; - gSkillData.voltigoreDmgPunch = 40.0f; - - // Baby Voltigore - gSkillData.babyVoltigoreHealth = 60.0f; - gSkillData.babyVoltigoreDmgPunch = 15.0f; - - // Baby Gargantua - gSkillData.babygargHealth = 640.0f; - gSkillData.babygargDmgSlash = 24.0f; - gSkillData.babygargDmgFire = 4.0f; - gSkillData.babygargDmgStomp = 80.0f; - - // MONSTER WEAPONS - gSkillData.monDmg9MM = 5.0f; - gSkillData.monDmgMP5 = 4.0f; - gSkillData.monDmg12MM = 10.0f; - gSkillData.monDmgM203Grenade = 100.0f; - gSkillData.monDmg762 = 100.0f; - gSkillData.monDmg357 = 40.0f; - - // HORNET - gSkillData.monDmgHornet = 5.0f; - - // SHOCK ROACH - gSkillData.monDmgShockroach = 15.0f; - - // SPORE GRENADE - gSkillData.monDmgSpore = 50.0f; - - // find the directory name of the currently running MOD... - (*g_engfuncs.pfnGetGameDir)(game_dir); - - strcpy(filename, game_dir); - strcat(filename, "/monster_skill.cfg"); - - // check if the map specific filename exists... - if (access(filename, 0) == 0) - { - if (dllapi_log->value) - { - //META_CONS("[MONSTER] Processing monster skill file=%s", filename); - LOG_MESSAGE(PLID, "Processing monster skill file=%s", filename); - } - - if ((fp = fopen(filename, "r")) == NULL) - { - //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); - LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\"!", filename); - } - - scan_monster_skill(fp); - - fclose(fp); - } - else - { - //META_CONS("[MONSTER] ERROR: Could not find \"%s\" (default skill used)", filename); - LOG_MESSAGE(PLID, "ERROR: Could not find \"%s\" (default skill used)", filename); - } -} - + +#include +#include +#include + +#ifndef __linux__ +#include +#else +#include +#endif + +#include "extdll.h" +#include "dllapi.h" +#include "meta_api.h" +#include "skill.h" + +extern cvar_t *dllapi_log; + +skilldata_t gSkillData; + +struct skill_cfg_t +{ + char *name; + float *value; +}; + +skill_cfg_t skill_cfg[] = { + {"sk_agrunt_health", &gSkillData.agruntHealth}, + {"sk_agrunt_dmg_punch", &gSkillData.agruntDmgPunch}, + {"sk_apache_health", &gSkillData.apacheHealth}, + {"sk_barney_health", &gSkillData.barneyHealth}, + {"sk_bigmomma_health_factor", &gSkillData.bigmommaHealthFactor}, + {"sk_bigmomma_dmg_slash", &gSkillData.bigmommaDmgSlash}, + {"sk_bigmomma_dmg_blast", &gSkillData.bigmommaDmgBlast}, + {"sk_bigmomma_radius_blast", &gSkillData.bigmommaRadiusBlast}, + {"sk_bullsquid_health", &gSkillData.bullsquidHealth}, + {"sk_bullsquid_dmg_bite", &gSkillData.bullsquidDmgBite}, + {"sk_bullsquid_dmg_whip", &gSkillData.bullsquidDmgWhip}, + {"sk_bullsquid_dmg_spit", &gSkillData.bullsquidDmgSpit}, + {"sk_gargantua_health", &gSkillData.gargantuaHealth}, + {"sk_gargantua_armor", &gSkillData.gargantuaArmor}, + {"sk_gargantua_dmg_slash", &gSkillData.gargantuaDmgSlash}, + {"sk_gargantua_dmg_fire", &gSkillData.gargantuaDmgFire}, + {"sk_gargantua_dmg_stomp", &gSkillData.gargantuaDmgStomp}, + {"sk_hassassin_health", &gSkillData.hassassinHealth}, + {"sk_headcrab_health", &gSkillData.headcrabHealth}, + {"sk_headcrab_dmg_bite", &gSkillData.headcrabDmgBite}, + {"sk_hgrunt_health", &gSkillData.hgruntHealth}, + {"sk_hgrunt_kick", &gSkillData.hgruntDmgKick}, + {"sk_hgrunt_pellets", &gSkillData.hgruntShotgunPellets}, + {"sk_hgrunt_gspeed", &gSkillData.hgruntGrenadeSpeed}, + {"sk_houndeye_health", &gSkillData.houndeyeHealth}, + {"sk_houndeye_dmg_blast", &gSkillData.houndeyeDmgBlast}, + {"sk_islave_health", &gSkillData.slaveHealth}, + {"sk_islave_dmg_claw", &gSkillData.slaveDmgClaw}, + {"sk_islave_dmg_clawrake", &gSkillData.slaveDmgClawrake}, + {"sk_islave_dmg_zap", &gSkillData.slaveDmgZap}, + {"sk_ichthyosaur_health", &gSkillData.ichthyosaurHealth}, + {"sk_ichthyosaur_shake", &gSkillData.ichthyosaurDmgShake}, + {"sk_controller_health", &gSkillData.controllerHealth}, + {"sk_controller_dmgzap", &gSkillData.controllerDmgZap}, + {"sk_controller_speedball", &gSkillData.controllerSpeedBall}, + {"sk_controller_dmgball", &gSkillData.controllerDmgBall}, + {"sk_scientist_health", &gSkillData.scientistHealth}, + {"sk_scientist_heal", &gSkillData.scientistHeal}, + {"sk_snark_health", &gSkillData.snarkHealth}, + {"sk_snark_dmg_bite", &gSkillData.snarkDmgBite}, + {"sk_snark_dmg_pop", &gSkillData.snarkDmgPop}, + {"sk_zombie_health", &gSkillData.zombieHealth}, + {"sk_zombie_dmg_one_slash", &gSkillData.zombieDmgOneSlash}, + {"sk_zombie_dmg_both_slash", &gSkillData.zombieDmgBothSlash}, + {"sk_turret_health", &gSkillData.turretHealth}, + {"sk_miniturret_health", &gSkillData.miniturretHealth}, + {"sk_sentry_health", &gSkillData.sentryHealth}, + {"sk_gonome_health", &gSkillData.gonomeHealth}, + {"sk_gonome_dmg_guts", &gSkillData.gonomeDmgGuts}, + {"sk_gonome_dmg_one_slash", &gSkillData.gonomeDmgOneSlash}, + {"sk_gonome_dmg_one_bite", &gSkillData.gonomeDmgOneBite}, + {"sk_massassin_health", &gSkillData.massnHealth}, + {"sk_massassin_kick", &gSkillData.massnDmgKick}, + {"sk_otis_health", &gSkillData.otisHealth}, + {"sk_pitdrone_health", &gSkillData.pitdroneHealth}, + {"sk_pitdrone_dmg_spit", &gSkillData.pitdroneDmgSpit}, + {"sk_pitdrone_dmg_whip", &gSkillData.pitdroneDmgWhip}, + {"sk_pitdrone_dmg_bite", &gSkillData.pitdroneDmgBite}, + {"sk_shockroach_health", &gSkillData.roachHealth}, + {"sk_shockroach_lifespan", &gSkillData.roachLifespan}, + {"sk_shocktrooper_health", &gSkillData.strooperHealth}, + {"sk_shocktrooper_kick", &gSkillData.strooperDmgKick}, + {"sk_shocktrooper_maxcharge", &gSkillData.strooperMaxCharge}, + {"sk_shocktrooper_rchgspeed", &gSkillData.strooperRchgSpeed}, + {"sk_voltigore_health", &gSkillData.voltigoreHealth}, + {"sk_voltigore_dmg_beam", &gSkillData.voltigoreDmgBeam}, + {"sk_voltigore_dmg_punch", &gSkillData.voltigoreDmgPunch}, + {"sk_babyvoltigore_health", &gSkillData.babyVoltigoreHealth}, + {"sk_babyvoltigore_dmg_punch", &gSkillData.babyVoltigoreDmgPunch}, + {"sk_babygarg_health", &gSkillData.babygargHealth}, + {"sk_babygarg_dmg_slash", &gSkillData.babygargDmgSlash}, + {"sk_babygarg_dmg_fire", &gSkillData.babygargDmgFire}, + {"sk_babygarg_dmg_stomp", &gSkillData.babygargDmgStomp}, + {"sk_12mm_bullet", &gSkillData.monDmg9MM}, + {"sk_9mmAR_bullet", &gSkillData.monDmgMP5}, + {"sk_9mm_bullet", &gSkillData.monDmg12MM}, + {"sk_9mmAR_grenade", &gSkillData.monDmgM203Grenade}, + {"sk_762_bullet", &gSkillData.monDmg762}, + {"sk_357_bullet", &gSkillData.monDmg357}, + {"sk_hornet_dmg", &gSkillData.monDmgHornet}, + {"sk_shock_dmg", &gSkillData.monDmgShockroach}, + {"sk_spore_dmg", &gSkillData.monDmgSpore}, + {"", NULL} +}; + +bool get_input(FILE *fp, char *input); + + +void scan_monster_skill(FILE *fp) +{ + char input[1024]; + int index, len, pos; + bool found; + + while (get_input(fp, input)) + { + index = 0; + found = FALSE; + + while (skill_cfg[index].name[0]) + { + len = strlen(skill_cfg[index].name); + if (strncmp(input, skill_cfg[index].name, len) == 0) + { + found = TRUE; + pos = len; + sscanf(&input[pos], "%f", skill_cfg[index].value); + + if (dllapi_log->value) + LOG_MESSAGE(PLID, "skill setting %s set to %f", + skill_cfg[index].name, *skill_cfg[index].value); + + break; + } + index++; + } + + if (!found) + { + //META_CONS("[MONSTER] ERROR: unknown monster_skill.cfg item: %s", input); + LOG_MESSAGE(PLID, "ERROR: unknown monster_skill.cfg item: %s", input); + } + } +} + + +void monster_skill_init(void) +{ + char game_dir[256]; + char filename[256]; + FILE *fp = NULL; + + // Alien Grunt + gSkillData.agruntHealth = 90.0f; + gSkillData.agruntDmgPunch = 20.0f; + + // Apache + gSkillData.apacheHealth = 250.0f; + + // Barney + gSkillData.barneyHealth = 35.0f; + + // Big momma + gSkillData.bigmommaHealthFactor = 1.5f; + gSkillData.bigmommaDmgSlash = 60.0f; + gSkillData.bigmommaDmgBlast = 120.0f; + gSkillData.bigmommaRadiusBlast = 250.0f; + + // Bullsquid + gSkillData.bullsquidHealth = 40.0f; + gSkillData.bullsquidDmgBite = 25.0f; + gSkillData.bullsquidDmgWhip = 35.0f; + gSkillData.bullsquidDmgSpit = 10.0f; + + // Gargantua + gSkillData.gargantuaHealth = 800.0f; + gSkillData.gargantuaArmor = 0.85f; + gSkillData.gargantuaDmgSlash = 30.0f; + gSkillData.gargantuaDmgFire = 5.0f; + gSkillData.gargantuaDmgStomp = 100.0f; + + // Hassassin (Female Assassin) + gSkillData.hassassinHealth = 50.0f; + + // Headcrab + gSkillData.headcrabHealth = 10.0f; + gSkillData.headcrabDmgBite = 10.0f; + + // Hgrunt (Human Grunt) + gSkillData.hgruntHealth = 50.0f; + gSkillData.hgruntDmgKick = 10.0f; + gSkillData.hgruntShotgunPellets = 5.0f; + gSkillData.hgruntGrenadeSpeed = 600.0f; + + // Houndeye + gSkillData.houndeyeHealth = 20.0f; + gSkillData.houndeyeDmgBlast = 15.0f; + + // Alien Slave + gSkillData.slaveHealth = 30.0f; + gSkillData.slaveDmgClaw = 10.0f; + gSkillData.slaveDmgClawrake = 25.0f; + gSkillData.slaveDmgZap = 10.0f; + + // Icthyosaur + gSkillData.ichthyosaurHealth = 200.0f; + gSkillData.ichthyosaurDmgShake = 35.0f; + + // Controller + gSkillData.controllerHealth = 60.0f; + gSkillData.controllerDmgZap = 25.0f; + gSkillData.controllerSpeedBall = 800.0f; + gSkillData.controllerDmgBall = 4.0f; + + // Scientist + gSkillData.scientistHealth = 20.0f; + gSkillData.scientistHeal = 25.0f; + + // Snark + gSkillData.snarkHealth = 2.0f; + gSkillData.snarkDmgBite = 10.0f; + gSkillData.snarkDmgPop = 5.0f; + + // Zombie + gSkillData.zombieHealth = 50.0f; + gSkillData.zombieDmgOneSlash = 20.0f; + gSkillData.zombieDmgBothSlash = 40.0f; + + // Turret + gSkillData.turretHealth = 50.0f; + + // Mini-Turret + gSkillData.miniturretHealth = 40.0f; + + // Sentry + gSkillData.sentryHealth = 40.0f; + + // Gonome + gSkillData.gonomeHealth = 85.0f; + gSkillData.gonomeDmgGuts = 10.0f; + gSkillData.gonomeDmgOneSlash = 20.0f; + gSkillData.gonomeDmgOneBite = 14.0f; + + // Male Assassin + gSkillData.massnHealth = 50.0f; + gSkillData.massnDmgKick = 25.0f; + + // Otis + gSkillData.otisHealth = 35.0f; + + // Pit Drone + gSkillData.pitdroneHealth = 40.0f; + gSkillData.pitdroneDmgSpit = 10.0f; + gSkillData.pitdroneDmgWhip = 35.0f; + gSkillData.pitdroneDmgBite = 25.0f; + + // Shock Roach + gSkillData.roachHealth = 10.0f; + gSkillData.roachLifespan = 10.0f; + + // Shock Trooper + gSkillData.strooperHealth = 50.0f; + gSkillData.strooperDmgKick = 10.0f; + gSkillData.strooperMaxCharge = 8.0f; + gSkillData.strooperRchgSpeed = 1.0f; + + // Voltigore + gSkillData.voltigoreHealth = 320.0f; + gSkillData.voltigoreDmgBeam = 50.0f; + gSkillData.voltigoreDmgPunch = 40.0f; + + // Baby Voltigore + gSkillData.babyVoltigoreHealth = 60.0f; + gSkillData.babyVoltigoreDmgPunch = 15.0f; + + // Baby Gargantua + gSkillData.babygargHealth = 640.0f; + gSkillData.babygargDmgSlash = 24.0f; + gSkillData.babygargDmgFire = 4.0f; + gSkillData.babygargDmgStomp = 80.0f; + + // MONSTER WEAPONS + gSkillData.monDmg9MM = 5.0f; + gSkillData.monDmgMP5 = 4.0f; + gSkillData.monDmg12MM = 10.0f; + gSkillData.monDmgM203Grenade = 100.0f; + gSkillData.monDmg762 = 100.0f; + gSkillData.monDmg357 = 40.0f; + + // HORNET + gSkillData.monDmgHornet = 5.0f; + + // SHOCK ROACH + gSkillData.monDmgShockroach = 15.0f; + + // SPORE GRENADE + gSkillData.monDmgSpore = 50.0f; + + // find the directory name of the currently running MOD... + (*g_engfuncs.pfnGetGameDir)(game_dir); + + strcpy(filename, game_dir); + strcat(filename, "/monster_skill.cfg"); + + // check if the map specific filename exists... + if (access(filename, 0) == 0) + { + if (dllapi_log->value) + { + //META_CONS("[MONSTER] Processing monster skill file=%s", filename); + LOG_MESSAGE(PLID, "Processing monster skill file=%s", filename); + } + + if ((fp = fopen(filename, "r")) == NULL) + { + //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); + LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\"!", filename); + } + + scan_monster_skill(fp); + + fclose(fp); + } + else + { + //META_CONS("[MONSTER] ERROR: Could not find \"%s\" (default skill used)", filename); + LOG_MESSAGE(PLID, "ERROR: Could not find \"%s\" (default skill used)", filename); + } +} + diff --git a/src/dlls/skill.h b/src/dlls/skill.h index 5bc62d0..c4753c6 100644 --- a/src/dlls/skill.h +++ b/src/dlls/skill.h @@ -1,144 +1,144 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -//========================================================= -// skill.h - skill level concerns -//========================================================= - -#ifndef SKILL_H -#define SKILL_H - -struct skilldata_t -{ -// Monster Health & Damage - float agruntHealth; - float agruntDmgPunch; - - float apacheHealth; - - float barneyHealth; - - float bigmommaHealthFactor; // Multiply each node's health by this - float bigmommaDmgSlash; // melee attack damage - float bigmommaDmgBlast; // mortar attack damage - float bigmommaRadiusBlast; // mortar attack radius - - float bullsquidHealth; - float bullsquidDmgBite; - float bullsquidDmgWhip; - float bullsquidDmgSpit; - - float gargantuaHealth; - float gargantuaArmor; // Non-explosives will deal this much damage - float gargantuaDmgSlash; - float gargantuaDmgFire; - float gargantuaDmgStomp; - - float hassassinHealth; - - float headcrabHealth; - float headcrabDmgBite; - - float hgruntHealth; - float hgruntDmgKick; - float hgruntShotgunPellets; - float hgruntGrenadeSpeed; - - float houndeyeHealth; - float houndeyeDmgBlast; - - float slaveHealth; - float slaveDmgClaw; - float slaveDmgClawrake; - float slaveDmgZap; - - float ichthyosaurHealth; - float ichthyosaurDmgShake; - - float controllerHealth; - float controllerDmgZap; - float controllerSpeedBall; - float controllerDmgBall; - - float scientistHealth; - float scientistHeal; - - float snarkHealth; - float snarkDmgBite; - float snarkDmgPop; - - float zombieHealth; - float zombieDmgOneSlash; - float zombieDmgBothSlash; - - float turretHealth; - float miniturretHealth; - float sentryHealth; - -//OP4 monsters - float gonomeHealth; - float gonomeDmgGuts; - float gonomeDmgOneSlash; - float gonomeDmgOneBite; - - float massnHealth; - float massnDmgKick; - - float otisHealth; - - float pitdroneHealth; - float pitdroneDmgSpit; - float pitdroneDmgWhip; - float pitdroneDmgBite; - - float roachHealth; - float roachLifespan; - - float strooperHealth; - float strooperDmgKick; - float strooperMaxCharge; - float strooperRchgSpeed; - - float voltigoreHealth; - float voltigoreDmgBeam; - float voltigoreDmgPunch; - - float babyVoltigoreHealth; - float babyVoltigoreDmgPunch; - - -//SC monsters - float babygargHealth; - float babygargDmgSlash; - float babygargDmgFire; - float babygargDmgStomp; - -// weapons shared by monsters - float monDmg9MM; - float monDmgMP5; - float monDmg12MM; - float monDmgM203Grenade; - float monDmg762; - float monDmg357; - - float monDmgHornet; - float monDmgShockroach; - float monDmgSpore; -}; - -extern DLL_GLOBAL skilldata_t gSkillData; - -void monster_skill_init(void); - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +//========================================================= +// skill.h - skill level concerns +//========================================================= + +#ifndef SKILL_H +#define SKILL_H + +struct skilldata_t +{ +// Monster Health & Damage + float agruntHealth; + float agruntDmgPunch; + + float apacheHealth; + + float barneyHealth; + + float bigmommaHealthFactor; // Multiply each node's health by this + float bigmommaDmgSlash; // melee attack damage + float bigmommaDmgBlast; // mortar attack damage + float bigmommaRadiusBlast; // mortar attack radius + + float bullsquidHealth; + float bullsquidDmgBite; + float bullsquidDmgWhip; + float bullsquidDmgSpit; + + float gargantuaHealth; + float gargantuaArmor; // Non-explosives will deal this much damage + float gargantuaDmgSlash; + float gargantuaDmgFire; + float gargantuaDmgStomp; + + float hassassinHealth; + + float headcrabHealth; + float headcrabDmgBite; + + float hgruntHealth; + float hgruntDmgKick; + float hgruntShotgunPellets; + float hgruntGrenadeSpeed; + + float houndeyeHealth; + float houndeyeDmgBlast; + + float slaveHealth; + float slaveDmgClaw; + float slaveDmgClawrake; + float slaveDmgZap; + + float ichthyosaurHealth; + float ichthyosaurDmgShake; + + float controllerHealth; + float controllerDmgZap; + float controllerSpeedBall; + float controllerDmgBall; + + float scientistHealth; + float scientistHeal; + + float snarkHealth; + float snarkDmgBite; + float snarkDmgPop; + + float zombieHealth; + float zombieDmgOneSlash; + float zombieDmgBothSlash; + + float turretHealth; + float miniturretHealth; + float sentryHealth; + +//OP4 monsters + float gonomeHealth; + float gonomeDmgGuts; + float gonomeDmgOneSlash; + float gonomeDmgOneBite; + + float massnHealth; + float massnDmgKick; + + float otisHealth; + + float pitdroneHealth; + float pitdroneDmgSpit; + float pitdroneDmgWhip; + float pitdroneDmgBite; + + float roachHealth; + float roachLifespan; + + float strooperHealth; + float strooperDmgKick; + float strooperMaxCharge; + float strooperRchgSpeed; + + float voltigoreHealth; + float voltigoreDmgBeam; + float voltigoreDmgPunch; + + float babyVoltigoreHealth; + float babyVoltigoreDmgPunch; + + +//SC monsters + float babygargHealth; + float babygargDmgSlash; + float babygargDmgFire; + float babygargDmgStomp; + +// weapons shared by monsters + float monDmg9MM; + float monDmgMP5; + float monDmg12MM; + float monDmgM203Grenade; + float monDmg762; + float monDmg357; + + float monDmgHornet; + float monDmgShockroach; + float monDmgSpore; +}; + +extern DLL_GLOBAL skilldata_t gSkillData; + +void monster_skill_init(void); + +#endif diff --git a/src/dlls/sound.cpp b/src/dlls/sound.cpp index 63268cd..ca2eac6 100644 --- a/src/dlls/sound.cpp +++ b/src/dlls/sound.cpp @@ -1,918 +1,918 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -//========================================================= -// sound.cpp -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "weapons.h" -#include "cmtalkmonster.h" -#include "pm_materials.h" - -//#if !defined ( _WIN32 ) -#include -//#endif - -static char *memfgets( byte *pMemFile, int fileSize, int &filePos, char *pBuffer, int bufferSize ); - - -// ==================== GENERIC AMBIENT SOUND ====================================== - -// runtime pitch shift and volume fadein/out structure - -// NOTE: IF YOU CHANGE THIS STRUCT YOU MUST CHANGE THE SAVE/RESTORE VERSION NUMBER -// SEE BELOW (in the typedescription for the class) -typedef struct dynpitchvol -{ - // NOTE: do not change the order of these parameters - // NOTE: unless you also change order of rgdpvpreset array elements! - int preset; - - int pitchrun; // pitch shift % when sound is running 0 - 255 - int pitchstart; // pitch shift % when sound stops or starts 0 - 255 - int spinup; // spinup time 0 - 100 - int spindown; // spindown time 0 - 100 - - int volrun; // volume change % when sound is running 0 - 10 - int volstart; // volume change % when sound stops or starts 0 - 10 - int fadein; // volume fade in time 0 - 100 - int fadeout; // volume fade out time 0 - 100 - - // Low Frequency Oscillator - int lfotype; // 0) off 1) square 2) triangle 3) random - int lforate; // 0 - 1000, how fast lfo osciallates - - int lfomodpitch; // 0-100 mod of current pitch. 0 is off. - int lfomodvol; // 0-100 mod of current volume. 0 is off. - - int cspinup; // each trigger hit increments counter and spinup pitch - - - int cspincount; - - int pitch; - int spinupsav; - int spindownsav; - int pitchfrac; - - int vol; - int fadeinsav; - int fadeoutsav; - int volfrac; - - int lfofrac; - int lfomult; - - -} dynpitchvol_t; - -#define CDPVPRESETMAX 27 - -// presets for runtime pitch and vol modulation of ambient sounds - -/*jlb -dynpitchvol_t rgdpvpreset[CDPVPRESETMAX] = -{ -// pitch pstart spinup spindwn volrun volstrt fadein fadeout lfotype lforate modptch modvol cspnup -{1, 255, 75, 95, 95, 10, 1, 50, 95, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{2, 255, 85, 70, 88, 10, 1, 20, 88, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{3, 255, 100, 50, 75, 10, 1, 10, 75, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{4, 100, 100, 0, 0, 10, 1, 90, 90, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{5, 100, 100, 0, 0, 10, 1, 80, 80, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{6, 100, 100, 0, 0, 10, 1, 50, 70, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{7, 100, 100, 0, 0, 5, 1, 40, 50, 1, 50, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, -{8, 100, 100, 0, 0, 5, 1, 40, 50, 1, 150, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, -{9, 100, 100, 0, 0, 5, 1, 40, 50, 1, 750, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, -{10,128, 100, 50, 75, 10, 1, 30, 40, 2, 8, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{11,128, 100, 50, 75, 10, 1, 30, 40, 2, 25, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{12,128, 100, 50, 75, 10, 1, 30, 40, 2, 70, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{13,50, 50, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{14,70, 70, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{15,90, 90, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{16,120, 120, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{17,180, 180, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{18,255, 255, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{19,200, 75, 90, 90, 10, 1, 50, 90, 2, 100, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{20,255, 75, 97, 90, 10, 1, 50, 90, 1, 40, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{21,100, 100, 0, 0, 10, 1, 30, 50, 3, 15, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{22,160, 160, 0, 0, 10, 1, 50, 50, 3, 500, 25, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{23,255, 75, 88, 0, 10, 1, 40, 0, 0, 0, 0, 0, 5, 0,0,0,0,0,0,0,0,0,0}, -{24,200, 20, 95, 70, 10, 1, 70, 70, 3, 20, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, -{25,180, 100, 50, 60, 10, 1, 40, 60, 2, 90, 100, 100, 0, 0,0,0,0,0,0,0,0,0,0}, -{26,60, 60, 0, 0, 10, 1, 40, 70, 3, 80, 20, 50, 0, 0,0,0,0,0,0,0,0,0,0}, -{27,128, 90, 10, 10, 10, 1, 20, 40, 1, 5, 10, 20, 0, 0,0,0,0,0,0,0,0,0,0} -}; -jlb*/ - - -// ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ====================================== - -#define CSENTENCE_LRU_MAX 32 // max number of elements per sentence group - -// group of related sentences - -typedef struct sentenceg -{ - char szgroupname[CBSENTENCENAME_MAX]; - int count; - unsigned char rgblru[CSENTENCE_LRU_MAX]; - -} SENTENCEG; - -#define CSENTENCEG_MAX 200 // max number of sentence groups -// globals - -SENTENCEG rgsentenceg[CSENTENCEG_MAX]; -int fSentencesInit = FALSE; - -char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; -int gcallsentences = 0; - -// randomize list of sentence name indices - -void USENTENCEG_InitLRU(unsigned char *plru, int count) -{ - int i, j, k; - unsigned char temp; - - if (!fSentencesInit) - return; - - if (count > CSENTENCE_LRU_MAX) - count = CSENTENCE_LRU_MAX; - - for (i = 0; i < count; i++) - plru[i] = (unsigned char) i; - - // randomize array - for (i = 0; i < (count * 4); i++) - { - j = RANDOM_LONG(0,count-1); - k = RANDOM_LONG(0,count-1); - temp = plru[j]; - plru[j] = plru[k]; - plru[k] = temp; - } -} - -// ignore lru. pick next sentence from sentence group. Go in order until we hit the last sentence, -// then repeat list if freset is true. If freset is false, then repeat last sentence. -// ipick is passed in as the requested sentence ordinal. -// ipick 'next' is returned. -// return of -1 indicates an error. - -int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset) -{ - char *szgroupname; - unsigned char count; - char sznum[8]; - - if (!fSentencesInit) - return -1; - - if (isentenceg < 0) - return -1; - - szgroupname = rgsentenceg[isentenceg].szgroupname; - count = rgsentenceg[isentenceg].count; - - if (count == 0) - return -1; - - if (ipick >= count) - ipick = count-1; - - strcpy(szfound, "!"); - strcat(szfound, szgroupname); - sprintf(sznum, "%d", ipick); - strcat(szfound, sznum); - - if (ipick >= count) - { - if (freset) - // reset at end of list - return 0; - else - return count; - } - - return ipick + 1; -} - - - -// pick a random sentence from rootname0 to rootnameX. -// picks from the rgsentenceg[isentenceg] least -// recently used, modifies lru array. returns the sentencename. -// note, lru must be seeded with 0-n randomized sentence numbers, with the -// rest of the lru filled with -1. The first integer in the lru is -// actually the size of the list. Returns ipick, the ordinal -// of the picked sentence within the group. - -int USENTENCEG_Pick(int isentenceg, char *szfound) -{ - char *szgroupname; - unsigned char *plru; - unsigned char i; - unsigned char count; - char sznum[8]; - unsigned char ipick; - int ffound = FALSE; - - if (!fSentencesInit) - return -1; - - if (isentenceg < 0) - return -1; - - szgroupname = rgsentenceg[isentenceg].szgroupname; - count = rgsentenceg[isentenceg].count; - plru = rgsentenceg[isentenceg].rgblru; - - while (!ffound) - { - for (i = 0; i < count; i++) - if (plru[i] != 0xFF) - { - ipick = plru[i]; - plru[i] = 0xFF; - ffound = TRUE; - break; - } - - if (!ffound) - USENTENCEG_InitLRU(plru, count); - else - { - strcpy(szfound, "!"); - strcat(szfound, szgroupname); - sprintf(sznum, "%d", ipick); - strcat(szfound, sznum); - return ipick; - } - } - return -1; -} - -// ===================== SENTENCE GROUPS, MAIN ROUTINES ======================== - -// Given sentence group rootname (name without number suffix), -// get sentence group index (isentenceg). Returns -1 if no such name. - -int SENTENCEG_GetIndex(const char *szgroupname) -{ - int i; - - if (!fSentencesInit || !szgroupname) - return -1; - - // search rgsentenceg for match on szgroupname - - i = 0; - while (rgsentenceg[i].count) - { - if (!strcmp(szgroupname, rgsentenceg[i].szgroupname)) - return i; - i++; - } - - return -1; -} - -// given sentence group index, play random sentence for given entity. -// returns ipick - which sentence was picked to -// play from the group. Ipick is only needed if you plan on stopping -// the sound before playback is done (see SENTENCEG_Stop). - -int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, - float volume, float attenuation, int flags, int pitch) -{ - char name[64]; - int ipick; - - if (!fSentencesInit) - return -1; - - name[0] = 0; - - ipick = USENTENCEG_Pick(isentenceg, name); - if (ipick > 0 && name) - EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); - return ipick; -} - -// same as above, but takes sentence group name instead of index - -int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, - float volume, float attenuation, int flags, int pitch) -{ - char name[64]; - int ipick; - int isentenceg; - - if (!fSentencesInit) - return -1; - - name[0] = 0; - - isentenceg = SENTENCEG_GetIndex(szgroupname); - if (isentenceg < 0) - { - ALERT( at_console, "No such sentence group %s\n", szgroupname ); - return -1; - } - - ipick = USENTENCEG_Pick(isentenceg, name); - if (ipick >= 0 && name[0]) - EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); - - return ipick; -} - -// play sentences in sequential order from sentence group. Reset after last sentence. - -int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname, - float volume, float attenuation, int flags, int pitch, int ipick, int freset) -{ - char name[64]; - int ipicknext; - int isentenceg; - - if (!fSentencesInit) - return -1; - - name[0] = 0; - - isentenceg = SENTENCEG_GetIndex(szgroupname); - if (isentenceg < 0) - return -1; - - ipicknext = USENTENCEG_PickSequential(isentenceg, name, ipick, freset); - if (ipicknext >= 0 && name[0]) - EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); - return ipicknext; -} - - -// for this entity, for the given sentence within the sentence group, stop -// the sentence. - -void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick) -{ - char buffer[64]; - char sznum[8]; - - if (!fSentencesInit) - return; - - if (isentenceg < 0 || ipick < 0) - return; - - strcpy(buffer, "!"); - strcat(buffer, rgsentenceg[isentenceg].szgroupname); - sprintf(sznum, "%d", ipick); - strcat(buffer, sznum); - - STOP_SOUND(entity, CHAN_VOICE, buffer); -} - -// open sentences.txt, scan for groups, build rgsentenceg -// Should be called from world spawn, only works on the -// first call and is ignored subsequently. - -void SENTENCEG_Init() -{ - char buffer[512]; - char szgroup[64]; - int i, j; - int isentencegs; - - if (fSentencesInit) - return; - - memset(gszallsentencenames, 0, CVOXFILESENTENCEMAX * CBSENTENCENAME_MAX); - gcallsentences = 0; - - memset(rgsentenceg, 0, CSENTENCEG_MAX * sizeof(SENTENCEG)); - memset(buffer, 0, 512); - memset(szgroup, 0, 64); - isentencegs = -1; - - - int filePos = 0, fileSize; - byte *pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/sentences.txt", &fileSize ); - if ( !pMemFile ) - return; - - // for each line in the file... - while ( memfgets(pMemFile, fileSize, filePos, buffer, 511) != NULL ) - { - // skip whitespace - i = 0; - while(buffer[i] && buffer[i] == ' ') - i++; - - if (!buffer[i]) - continue; - - if (buffer[i] == '/' || !isalpha(buffer[i])) - continue; - - // get sentence name - j = i; - while (buffer[j] && buffer[j] != ' ') - j++; - - if (!buffer[j]) - continue; - - if (gcallsentences > CVOXFILESENTENCEMAX) - { - ALERT (at_error, "Too many sentences in sentences.txt!\n"); - break; - } - - // null-terminate name and save in sentences array - buffer[j] = 0; - const char *pString = buffer + i; - - if ( strlen( pString ) >= CBSENTENCENAME_MAX ) - ALERT( at_warning, "Sentence %s longer than %d letters\n", pString, CBSENTENCENAME_MAX-1 ); - - strcpy( gszallsentencenames[gcallsentences++], pString ); - - j--; - if (j <= i) - continue; - if (!isdigit(buffer[j])) - continue; - - // cut out suffix numbers - while (j > i && isdigit(buffer[j])) - j--; - - if (j <= i) - continue; - - buffer[j+1] = 0; - - // if new name doesn't match previous group name, - // make a new group. - - if (strcmp(szgroup, &(buffer[i]))) - { - // name doesn't match with prev name, - // copy name into group, init count to 1 - isentencegs++; - if (isentencegs >= CSENTENCEG_MAX) - { - ALERT (at_error, "Too many sentence groups in sentences.txt!\n"); - break; - } - - strcpy(rgsentenceg[isentencegs].szgroupname, &(buffer[i])); - rgsentenceg[isentencegs].count = 1; - - strcpy(szgroup, &(buffer[i])); - - continue; - } - else - { - //name matches with previous, increment group count - if (isentencegs >= 0) - rgsentenceg[isentencegs].count++; - } - } - - g_engfuncs.pfnFreeFile( pMemFile ); - - fSentencesInit = TRUE; - - // init lru lists - - i = 0; - - while (rgsentenceg[i].count && i < CSENTENCEG_MAX) - { - USENTENCEG_InitLRU(&(rgsentenceg[i].rgblru[0]), rgsentenceg[i].count); - i++; - } - -} - -// convert sentence (sample) name to !sentencenum, return !sentencenum - -int SENTENCEG_Lookup(const char *sample, char *sentencenum) -{ - char sznum[8]; - - int i; - // this is a sentence name; lookup sentence number - // and give to engine as string. - for (i = 0; i < gcallsentences; i++) - if (!stricmp(gszallsentencenames[i], sample+1)) - { - if (sentencenum) - { - strcpy(sentencenum, "!"); - sprintf(sznum, "%d", i); - strcat(sentencenum, sznum); - } - return i; - } - // sentence name not found! - return -1; -} - -void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, - int flags, int pitch) -{ - if (sample && *sample == '!') - { - char name[32]; - if (SENTENCEG_Lookup(sample, name) >= 0) - EMIT_SOUND_DYN2(entity, channel, name, volume, attenuation, flags, pitch); - else - ALERT( at_aiconsole, "Unable to find %s in sentences.txt\n", sample ); - } - else - EMIT_SOUND_DYN2(entity, channel, sample, volume, attenuation, flags, pitch); -} - -// play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename - -void EMIT_SOUND_SUIT(edict_t *entity, const char *sample) -{ - float fvol; - int pitch = PITCH_NORM; - - fvol = CVAR_GET_FLOAT("suitvolume"); - if (RANDOM_LONG(0,1)) - pitch = RANDOM_LONG(0,6) + 98; - - if (fvol > 0.05) - EMIT_SOUND_DYN(entity, CHAN_STATIC, sample, fvol, ATTN_NORM, 0, pitch); -} - -// play a sentence, randomly selected from the passed in group id, over the HEV suit speaker - -void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg) -{ - float fvol; - int pitch = PITCH_NORM; - - fvol = CVAR_GET_FLOAT("suitvolume"); - if (RANDOM_LONG(0,1)) - pitch = RANDOM_LONG(0,6) + 98; - - if (fvol > 0.05) - SENTENCEG_PlayRndI(entity, isentenceg, fvol, ATTN_NORM, 0, pitch); -} - -// play a sentence, randomly selected from the passed in groupname - -void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname) -{ - float fvol; - int pitch = PITCH_NORM; - - fvol = CVAR_GET_FLOAT("suitvolume"); - if (RANDOM_LONG(0,1)) - pitch = RANDOM_LONG(0,6) + 98; - - if (fvol > 0.05) - SENTENCEG_PlayRndSz(entity, groupname, fvol, ATTN_NORM, 0, pitch); -} - -// ===================== MATERIAL TYPE DETECTION, MAIN ROUTINES ======================== -// -// Used to detect the texture the player is standing on, map the -// texture name to a material type. Play footstep sound based -// on material type. - -int fTextureTypeInit = FALSE; - -#define CTEXTURESMAX 512 // max number of textures loaded - -//jlbint gcTextures = 0; -//jlbchar grgszTextureName[CTEXTURESMAX][CBTEXTURENAMEMAX]; // texture names -//jlbchar grgchTextureType[CTEXTURESMAX]; // parallel array of texture types - -// open materials.txt, get size, alloc space, -// save in array. Only works first time called, -// ignored on subsequent calls. - -static char *memfgets( byte *pMemFile, int fileSize, int &filePos, char *pBuffer, int bufferSize ) -{ - // Bullet-proofing - if ( !pMemFile || !pBuffer ) - return NULL; - - if ( filePos >= fileSize ) - return NULL; - - int i = filePos; - int last = fileSize; - - // fgets always NULL terminates, so only read bufferSize-1 characters - if ( last - filePos > (bufferSize-1) ) - last = filePos + (bufferSize-1); - - int stop = 0; - - // Stop at the next newline (inclusive) or end of buffer - while ( i < last && !stop ) - { - if ( pMemFile[i] == '\n' ) - stop = 1; - i++; - } - - - // If we actually advanced the pointer, copy it over - if ( i != filePos ) - { - // We read in size bytes - int size = i - filePos; - // copy it out - memcpy( pBuffer, pMemFile + filePos, sizeof(byte)*size ); - - // If the buffer isn't full, terminate (this is always true) - if ( size < bufferSize ) - pBuffer[size] = 0; - - // Update file pointer - filePos = i; - return pBuffer; - } - - // No data read, bail - return NULL; -} - - -void TEXTURETYPE_Init() -{ -/*jlb - char buffer[512]; - int i, j; - byte *pMemFile; - int fileSize, filePos; - - if (fTextureTypeInit) - return; - - memset(&(grgszTextureName[0][0]), 0, CTEXTURESMAX * CBTEXTURENAMEMAX); - memset(grgchTextureType, 0, CTEXTURESMAX); - - gcTextures = 0; - memset(buffer, 0, 512); - - pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/materials.txt", &fileSize ); - if ( !pMemFile ) - return; - - // for each line in the file... - while (memfgets(pMemFile, fileSize, filePos, buffer, 511) != NULL && (gcTextures < CTEXTURESMAX)) - { - // 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 - grgchTextureType[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 = min (j, CBTEXTURENAMEMAX-1+i); - buffer[j] = 0; - strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); - } - - g_engfuncs.pfnFreeFile( pMemFile ); - - fTextureTypeInit = TRUE; -jlb*/ -} - -// given texture name, find texture type -// if not found, return type 'concrete' - -// NOTE: this routine should ONLY be called if the -// current texture under the player changes! - -char TEXTURETYPE_Find(char *name) -{ - // CONSIDER: pre-sort texture names and perform faster binary search here -/*jlb - for (int i = 0; i < gcTextures; i++) - { - if (!strnicmp(name, &(grgszTextureName[i][0]), CBTEXTURENAMEMAX-1)) - return (grgchTextureType[i]); - } -jlb*/ - return CHAR_TEX_CONCRETE; -} - -// play a strike sound based on the texture that was hit by the attack traceline. VecSrc/VecEnd are the -// original traceline endpoints used by the attacker, iBulletType is the type of bullet that hit the texture. -// returns volume of strike instrument (crowbar) to play - -float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType) -{ -// hit the world, try to play sound based on texture material type - - char chTextureType; - float fvol; - float fvolbar; - char szbuffer[64]; - const char *pTextureName; - float rgfl1[3]; - float rgfl2[3]; - char *rgsz[4]; - int cnt; - float fattn = ATTN_NORM; - - CMBaseEntity *pEntity = CMBaseEntity::Instance(ptr->pHit); - - chTextureType = 0; - - if (pEntity && pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE) - // hit body - chTextureType = CHAR_TEX_FLESH; - else - { - // hit world - - // find texture under strike, get material type - - // copy trace vector into array for trace_texture - - vecSrc.CopyToArray(rgfl1); - vecEnd.CopyToArray(rgfl2); - - // get texture from entity or world (world is ent(0)) - if (pEntity) - pTextureName = TRACE_TEXTURE( ENT(pEntity->pev), rgfl1, rgfl2 ); - else - pTextureName = TRACE_TEXTURE( ENT(0), rgfl1, rgfl2 ); - - if ( pTextureName ) - { - // strip leading '-0' or '+0~' or '{' or '!' - if (*pTextureName == '-' || *pTextureName == '+') - pTextureName += 2; - - if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') - pTextureName++; - // '}}' - strcpy(szbuffer, pTextureName); - szbuffer[CBTEXTURENAMEMAX - 1] = 0; - - // ALERT ( at_console, "texture hit: %s\n", szbuffer); - - // get texture type - chTextureType = TEXTURETYPE_Find(szbuffer); - } - } - - switch (chTextureType) - { - default: - case CHAR_TEX_CONCRETE: fvol = 0.9; fvolbar = 0.6; - rgsz[0] = "player/pl_step1.wav"; - rgsz[1] = "player/pl_step2.wav"; - cnt = 2; - break; - case CHAR_TEX_METAL: fvol = 0.9; fvolbar = 0.3; - rgsz[0] = "player/pl_metal1.wav"; - rgsz[1] = "player/pl_metal2.wav"; - cnt = 2; - break; - case CHAR_TEX_DIRT: fvol = 0.9; fvolbar = 0.1; - rgsz[0] = "player/pl_dirt1.wav"; - rgsz[1] = "player/pl_dirt2.wav"; - rgsz[2] = "player/pl_dirt3.wav"; - cnt = 3; - break; - case CHAR_TEX_VENT: fvol = 0.5; fvolbar = 0.3; - rgsz[0] = "player/pl_duct1.wav"; - rgsz[1] = "player/pl_duct1.wav"; - cnt = 2; - break; - case CHAR_TEX_GRATE: fvol = 0.9; fvolbar = 0.5; - rgsz[0] = "player/pl_grate1.wav"; - rgsz[1] = "player/pl_grate4.wav"; - cnt = 2; - break; - case CHAR_TEX_TILE: fvol = 0.8; fvolbar = 0.2; - rgsz[0] = "player/pl_tile1.wav"; - rgsz[1] = "player/pl_tile3.wav"; - rgsz[2] = "player/pl_tile2.wav"; - rgsz[3] = "player/pl_tile4.wav"; - cnt = 4; - break; - case CHAR_TEX_SLOSH: fvol = 0.9; fvolbar = 0.0; - rgsz[0] = "player/pl_slosh1.wav"; - rgsz[1] = "player/pl_slosh3.wav"; - rgsz[2] = "player/pl_slosh2.wav"; - rgsz[3] = "player/pl_slosh4.wav"; - cnt = 4; - break; - case CHAR_TEX_WOOD: fvol = 0.9; fvolbar = 0.2; - rgsz[0] = "debris/wood1.wav"; - rgsz[1] = "debris/wood2.wav"; - rgsz[2] = "debris/wood3.wav"; - cnt = 3; - break; - case CHAR_TEX_GLASS: - case CHAR_TEX_COMPUTER: - fvol = 0.8; fvolbar = 0.2; - rgsz[0] = "debris/glass1.wav"; - rgsz[1] = "debris/glass2.wav"; - rgsz[2] = "debris/glass3.wav"; - cnt = 3; - break; - case CHAR_TEX_FLESH: - if (iBulletType == BULLET_PLAYER_CROWBAR) - return 0.0; // crowbar already makes this sound - fvol = 1.0; fvolbar = 0.2; - rgsz[0] = "weapons/bullet_hit1.wav"; - rgsz[1] = "weapons/bullet_hit2.wav"; - fattn = 1.0; - cnt = 2; - break; - } - - // did we hit a breakable? - - if (pEntity && FClassnameIs(pEntity->pev, "func_breakable")) - { - // drop volumes, the object will already play a damaged sound - fvol /= 1.5; - fvolbar /= 2.0; - } - else if (chTextureType == CHAR_TEX_COMPUTER) - { - // play random spark if computer - - if ( ptr->flFraction != 1.0 && RANDOM_LONG(0,1)) - { - UTIL_Sparks( ptr->vecEndPos ); - - float flVolume = RANDOM_FLOAT ( 0.7 , 1.0 );//random volume range - switch ( RANDOM_LONG(0,1) ) - { - case 0: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark5.wav", flVolume, ATTN_NORM, 0, 100); break; - case 1: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark6.wav", flVolume, ATTN_NORM, 0, 100); break; - // 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; - } - } - } - - // play material hit sound - UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, rgsz[RANDOM_LONG(0,cnt-1)], fvol, fattn, 0, 96 + RANDOM_LONG(0,0xf)); - //EMIT_SOUND_DYN( ENT(m_pPlayer->pev), CHAN_WEAPON, rgsz[RANDOM_LONG(0,cnt-1)], fvol, ATTN_NORM, 0, 96 + RANDOM_LONG(0,0xf)); - - return fvolbar; -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +//========================================================= +// sound.cpp +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "weapons.h" +#include "cmtalkmonster.h" +#include "pm_materials.h" + +//#if !defined ( _WIN32 ) +#include +//#endif + +static char *memfgets( byte *pMemFile, int fileSize, int &filePos, char *pBuffer, int bufferSize ); + + +// ==================== GENERIC AMBIENT SOUND ====================================== + +// runtime pitch shift and volume fadein/out structure + +// NOTE: IF YOU CHANGE THIS STRUCT YOU MUST CHANGE THE SAVE/RESTORE VERSION NUMBER +// SEE BELOW (in the typedescription for the class) +typedef struct dynpitchvol +{ + // NOTE: do not change the order of these parameters + // NOTE: unless you also change order of rgdpvpreset array elements! + int preset; + + int pitchrun; // pitch shift % when sound is running 0 - 255 + int pitchstart; // pitch shift % when sound stops or starts 0 - 255 + int spinup; // spinup time 0 - 100 + int spindown; // spindown time 0 - 100 + + int volrun; // volume change % when sound is running 0 - 10 + int volstart; // volume change % when sound stops or starts 0 - 10 + int fadein; // volume fade in time 0 - 100 + int fadeout; // volume fade out time 0 - 100 + + // Low Frequency Oscillator + int lfotype; // 0) off 1) square 2) triangle 3) random + int lforate; // 0 - 1000, how fast lfo osciallates + + int lfomodpitch; // 0-100 mod of current pitch. 0 is off. + int lfomodvol; // 0-100 mod of current volume. 0 is off. + + int cspinup; // each trigger hit increments counter and spinup pitch + + + int cspincount; + + int pitch; + int spinupsav; + int spindownsav; + int pitchfrac; + + int vol; + int fadeinsav; + int fadeoutsav; + int volfrac; + + int lfofrac; + int lfomult; + + +} dynpitchvol_t; + +#define CDPVPRESETMAX 27 + +// presets for runtime pitch and vol modulation of ambient sounds + +/*jlb +dynpitchvol_t rgdpvpreset[CDPVPRESETMAX] = +{ +// pitch pstart spinup spindwn volrun volstrt fadein fadeout lfotype lforate modptch modvol cspnup +{1, 255, 75, 95, 95, 10, 1, 50, 95, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{2, 255, 85, 70, 88, 10, 1, 20, 88, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{3, 255, 100, 50, 75, 10, 1, 10, 75, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{4, 100, 100, 0, 0, 10, 1, 90, 90, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{5, 100, 100, 0, 0, 10, 1, 80, 80, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{6, 100, 100, 0, 0, 10, 1, 50, 70, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{7, 100, 100, 0, 0, 5, 1, 40, 50, 1, 50, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, +{8, 100, 100, 0, 0, 5, 1, 40, 50, 1, 150, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, +{9, 100, 100, 0, 0, 5, 1, 40, 50, 1, 750, 0, 10, 0, 0,0,0,0,0,0,0,0,0,0}, +{10,128, 100, 50, 75, 10, 1, 30, 40, 2, 8, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{11,128, 100, 50, 75, 10, 1, 30, 40, 2, 25, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{12,128, 100, 50, 75, 10, 1, 30, 40, 2, 70, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{13,50, 50, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{14,70, 70, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{15,90, 90, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{16,120, 120, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{17,180, 180, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{18,255, 255, 0, 0, 10, 1, 20, 50, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{19,200, 75, 90, 90, 10, 1, 50, 90, 2, 100, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{20,255, 75, 97, 90, 10, 1, 50, 90, 1, 40, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{21,100, 100, 0, 0, 10, 1, 30, 50, 3, 15, 20, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{22,160, 160, 0, 0, 10, 1, 50, 50, 3, 500, 25, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{23,255, 75, 88, 0, 10, 1, 40, 0, 0, 0, 0, 0, 5, 0,0,0,0,0,0,0,0,0,0}, +{24,200, 20, 95, 70, 10, 1, 70, 70, 3, 20, 50, 0, 0, 0,0,0,0,0,0,0,0,0,0}, +{25,180, 100, 50, 60, 10, 1, 40, 60, 2, 90, 100, 100, 0, 0,0,0,0,0,0,0,0,0,0}, +{26,60, 60, 0, 0, 10, 1, 40, 70, 3, 80, 20, 50, 0, 0,0,0,0,0,0,0,0,0,0}, +{27,128, 90, 10, 10, 10, 1, 20, 40, 1, 5, 10, 20, 0, 0,0,0,0,0,0,0,0,0,0} +}; +jlb*/ + + +// ==================== SENTENCE GROUPS, UTILITY FUNCTIONS ====================================== + +#define CSENTENCE_LRU_MAX 32 // max number of elements per sentence group + +// group of related sentences + +typedef struct sentenceg +{ + char szgroupname[CBSENTENCENAME_MAX]; + int count; + unsigned char rgblru[CSENTENCE_LRU_MAX]; + +} SENTENCEG; + +#define CSENTENCEG_MAX 200 // max number of sentence groups +// globals + +SENTENCEG rgsentenceg[CSENTENCEG_MAX]; +int fSentencesInit = FALSE; + +char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; +int gcallsentences = 0; + +// randomize list of sentence name indices + +void USENTENCEG_InitLRU(unsigned char *plru, int count) +{ + int i, j, k; + unsigned char temp; + + if (!fSentencesInit) + return; + + if (count > CSENTENCE_LRU_MAX) + count = CSENTENCE_LRU_MAX; + + for (i = 0; i < count; i++) + plru[i] = (unsigned char) i; + + // randomize array + for (i = 0; i < (count * 4); i++) + { + j = RANDOM_LONG(0,count-1); + k = RANDOM_LONG(0,count-1); + temp = plru[j]; + plru[j] = plru[k]; + plru[k] = temp; + } +} + +// ignore lru. pick next sentence from sentence group. Go in order until we hit the last sentence, +// then repeat list if freset is true. If freset is false, then repeat last sentence. +// ipick is passed in as the requested sentence ordinal. +// ipick 'next' is returned. +// return of -1 indicates an error. + +int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset) +{ + char *szgroupname; + unsigned char count; + char sznum[8]; + + if (!fSentencesInit) + return -1; + + if (isentenceg < 0) + return -1; + + szgroupname = rgsentenceg[isentenceg].szgroupname; + count = rgsentenceg[isentenceg].count; + + if (count == 0) + return -1; + + if (ipick >= count) + ipick = count-1; + + strcpy(szfound, "!"); + strcat(szfound, szgroupname); + sprintf(sznum, "%d", ipick); + strcat(szfound, sznum); + + if (ipick >= count) + { + if (freset) + // reset at end of list + return 0; + else + return count; + } + + return ipick + 1; +} + + + +// pick a random sentence from rootname0 to rootnameX. +// picks from the rgsentenceg[isentenceg] least +// recently used, modifies lru array. returns the sentencename. +// note, lru must be seeded with 0-n randomized sentence numbers, with the +// rest of the lru filled with -1. The first integer in the lru is +// actually the size of the list. Returns ipick, the ordinal +// of the picked sentence within the group. + +int USENTENCEG_Pick(int isentenceg, char *szfound) +{ + char *szgroupname; + unsigned char *plru; + unsigned char i; + unsigned char count; + char sznum[8]; + unsigned char ipick; + int ffound = FALSE; + + if (!fSentencesInit) + return -1; + + if (isentenceg < 0) + return -1; + + szgroupname = rgsentenceg[isentenceg].szgroupname; + count = rgsentenceg[isentenceg].count; + plru = rgsentenceg[isentenceg].rgblru; + + while (!ffound) + { + for (i = 0; i < count; i++) + if (plru[i] != 0xFF) + { + ipick = plru[i]; + plru[i] = 0xFF; + ffound = TRUE; + break; + } + + if (!ffound) + USENTENCEG_InitLRU(plru, count); + else + { + strcpy(szfound, "!"); + strcat(szfound, szgroupname); + sprintf(sznum, "%d", ipick); + strcat(szfound, sznum); + return ipick; + } + } + return -1; +} + +// ===================== SENTENCE GROUPS, MAIN ROUTINES ======================== + +// Given sentence group rootname (name without number suffix), +// get sentence group index (isentenceg). Returns -1 if no such name. + +int SENTENCEG_GetIndex(const char *szgroupname) +{ + int i; + + if (!fSentencesInit || !szgroupname) + return -1; + + // search rgsentenceg for match on szgroupname + + i = 0; + while (rgsentenceg[i].count) + { + if (!strcmp(szgroupname, rgsentenceg[i].szgroupname)) + return i; + i++; + } + + return -1; +} + +// given sentence group index, play random sentence for given entity. +// returns ipick - which sentence was picked to +// play from the group. Ipick is only needed if you plan on stopping +// the sound before playback is done (see SENTENCEG_Stop). + +int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, + float volume, float attenuation, int flags, int pitch) +{ + char name[64]; + int ipick; + + if (!fSentencesInit) + return -1; + + name[0] = 0; + + ipick = USENTENCEG_Pick(isentenceg, name); + if (ipick > 0 && name) + EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); + return ipick; +} + +// same as above, but takes sentence group name instead of index + +int SENTENCEG_PlayRndSz(edict_t *entity, const char *szgroupname, + float volume, float attenuation, int flags, int pitch) +{ + char name[64]; + int ipick; + int isentenceg; + + if (!fSentencesInit) + return -1; + + name[0] = 0; + + isentenceg = SENTENCEG_GetIndex(szgroupname); + if (isentenceg < 0) + { + ALERT( at_console, "No such sentence group %s\n", szgroupname ); + return -1; + } + + ipick = USENTENCEG_Pick(isentenceg, name); + if (ipick >= 0 && name[0]) + EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); + + return ipick; +} + +// play sentences in sequential order from sentence group. Reset after last sentence. + +int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szgroupname, + float volume, float attenuation, int flags, int pitch, int ipick, int freset) +{ + char name[64]; + int ipicknext; + int isentenceg; + + if (!fSentencesInit) + return -1; + + name[0] = 0; + + isentenceg = SENTENCEG_GetIndex(szgroupname); + if (isentenceg < 0) + return -1; + + ipicknext = USENTENCEG_PickSequential(isentenceg, name, ipick, freset); + if (ipicknext >= 0 && name[0]) + EMIT_SOUND_DYN(entity, CHAN_VOICE, name, volume, attenuation, flags, pitch); + return ipicknext; +} + + +// for this entity, for the given sentence within the sentence group, stop +// the sentence. + +void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick) +{ + char buffer[64]; + char sznum[8]; + + if (!fSentencesInit) + return; + + if (isentenceg < 0 || ipick < 0) + return; + + strcpy(buffer, "!"); + strcat(buffer, rgsentenceg[isentenceg].szgroupname); + sprintf(sznum, "%d", ipick); + strcat(buffer, sznum); + + STOP_SOUND(entity, CHAN_VOICE, buffer); +} + +// open sentences.txt, scan for groups, build rgsentenceg +// Should be called from world spawn, only works on the +// first call and is ignored subsequently. + +void SENTENCEG_Init() +{ + char buffer[512]; + char szgroup[64]; + int i, j; + int isentencegs; + + if (fSentencesInit) + return; + + memset(gszallsentencenames, 0, CVOXFILESENTENCEMAX * CBSENTENCENAME_MAX); + gcallsentences = 0; + + memset(rgsentenceg, 0, CSENTENCEG_MAX * sizeof(SENTENCEG)); + memset(buffer, 0, 512); + memset(szgroup, 0, 64); + isentencegs = -1; + + + int filePos = 0, fileSize; + byte *pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/sentences.txt", &fileSize ); + if ( !pMemFile ) + return; + + // for each line in the file... + while ( memfgets(pMemFile, fileSize, filePos, buffer, 511) != NULL ) + { + // skip whitespace + i = 0; + while(buffer[i] && buffer[i] == ' ') + i++; + + if (!buffer[i]) + continue; + + if (buffer[i] == '/' || !isalpha(buffer[i])) + continue; + + // get sentence name + j = i; + while (buffer[j] && buffer[j] != ' ') + j++; + + if (!buffer[j]) + continue; + + if (gcallsentences > CVOXFILESENTENCEMAX) + { + ALERT (at_error, "Too many sentences in sentences.txt!\n"); + break; + } + + // null-terminate name and save in sentences array + buffer[j] = 0; + const char *pString = buffer + i; + + if ( strlen( pString ) >= CBSENTENCENAME_MAX ) + ALERT( at_warning, "Sentence %s longer than %d letters\n", pString, CBSENTENCENAME_MAX-1 ); + + strcpy( gszallsentencenames[gcallsentences++], pString ); + + j--; + if (j <= i) + continue; + if (!isdigit(buffer[j])) + continue; + + // cut out suffix numbers + while (j > i && isdigit(buffer[j])) + j--; + + if (j <= i) + continue; + + buffer[j+1] = 0; + + // if new name doesn't match previous group name, + // make a new group. + + if (strcmp(szgroup, &(buffer[i]))) + { + // name doesn't match with prev name, + // copy name into group, init count to 1 + isentencegs++; + if (isentencegs >= CSENTENCEG_MAX) + { + ALERT (at_error, "Too many sentence groups in sentences.txt!\n"); + break; + } + + strcpy(rgsentenceg[isentencegs].szgroupname, &(buffer[i])); + rgsentenceg[isentencegs].count = 1; + + strcpy(szgroup, &(buffer[i])); + + continue; + } + else + { + //name matches with previous, increment group count + if (isentencegs >= 0) + rgsentenceg[isentencegs].count++; + } + } + + g_engfuncs.pfnFreeFile( pMemFile ); + + fSentencesInit = TRUE; + + // init lru lists + + i = 0; + + while (rgsentenceg[i].count && i < CSENTENCEG_MAX) + { + USENTENCEG_InitLRU(&(rgsentenceg[i].rgblru[0]), rgsentenceg[i].count); + i++; + } + +} + +// convert sentence (sample) name to !sentencenum, return !sentencenum + +int SENTENCEG_Lookup(const char *sample, char *sentencenum) +{ + char sznum[8]; + + int i; + // this is a sentence name; lookup sentence number + // and give to engine as string. + for (i = 0; i < gcallsentences; i++) + if (!stricmp(gszallsentencenames[i], sample+1)) + { + if (sentencenum) + { + strcpy(sentencenum, "!"); + sprintf(sznum, "%d", i); + strcat(sentencenum, sznum); + } + return i; + } + // sentence name not found! + return -1; +} + +void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, + int flags, int pitch) +{ + if (sample && *sample == '!') + { + char name[32]; + if (SENTENCEG_Lookup(sample, name) >= 0) + EMIT_SOUND_DYN2(entity, channel, name, volume, attenuation, flags, pitch); + else + ALERT( at_aiconsole, "Unable to find %s in sentences.txt\n", sample ); + } + else + EMIT_SOUND_DYN2(entity, channel, sample, volume, attenuation, flags, pitch); +} + +// play a specific sentence over the HEV suit speaker - just pass player entity, and !sentencename + +void EMIT_SOUND_SUIT(edict_t *entity, const char *sample) +{ + float fvol; + int pitch = PITCH_NORM; + + fvol = CVAR_GET_FLOAT("suitvolume"); + if (RANDOM_LONG(0,1)) + pitch = RANDOM_LONG(0,6) + 98; + + if (fvol > 0.05) + EMIT_SOUND_DYN(entity, CHAN_STATIC, sample, fvol, ATTN_NORM, 0, pitch); +} + +// play a sentence, randomly selected from the passed in group id, over the HEV suit speaker + +void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg) +{ + float fvol; + int pitch = PITCH_NORM; + + fvol = CVAR_GET_FLOAT("suitvolume"); + if (RANDOM_LONG(0,1)) + pitch = RANDOM_LONG(0,6) + 98; + + if (fvol > 0.05) + SENTENCEG_PlayRndI(entity, isentenceg, fvol, ATTN_NORM, 0, pitch); +} + +// play a sentence, randomly selected from the passed in groupname + +void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname) +{ + float fvol; + int pitch = PITCH_NORM; + + fvol = CVAR_GET_FLOAT("suitvolume"); + if (RANDOM_LONG(0,1)) + pitch = RANDOM_LONG(0,6) + 98; + + if (fvol > 0.05) + SENTENCEG_PlayRndSz(entity, groupname, fvol, ATTN_NORM, 0, pitch); +} + +// ===================== MATERIAL TYPE DETECTION, MAIN ROUTINES ======================== +// +// Used to detect the texture the player is standing on, map the +// texture name to a material type. Play footstep sound based +// on material type. + +int fTextureTypeInit = FALSE; + +#define CTEXTURESMAX 512 // max number of textures loaded + +//jlbint gcTextures = 0; +//jlbchar grgszTextureName[CTEXTURESMAX][CBTEXTURENAMEMAX]; // texture names +//jlbchar grgchTextureType[CTEXTURESMAX]; // parallel array of texture types + +// open materials.txt, get size, alloc space, +// save in array. Only works first time called, +// ignored on subsequent calls. + +static char *memfgets( byte *pMemFile, int fileSize, int &filePos, char *pBuffer, int bufferSize ) +{ + // Bullet-proofing + if ( !pMemFile || !pBuffer ) + return NULL; + + if ( filePos >= fileSize ) + return NULL; + + int i = filePos; + int last = fileSize; + + // fgets always NULL terminates, so only read bufferSize-1 characters + if ( last - filePos > (bufferSize-1) ) + last = filePos + (bufferSize-1); + + int stop = 0; + + // Stop at the next newline (inclusive) or end of buffer + while ( i < last && !stop ) + { + if ( pMemFile[i] == '\n' ) + stop = 1; + i++; + } + + + // If we actually advanced the pointer, copy it over + if ( i != filePos ) + { + // We read in size bytes + int size = i - filePos; + // copy it out + memcpy( pBuffer, pMemFile + filePos, sizeof(byte)*size ); + + // If the buffer isn't full, terminate (this is always true) + if ( size < bufferSize ) + pBuffer[size] = 0; + + // Update file pointer + filePos = i; + return pBuffer; + } + + // No data read, bail + return NULL; +} + + +void TEXTURETYPE_Init() +{ +/*jlb + char buffer[512]; + int i, j; + byte *pMemFile; + int fileSize, filePos; + + if (fTextureTypeInit) + return; + + memset(&(grgszTextureName[0][0]), 0, CTEXTURESMAX * CBTEXTURENAMEMAX); + memset(grgchTextureType, 0, CTEXTURESMAX); + + gcTextures = 0; + memset(buffer, 0, 512); + + pMemFile = g_engfuncs.pfnLoadFileForMe( "sound/materials.txt", &fileSize ); + if ( !pMemFile ) + return; + + // for each line in the file... + while (memfgets(pMemFile, fileSize, filePos, buffer, 511) != NULL && (gcTextures < CTEXTURESMAX)) + { + // 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 + grgchTextureType[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 = min (j, CBTEXTURENAMEMAX-1+i); + buffer[j] = 0; + strcpy(&(grgszTextureName[gcTextures++][0]), &(buffer[i])); + } + + g_engfuncs.pfnFreeFile( pMemFile ); + + fTextureTypeInit = TRUE; +jlb*/ +} + +// given texture name, find texture type +// if not found, return type 'concrete' + +// NOTE: this routine should ONLY be called if the +// current texture under the player changes! + +char TEXTURETYPE_Find(char *name) +{ + // CONSIDER: pre-sort texture names and perform faster binary search here +/*jlb + for (int i = 0; i < gcTextures; i++) + { + if (!strnicmp(name, &(grgszTextureName[i][0]), CBTEXTURENAMEMAX-1)) + return (grgchTextureType[i]); + } +jlb*/ + return CHAR_TEX_CONCRETE; +} + +// play a strike sound based on the texture that was hit by the attack traceline. VecSrc/VecEnd are the +// original traceline endpoints used by the attacker, iBulletType is the type of bullet that hit the texture. +// returns volume of strike instrument (crowbar) to play + +float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType) +{ +// hit the world, try to play sound based on texture material type + + char chTextureType; + float fvol; + float fvolbar; + char szbuffer[64]; + const char *pTextureName; + float rgfl1[3]; + float rgfl2[3]; + char *rgsz[4]; + int cnt; + float fattn = ATTN_NORM; + + CMBaseEntity *pEntity = CMBaseEntity::Instance(ptr->pHit); + + chTextureType = 0; + + if (pEntity && pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE) + // hit body + chTextureType = CHAR_TEX_FLESH; + else + { + // hit world + + // find texture under strike, get material type + + // copy trace vector into array for trace_texture + + vecSrc.CopyToArray(rgfl1); + vecEnd.CopyToArray(rgfl2); + + // get texture from entity or world (world is ent(0)) + if (pEntity) + pTextureName = TRACE_TEXTURE( ENT(pEntity->pev), rgfl1, rgfl2 ); + else + pTextureName = TRACE_TEXTURE( ENT(0), rgfl1, rgfl2 ); + + if ( pTextureName ) + { + // strip leading '-0' or '+0~' or '{' or '!' + if (*pTextureName == '-' || *pTextureName == '+') + pTextureName += 2; + + if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ') + pTextureName++; + // '}}' + strcpy(szbuffer, pTextureName); + szbuffer[CBTEXTURENAMEMAX - 1] = 0; + + // ALERT ( at_console, "texture hit: %s\n", szbuffer); + + // get texture type + chTextureType = TEXTURETYPE_Find(szbuffer); + } + } + + switch (chTextureType) + { + default: + case CHAR_TEX_CONCRETE: fvol = 0.9; fvolbar = 0.6; + rgsz[0] = "player/pl_step1.wav"; + rgsz[1] = "player/pl_step2.wav"; + cnt = 2; + break; + case CHAR_TEX_METAL: fvol = 0.9; fvolbar = 0.3; + rgsz[0] = "player/pl_metal1.wav"; + rgsz[1] = "player/pl_metal2.wav"; + cnt = 2; + break; + case CHAR_TEX_DIRT: fvol = 0.9; fvolbar = 0.1; + rgsz[0] = "player/pl_dirt1.wav"; + rgsz[1] = "player/pl_dirt2.wav"; + rgsz[2] = "player/pl_dirt3.wav"; + cnt = 3; + break; + case CHAR_TEX_VENT: fvol = 0.5; fvolbar = 0.3; + rgsz[0] = "player/pl_duct1.wav"; + rgsz[1] = "player/pl_duct1.wav"; + cnt = 2; + break; + case CHAR_TEX_GRATE: fvol = 0.9; fvolbar = 0.5; + rgsz[0] = "player/pl_grate1.wav"; + rgsz[1] = "player/pl_grate4.wav"; + cnt = 2; + break; + case CHAR_TEX_TILE: fvol = 0.8; fvolbar = 0.2; + rgsz[0] = "player/pl_tile1.wav"; + rgsz[1] = "player/pl_tile3.wav"; + rgsz[2] = "player/pl_tile2.wav"; + rgsz[3] = "player/pl_tile4.wav"; + cnt = 4; + break; + case CHAR_TEX_SLOSH: fvol = 0.9; fvolbar = 0.0; + rgsz[0] = "player/pl_slosh1.wav"; + rgsz[1] = "player/pl_slosh3.wav"; + rgsz[2] = "player/pl_slosh2.wav"; + rgsz[3] = "player/pl_slosh4.wav"; + cnt = 4; + break; + case CHAR_TEX_WOOD: fvol = 0.9; fvolbar = 0.2; + rgsz[0] = "debris/wood1.wav"; + rgsz[1] = "debris/wood2.wav"; + rgsz[2] = "debris/wood3.wav"; + cnt = 3; + break; + case CHAR_TEX_GLASS: + case CHAR_TEX_COMPUTER: + fvol = 0.8; fvolbar = 0.2; + rgsz[0] = "debris/glass1.wav"; + rgsz[1] = "debris/glass2.wav"; + rgsz[2] = "debris/glass3.wav"; + cnt = 3; + break; + case CHAR_TEX_FLESH: + if (iBulletType == BULLET_PLAYER_CROWBAR) + return 0.0; // crowbar already makes this sound + fvol = 1.0; fvolbar = 0.2; + rgsz[0] = "weapons/bullet_hit1.wav"; + rgsz[1] = "weapons/bullet_hit2.wav"; + fattn = 1.0; + cnt = 2; + break; + } + + // did we hit a breakable? + + if (pEntity && FClassnameIs(pEntity->pev, "func_breakable")) + { + // drop volumes, the object will already play a damaged sound + fvol /= 1.5; + fvolbar /= 2.0; + } + else if (chTextureType == CHAR_TEX_COMPUTER) + { + // play random spark if computer + + if ( ptr->flFraction != 1.0 && RANDOM_LONG(0,1)) + { + UTIL_Sparks( ptr->vecEndPos ); + + float flVolume = RANDOM_FLOAT ( 0.7 , 1.0 );//random volume range + switch ( RANDOM_LONG(0,1) ) + { + case 0: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark5.wav", flVolume, ATTN_NORM, 0, 100); break; + case 1: UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, "buttons/spark6.wav", flVolume, ATTN_NORM, 0, 100); break; + // 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; + } + } + } + + // play material hit sound + UTIL_EmitAmbientSound(ENT(0), ptr->vecEndPos, rgsz[RANDOM_LONG(0,cnt-1)], fvol, fattn, 0, 96 + RANDOM_LONG(0,0xf)); + //EMIT_SOUND_DYN( ENT(m_pPlayer->pev), CHAN_WEAPON, rgsz[RANDOM_LONG(0,cnt-1)], fvol, ATTN_NORM, 0, 96 + RANDOM_LONG(0,0xf)); + + return fvolbar; +} + diff --git a/src/dlls/sporegrenade.cpp b/src/dlls/sporegrenade.cpp index 7b95baa..e270b26 100644 --- a/src/dlls/sporegrenade.cpp +++ b/src/dlls/sporegrenade.cpp @@ -1,345 +1,345 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "decals.h" -#include "explode.h" - -int gSporeExplode, gSporeExplodeC; - -void CMSporeGrenade::Precache() -{ - PRECACHE_MODEL("models/spore.mdl"); - PRECACHE_MODEL("sprites/glow02.spr"); - g_sModelIndexTinySpit = PRECACHE_MODEL("sprites/tinyspit.spr"); - gSporeExplode = PRECACHE_MODEL("sprites/spore_exp_01.spr"); - gSporeExplodeC = PRECACHE_MODEL("sprites/spore_exp_c_01.spr"); - PRECACHE_SOUND("weapons/splauncher_bounce.wav"); - PRECACHE_SOUND("weapons/splauncher_impact.wav"); -} - -void CMSporeGrenade::Explode(TraceResult *pTrace) -{ - pev->solid = SOLID_NOT;// intangible - pev->takedamage = DAMAGE_NO; - - // Pull out of the wall a bit - if (pTrace->flFraction != 1.0) - { - pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6); - } - - Vector vecSpraySpot = pTrace->vecEndPos; - float flSpraySpeed = RANDOM_LONG(10, 15); - - // If the trace is pointing up, then place - // spawn position a few units higher. - if (pTrace->vecPlaneNormal.z > 0) - { - vecSpraySpot = vecSpraySpot + (pTrace->vecPlaneNormal * 8); - flSpraySpeed *= 2; // Double the speed to make them fly higher - // in the air. - } - - // Spawn small particles at the explosion origin. - SpawnExplosionParticles( - vecSpraySpot, // position - pTrace->vecPlaneNormal, // direction - g_sModelIndexTinySpit, // modelindex - RANDOM_LONG(40, 50), // count - flSpraySpeed, // speed - RANDOM_FLOAT(600, 640)); // noise - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( pev->origin.x ); - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( RANDOM_LONG( 0, 1 ) ? gSporeExplode : gSporeExplodeC ); - WRITE_BYTE( 25 ); // scale * 10 - WRITE_BYTE( 155 ); // framerate - MESSAGE_END(); - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE(TE_DLIGHT); - WRITE_COORD( pev->origin.x ); // X - WRITE_COORD( pev->origin.y ); // Y - WRITE_COORD( pev->origin.z ); // Z - WRITE_BYTE( 12 ); // radius * 0.1 - WRITE_BYTE( 0 ); // r - WRITE_BYTE( 180 ); // g - WRITE_BYTE( 0 ); // b - WRITE_BYTE( 20 ); // time * 10 - WRITE_BYTE( 20 ); // decay * 0.1 - MESSAGE_END( ); - - // Play explode sound. - EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_impact.wav", 1, ATTN_NORM); - - entvars_t *pevOwner; - if (pev->owner) - pevOwner = VARS(pev->owner); - else - pevOwner = NULL; - - pev->owner = NULL; // can't traceline attack owner if this is set - - RadiusDamage(pev, pevOwner, pev->dmg, CLASS_NONE, DMG_BLAST); - - // Place a decal on the surface that was hit. - UTIL_DecalTrace(pTrace, DECAL_SPIT1 + RANDOM_LONG(0, 1)); - - UpdateOnRemove(); - UTIL_Remove( this->edict() ); -} - -void CMSporeGrenade::Detonate() -{ - TraceResult tr; - Vector vecSpot = pev->origin + Vector(0, 0, 8); - UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr); - - Explode(&tr); -} - - -void CMSporeGrenade::BounceSound() -{ - EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM); -} - -void CMSporeGrenade::TumbleThink() -{ - if (!IsInWorld()) - { - UpdateOnRemove(); - UTIL_Remove( this->edict() ); - return; - } - - pev->nextthink = gpGlobals->time + 0.1; - - if (pev->dmgtime <= gpGlobals->time) - { - SetThink(&CMSporeGrenade::Detonate); - } - - // Spawn particles. - SpawnTrailParticles( - pev->origin, // position - -pev->velocity.Normalize(), // dir - g_sModelIndexTinySpit, // modelindex - RANDOM_LONG( 2, 4 ), // count - RANDOM_FLOAT(10, 15), // speed - RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 ) -} - -// -// Contact grenade, explode when it touches something -// -void CMSporeGrenade::ExplodeTouch(edict_t *pOther) -{ - TraceResult tr; - Vector vecSpot;// trace starts here! - - pev->enemy = pOther; - - vecSpot = pev->origin - pev->velocity.Normalize() * 32; - UTIL_TraceLine(vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr); - - Explode(&tr); -} - -void CMSporeGrenade::DangerSoundThink() -{ - if (!IsInWorld()) - { - UpdateOnRemove(); - UTIL_Remove( this->edict() ); - return; - } - - pev->nextthink = gpGlobals->time + 0.2; - - // Spawn particles. - SpawnTrailParticles( - pev->origin, // position - -pev->velocity.Normalize(), // dir - g_sModelIndexTinySpit, // modelindex - RANDOM_LONG( 5, 10), // count - RANDOM_FLOAT(10, 15), // speed - RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 ) -} - -void CMSporeGrenade::BounceTouch(edict_t *pOther) -{ - if ( !pOther->v.takedamage ) - { - if (!(pev->flags & FL_ONGROUND)) { - if (pev->dmg_save < gpGlobals->time) { - BounceSound(); - pev->dmg_save = gpGlobals->time + 0.1; - } - } else { - pev->velocity = pev->velocity * 0.9; - } - if (pev->flags & FL_SWIM) - { - pev->velocity = pev->velocity * 0.5; - } - } - else - { - TraceResult tr = UTIL_GetGlobalTrace(); - Explode(&tr); - } -} - -void CMSporeGrenade::Spawn() -{ - Precache(); - pev->classname = MAKE_STRING("spore"); - pev->movetype = MOVETYPE_BOUNCE; - - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "models/spore.mdl"); - UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - - //pev->gravity = 0.5; - - pev->dmg = gSkillData.monDmgSpore; - - m_pSporeGlow = CMSprite::SpriteCreate("sprites/glow02.spr", pev->origin, FALSE); - - if (m_pSporeGlow) - { - m_pSporeGlow->SetTransparency(kRenderGlow, 150, 158, 19, 155, kRenderFxNoDissipation); - m_pSporeGlow->SetAttachment(edict(), 0); - m_pSporeGlow->SetScale(.75f); - } -} - -CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai) -{ - CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL); - - if (pGrenade == NULL) - return NULL; - - UTIL_SetOrigin(pGrenade->pev, vecStart); - pGrenade->Spawn(); - pGrenade->pev->velocity = vecVelocity; - pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); - pGrenade->pev->owner = ENT(pevOwner); - - pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched - - float lifetime = 2.0; - if (ai) { - lifetime = 4.0; - pGrenade->pev->gravity = 0.5; - pGrenade->pev->friction = 0.9; - } - pGrenade->pev->dmgtime = gpGlobals->time + lifetime; - pGrenade->SetThink(&CMSporeGrenade::TumbleThink); - pGrenade->pev->nextthink = gpGlobals->time + 0.1; - if (lifetime < 0.1) - { - pGrenade->pev->nextthink = gpGlobals->time; - pGrenade->pev->velocity = Vector(0, 0, 0); - } - - return pGrenade; -} - -CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity) -{ - CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL); - - if (pGrenade == NULL) - return NULL; - - UTIL_SetOrigin(pGrenade->pev, vecStart); - pGrenade->Spawn(); - pGrenade->pev->movetype = MOVETYPE_FLY; - pGrenade->pev->velocity = vecVelocity; - pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); - pGrenade->pev->owner = ENT(pevOwner); - - // make monsters afraid of it while in the air - pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink); - pGrenade->pev->nextthink = gpGlobals->time; - - // Explode on contact - pGrenade->SetTouch(&CMSporeGrenade::ExplodeTouch); - - pGrenade->pev->gravity = 0.5; - pGrenade->pev->friction = 0.7; - - return pGrenade; -} - -void CMSporeGrenade::SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise) -{ - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); - WRITE_BYTE(TE_SPRITE_SPRAY); - WRITE_COORD(origin.x); // pos - WRITE_COORD(origin.y); - WRITE_COORD(origin.z); - WRITE_COORD(direction.x); // dir - WRITE_COORD(direction.y); - WRITE_COORD(direction.z); - WRITE_SHORT(modelindex); // model - WRITE_BYTE(count); // count - WRITE_BYTE(speed); // speed - WRITE_BYTE(noise); // noise ( client will divide by 100 ) - MESSAGE_END(); -} - -void CMSporeGrenade::SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise) -{ - MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); - WRITE_BYTE(TE_SPRITE_SPRAY); - WRITE_COORD(origin.x); // pos - WRITE_COORD(origin.y); - WRITE_COORD(origin.z); - WRITE_COORD(direction.x); // dir - WRITE_COORD(direction.y); - WRITE_COORD(direction.z); - WRITE_SHORT(modelindex); // model - WRITE_BYTE(count); // count - WRITE_BYTE(speed); // speed - WRITE_BYTE(noise); // noise ( client will divide by 100 ) - MESSAGE_END(); -} - -void CMSporeGrenade::UpdateOnRemove() -{ - CMBaseMonster::UpdateOnRemove(); - if (m_pSporeGlow) - { - UTIL_Remove(m_pSporeGlow->edict()); - m_pSporeGlow = NULL; - } -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "decals.h" +#include "explode.h" + +int gSporeExplode, gSporeExplodeC; + +void CMSporeGrenade::Precache() +{ + PRECACHE_MODEL("models/spore.mdl"); + PRECACHE_MODEL("sprites/glow02.spr"); + g_sModelIndexTinySpit = PRECACHE_MODEL("sprites/tinyspit.spr"); + gSporeExplode = PRECACHE_MODEL("sprites/spore_exp_01.spr"); + gSporeExplodeC = PRECACHE_MODEL("sprites/spore_exp_c_01.spr"); + PRECACHE_SOUND("weapons/splauncher_bounce.wav"); + PRECACHE_SOUND("weapons/splauncher_impact.wav"); +} + +void CMSporeGrenade::Explode(TraceResult *pTrace) +{ + pev->solid = SOLID_NOT;// intangible + pev->takedamage = DAMAGE_NO; + + // Pull out of the wall a bit + if (pTrace->flFraction != 1.0) + { + pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * (pev->dmg - 24) * 0.6); + } + + Vector vecSpraySpot = pTrace->vecEndPos; + float flSpraySpeed = RANDOM_LONG(10, 15); + + // If the trace is pointing up, then place + // spawn position a few units higher. + if (pTrace->vecPlaneNormal.z > 0) + { + vecSpraySpot = vecSpraySpot + (pTrace->vecPlaneNormal * 8); + flSpraySpeed *= 2; // Double the speed to make them fly higher + // in the air. + } + + // Spawn small particles at the explosion origin. + SpawnExplosionParticles( + vecSpraySpot, // position + pTrace->vecPlaneNormal, // direction + g_sModelIndexTinySpit, // modelindex + RANDOM_LONG(40, 50), // count + flSpraySpeed, // speed + RANDOM_FLOAT(600, 640)); // noise + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE( TE_SPRITE ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); + WRITE_SHORT( RANDOM_LONG( 0, 1 ) ? gSporeExplode : gSporeExplodeC ); + WRITE_BYTE( 25 ); // scale * 10 + WRITE_BYTE( 155 ); // framerate + MESSAGE_END(); + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); + WRITE_BYTE(TE_DLIGHT); + WRITE_COORD( pev->origin.x ); // X + WRITE_COORD( pev->origin.y ); // Y + WRITE_COORD( pev->origin.z ); // Z + WRITE_BYTE( 12 ); // radius * 0.1 + WRITE_BYTE( 0 ); // r + WRITE_BYTE( 180 ); // g + WRITE_BYTE( 0 ); // b + WRITE_BYTE( 20 ); // time * 10 + WRITE_BYTE( 20 ); // decay * 0.1 + MESSAGE_END( ); + + // Play explode sound. + EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_impact.wav", 1, ATTN_NORM); + + entvars_t *pevOwner; + if (pev->owner) + pevOwner = VARS(pev->owner); + else + pevOwner = NULL; + + pev->owner = NULL; // can't traceline attack owner if this is set + + RadiusDamage(pev, pevOwner, pev->dmg, CLASS_NONE, DMG_BLAST); + + // Place a decal on the surface that was hit. + UTIL_DecalTrace(pTrace, DECAL_SPIT1 + RANDOM_LONG(0, 1)); + + UpdateOnRemove(); + UTIL_Remove( this->edict() ); +} + +void CMSporeGrenade::Detonate() +{ + TraceResult tr; + Vector vecSpot = pev->origin + Vector(0, 0, 8); + UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr); + + Explode(&tr); +} + + +void CMSporeGrenade::BounceSound() +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/splauncher_bounce.wav", 0.25, ATTN_NORM); +} + +void CMSporeGrenade::TumbleThink() +{ + if (!IsInWorld()) + { + UpdateOnRemove(); + UTIL_Remove( this->edict() ); + return; + } + + pev->nextthink = gpGlobals->time + 0.1; + + if (pev->dmgtime <= gpGlobals->time) + { + SetThink(&CMSporeGrenade::Detonate); + } + + // Spawn particles. + SpawnTrailParticles( + pev->origin, // position + -pev->velocity.Normalize(), // dir + g_sModelIndexTinySpit, // modelindex + RANDOM_LONG( 2, 4 ), // count + RANDOM_FLOAT(10, 15), // speed + RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 ) +} + +// +// Contact grenade, explode when it touches something +// +void CMSporeGrenade::ExplodeTouch(edict_t *pOther) +{ + TraceResult tr; + Vector vecSpot;// trace starts here! + + pev->enemy = pOther; + + vecSpot = pev->origin - pev->velocity.Normalize() * 32; + UTIL_TraceLine(vecSpot, vecSpot + pev->velocity.Normalize() * 64, ignore_monsters, ENT(pev), &tr); + + Explode(&tr); +} + +void CMSporeGrenade::DangerSoundThink() +{ + if (!IsInWorld()) + { + UpdateOnRemove(); + UTIL_Remove( this->edict() ); + return; + } + + pev->nextthink = gpGlobals->time + 0.2; + + // Spawn particles. + SpawnTrailParticles( + pev->origin, // position + -pev->velocity.Normalize(), // dir + g_sModelIndexTinySpit, // modelindex + RANDOM_LONG( 5, 10), // count + RANDOM_FLOAT(10, 15), // speed + RANDOM_FLOAT(2, 3) * 100); // noise ( client will divide by 100 ) +} + +void CMSporeGrenade::BounceTouch(edict_t *pOther) +{ + if ( !pOther->v.takedamage ) + { + if (!(pev->flags & FL_ONGROUND)) { + if (pev->dmg_save < gpGlobals->time) { + BounceSound(); + pev->dmg_save = gpGlobals->time + 0.1; + } + } else { + pev->velocity = pev->velocity * 0.9; + } + if (pev->flags & FL_SWIM) + { + pev->velocity = pev->velocity * 0.5; + } + } + else + { + TraceResult tr = UTIL_GetGlobalTrace(); + Explode(&tr); + } +} + +void CMSporeGrenade::Spawn() +{ + Precache(); + pev->classname = MAKE_STRING("spore"); + pev->movetype = MOVETYPE_BOUNCE; + + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/spore.mdl"); + UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); + + //pev->gravity = 0.5; + + pev->dmg = gSkillData.monDmgSpore; + + m_pSporeGlow = CMSprite::SpriteCreate("sprites/glow02.spr", pev->origin, FALSE); + + if (m_pSporeGlow) + { + m_pSporeGlow->SetTransparency(kRenderGlow, 150, 158, 19, 155, kRenderFxNoDissipation); + m_pSporeGlow->SetAttachment(edict(), 0); + m_pSporeGlow->SetScale(.75f); + } +} + +CMSporeGrenade* CMSporeGrenade::ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai) +{ + CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL); + + if (pGrenade == NULL) + return NULL; + + UTIL_SetOrigin(pGrenade->pev, vecStart); + pGrenade->Spawn(); + pGrenade->pev->velocity = vecVelocity; + pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); + pGrenade->pev->owner = ENT(pevOwner); + + pGrenade->SetTouch(&CMSporeGrenade::BounceTouch); // Bounce if touched + + float lifetime = 2.0; + if (ai) { + lifetime = 4.0; + pGrenade->pev->gravity = 0.5; + pGrenade->pev->friction = 0.9; + } + pGrenade->pev->dmgtime = gpGlobals->time + lifetime; + pGrenade->SetThink(&CMSporeGrenade::TumbleThink); + pGrenade->pev->nextthink = gpGlobals->time + 0.1; + if (lifetime < 0.1) + { + pGrenade->pev->nextthink = gpGlobals->time; + pGrenade->pev->velocity = Vector(0, 0, 0); + } + + return pGrenade; +} + +CMSporeGrenade *CMSporeGrenade::ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity) +{ + CMSporeGrenade *pGrenade = CreateClassPtr((CMSporeGrenade *)NULL); + + if (pGrenade == NULL) + return NULL; + + UTIL_SetOrigin(pGrenade->pev, vecStart); + pGrenade->Spawn(); + pGrenade->pev->movetype = MOVETYPE_FLY; + pGrenade->pev->velocity = vecVelocity; + pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity); + pGrenade->pev->owner = ENT(pevOwner); + + // make monsters afraid of it while in the air + pGrenade->SetThink(&CMSporeGrenade::DangerSoundThink); + pGrenade->pev->nextthink = gpGlobals->time; + + // Explode on contact + pGrenade->SetTouch(&CMSporeGrenade::ExplodeTouch); + + pGrenade->pev->gravity = 0.5; + pGrenade->pev->friction = 0.7; + + return pGrenade; +} + +void CMSporeGrenade::SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise) +{ + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); + WRITE_BYTE(TE_SPRITE_SPRAY); + WRITE_COORD(origin.x); // pos + WRITE_COORD(origin.y); + WRITE_COORD(origin.z); + WRITE_COORD(direction.x); // dir + WRITE_COORD(direction.y); + WRITE_COORD(direction.z); + WRITE_SHORT(modelindex); // model + WRITE_BYTE(count); // count + WRITE_BYTE(speed); // speed + WRITE_BYTE(noise); // noise ( client will divide by 100 ) + MESSAGE_END(); +} + +void CMSporeGrenade::SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise) +{ + MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, origin); + WRITE_BYTE(TE_SPRITE_SPRAY); + WRITE_COORD(origin.x); // pos + WRITE_COORD(origin.y); + WRITE_COORD(origin.z); + WRITE_COORD(direction.x); // dir + WRITE_COORD(direction.y); + WRITE_COORD(direction.z); + WRITE_SHORT(modelindex); // model + WRITE_BYTE(count); // count + WRITE_BYTE(speed); // speed + WRITE_BYTE(noise); // noise ( client will divide by 100 ) + MESSAGE_END(); +} + +void CMSporeGrenade::UpdateOnRemove() +{ + CMBaseMonster::UpdateOnRemove(); + if (m_pSporeGlow) + { + UTIL_Remove(m_pSporeGlow->edict()); + m_pSporeGlow = NULL; + } +} diff --git a/src/dlls/squeakgrenade.cpp b/src/dlls/squeakgrenade.cpp index bcc60f0..78d00b4 100644 --- a/src/dlls/squeakgrenade.cpp +++ b/src/dlls/squeakgrenade.cpp @@ -1,350 +1,350 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" - -enum w_squeak_e { - WSQUEAK_IDLE1 = 0, - WSQUEAK_FIDGET, - WSQUEAK_JUMP, - WSQUEAK_RUN, -}; - -enum squeak_e { - SQUEAK_IDLE1 = 0, - SQUEAK_FIDGETFIT, - SQUEAK_FIDGETNIP, - SQUEAK_DOWN, - SQUEAK_UP, - SQUEAK_THROW -}; - -#ifndef CLIENT_DLL - - -float CMSqueakGrenade::m_flNextBounceSoundTime = 0; - -#define SQUEEK_DETONATE_DELAY 15.0 - -int CMSqueakGrenade :: Classify ( void ) -{ - // E - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -void CMSqueakGrenade :: Spawn( void ) -{ - Precache( ); - // motor - pev->movetype = MOVETYPE_BOUNCE; - pev->solid = SOLID_BBOX; - - SET_MODEL(ENT(pev), "models/w_squeak.mdl"); - UTIL_SetSize(pev, Vector( -4, -4, 0), Vector(4, 4, 8)); - UTIL_SetOrigin( pev, pev->origin ); - - SetTouch( &CMSqueakGrenade::SuperBounceTouch ); - SetThink( &CMSqueakGrenade::HuntThink ); - pev->nextthink = gpGlobals->time + 0.1; - m_flNextHunt = gpGlobals->time + 1E6; - - pev->flags |= FL_MONSTER; - pev->takedamage = DAMAGE_AIM; - pev->health = gSkillData.snarkHealth; - pev->gravity = 0.5; - pev->friction = 0.5; - - pev->dmg = gSkillData.snarkDmgPop; - - m_flDie = gpGlobals->time + SQUEEK_DETONATE_DELAY; - - m_flFieldOfView = 0; // 180 degrees - - m_flNextBounceSoundTime = gpGlobals->time;// reset each time a snark is spawned. - - pev->sequence = WSQUEAK_RUN; - ResetSequenceInfo( ); - - m_hEnemy = NULL; - - pev->classname = MAKE_STRING( "monster_snark" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // hi :3 - m_szMonsterName = MAKE_STRING( "Snark" ); - } -} - -void CMSqueakGrenade::Precache( void ) -{ - PRECACHE_MODEL("models/w_squeak.mdl"); - PRECACHE_SOUND("squeek/sqk_blast1.wav"); - PRECACHE_SOUND("common/bodysplat.wav"); - PRECACHE_SOUND("squeek/sqk_die1.wav"); - PRECACHE_SOUND("squeek/sqk_hunt1.wav"); - PRECACHE_SOUND("squeek/sqk_hunt2.wav"); - PRECACHE_SOUND("squeek/sqk_hunt3.wav"); - PRECACHE_SOUND("squeek/sqk_deploy1.wav"); -} - - -void CMSqueakGrenade :: Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->model = iStringNull;// make invisible - SetThink( &CMSqueakGrenade::SUB_Remove ); - SetTouch( NULL ); - pev->nextthink = gpGlobals->time + 0.1; - - // since squeak grenades never leave a body behind, clear out their takedamage now. - // Squeaks do a bit of radius damage when they pop, and that radius damage will - // continue to call this function unless we acknowledge the Squeak's death now. (sjb) - pev->takedamage = DAMAGE_NO; - - // play squeek blast - EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "squeek/sqk_blast1.wav", 1, 0.5, 0, PITCH_NORM); - - UTIL_BloodDrips( pev->origin, g_vecZero, BloodColor(), 80 ); - - RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST ); - - CMBaseMonster :: Killed( pevAttacker, GIB_ALWAYS ); -} - -void CMSqueakGrenade :: GibMonster( void ) -{ - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200); -} - - - -void CMSqueakGrenade::HuntThink( void ) -{ - // ALERT( at_console, "think\n" ); - - if (!IsInWorld()) - { - SetTouch( NULL ); - UTIL_Remove( this->edict() ); - return; - } - - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - // explode when ready - if (gpGlobals->time >= m_flDie) - { - g_vecAttackDir = pev->velocity.Normalize( ); - pev->health = -1; - Killed( pev, 0 ); - return; - } - - // float - if (pev->waterlevel != 0) - { - if (pev->movetype == MOVETYPE_BOUNCE) - { - pev->movetype = MOVETYPE_FLY; - } - pev->velocity = pev->velocity * 0.9; - pev->velocity.z += 8.0; - } - else if (pev->movetype = MOVETYPE_FLY) - { - pev->movetype = MOVETYPE_BOUNCE; - } - - // return if not time to hunt - if (m_flNextHunt > gpGlobals->time) - return; - - m_flNextHunt = gpGlobals->time + 2.0; - - Vector vecDir; - TraceResult tr; - - Vector vecFlat = pev->velocity; - vecFlat.z = 0; - vecFlat = vecFlat.Normalize( ); - - UTIL_MakeVectors( pev->angles ); - - if (m_hEnemy == NULL || !UTIL_IsAlive(m_hEnemy)) - { - // find target, bounce a bit towards it. - Look( 512 ); - m_hEnemy = BestVisibleEnemy( ); - } - - // squeek if it's about time blow up - if ((m_flDie - gpGlobals->time <= 0.5) && (m_flDie - gpGlobals->time >= 0.3)) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + RANDOM_LONG(0,0x3F)); - } - - // higher pitch as squeeker gets closer to detonation time - float flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); - if (flpitch < 80) - flpitch = 80; - - if (m_hEnemy != NULL) - { - if (UTIL_FVisible( m_hEnemy, ENT(pev) )) - { - vecDir = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs) - pev->origin; - m_vecTarget = vecDir.Normalize( ); - } - - float flVel = pev->velocity.Length(); - float flAdj = 50.0 / (flVel + 10.0); - - if (flAdj > 1.2) - flAdj = 1.2; - - // ALERT( at_console, "think : enemy\n"); - - // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); - - pev->velocity = pev->velocity * flAdj + m_vecTarget * 300; - } - - if (pev->flags & FL_ONGROUND) - { - pev->avelocity = Vector( 0, 0, 0 ); - } - else - { - if (pev->avelocity == Vector( 0, 0, 0)) - { - pev->avelocity.x = RANDOM_FLOAT( -100, 100 ); - pev->avelocity.z = RANDOM_FLOAT( -100, 100 ); - } - } - - if ((pev->origin - m_posPrev).Length() < 1.0) - { - pev->velocity.x = RANDOM_FLOAT( -100, 100 ); - pev->velocity.y = RANDOM_FLOAT( -100, 100 ); - } - m_posPrev = pev->origin; - - pev->angles = UTIL_VecToAngles( pev->velocity ); - pev->angles.z = 0; - pev->angles.x = 0; -} - - -void CMSqueakGrenade::SuperBounceTouch( edict_t *pOther ) -{ - float flpitch; - - TraceResult tr = UTIL_GetGlobalTrace( ); - - // don't hit the guy that launched this grenade - if ( pev->owner && (pOther == pev->owner) ) - return; - - // at least until we've bounced once - pev->owner = NULL; - - pev->angles.x = 0; - pev->angles.z = 0; - - // avoid bouncing too much - if (m_flNextHit > gpGlobals->time) - return; - - // higher pitch as squeeker gets closer to detonation time - flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); - - if ( pOther->v.takedamage && m_flNextAttack < gpGlobals->time ) - { - // attack! - - // make sure it's me who has touched them - if (tr.pHit == pOther) - { - // and it's not another squeakgrenade - if (tr.pHit->v.modelindex != pev->modelindex) - { - // ALERT( at_console, "hit enemy\n"); - ClearMultiDamage( ); - - if (UTIL_IsPlayer(pOther)) - UTIL_TraceAttack(pOther, pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TraceAttack(pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); - } - - ApplyMultiDamage( pev, pev ); - - pev->dmg += gSkillData.snarkDmgPop; // add more explosion damage - // m_flDie += 2.0; // add more life - - // make bite sound - EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "squeek/sqk_deploy1.wav", 1.0, ATTN_NORM, 0, (int)flpitch); - m_flNextAttack = gpGlobals->time + 0.5; - } - } - else - { - // ALERT( at_console, "been hit\n"); - } - } - - m_flNextHit = gpGlobals->time + 0.1; - m_flNextHunt = gpGlobals->time; - - // in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows. - if ( gpGlobals->time < m_flNextBounceSoundTime ) - { - // too soon! - return; - } - - if (!(pev->flags & FL_ONGROUND)) - { - // play bounce sound - float flRndSound = RANDOM_FLOAT ( 0 , 1 ); - - if ( flRndSound <= 0.33 ) - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt1.wav", 1, ATTN_NORM, 0, (int)flpitch); - else if (flRndSound <= 0.66) - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, (int)flpitch); - else - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, (int)flpitch); - } - - m_flNextBounceSoundTime = gpGlobals->time + 0.5;// half second. -} - -#endif - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD ) + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" + +enum w_squeak_e { + WSQUEAK_IDLE1 = 0, + WSQUEAK_FIDGET, + WSQUEAK_JUMP, + WSQUEAK_RUN, +}; + +enum squeak_e { + SQUEAK_IDLE1 = 0, + SQUEAK_FIDGETFIT, + SQUEAK_FIDGETNIP, + SQUEAK_DOWN, + SQUEAK_UP, + SQUEAK_THROW +}; + +#ifndef CLIENT_DLL + + +float CMSqueakGrenade::m_flNextBounceSoundTime = 0; + +#define SQUEEK_DETONATE_DELAY 15.0 + +int CMSqueakGrenade :: Classify ( void ) +{ + // E + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +void CMSqueakGrenade :: Spawn( void ) +{ + Precache( ); + // motor + pev->movetype = MOVETYPE_BOUNCE; + pev->solid = SOLID_BBOX; + + SET_MODEL(ENT(pev), "models/w_squeak.mdl"); + UTIL_SetSize(pev, Vector( -4, -4, 0), Vector(4, 4, 8)); + UTIL_SetOrigin( pev, pev->origin ); + + SetTouch( &CMSqueakGrenade::SuperBounceTouch ); + SetThink( &CMSqueakGrenade::HuntThink ); + pev->nextthink = gpGlobals->time + 0.1; + m_flNextHunt = gpGlobals->time + 1E6; + + pev->flags |= FL_MONSTER; + pev->takedamage = DAMAGE_AIM; + pev->health = gSkillData.snarkHealth; + pev->gravity = 0.5; + pev->friction = 0.5; + + pev->dmg = gSkillData.snarkDmgPop; + + m_flDie = gpGlobals->time + SQUEEK_DETONATE_DELAY; + + m_flFieldOfView = 0; // 180 degrees + + m_flNextBounceSoundTime = gpGlobals->time;// reset each time a snark is spawned. + + pev->sequence = WSQUEAK_RUN; + ResetSequenceInfo( ); + + m_hEnemy = NULL; + + pev->classname = MAKE_STRING( "monster_snark" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // hi :3 + m_szMonsterName = MAKE_STRING( "Snark" ); + } +} + +void CMSqueakGrenade::Precache( void ) +{ + PRECACHE_MODEL("models/w_squeak.mdl"); + PRECACHE_SOUND("squeek/sqk_blast1.wav"); + PRECACHE_SOUND("common/bodysplat.wav"); + PRECACHE_SOUND("squeek/sqk_die1.wav"); + PRECACHE_SOUND("squeek/sqk_hunt1.wav"); + PRECACHE_SOUND("squeek/sqk_hunt2.wav"); + PRECACHE_SOUND("squeek/sqk_hunt3.wav"); + PRECACHE_SOUND("squeek/sqk_deploy1.wav"); +} + + +void CMSqueakGrenade :: Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->model = iStringNull;// make invisible + SetThink( &CMSqueakGrenade::SUB_Remove ); + SetTouch( NULL ); + pev->nextthink = gpGlobals->time + 0.1; + + // since squeak grenades never leave a body behind, clear out their takedamage now. + // Squeaks do a bit of radius damage when they pop, and that radius damage will + // continue to call this function unless we acknowledge the Squeak's death now. (sjb) + pev->takedamage = DAMAGE_NO; + + // play squeek blast + EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "squeek/sqk_blast1.wav", 1, 0.5, 0, PITCH_NORM); + + UTIL_BloodDrips( pev->origin, g_vecZero, BloodColor(), 80 ); + + RadiusDamage ( pev, pev, pev->dmg, CLASS_NONE, DMG_BLAST ); + + CMBaseMonster :: Killed( pevAttacker, GIB_ALWAYS ); +} + +void CMSqueakGrenade :: GibMonster( void ) +{ + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "common/bodysplat.wav", 0.75, ATTN_NORM, 0, 200); +} + + + +void CMSqueakGrenade::HuntThink( void ) +{ + // ALERT( at_console, "think\n" ); + + if (!IsInWorld()) + { + SetTouch( NULL ); + UTIL_Remove( this->edict() ); + return; + } + + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + // explode when ready + if (gpGlobals->time >= m_flDie) + { + g_vecAttackDir = pev->velocity.Normalize( ); + pev->health = -1; + Killed( pev, 0 ); + return; + } + + // float + if (pev->waterlevel != 0) + { + if (pev->movetype == MOVETYPE_BOUNCE) + { + pev->movetype = MOVETYPE_FLY; + } + pev->velocity = pev->velocity * 0.9; + pev->velocity.z += 8.0; + } + else if (pev->movetype = MOVETYPE_FLY) + { + pev->movetype = MOVETYPE_BOUNCE; + } + + // return if not time to hunt + if (m_flNextHunt > gpGlobals->time) + return; + + m_flNextHunt = gpGlobals->time + 2.0; + + Vector vecDir; + TraceResult tr; + + Vector vecFlat = pev->velocity; + vecFlat.z = 0; + vecFlat = vecFlat.Normalize( ); + + UTIL_MakeVectors( pev->angles ); + + if (m_hEnemy == NULL || !UTIL_IsAlive(m_hEnemy)) + { + // find target, bounce a bit towards it. + Look( 512 ); + m_hEnemy = BestVisibleEnemy( ); + } + + // squeek if it's about time blow up + if ((m_flDie - gpGlobals->time <= 0.5) && (m_flDie - gpGlobals->time >= 0.3)) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + RANDOM_LONG(0,0x3F)); + } + + // higher pitch as squeeker gets closer to detonation time + float flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); + if (flpitch < 80) + flpitch = 80; + + if (m_hEnemy != NULL) + { + if (UTIL_FVisible( m_hEnemy, ENT(pev) )) + { + vecDir = (m_hEnemy->v.origin + m_hEnemy->v.view_ofs) - pev->origin; + m_vecTarget = vecDir.Normalize( ); + } + + float flVel = pev->velocity.Length(); + float flAdj = 50.0 / (flVel + 10.0); + + if (flAdj > 1.2) + flAdj = 1.2; + + // ALERT( at_console, "think : enemy\n"); + + // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); + + pev->velocity = pev->velocity * flAdj + m_vecTarget * 300; + } + + if (pev->flags & FL_ONGROUND) + { + pev->avelocity = Vector( 0, 0, 0 ); + } + else + { + if (pev->avelocity == Vector( 0, 0, 0)) + { + pev->avelocity.x = RANDOM_FLOAT( -100, 100 ); + pev->avelocity.z = RANDOM_FLOAT( -100, 100 ); + } + } + + if ((pev->origin - m_posPrev).Length() < 1.0) + { + pev->velocity.x = RANDOM_FLOAT( -100, 100 ); + pev->velocity.y = RANDOM_FLOAT( -100, 100 ); + } + m_posPrev = pev->origin; + + pev->angles = UTIL_VecToAngles( pev->velocity ); + pev->angles.z = 0; + pev->angles.x = 0; +} + + +void CMSqueakGrenade::SuperBounceTouch( edict_t *pOther ) +{ + float flpitch; + + TraceResult tr = UTIL_GetGlobalTrace( ); + + // don't hit the guy that launched this grenade + if ( pev->owner && (pOther == pev->owner) ) + return; + + // at least until we've bounced once + pev->owner = NULL; + + pev->angles.x = 0; + pev->angles.z = 0; + + // avoid bouncing too much + if (m_flNextHit > gpGlobals->time) + return; + + // higher pitch as squeeker gets closer to detonation time + flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); + + if ( pOther->v.takedamage && m_flNextAttack < gpGlobals->time ) + { + // attack! + + // make sure it's me who has touched them + if (tr.pHit == pOther) + { + // and it's not another squeakgrenade + if (tr.pHit->v.modelindex != pev->modelindex) + { + // ALERT( at_console, "hit enemy\n"); + ClearMultiDamage( ); + + if (UTIL_IsPlayer(pOther)) + UTIL_TraceAttack(pOther, pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TraceAttack(pev, gSkillData.snarkDmgBite, gpGlobals->v_forward, &tr, DMG_SLASH ); + } + + ApplyMultiDamage( pev, pev ); + + pev->dmg += gSkillData.snarkDmgPop; // add more explosion damage + // m_flDie += 2.0; // add more life + + // make bite sound + EMIT_SOUND_DYN(ENT(pev), CHAN_WEAPON, "squeek/sqk_deploy1.wav", 1.0, ATTN_NORM, 0, (int)flpitch); + m_flNextAttack = gpGlobals->time + 0.5; + } + } + else + { + // ALERT( at_console, "been hit\n"); + } + } + + m_flNextHit = gpGlobals->time + 0.1; + m_flNextHunt = gpGlobals->time; + + // in multiplayer, we limit how often snarks can make their bounce sounds to prevent overflows. + if ( gpGlobals->time < m_flNextBounceSoundTime ) + { + // too soon! + return; + } + + if (!(pev->flags & FL_ONGROUND)) + { + // play bounce sound + float flRndSound = RANDOM_FLOAT ( 0 , 1 ); + + if ( flRndSound <= 0.33 ) + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt1.wav", 1, ATTN_NORM, 0, (int)flpitch); + else if (flRndSound <= 0.66) + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt2.wav", 1, ATTN_NORM, 0, (int)flpitch); + else + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_hunt3.wav", 1, ATTN_NORM, 0, (int)flpitch); + } + + m_flNextBounceSoundTime = gpGlobals->time + 0.5;// half second. +} + +#endif + +#endif diff --git a/src/dlls/strooper.cpp b/src/dlls/strooper.cpp index ada52fa..76c8554 100644 --- a/src/dlls/strooper.cpp +++ b/src/dlls/strooper.cpp @@ -1,899 +1,899 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// shocktrooper -//========================================================= - -#include "extdll.h" -#include "plane.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "animation.h" -#include "weapons.h" -#include "cmtalkmonster.h" -#include "effects.h" -#include "customentity.h" -#include "shock.h" - -int g_fStrooperQuestion; // true if an idle grunt asked a question. Cleared when someone answers. - -extern Schedule_t slGruntTakeCover[]; -extern Schedule_t slGruntGrenadeCover[]; - -//========================================================= -// monster-specific DEFINE's -//========================================================= -#define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! -#define STROOPER_VOL 0.35 // volume of grunt sounds -#define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences -#define STROOPER_LIMP_HEALTH 20 -#define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. -#define STROOPER_NUM_HEADS 2 // how many grunt heads are there? -#define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill -#define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences -#define STROOPER_MUZZLEFLASH "sprites/muzzle_shock.spr" - -#define STROOPER_SHOCKRIFLE (1 << 0) -#define STROOPER_HANDGRENADE (1 << 1) - -#define GUN_GROUP 1 -#define GUN_SHOCKRIFLE 0 -#define GUN_NONE 1 - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define STROOPER_AE_RELOAD ( 2 ) -#define STROOPER_AE_KICK ( 3 ) -#define STROOPER_AE_BURST1 ( 4 ) -#define STROOPER_AE_BURST2 ( 5 ) -#define STROOPER_AE_BURST3 ( 6 ) -#define STROOPER_AE_GREN_TOSS ( 7 ) -#define STROOPER_AE_GREN_LAUNCH ( 8 ) -#define STROOPER_AE_GREN_DROP ( 9 ) -#define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad. -#define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle. - - -//========================================================= -// monster-specific schedule types -//========================================================= -enum -{ - SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1, - SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). - SCHED_STROOPER_COVER_AND_RELOAD, - SCHED_STROOPER_SWEEP, - SCHED_STROOPER_FOUND_ENEMY, - SCHED_STROOPER_REPEL, - SCHED_STROOPER_REPEL_ATTACK, - SCHED_STROOPER_REPEL_LAND, - SCHED_STROOPER_WAIT_FACE_ENEMY, - SCHED_STROOPER_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. - SCHED_STROOPER_ELOF_FAIL, -}; - -//========================================================= -// monster-specific tasks -//========================================================= -enum -{ - TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, - TASK_STROOPER_SPEAK_SENTENCE, - TASK_STROOPER_CHECK_FIRE, -}; - -int iStrooperMuzzleFlash; - -const char *CMStrooper::pGruntSentences[] = -{ - "ST_GREN", // grenade scared grunt - "ST_ALERT", // sees player - "ST_MONST", // sees monster - "ST_COVER", // running to cover - "ST_THROW", // about to throw grenade - "ST_CHARGE", // running out to get the enemy - "ST_TAUNT", // say rude things -}; - -typedef enum -{ - STROOPER_SENT_NONE = -1, - STROOPER_SENT_GREN = 0, - STROOPER_SENT_ALERT, - STROOPER_SENT_MONSTER, - STROOPER_SENT_COVER, - STROOPER_SENT_THROW, - STROOPER_SENT_CHARGE, - STROOPER_SENT_TAUNT, -} STROOPER_SENTENCE_TYPES; - -void CMStrooper::SpeakSentence() -{ - if( m_iSentence == STROOPER_SENT_NONE ) - { - // no sentence cued up. - return; - } - - if( FOkToSpeak() ) - { - SENTENCEG_PlayRndSz( ENT( pev ), pGruntSentences[m_iSentence], STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch ); - JustSpoke(); - } -} - -#define STROOPER_GIB_COUNT 8 -//========================================================= -// GibMonster - make gun fly through the air. -//========================================================= -void CMStrooper::GibMonster() -{ - if (GetBodygroup(GUN_GROUP) != GUN_NONE) - { - DropShockRoach(true); - } - - EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); - - if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly - { - CMGib::SpawnRandomGibs( pev, STROOPER_GIB_COUNT, "models/strooper_gibs.mdl", 0 ); // Throw alien gibs - } - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time; -} - -void CMStrooper::IdleSound() -{ - if (FOkToSpeak() && (g_fStrooperQuestion || RANDOM_LONG(0, 1))) - { - if (!g_fStrooperQuestion) - { - // ask question or make statement - switch (RANDOM_LONG(0, 2)) - { - case 0: // check in - SENTENCEG_PlayRndSz(ENT(pev), "ST_CHECK", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fStrooperQuestion = 1; - break; - case 1: // question - SENTENCEG_PlayRndSz(ENT(pev), "ST_QUEST", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - g_fStrooperQuestion = 2; - break; - case 2: // statement - SENTENCEG_PlayRndSz(ENT(pev), "ST_IDLE", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - } - else - { - switch (g_fStrooperQuestion) - { - case 1: // check in - SENTENCEG_PlayRndSz(ENT(pev), "ST_CLEAR", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - case 2: // question - SENTENCEG_PlayRndSz(ENT(pev), "ST_ANSWER", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); - break; - } - g_fStrooperQuestion = 0; - } - JustSpoke(); - } -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMStrooper::Classify() -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_RACEX_SHOCK; -} - -BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist) -{ - return m_cAmmoLoaded >= 1;// && CMHGrunt::CheckRangeAttack1(flDot, flDist); -} - -BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist ) -{ - if( !FBitSet( pev->weapons, STROOPER_HANDGRENADE ) ) - { - return FALSE; - } - return CMHGrunt::CheckRangeAttack2(flDot, flDist); -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch (pEvent->event) - { - case STROOPER_AE_DROP_GUN: - { - if (GetBodygroup(GUN_GROUP) != GUN_NONE) - { - DropShockRoach(false); - } - } - break; - - case STROOPER_AE_RELOAD: - m_cAmmoLoaded = m_cClipSize; - ClearConditions(bits_COND_NO_AMMO_LOADED); - break; - - case STROOPER_AE_GREN_TOSS: - { - UTIL_MakeVectors(pev->angles); - // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); - CMSporeGrenade::ShootTimed(pev, pev->origin + Vector(0,0,98), m_vecTossVelocity, 3.5); - - m_fThrowGrenade = FALSE; - m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. - // !!!LATER - when in a group, only try to throw grenade if ordered. - } - break; - - case STROOPER_AE_GREN_LAUNCH: - case STROOPER_AE_GREN_DROP: - break; - - case STROOPER_AE_BURST1: - { - if (m_hEnemy) - { - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment(0, vecGunPos, vecGunAngles); - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecGunPos ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( vecGunPos.x ); // pos - WRITE_COORD( vecGunPos.y ); - WRITE_COORD( vecGunPos.z ); - WRITE_SHORT( iStrooperMuzzleFlash ); // model - WRITE_BYTE( 4 ); // size * 10 - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); - - UTIL_MakeVectors(pev->angles); - Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32; - Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); - vecGunAngles = UTIL_VecToAngles(vecShootDir); - - //CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecShootOrigin, vecGunAngles, edict()); - CMShock *pShock = CreateClassPtr((CMShock *)NULL); - - if (pShock != NULL) - { - pShock->pev->origin = vecShootOrigin; - - vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 ); - pShock->pev->angles = UTIL_VecToAngles( vecGunAngles ); - pShock->pev->owner = edict(); - - // Initialize these for entities who don't link to the world - pShock->pev->absmin = pShock->pev->origin - Vector(1,1,1); - pShock->pev->absmax = pShock->pev->origin + Vector(1,1,1); - - pShock->Spawn(); - - pShock->pev->velocity = vecShootDir * 2000; - pShock->pev->nextthink = gpGlobals->time; - } - - m_cAmmoLoaded--; - SetBlending( 0, vecGunAngles.x ); - - // Play fire sound. - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_fire.wav", 1, ATTN_NORM); - } - } - break; - - case STROOPER_AE_KICK: - { - EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "zombie/claw_miss2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -5, 5 ) ); - edict_t *pHurt = Kick(); - - if (pHurt) - { - // SOUND HERE! - UTIL_MakeVectors(pev->angles); - pHurt->v.punchangle.x = 15; - pHurt->v.punchangle.z = (m_fRightClaw) ? -10 : 10; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; - - if ( UTIL_IsPlayer( pHurt ) ) - UTIL_TakeDamage( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB ); - else if ( pHurt->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); - pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB ); - } - } - - m_fRightClaw = !m_fRightClaw; - } - break; - - case STROOPER_AE_CAUGHT_ENEMY: - { - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); - JustSpoke(); - } - - } - - default: - CMHGrunt::HandleAnimEvent(pEvent); - break; - } -} - - -//========================================================= -// Spawn -//========================================================= -void CMStrooper::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/strooper.mdl"); - UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.strooperHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_flNextGrenadeCheck = gpGlobals->time + 1; - m_flNextPainTime = gpGlobals->time; - m_iSentence = STROOPER_SENT_NONE; - - m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; - - //m_fEnemyEluded = FALSE; - m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. - - m_HackedGunPos = Vector(0, 0, 55); - - if (pev->weapons == 0) - { - // initialize to original values - pev->weapons = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE; - } - - m_cClipSize = gSkillData.strooperMaxCharge; - - m_cAmmoLoaded = m_cClipSize; - - m_fRightClaw = FALSE; - - CMTalkMonster::g_talkWaitTime = 0; - m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed; - m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f); - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_shocktrooper" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Shock Trooper" ); - } -} - -void CMStrooper::MonsterThink() -{ - if (m_cAmmoLoaded < m_cClipSize) - { - if (m_rechargeTime < gpGlobals->time) - { - m_cAmmoLoaded++; - m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed; - } - } - if (m_blinkTime <= gpGlobals->time && pev->skin == 0) { - pev->skin = 1; - m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f); - m_eyeChangeTime = gpGlobals->time + 0.1; - } - if (pev->skin != 0) { - if (m_eyeChangeTime <= gpGlobals->time) { - m_eyeChangeTime = gpGlobals->time + 0.1; - pev->skin++; - if (pev->skin > 3) { - pev->skin = 0; - } - } - } - CMHGrunt::MonsterThink(); -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMStrooper::Precache() -{ - PRECACHE_MODEL("models/strooper.mdl"); - PRECACHE_MODEL("models/strooper_gibs.mdl"); - iStrooperMuzzleFlash = PRECACHE_MODEL(STROOPER_MUZZLEFLASH); - PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav"); - - PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_die2.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_die3.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_die4.wav"); - - PRECACHE_SOUND("shocktrooper/shock_trooper_pain1.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_pain2.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_pain3.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_pain4.wav"); - PRECACHE_SOUND("shocktrooper/shock_trooper_pain5.wav"); - - PRECACHE_SOUND("weapons/shock_fire.wav"); - PRECACHE_SOUND("weapons/shock_impact.wav"); - - PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event - - // shock_beam - CMShock shock; - shock.Precache(); - - // spore - CMSporeGrenade spore; - spore.Precache(); - - // shockroach - CMShockRoach shockroach; - shockroach.Precache(); - - // get voice pitch - if (RANDOM_LONG(0, 1)) - m_voicePitch = 109 + RANDOM_LONG(0, 7); - else - m_voicePitch = 100; - - m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell -} - - -//========================================================= -// PainSound -//========================================================= -void CMStrooper::PainSound() -{ - if (gpGlobals->time > m_flNextPainTime) - { -#if 0 - if (RANDOM_LONG(0, 99) < 5) - { - // pain sentences are rare - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); - JustSpoke(); - return; - } - } -#endif - switch (RANDOM_LONG(0, 4)) - { - case 0: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain1.wav", 1, ATTN_NORM); - break; - case 1: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain2.wav", 1, ATTN_NORM); - break; - case 2: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain3.wav", 1, ATTN_NORM); - break; - case 3: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain4.wav", 1, ATTN_NORM); - break; - case 4: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain5.wav", 1, ATTN_NORM); - break; - } - - m_flNextPainTime = gpGlobals->time + 1; - } -} - -//========================================================= -// DeathSound -//========================================================= -void CMStrooper::DeathSound() -{ - switch (RANDOM_LONG(0, 3)) - { - case 0: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die1.wav", 1, ATTN_IDLE); - break; - case 1: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die2.wav", 1, ATTN_IDLE); - break; - case 2: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die3.wav", 1, ATTN_IDLE); - break; - case 3: - EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die4.wav", 1, ATTN_IDLE); - break; - } -} - -//========================================================= -// TraceAttack - reimplemented in shock trooper because they never have helmets -//========================================================= -void CMStrooper::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); -} - -void CMStrooper::DropShockRoach(bool gibbed) -{ - Vector vecGunPos; - Vector vecGunAngles; - - GetAttachment(0, vecGunPos, vecGunAngles); - SetBodygroup(GUN_GROUP, GUN_NONE); - - Vector vecDropAngles; - - // Remove any pitch. - vecDropAngles.x = 0; - vecDropAngles.y = vecGunAngles.y; - vecDropAngles.z = 0; - - Vector vecPos = pev->origin; - if (gibbed) - vecPos.z += 32; - else - vecPos.z += 48; - - // now spawn a shockroach. - //CBaseEntity* roach = CBaseEntity::Create( "monster_shockroach", vecPos, vecDropAngles ); - CMShockRoach *roach = CreateClassPtr((CMShockRoach *)NULL); - if (roach != NULL) - { - roach->pev->origin = vecPos; - roach->pev->angles = UTIL_VecToAngles( vecDropAngles ); - - // Initialize these for entities who don't link to the world - roach->pev->absmin = roach->pev->origin - Vector(1,1,1); - roach->pev->absmax = roach->pev->origin + Vector(1,1,1); - - roach->Spawn(); - - if (ShouldFadeOnDeath()) - roach->pev->spawnflags |= SF_MONSTER_FADECORPSE; - if (gibbed) - { - roach->pev->velocity = Vector(RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(200.0f, 300.0f)); - roach->pev->avelocity = Vector(0, RANDOM_FLOAT(200.0f, 300.0f), 0); - } - else - { - roach->pev->velocity = Vector(RANDOM_FLOAT(-20.0f, 20.0f) , RANDOM_FLOAT(-20.0f, 20.0f), RANDOM_FLOAT(20.0f, 30.0f)); - roach->pev->avelocity = Vector(0, RANDOM_FLOAT(20.0f, 40.0f), 0); - } - } -} - - -//========================================================= -// SetActivity -//========================================================= -void CMStrooper::SetActivity(Activity NewActivity) -{ - int iSequence = ACTIVITY_NOT_AVAILABLE; - void *pmodel = GET_MODEL_PTR(ENT(pev)); - - switch (NewActivity) - { - case ACT_RANGE_ATTACK1: - // shocktrooper is either shooting standing or shooting crouched - if (m_fStanding) - { - // get aimable sequence - iSequence = LookupSequence("standing_mp5"); - } - else - { - // get crouching shoot - iSequence = LookupSequence("crouching_mp5"); - } - break; - case ACT_RANGE_ATTACK2: - // shocktrooper is going to throw a grenade. - - // get toss anim - iSequence = LookupSequence("throwgrenade"); - break; - - case ACT_RUN: - if (pev->health <= STROOPER_LIMP_HEALTH) - { - // limp! - iSequence = LookupActivity(ACT_RUN_HURT); - } - else - { - iSequence = LookupActivity(NewActivity); - } - break; - case ACT_WALK: - if (pev->health <= STROOPER_LIMP_HEALTH) - { - // limp! - iSequence = LookupActivity(ACT_WALK_HURT); - } - else - { - iSequence = LookupActivity(NewActivity); - } - break; - case ACT_IDLE: - if (m_MonsterState == MONSTERSTATE_COMBAT) - { - NewActivity = ACT_IDLE_ANGRY; - } - iSequence = LookupActivity(NewActivity); - break; - default: - iSequence = LookupActivity(NewActivity); - break; - } - - m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present - - // Set to the desired anim, or default anim if the desired is not present - if (iSequence > ACTIVITY_NOT_AVAILABLE) - { - if (pev->sequence != iSequence || !m_fSequenceLoops) - { - pev->frame = 0; - } - - pev->sequence = iSequence; // Set to the reset anim (if it's there) - ResetSequenceInfo(); - SetYawSpeed(); - } - else - { - // Not available try to get default anim - ALERT(at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity); - pev->sequence = 0; // Set to the reset anim (if it's there) - } -} - - -//========================================================= -// Get Schedule! -//========================================================= -Schedule_t *CMStrooper::GetSchedule(void) -{ - - // clear old sentence - m_iSentence = STROOPER_SENT_NONE; - - // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. - if (pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE) - { - if (pev->flags & FL_ONGROUND) - { - // just landed - pev->movetype = MOVETYPE_STEP; - return GetScheduleOfType(SCHED_STROOPER_REPEL_LAND); - } - else - { - // repel down a rope, - if (m_MonsterState == MONSTERSTATE_COMBAT) - return GetScheduleOfType(SCHED_STROOPER_REPEL_ATTACK); - else - return GetScheduleOfType(SCHED_STROOPER_REPEL); - } - } - - switch (m_MonsterState) - { - case MONSTERSTATE_COMBAT: - { - // dead enemy - if (HasConditions(bits_COND_ENEMY_DEAD)) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster::GetSchedule(); - } - - // new enemy - if (HasConditions(bits_COND_NEW_ENEMY)) - { - //!!!KELLY - the leader of a squad of grunts has just seen the player or a - // monster and has made it the squad's enemy. You - // can check pev->flags for FL_CLIENT to determine whether this is the player - // or a monster. He's going to immediately start - // firing, though. If you'd like, we can make an alternate "first sight" - // schedule where the leader plays a handsign anim - // that gives us enough time to hear a short sentence or spoken command - // before he starts pluggin away. - if (FOkToSpeak())// && RANDOM_LONG(0,1)) - { - if ((m_hEnemy != 0) && UTIL_IsPlayer( m_hEnemy )) - // player - SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); -/* - else if ((m_hEnemy != 0) && - (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && - (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && - (m_hEnemy->Classify() != CLASS_MACHINE)) - // monster - SENTENCEG_PlayRndSz(ENT(pev), "ST_MONST", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); -*/ - JustSpoke(); - } - - if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) - { - return GetScheduleOfType(SCHED_STROOPER_SUPPRESS); - } - else - { - return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE); - } - } - // no ammo - else if (HasConditions(bits_COND_NO_AMMO_LOADED)) - { - //!!!KELLY - this individual just realized he's out of bullet ammo. - // He's going to try to find cover to run to and reload, but rarely, if - // none is available, he'll drop and reload in the open here. - return GetScheduleOfType(SCHED_STROOPER_COVER_AND_RELOAD); - } - - // damaged just a little - else if (HasConditions(bits_COND_LIGHT_DAMAGE)) - { - // if hurt: - // 90% chance of taking cover - // 10% chance of flinch. - int iPercent = RANDOM_LONG(0, 99); - - if (iPercent <= 90 && m_hEnemy != 0) - { - // only try to take cover if we actually have an enemy! - - //!!!KELLY - this grunt was hit and is going to run to cover. - if (FOkToSpeak()) // && RANDOM_LONG(0,1)) - { - //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); - m_iSentence = STROOPER_SENT_COVER; - //JustSpoke(); - } - return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY); - } - else - { - return GetScheduleOfType(SCHED_SMALL_FLINCH); - } - } - // can kick - else if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) - { - return GetScheduleOfType(SCHED_MELEE_ATTACK1); - } - - // can shoot - if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) - { - if (HasConditions(bits_COND_CAN_RANGE_ATTACK2)) - { - // throw a grenade if can and no engage slots are available - return GetScheduleOfType(SCHED_RANGE_ATTACK2); - } - else - { - // hide! - return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY); - } - } - // can't see enemy - else if (HasConditions(bits_COND_ENEMY_OCCLUDED)) - { - if (HasConditions(bits_COND_CAN_RANGE_ATTACK2)) - { - //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc - if (FOkToSpeak()) - { - SENTENCEG_PlayRndSz(ENT(pev), "ST_THROW", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType(SCHED_RANGE_ATTACK2); - } - else - { - //!!!KELLY - grunt is going to stay put for a couple seconds to see if - // the enemy wanders back out into the open, or approaches the - // grunt's covered position. Good place for a taunt, I guess? - if (FOkToSpeak() && RANDOM_LONG(0, 1)) - { - SENTENCEG_PlayRndSz(ENT(pev), "ST_TAUNT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); - JustSpoke(); - } - return GetScheduleOfType(SCHED_STANDOFF); - } - } - - if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1)) - { - return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE); - } - } - } - - // no special cases here, call the base class - return CMBaseMonster::GetSchedule(); -} - - -//========================================================= -//========================================================= -Schedule_t* CMStrooper::GetScheduleOfType(int Type) -{ - switch (Type) - { - case SCHED_TAKE_COVER_FROM_ENEMY: - { - if (RANDOM_LONG(0, 1)) - { - return &slGruntTakeCover[0]; - } - else - { - return &slGruntGrenadeCover[0]; - } - } - break; - - default: - { - return CMHGrunt::GetScheduleOfType(Type); - } - break; - } -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// shocktrooper +//========================================================= + +#include "extdll.h" +#include "plane.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "animation.h" +#include "weapons.h" +#include "cmtalkmonster.h" +#include "effects.h" +#include "customentity.h" +#include "shock.h" + +int g_fStrooperQuestion; // true if an idle grunt asked a question. Cleared when someone answers. + +extern Schedule_t slGruntTakeCover[]; +extern Schedule_t slGruntGrenadeCover[]; + +//========================================================= +// monster-specific DEFINE's +//========================================================= +#define STROOPER_CLIP_SIZE 10 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x! +#define STROOPER_VOL 0.35 // volume of grunt sounds +#define STROOPER_ATTN ATTN_NORM // attenutation of grunt sentences +#define STROOPER_LIMP_HEALTH 20 +#define STROOPER_DMG_HEADSHOT ( DMG_BULLET | DMG_CLUB ) // damage types that can kill a grunt with a single headshot. +#define STROOPER_NUM_HEADS 2 // how many grunt heads are there? +#define STROOPER_MINIMUM_HEADSHOT_DAMAGE 15 // must do at least this much damage in one shot to head to score a headshot kill +#define STROOPER_SENTENCE_VOLUME (float)0.35 // volume of grunt sentences +#define STROOPER_MUZZLEFLASH "sprites/muzzle_shock.spr" + +#define STROOPER_SHOCKRIFLE (1 << 0) +#define STROOPER_HANDGRENADE (1 << 1) + +#define GUN_GROUP 1 +#define GUN_SHOCKRIFLE 0 +#define GUN_NONE 1 + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define STROOPER_AE_RELOAD ( 2 ) +#define STROOPER_AE_KICK ( 3 ) +#define STROOPER_AE_BURST1 ( 4 ) +#define STROOPER_AE_BURST2 ( 5 ) +#define STROOPER_AE_BURST3 ( 6 ) +#define STROOPER_AE_GREN_TOSS ( 7 ) +#define STROOPER_AE_GREN_LAUNCH ( 8 ) +#define STROOPER_AE_GREN_DROP ( 9 ) +#define STROOPER_AE_CAUGHT_ENEMY ( 10 ) // shocktrooper established sight with an enemy (player only) that had previously eluded the squad. +#define STROOPER_AE_DROP_GUN ( 11 ) // shocktrooper (probably dead) is dropping his shockrifle. + + +//========================================================= +// monster-specific schedule types +//========================================================= +enum +{ + SCHED_STROOPER_SUPPRESS = LAST_COMMON_SCHEDULE + 1, + SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE,// move to a location to set up an attack against the enemy. (usually when a friendly is in the way). + SCHED_STROOPER_COVER_AND_RELOAD, + SCHED_STROOPER_SWEEP, + SCHED_STROOPER_FOUND_ENEMY, + SCHED_STROOPER_REPEL, + SCHED_STROOPER_REPEL_ATTACK, + SCHED_STROOPER_REPEL_LAND, + SCHED_STROOPER_WAIT_FACE_ENEMY, + SCHED_STROOPER_TAKECOVER_FAILED,// special schedule type that forces analysis of conditions and picks the best possible schedule to recover from this type of failure. + SCHED_STROOPER_ELOF_FAIL, +}; + +//========================================================= +// monster-specific tasks +//========================================================= +enum +{ + TASK_STROOPER_FACE_TOSS_DIR = LAST_COMMON_TASK + 1, + TASK_STROOPER_SPEAK_SENTENCE, + TASK_STROOPER_CHECK_FIRE, +}; + +int iStrooperMuzzleFlash; + +const char *CMStrooper::pGruntSentences[] = +{ + "ST_GREN", // grenade scared grunt + "ST_ALERT", // sees player + "ST_MONST", // sees monster + "ST_COVER", // running to cover + "ST_THROW", // about to throw grenade + "ST_CHARGE", // running out to get the enemy + "ST_TAUNT", // say rude things +}; + +typedef enum +{ + STROOPER_SENT_NONE = -1, + STROOPER_SENT_GREN = 0, + STROOPER_SENT_ALERT, + STROOPER_SENT_MONSTER, + STROOPER_SENT_COVER, + STROOPER_SENT_THROW, + STROOPER_SENT_CHARGE, + STROOPER_SENT_TAUNT, +} STROOPER_SENTENCE_TYPES; + +void CMStrooper::SpeakSentence() +{ + if( m_iSentence == STROOPER_SENT_NONE ) + { + // no sentence cued up. + return; + } + + if( FOkToSpeak() ) + { + SENTENCEG_PlayRndSz( ENT( pev ), pGruntSentences[m_iSentence], STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch ); + JustSpoke(); + } +} + +#define STROOPER_GIB_COUNT 8 +//========================================================= +// GibMonster - make gun fly through the air. +//========================================================= +void CMStrooper::GibMonster() +{ + if (GetBodygroup(GUN_GROUP) != GUN_NONE) + { + DropShockRoach(true); + } + + EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); + + if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly + { + CMGib::SpawnRandomGibs( pev, STROOPER_GIB_COUNT, "models/strooper_gibs.mdl", 0 ); // Throw alien gibs + } + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +void CMStrooper::IdleSound() +{ + if (FOkToSpeak() && (g_fStrooperQuestion || RANDOM_LONG(0, 1))) + { + if (!g_fStrooperQuestion) + { + // ask question or make statement + switch (RANDOM_LONG(0, 2)) + { + case 0: // check in + SENTENCEG_PlayRndSz(ENT(pev), "ST_CHECK", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fStrooperQuestion = 1; + break; + case 1: // question + SENTENCEG_PlayRndSz(ENT(pev), "ST_QUEST", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + g_fStrooperQuestion = 2; + break; + case 2: // statement + SENTENCEG_PlayRndSz(ENT(pev), "ST_IDLE", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + } + else + { + switch (g_fStrooperQuestion) + { + case 1: // check in + SENTENCEG_PlayRndSz(ENT(pev), "ST_CLEAR", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + case 2: // question + SENTENCEG_PlayRndSz(ENT(pev), "ST_ANSWER", STROOPER_SENTENCE_VOLUME, ATTN_NORM, 0, m_voicePitch); + break; + } + g_fStrooperQuestion = 0; + } + JustSpoke(); + } +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMStrooper::Classify() +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_RACEX_SHOCK; +} + +BOOL CMStrooper::CheckRangeAttack1(float flDot, float flDist) +{ + return m_cAmmoLoaded >= 1;// && CMHGrunt::CheckRangeAttack1(flDot, flDist); +} + +BOOL CMStrooper::CheckRangeAttack2( float flDot, float flDist ) +{ + if( !FBitSet( pev->weapons, STROOPER_HANDGRENADE ) ) + { + return FALSE; + } + return CMHGrunt::CheckRangeAttack2(flDot, flDist); +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMStrooper::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case STROOPER_AE_DROP_GUN: + { + if (GetBodygroup(GUN_GROUP) != GUN_NONE) + { + DropShockRoach(false); + } + } + break; + + case STROOPER_AE_RELOAD: + m_cAmmoLoaded = m_cClipSize; + ClearConditions(bits_COND_NO_AMMO_LOADED); + break; + + case STROOPER_AE_GREN_TOSS: + { + UTIL_MakeVectors(pev->angles); + // CGrenade::ShootTimed( pev, pev->origin + gpGlobals->v_forward * 34 + Vector (0, 0, 32), m_vecTossVelocity, 3.5 ); + CMSporeGrenade::ShootTimed(pev, pev->origin + Vector(0,0,98), m_vecTossVelocity, 3.5); + + m_fThrowGrenade = FALSE; + m_flNextGrenadeCheck = gpGlobals->time + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + // !!!LATER - when in a group, only try to throw grenade if ordered. + } + break; + + case STROOPER_AE_GREN_LAUNCH: + case STROOPER_AE_GREN_DROP: + break; + + case STROOPER_AE_BURST1: + { + if (m_hEnemy) + { + Vector vecGunPos; + Vector vecGunAngles; + + GetAttachment(0, vecGunPos, vecGunAngles); + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecGunPos ); + WRITE_BYTE( TE_SPRITE ); + WRITE_COORD( vecGunPos.x ); // pos + WRITE_COORD( vecGunPos.y ); + WRITE_COORD( vecGunPos.z ); + WRITE_SHORT( iStrooperMuzzleFlash ); // model + WRITE_BYTE( 4 ); // size * 10 + WRITE_BYTE( 128 ); // brightness + MESSAGE_END(); + + UTIL_MakeVectors(pev->angles); + Vector vecShootOrigin = vecGunPos + gpGlobals->v_forward * 32; + Vector vecShootDir = ShootAtEnemy( vecShootOrigin ); + vecGunAngles = UTIL_VecToAngles(vecShootDir); + + //CBaseEntity *pShock = CBaseEntity::Create("shock_beam", vecShootOrigin, vecGunAngles, edict()); + CMShock *pShock = CreateClassPtr((CMShock *)NULL); + + if (pShock != NULL) + { + pShock->pev->origin = vecShootOrigin; + + vecGunAngles.z += RANDOM_FLOAT( -0.05, 0 ); + pShock->pev->angles = UTIL_VecToAngles( vecGunAngles ); + pShock->pev->owner = edict(); + + // Initialize these for entities who don't link to the world + pShock->pev->absmin = pShock->pev->origin - Vector(1,1,1); + pShock->pev->absmax = pShock->pev->origin + Vector(1,1,1); + + pShock->Spawn(); + + pShock->pev->velocity = vecShootDir * 2000; + pShock->pev->nextthink = gpGlobals->time; + } + + m_cAmmoLoaded--; + SetBlending( 0, vecGunAngles.x ); + + // Play fire sound. + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/shock_fire.wav", 1, ATTN_NORM); + } + } + break; + + case STROOPER_AE_KICK: + { + EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "zombie/claw_miss2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM + RANDOM_LONG( -5, 5 ) ); + edict_t *pHurt = Kick(); + + if (pHurt) + { + // SOUND HERE! + UTIL_MakeVectors(pev->angles); + pHurt->v.punchangle.x = 15; + pHurt->v.punchangle.z = (m_fRightClaw) ? -10 : 10; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100 + gpGlobals->v_up * 50; + + if ( UTIL_IsPlayer( pHurt ) ) + UTIL_TakeDamage( pHurt, pev, pev, gSkillData.strooperDmgKick, DMG_CLUB ); + else if ( pHurt->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pHurt)); + pMonster->TakeDamage( pev, pev, gSkillData.strooperDmgKick, DMG_CLUB ); + } + } + + m_fRightClaw = !m_fRightClaw; + } + break; + + case STROOPER_AE_CAUGHT_ENEMY: + { + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); + JustSpoke(); + } + + } + + default: + CMHGrunt::HandleAnimEvent(pEvent); + break; + } +} + + +//========================================================= +// Spawn +//========================================================= +void CMStrooper::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/strooper.mdl"); + UTIL_SetSize( pev, Vector(-24, -24, 0), Vector(24, 24, 72) ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.strooperHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_flNextGrenadeCheck = gpGlobals->time + 1; + m_flNextPainTime = gpGlobals->time; + m_iSentence = STROOPER_SENT_NONE; + + m_afCapability = bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP; + + //m_fEnemyEluded = FALSE; + m_fFirstEncounter = TRUE;// this is true when the grunt spawns, because he hasn't encountered an enemy yet. + + m_HackedGunPos = Vector(0, 0, 55); + + if (pev->weapons == 0) + { + // initialize to original values + pev->weapons = STROOPER_SHOCKRIFLE | STROOPER_HANDGRENADE; + } + + m_cClipSize = gSkillData.strooperMaxCharge; + + m_cAmmoLoaded = m_cClipSize; + + m_fRightClaw = FALSE; + + CMTalkMonster::g_talkWaitTime = 0; + m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed; + m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f); + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_shocktrooper" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Shock Trooper" ); + } +} + +void CMStrooper::MonsterThink() +{ + if (m_cAmmoLoaded < m_cClipSize) + { + if (m_rechargeTime < gpGlobals->time) + { + m_cAmmoLoaded++; + m_rechargeTime = gpGlobals->time + gSkillData.strooperRchgSpeed; + } + } + if (m_blinkTime <= gpGlobals->time && pev->skin == 0) { + pev->skin = 1; + m_blinkTime = gpGlobals->time + RANDOM_FLOAT(3.0f, 7.0f); + m_eyeChangeTime = gpGlobals->time + 0.1; + } + if (pev->skin != 0) { + if (m_eyeChangeTime <= gpGlobals->time) { + m_eyeChangeTime = gpGlobals->time + 0.1; + pev->skin++; + if (pev->skin > 3) { + pev->skin = 0; + } + } + } + CMHGrunt::MonsterThink(); +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMStrooper::Precache() +{ + PRECACHE_MODEL("models/strooper.mdl"); + PRECACHE_MODEL("models/strooper_gibs.mdl"); + iStrooperMuzzleFlash = PRECACHE_MODEL(STROOPER_MUZZLEFLASH); + PRECACHE_SOUND("shocktrooper/shock_trooper_attack.wav"); + + PRECACHE_SOUND("shocktrooper/shock_trooper_die1.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_die2.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_die3.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_die4.wav"); + + PRECACHE_SOUND("shocktrooper/shock_trooper_pain1.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_pain2.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_pain3.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_pain4.wav"); + PRECACHE_SOUND("shocktrooper/shock_trooper_pain5.wav"); + + PRECACHE_SOUND("weapons/shock_fire.wav"); + PRECACHE_SOUND("weapons/shock_impact.wav"); + + PRECACHE_SOUND("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event + + // shock_beam + CMShock shock; + shock.Precache(); + + // spore + CMSporeGrenade spore; + spore.Precache(); + + // shockroach + CMShockRoach shockroach; + shockroach.Precache(); + + // get voice pitch + if (RANDOM_LONG(0, 1)) + m_voicePitch = 109 + RANDOM_LONG(0, 7); + else + m_voicePitch = 100; + + m_iBrassShell = PRECACHE_MODEL("models/shell.mdl");// brass shell +} + + +//========================================================= +// PainSound +//========================================================= +void CMStrooper::PainSound() +{ + if (gpGlobals->time > m_flNextPainTime) + { +#if 0 + if (RANDOM_LONG(0, 99) < 5) + { + // pain sentences are rare + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz(ENT(pev), "HG_PAIN", HGRUNT_SENTENCE_VOLUME, ATTN_NORM, 0, PITCH_NORM); + JustSpoke(); + return; + } + } +#endif + switch (RANDOM_LONG(0, 4)) + { + case 0: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain1.wav", 1, ATTN_NORM); + break; + case 1: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain2.wav", 1, ATTN_NORM); + break; + case 2: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain3.wav", 1, ATTN_NORM); + break; + case 3: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain4.wav", 1, ATTN_NORM); + break; + case 4: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_pain5.wav", 1, ATTN_NORM); + break; + } + + m_flNextPainTime = gpGlobals->time + 1; + } +} + +//========================================================= +// DeathSound +//========================================================= +void CMStrooper::DeathSound() +{ + switch (RANDOM_LONG(0, 3)) + { + case 0: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die1.wav", 1, ATTN_IDLE); + break; + case 1: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die2.wav", 1, ATTN_IDLE); + break; + case 2: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die3.wav", 1, ATTN_IDLE); + break; + case 3: + EMIT_SOUND(ENT(pev), CHAN_VOICE, "shocktrooper/shock_trooper_die4.wav", 1, ATTN_IDLE); + break; + } +} + +//========================================================= +// TraceAttack - reimplemented in shock trooper because they never have helmets +//========================================================= +void CMStrooper::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + CMBaseMonster::TraceAttack(pevAttacker, flDamage, vecDir, ptr, bitsDamageType); +} + +void CMStrooper::DropShockRoach(bool gibbed) +{ + Vector vecGunPos; + Vector vecGunAngles; + + GetAttachment(0, vecGunPos, vecGunAngles); + SetBodygroup(GUN_GROUP, GUN_NONE); + + Vector vecDropAngles; + + // Remove any pitch. + vecDropAngles.x = 0; + vecDropAngles.y = vecGunAngles.y; + vecDropAngles.z = 0; + + Vector vecPos = pev->origin; + if (gibbed) + vecPos.z += 32; + else + vecPos.z += 48; + + // now spawn a shockroach. + //CBaseEntity* roach = CBaseEntity::Create( "monster_shockroach", vecPos, vecDropAngles ); + CMShockRoach *roach = CreateClassPtr((CMShockRoach *)NULL); + if (roach != NULL) + { + roach->pev->origin = vecPos; + roach->pev->angles = UTIL_VecToAngles( vecDropAngles ); + + // Initialize these for entities who don't link to the world + roach->pev->absmin = roach->pev->origin - Vector(1,1,1); + roach->pev->absmax = roach->pev->origin + Vector(1,1,1); + + roach->Spawn(); + + if (ShouldFadeOnDeath()) + roach->pev->spawnflags |= SF_MONSTER_FADECORPSE; + if (gibbed) + { + roach->pev->velocity = Vector(RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(-100.0f, 100.0f), RANDOM_FLOAT(200.0f, 300.0f)); + roach->pev->avelocity = Vector(0, RANDOM_FLOAT(200.0f, 300.0f), 0); + } + else + { + roach->pev->velocity = Vector(RANDOM_FLOAT(-20.0f, 20.0f) , RANDOM_FLOAT(-20.0f, 20.0f), RANDOM_FLOAT(20.0f, 30.0f)); + roach->pev->avelocity = Vector(0, RANDOM_FLOAT(20.0f, 40.0f), 0); + } + } +} + + +//========================================================= +// SetActivity +//========================================================= +void CMStrooper::SetActivity(Activity NewActivity) +{ + int iSequence = ACTIVITY_NOT_AVAILABLE; + void *pmodel = GET_MODEL_PTR(ENT(pev)); + + switch (NewActivity) + { + case ACT_RANGE_ATTACK1: + // shocktrooper is either shooting standing or shooting crouched + if (m_fStanding) + { + // get aimable sequence + iSequence = LookupSequence("standing_mp5"); + } + else + { + // get crouching shoot + iSequence = LookupSequence("crouching_mp5"); + } + break; + case ACT_RANGE_ATTACK2: + // shocktrooper is going to throw a grenade. + + // get toss anim + iSequence = LookupSequence("throwgrenade"); + break; + + case ACT_RUN: + if (pev->health <= STROOPER_LIMP_HEALTH) + { + // limp! + iSequence = LookupActivity(ACT_RUN_HURT); + } + else + { + iSequence = LookupActivity(NewActivity); + } + break; + case ACT_WALK: + if (pev->health <= STROOPER_LIMP_HEALTH) + { + // limp! + iSequence = LookupActivity(ACT_WALK_HURT); + } + else + { + iSequence = LookupActivity(NewActivity); + } + break; + case ACT_IDLE: + if (m_MonsterState == MONSTERSTATE_COMBAT) + { + NewActivity = ACT_IDLE_ANGRY; + } + iSequence = LookupActivity(NewActivity); + break; + default: + iSequence = LookupActivity(NewActivity); + break; + } + + m_Activity = NewActivity; // Go ahead and set this so it doesn't keep trying when the anim is not present + + // Set to the desired anim, or default anim if the desired is not present + if (iSequence > ACTIVITY_NOT_AVAILABLE) + { + if (pev->sequence != iSequence || !m_fSequenceLoops) + { + pev->frame = 0; + } + + pev->sequence = iSequence; // Set to the reset anim (if it's there) + ResetSequenceInfo(); + SetYawSpeed(); + } + else + { + // Not available try to get default anim + ALERT(at_console, "%s has no sequence for act:%d\n", STRING(pev->classname), NewActivity); + pev->sequence = 0; // Set to the reset anim (if it's there) + } +} + + +//========================================================= +// Get Schedule! +//========================================================= +Schedule_t *CMStrooper::GetSchedule(void) +{ + + // clear old sentence + m_iSentence = STROOPER_SENT_NONE; + + // flying? If PRONE, barnacle has me. IF not, it's assumed I am rapelling. + if (pev->movetype == MOVETYPE_FLY && m_MonsterState != MONSTERSTATE_PRONE) + { + if (pev->flags & FL_ONGROUND) + { + // just landed + pev->movetype = MOVETYPE_STEP; + return GetScheduleOfType(SCHED_STROOPER_REPEL_LAND); + } + else + { + // repel down a rope, + if (m_MonsterState == MONSTERSTATE_COMBAT) + return GetScheduleOfType(SCHED_STROOPER_REPEL_ATTACK); + else + return GetScheduleOfType(SCHED_STROOPER_REPEL); + } + } + + switch (m_MonsterState) + { + case MONSTERSTATE_COMBAT: + { + // dead enemy + if (HasConditions(bits_COND_ENEMY_DEAD)) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster::GetSchedule(); + } + + // new enemy + if (HasConditions(bits_COND_NEW_ENEMY)) + { + //!!!KELLY - the leader of a squad of grunts has just seen the player or a + // monster and has made it the squad's enemy. You + // can check pev->flags for FL_CLIENT to determine whether this is the player + // or a monster. He's going to immediately start + // firing, though. If you'd like, we can make an alternate "first sight" + // schedule where the leader plays a handsign anim + // that gives us enough time to hear a short sentence or spoken command + // before he starts pluggin away. + if (FOkToSpeak())// && RANDOM_LONG(0,1)) + { + if ((m_hEnemy != 0) && UTIL_IsPlayer( m_hEnemy )) + // player + SENTENCEG_PlayRndSz(ENT(pev), "ST_ALERT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); +/* + else if ((m_hEnemy != 0) && + (m_hEnemy->Classify() != CLASS_PLAYER_ALLY) && + (m_hEnemy->Classify() != CLASS_HUMAN_PASSIVE) && + (m_hEnemy->Classify() != CLASS_MACHINE)) + // monster + SENTENCEG_PlayRndSz(ENT(pev), "ST_MONST", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); +*/ + JustSpoke(); + } + + if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) + { + return GetScheduleOfType(SCHED_STROOPER_SUPPRESS); + } + else + { + return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE); + } + } + // no ammo + else if (HasConditions(bits_COND_NO_AMMO_LOADED)) + { + //!!!KELLY - this individual just realized he's out of bullet ammo. + // He's going to try to find cover to run to and reload, but rarely, if + // none is available, he'll drop and reload in the open here. + return GetScheduleOfType(SCHED_STROOPER_COVER_AND_RELOAD); + } + + // damaged just a little + else if (HasConditions(bits_COND_LIGHT_DAMAGE)) + { + // if hurt: + // 90% chance of taking cover + // 10% chance of flinch. + int iPercent = RANDOM_LONG(0, 99); + + if (iPercent <= 90 && m_hEnemy != 0) + { + // only try to take cover if we actually have an enemy! + + //!!!KELLY - this grunt was hit and is going to run to cover. + if (FOkToSpeak()) // && RANDOM_LONG(0,1)) + { + //SENTENCEG_PlayRndSz( ENT(pev), "HG_COVER", HGRUNT_SENTENCE_VOLUME, GRUNT_ATTN, 0, m_voicePitch); + m_iSentence = STROOPER_SENT_COVER; + //JustSpoke(); + } + return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY); + } + else + { + return GetScheduleOfType(SCHED_SMALL_FLINCH); + } + } + // can kick + else if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) + { + return GetScheduleOfType(SCHED_MELEE_ATTACK1); + } + + // can shoot + if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) + { + if (HasConditions(bits_COND_CAN_RANGE_ATTACK2)) + { + // throw a grenade if can and no engage slots are available + return GetScheduleOfType(SCHED_RANGE_ATTACK2); + } + else + { + // hide! + return GetScheduleOfType(SCHED_TAKE_COVER_FROM_ENEMY); + } + } + // can't see enemy + else if (HasConditions(bits_COND_ENEMY_OCCLUDED)) + { + if (HasConditions(bits_COND_CAN_RANGE_ATTACK2)) + { + //!!!KELLY - this grunt is about to throw or fire a grenade at the player. Great place for "fire in the hole" "frag out" etc + if (FOkToSpeak()) + { + SENTENCEG_PlayRndSz(ENT(pev), "ST_THROW", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType(SCHED_RANGE_ATTACK2); + } + else + { + //!!!KELLY - grunt is going to stay put for a couple seconds to see if + // the enemy wanders back out into the open, or approaches the + // grunt's covered position. Good place for a taunt, I guess? + if (FOkToSpeak() && RANDOM_LONG(0, 1)) + { + SENTENCEG_PlayRndSz(ENT(pev), "ST_TAUNT", STROOPER_SENTENCE_VOLUME, STROOPER_ATTN, 0, m_voicePitch); + JustSpoke(); + } + return GetScheduleOfType(SCHED_STANDOFF); + } + } + + if (HasConditions(bits_COND_SEE_ENEMY) && !HasConditions(bits_COND_CAN_RANGE_ATTACK1)) + { + return GetScheduleOfType(SCHED_STROOPER_ESTABLISH_LINE_OF_FIRE); + } + } + } + + // no special cases here, call the base class + return CMBaseMonster::GetSchedule(); +} + + +//========================================================= +//========================================================= +Schedule_t* CMStrooper::GetScheduleOfType(int Type) +{ + switch (Type) + { + case SCHED_TAKE_COVER_FROM_ENEMY: + { + if (RANDOM_LONG(0, 1)) + { + return &slGruntTakeCover[0]; + } + else + { + return &slGruntGrenadeCover[0]; + } + } + break; + + default: + { + return CMHGrunt::GetScheduleOfType(Type); + } + break; + } +} diff --git a/src/dlls/subs.cpp b/src/dlls/subs.cpp index 598048c..f9fac79 100644 --- a/src/dlls/subs.cpp +++ b/src/dlls/subs.cpp @@ -1,470 +1,470 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== subs.cpp ======================================================== - - frequently used global functions - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "nodes.h" -#include "doors.h" - -extern CGraph WorldGraph; - -extern BOOL FEntIsVisible(entvars_t* pev, entvars_t* pevTarget); - -void Remove_Entity(edict_t *pEdict); - - -// Landmark class -void CMPointEntity :: Spawn( void ) -{ - pev->solid = SOLID_NOT; -// UTIL_SetSize(pev, g_vecZero, g_vecZero); -} - - -// This updates global tables that need to know about entities being removed -void CMBaseEntity::UpdateOnRemove( void ) -{ - int i; - - if ( FBitSet( pev->flags, FL_GRAPHED ) ) - { - // this entity was a LinkEnt in the world node graph, so we must remove it from - // the graph since we are removing it from the world. - for ( i = 0 ; i < WorldGraph.m_cLinks ; i++ ) - { - if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev ) - { - // if this link has a link ent which is the same ent that is removing itself, remove it! - WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL; - } - } - } -//jlb if ( pev->globalname ) -//jlb gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD ); -} - -// Convenient way to delay removing oneself -void CMBaseEntity :: SUB_Remove( void ) -{ - UpdateOnRemove(); - if (pev->health > 0) - { - // this situation can screw up monsters who can't tell their entity pointers are invalid. - pev->health = 0; - ALERT( at_aiconsole, "SUB_Remove called on entity with health > 0\n"); - } - -// REMOVE_ENTITY(ENT(pev)); - Remove_Entity(ENT(pev)); -} - - -// Convenient way to explicitly do nothing (passed to functions that require a method) -void CMBaseEntity :: SUB_DoNothing( void ) -{ -} - - -void CMBaseDelay :: KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "delay")) - { - m_flDelay = atof( pkvd->szValue ); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "killtarget")) - { - m_iszKillTarget = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - { - CMBaseEntity::KeyValue( pkvd ); - } -} - - -/* -============================== -SUB_UseTargets - -If self.delay is set, a DelayedUse entity will be created that will actually -do the SUB_UseTargets after that many seconds have passed. - -Removes all entities with a targetname that match self.killtarget, -and removes them, so some events can remove other triggers. - -Search for (string)targetname in all entities that -match (string)self.target and call their .use function (if they have one) - -============================== -*/ -void CMBaseEntity :: SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ) -{ - // - // fire targets - // - if (!FStringNull(pev->target)) - { - FireTargets( STRING(pev->target), pActivator, this->edict(), useType, value ); - } -} - - -void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - edict_t *pentTarget = NULL; - if ( !targetName ) - return; - - ALERT( at_aiconsole, "Firing: (%s)\n", targetName ); - - for (;;) - { - pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, targetName); - if (FNullEnt(pentTarget)) - break; - - CMBaseEntity *pTarget = CMBaseEntity::Instance( pentTarget ); - if ( pTarget && !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents - { - ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName ); - pTarget->Use( pActivator, pCaller, useType, value ); - } - } -} - -void CMBaseDelay :: SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ) -{ - // - // exit immediatly if we don't have a target or kill target - // - if (FStringNull(pev->target) && !m_iszKillTarget) - return; - - // - // check for a delay - // - if (m_flDelay != 0) - { - // create a temp object to fire at a later time - CMBaseDelay *pTemp = CreateClassPtr( (CMBaseDelay *)NULL); - - if (pTemp == NULL) - return; - - pTemp->pev->classname = MAKE_STRING("DelayedUse"); - - pTemp->pev->nextthink = gpGlobals->time + m_flDelay; - - pTemp->SetThink( &CMBaseDelay::DelayThink ); - - // Save the useType - pTemp->pev->button = (int)useType; - pTemp->m_iszKillTarget = m_iszKillTarget; - pTemp->m_flDelay = 0; // prevent "recursion" - pTemp->pev->target = pev->target; - - // HACKHACK - // This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class - // but changing member variable hierarchy would break save/restore without some ugly code. - // This code is not as ugly as that code - if ( pActivator && UTIL_IsPlayer(pActivator) ) // If a player activates, then save it - { - pTemp->pev->owner = pActivator; - } - else - { - pTemp->pev->owner = NULL; - } - - return; - } - - // - // kill the killtargets - // - - if ( m_iszKillTarget ) - { - edict_t *pentKillTarget = NULL; - - ALERT( at_aiconsole, "KillTarget: %s\n", STRING(m_iszKillTarget) ); - pentKillTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_iszKillTarget) ); - while ( !FNullEnt(pentKillTarget) ) - { - UTIL_Remove( CMBaseEntity::Instance(pentKillTarget)->edict() ); - - ALERT( at_aiconsole, "killing %s\n", STRING( pentKillTarget->v.classname ) ); - pentKillTarget = FIND_ENTITY_BY_TARGETNAME( pentKillTarget, STRING(m_iszKillTarget) ); - } - } - - // - // fire targets - // - if (!FStringNull(pev->target)) - { - FireTargets( STRING(pev->target), pActivator, this->edict(), useType, value ); - } -} - - -/* -QuakeEd only writes a single float for angles (bad idea), so up and down are -just constant angles. -*/ -void SetMovedir( entvars_t *pev ) -{ - if (pev->angles == Vector(0, -1, 0)) - { - pev->movedir = Vector(0, 0, 1); - } - else if (pev->angles == Vector(0, -2, 0)) - { - pev->movedir = Vector(0, 0, -1); - } - else - { - UTIL_MakeVectors(pev->angles); - pev->movedir = gpGlobals->v_forward; - } - - pev->angles = g_vecZero; -} - - - - -void CMBaseDelay::DelayThink( void ) -{ - edict_t *pActivator = NULL; - - if ( pev->owner != NULL ) // A player activated this on delay - { - pActivator = ENT(pev->owner); - } - // The use type is cached (and stashed) in pev->button - SUB_UseTargets( pActivator, (USE_TYPE)pev->button, 0 ); -// REMOVE_ENTITY(ENT(pev)); - Remove_Entity(ENT(pev)); -} - - -void CMBaseToggle::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "lip")) - { - m_flLip = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "wait")) - { - m_flWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "master")) - { - m_sMaster = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "distance")) - { - m_flMoveDistance = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBaseDelay::KeyValue( pkvd ); -} - -/* -============= -LinearMove - -calculate pev->velocity and pev->nextthink to reach vecDest from -pev->origin traveling at flSpeed -=============== -*/ -void CMBaseToggle :: LinearMove( Vector vecDest, float flSpeed ) -{ - ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!"); -// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined"); - - m_vecFinalDest = vecDest; - - // Already there? - if (vecDest == pev->origin) - { - LinearMoveDone(); - return; - } - - // set destdelta to the vector needed to move - Vector vecDestDelta = vecDest - pev->origin; - - // divide vector length by speed to get time to reach dest - float flTravelTime = vecDestDelta.Length() / flSpeed; - - // set nextthink to trigger a call to LinearMoveDone when dest is reached - pev->nextthink = pev->ltime + flTravelTime; - SetThink( &CMBaseToggle::LinearMoveDone ); - - // scale the destdelta vector by the time spent traveling to get velocity - pev->velocity = vecDestDelta / flTravelTime; -} - - -/* -============ -After moving, set origin to exact final destination, call "move done" function -============ -*/ -void CMBaseToggle :: LinearMoveDone( void ) -{ - UTIL_SetOrigin(pev, m_vecFinalDest); - pev->velocity = g_vecZero; - pev->nextthink = -1; - if ( m_pfnCallWhenMoveDone ) - (this->*m_pfnCallWhenMoveDone)(); -} - -BOOL CMBaseToggle :: IsLockedByMaster( void ) -{ - return FALSE; -} - -/* -============= -AngularMove - -calculate pev->velocity and pev->nextthink to reach vecDest from -pev->origin traveling at flSpeed -Just like LinearMove, but rotational. -=============== -*/ -void CMBaseToggle :: AngularMove( Vector vecDestAngle, float flSpeed ) -{ - ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!"); -// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined"); - - m_vecFinalAngle = vecDestAngle; - - // Already there? - if (vecDestAngle == pev->angles) - { - AngularMoveDone(); - return; - } - - // set destdelta to the vector needed to move - Vector vecDestDelta = vecDestAngle - pev->angles; - - // divide by speed to get time to reach dest - float flTravelTime = vecDestDelta.Length() / flSpeed; - - // set nextthink to trigger a call to AngularMoveDone when dest is reached - pev->nextthink = pev->ltime + flTravelTime; - SetThink( &CMBaseToggle::AngularMoveDone ); - - // scale the destdelta vector by the time spent traveling to get velocity - pev->avelocity = vecDestDelta / flTravelTime; -} - - -/* -============ -After rotating, set angle to exact final angle, call "move done" function -============ -*/ -void CMBaseToggle :: AngularMoveDone( void ) -{ - pev->angles = m_vecFinalAngle; - pev->avelocity = g_vecZero; - pev->nextthink = -1; - if ( m_pfnCallWhenMoveDone ) - (this->*m_pfnCallWhenMoveDone)(); -} - - -float CMBaseToggle :: AxisValue( int flags, const Vector &angles ) -{ - if ( FBitSet(flags, SF_DOOR_ROTATE_Z) ) - return angles.z; - if ( FBitSet(flags, SF_DOOR_ROTATE_X) ) - return angles.x; - - return angles.y; -} - - -void CMBaseToggle :: AxisDir( entvars_t *pev ) -{ - if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_Z) ) - pev->movedir = Vector ( 0, 0, 1 ); // around z-axis - else if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_X) ) - pev->movedir = Vector ( 1, 0, 0 ); // around x-axis - else - pev->movedir = Vector ( 0, 1, 0 ); // around y-axis -} - - -float CMBaseToggle :: AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ) -{ - if ( FBitSet (flags, SF_DOOR_ROTATE_Z) ) - return angle1.z - angle2.z; - - if ( FBitSet (flags, SF_DOOR_ROTATE_X) ) - return angle1.x - angle2.x; - - return angle1.y - angle2.y; -} - - -/* -============= -FEntIsVisible - -returns TRUE if the passed entity is visible to caller, even if not infront () -============= -*/ - BOOL -FEntIsVisible( - entvars_t* pev, - entvars_t* pevTarget) - { - Vector vecSpot1 = pev->origin + pev->view_ofs; - Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs; - TraceResult tr; - - UTIL_TraceLine(vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr); - - if (tr.fInOpen && tr.fInWater) - return FALSE; // sight line crossed contents - - if (tr.flFraction == 1) - return TRUE; - - return FALSE; - } - - +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== subs.cpp ======================================================== + + frequently used global functions + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "nodes.h" +#include "doors.h" + +extern CGraph WorldGraph; + +extern BOOL FEntIsVisible(entvars_t* pev, entvars_t* pevTarget); + +void Remove_Entity(edict_t *pEdict); + + +// Landmark class +void CMPointEntity :: Spawn( void ) +{ + pev->solid = SOLID_NOT; +// UTIL_SetSize(pev, g_vecZero, g_vecZero); +} + + +// This updates global tables that need to know about entities being removed +void CMBaseEntity::UpdateOnRemove( void ) +{ + int i; + + if ( FBitSet( pev->flags, FL_GRAPHED ) ) + { + // this entity was a LinkEnt in the world node graph, so we must remove it from + // the graph since we are removing it from the world. + for ( i = 0 ; i < WorldGraph.m_cLinks ; i++ ) + { + if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev ) + { + // if this link has a link ent which is the same ent that is removing itself, remove it! + WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL; + } + } + } +//jlb if ( pev->globalname ) +//jlb gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD ); +} + +// Convenient way to delay removing oneself +void CMBaseEntity :: SUB_Remove( void ) +{ + UpdateOnRemove(); + if (pev->health > 0) + { + // this situation can screw up monsters who can't tell their entity pointers are invalid. + pev->health = 0; + ALERT( at_aiconsole, "SUB_Remove called on entity with health > 0\n"); + } + +// REMOVE_ENTITY(ENT(pev)); + Remove_Entity(ENT(pev)); +} + + +// Convenient way to explicitly do nothing (passed to functions that require a method) +void CMBaseEntity :: SUB_DoNothing( void ) +{ +} + + +void CMBaseDelay :: KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "delay")) + { + m_flDelay = atof( pkvd->szValue ); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "killtarget")) + { + m_iszKillTarget = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + { + CMBaseEntity::KeyValue( pkvd ); + } +} + + +/* +============================== +SUB_UseTargets + +If self.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Removes all entities with a targetname that match self.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)self.target and call their .use function (if they have one) + +============================== +*/ +void CMBaseEntity :: SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ) +{ + // + // fire targets + // + if (!FStringNull(pev->target)) + { + FireTargets( STRING(pev->target), pActivator, this->edict(), useType, value ); + } +} + + +void FireTargets( const char *targetName, edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + edict_t *pentTarget = NULL; + if ( !targetName ) + return; + + ALERT( at_aiconsole, "Firing: (%s)\n", targetName ); + + for (;;) + { + pentTarget = FIND_ENTITY_BY_TARGETNAME(pentTarget, targetName); + if (FNullEnt(pentTarget)) + break; + + CMBaseEntity *pTarget = CMBaseEntity::Instance( pentTarget ); + if ( pTarget && !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents + { + ALERT( at_aiconsole, "Found: %s, firing (%s)\n", STRING(pTarget->pev->classname), targetName ); + pTarget->Use( pActivator, pCaller, useType, value ); + } + } +} + +void CMBaseDelay :: SUB_UseTargets( edict_t *pActivator, USE_TYPE useType, float value ) +{ + // + // exit immediatly if we don't have a target or kill target + // + if (FStringNull(pev->target) && !m_iszKillTarget) + return; + + // + // check for a delay + // + if (m_flDelay != 0) + { + // create a temp object to fire at a later time + CMBaseDelay *pTemp = CreateClassPtr( (CMBaseDelay *)NULL); + + if (pTemp == NULL) + return; + + pTemp->pev->classname = MAKE_STRING("DelayedUse"); + + pTemp->pev->nextthink = gpGlobals->time + m_flDelay; + + pTemp->SetThink( &CMBaseDelay::DelayThink ); + + // Save the useType + pTemp->pev->button = (int)useType; + pTemp->m_iszKillTarget = m_iszKillTarget; + pTemp->m_flDelay = 0; // prevent "recursion" + pTemp->pev->target = pev->target; + + // HACKHACK + // This wasn't in the release build of Half-Life. We should have moved m_hActivator into this class + // but changing member variable hierarchy would break save/restore without some ugly code. + // This code is not as ugly as that code + if ( pActivator && UTIL_IsPlayer(pActivator) ) // If a player activates, then save it + { + pTemp->pev->owner = pActivator; + } + else + { + pTemp->pev->owner = NULL; + } + + return; + } + + // + // kill the killtargets + // + + if ( m_iszKillTarget ) + { + edict_t *pentKillTarget = NULL; + + ALERT( at_aiconsole, "KillTarget: %s\n", STRING(m_iszKillTarget) ); + pentKillTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING(m_iszKillTarget) ); + while ( !FNullEnt(pentKillTarget) ) + { + UTIL_Remove( CMBaseEntity::Instance(pentKillTarget)->edict() ); + + ALERT( at_aiconsole, "killing %s\n", STRING( pentKillTarget->v.classname ) ); + pentKillTarget = FIND_ENTITY_BY_TARGETNAME( pentKillTarget, STRING(m_iszKillTarget) ); + } + } + + // + // fire targets + // + if (!FStringNull(pev->target)) + { + FireTargets( STRING(pev->target), pActivator, this->edict(), useType, value ); + } +} + + +/* +QuakeEd only writes a single float for angles (bad idea), so up and down are +just constant angles. +*/ +void SetMovedir( entvars_t *pev ) +{ + if (pev->angles == Vector(0, -1, 0)) + { + pev->movedir = Vector(0, 0, 1); + } + else if (pev->angles == Vector(0, -2, 0)) + { + pev->movedir = Vector(0, 0, -1); + } + else + { + UTIL_MakeVectors(pev->angles); + pev->movedir = gpGlobals->v_forward; + } + + pev->angles = g_vecZero; +} + + + + +void CMBaseDelay::DelayThink( void ) +{ + edict_t *pActivator = NULL; + + if ( pev->owner != NULL ) // A player activated this on delay + { + pActivator = ENT(pev->owner); + } + // The use type is cached (and stashed) in pev->button + SUB_UseTargets( pActivator, (USE_TYPE)pev->button, 0 ); +// REMOVE_ENTITY(ENT(pev)); + Remove_Entity(ENT(pev)); +} + + +void CMBaseToggle::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "lip")) + { + m_flLip = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "wait")) + { + m_flWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "master")) + { + m_sMaster = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "distance")) + { + m_flMoveDistance = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseDelay::KeyValue( pkvd ); +} + +/* +============= +LinearMove + +calculate pev->velocity and pev->nextthink to reach vecDest from +pev->origin traveling at flSpeed +=============== +*/ +void CMBaseToggle :: LinearMove( Vector vecDest, float flSpeed ) +{ + ASSERTSZ(flSpeed != 0, "LinearMove: no speed is defined!"); +// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "LinearMove: no post-move function defined"); + + m_vecFinalDest = vecDest; + + // Already there? + if (vecDest == pev->origin) + { + LinearMoveDone(); + return; + } + + // set destdelta to the vector needed to move + Vector vecDestDelta = vecDest - pev->origin; + + // divide vector length by speed to get time to reach dest + float flTravelTime = vecDestDelta.Length() / flSpeed; + + // set nextthink to trigger a call to LinearMoveDone when dest is reached + pev->nextthink = pev->ltime + flTravelTime; + SetThink( &CMBaseToggle::LinearMoveDone ); + + // scale the destdelta vector by the time spent traveling to get velocity + pev->velocity = vecDestDelta / flTravelTime; +} + + +/* +============ +After moving, set origin to exact final destination, call "move done" function +============ +*/ +void CMBaseToggle :: LinearMoveDone( void ) +{ + UTIL_SetOrigin(pev, m_vecFinalDest); + pev->velocity = g_vecZero; + pev->nextthink = -1; + if ( m_pfnCallWhenMoveDone ) + (this->*m_pfnCallWhenMoveDone)(); +} + +BOOL CMBaseToggle :: IsLockedByMaster( void ) +{ + return FALSE; +} + +/* +============= +AngularMove + +calculate pev->velocity and pev->nextthink to reach vecDest from +pev->origin traveling at flSpeed +Just like LinearMove, but rotational. +=============== +*/ +void CMBaseToggle :: AngularMove( Vector vecDestAngle, float flSpeed ) +{ + ASSERTSZ(flSpeed != 0, "AngularMove: no speed is defined!"); +// ASSERTSZ(m_pfnCallWhenMoveDone != NULL, "AngularMove: no post-move function defined"); + + m_vecFinalAngle = vecDestAngle; + + // Already there? + if (vecDestAngle == pev->angles) + { + AngularMoveDone(); + return; + } + + // set destdelta to the vector needed to move + Vector vecDestDelta = vecDestAngle - pev->angles; + + // divide by speed to get time to reach dest + float flTravelTime = vecDestDelta.Length() / flSpeed; + + // set nextthink to trigger a call to AngularMoveDone when dest is reached + pev->nextthink = pev->ltime + flTravelTime; + SetThink( &CMBaseToggle::AngularMoveDone ); + + // scale the destdelta vector by the time spent traveling to get velocity + pev->avelocity = vecDestDelta / flTravelTime; +} + + +/* +============ +After rotating, set angle to exact final angle, call "move done" function +============ +*/ +void CMBaseToggle :: AngularMoveDone( void ) +{ + pev->angles = m_vecFinalAngle; + pev->avelocity = g_vecZero; + pev->nextthink = -1; + if ( m_pfnCallWhenMoveDone ) + (this->*m_pfnCallWhenMoveDone)(); +} + + +float CMBaseToggle :: AxisValue( int flags, const Vector &angles ) +{ + if ( FBitSet(flags, SF_DOOR_ROTATE_Z) ) + return angles.z; + if ( FBitSet(flags, SF_DOOR_ROTATE_X) ) + return angles.x; + + return angles.y; +} + + +void CMBaseToggle :: AxisDir( entvars_t *pev ) +{ + if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_Z) ) + pev->movedir = Vector ( 0, 0, 1 ); // around z-axis + else if ( FBitSet(pev->spawnflags, SF_DOOR_ROTATE_X) ) + pev->movedir = Vector ( 1, 0, 0 ); // around x-axis + else + pev->movedir = Vector ( 0, 1, 0 ); // around y-axis +} + + +float CMBaseToggle :: AxisDelta( int flags, const Vector &angle1, const Vector &angle2 ) +{ + if ( FBitSet (flags, SF_DOOR_ROTATE_Z) ) + return angle1.z - angle2.z; + + if ( FBitSet (flags, SF_DOOR_ROTATE_X) ) + return angle1.x - angle2.x; + + return angle1.y - angle2.y; +} + + +/* +============= +FEntIsVisible + +returns TRUE if the passed entity is visible to caller, even if not infront () +============= +*/ + BOOL +FEntIsVisible( + entvars_t* pev, + entvars_t* pevTarget) + { + Vector vecSpot1 = pev->origin + pev->view_ofs; + Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs; + TraceResult tr; + + UTIL_TraceLine(vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr); + + if (tr.fInOpen && tr.fInWater) + return FALSE; // sight line crossed contents + + if (tr.flFraction == 1) + return TRUE; + + return FALSE; + } + + diff --git a/src/dlls/talkmonster.cpp b/src/dlls/talkmonster.cpp index d428b1b..bdc89fc 100644 --- a/src/dlls/talkmonster.cpp +++ b/src/dlls/talkmonster.cpp @@ -1,1419 +1,1419 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "cmtalkmonster.h" -#include "defaultai.h" -#include "animation.h" - -//========================================================= -// Talking monster base class -// Used for scientists and barneys -//========================================================= -float CMTalkMonster::g_talkWaitTime = 0; // time delay until it's ok to speak: used so that two NPCs don't talk at once - -// NOTE: m_voicePitch & m_szGrp should be fixed up by precache each save/restore - -// array of friend names -char *CMTalkMonster::m_szFriends[TLK_CFRIENDS] = -{ - "monster_barney", - "monster_scientist", - "monster_sitting_scientist", -}; - - -//========================================================= -// AI Schedules Specific to talking monsters -//========================================================= - -Task_t tlIdleResponse[] = -{ - { TASK_SET_ACTIVITY, (float)ACT_IDLE },// Stop and listen - { TASK_WAIT, (float)0.5 },// Wait until sure it's me they are talking to - { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done - { TASK_TLK_RESPOND, (float)0 },// Wait and then say my response - { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, - { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done -}; - -Schedule_t slIdleResponse[] = -{ - { - tlIdleResponse, - ARRAYSIZE ( tlIdleResponse ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Idle Response" - - }, -}; - -Task_t tlIdleSpeak[] = -{ - { TASK_TLK_SPEAK, (float)0 },// question or remark - { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, - { TASK_TLK_EYECONTACT, (float)0 }, - { TASK_WAIT_RANDOM, (float)0.5 }, -}; - -Schedule_t slIdleSpeak[] = -{ - { - tlIdleSpeak, - ARRAYSIZE ( tlIdleSpeak ), - bits_COND_NEW_ENEMY | - bits_COND_CLIENT_PUSH | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Idle Speak" - }, -}; - -Task_t tlIdleSpeakWait[] = -{ - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 },// Stop and talk - { TASK_TLK_SPEAK, (float)0 },// question or remark - { TASK_TLK_EYECONTACT, (float)0 },// - { TASK_WAIT, (float)2 },// wait - used when sci is in 'use' mode to keep head turned -}; - -Schedule_t slIdleSpeakWait[] = -{ - { - tlIdleSpeakWait, - ARRAYSIZE ( tlIdleSpeakWait ), - bits_COND_NEW_ENEMY | - bits_COND_CLIENT_PUSH | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "Idle Speak Wait" - }, -}; - -Task_t tlIdleHello[] = -{ - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 },// Stop and talk - { TASK_TLK_HELLO, (float)0 },// Try to say hello to player - { TASK_TLK_EYECONTACT, (float)0 }, - { TASK_WAIT, (float)0.5 },// wait a bit - { TASK_TLK_HELLO, (float)0 },// Try to say hello to player - { TASK_TLK_EYECONTACT, (float)0 }, - { TASK_WAIT, (float)0.5 },// wait a bit - { TASK_TLK_HELLO, (float)0 },// Try to say hello to player - { TASK_TLK_EYECONTACT, (float)0 }, - { TASK_WAIT, (float)0.5 },// wait a bit - { TASK_TLK_HELLO, (float)0 },// Try to say hello to player - { TASK_TLK_EYECONTACT, (float)0 }, - { TASK_WAIT, (float)0.5 },// wait a bit - -}; - -Schedule_t slIdleHello[] = -{ - { - tlIdleHello, - ARRAYSIZE ( tlIdleHello ), - bits_COND_NEW_ENEMY | - bits_COND_CLIENT_PUSH | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_PROVOKED, - 0, - "Idle Hello" - }, -}; - -Task_t tlIdleStopShooting[] = -{ - { TASK_TLK_STOPSHOOTING, (float)0 },// tell player to stop shooting friend - // { TASK_TLK_EYECONTACT, (float)0 },// look at the player -}; - -Schedule_t slIdleStopShooting[] = -{ - { - tlIdleStopShooting, - ARRAYSIZE ( tlIdleStopShooting ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND, - 0, - "Idle Stop Shooting" - }, -}; - -Task_t tlMoveAway[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MOVE_AWAY_FAIL }, - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_MOVE_AWAY_PATH, (float)100 }, - { TASK_WALK_PATH_FOR_UNITS, (float)100 }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_PLAYER, (float)0.5 }, -}; - -Schedule_t slMoveAway[] = -{ - { - tlMoveAway, - ARRAYSIZE ( tlMoveAway ), - 0, - 0, - "MoveAway" - }, -}; - - -Task_t tlMoveAwayFail[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_FACE_PLAYER, (float)0.5 }, -}; - -Schedule_t slMoveAwayFail[] = -{ - { - tlMoveAwayFail, - ARRAYSIZE ( tlMoveAwayFail ), - 0, - 0, - "MoveAwayFail" - }, -}; - - - -Task_t tlMoveAwayFollow[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_FACE }, - { TASK_STORE_LASTPOSITION, (float)0 }, - { TASK_MOVE_AWAY_PATH, (float)100 }, - { TASK_WALK_PATH_FOR_UNITS, (float)100 }, - { TASK_STOP_MOVING, (float)0 }, - { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, -}; - -Schedule_t slMoveAwayFollow[] = -{ - { - tlMoveAwayFollow, - ARRAYSIZE ( tlMoveAwayFollow ), - 0, - 0, - "MoveAwayFollow" - }, -}; - -Task_t tlTlkIdleWatchClient[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_TLK_LOOK_AT_CLIENT, (float)6 }, -}; - -Task_t tlTlkIdleWatchClientStare[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, - { TASK_TLK_CLIENT_STARE, (float)6 }, - { TASK_TLK_STARE, (float)0 }, - { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, - { TASK_TLK_EYECONTACT, (float)0 }, -}; - -Schedule_t slTlkIdleWatchClient[] = -{ - { - tlTlkIdleWatchClient, - ARRAYSIZE ( tlTlkIdleWatchClient ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL | - bits_COND_CLIENT_PUSH | - bits_COND_CLIENT_UNSEEN | - bits_COND_PROVOKED, - 0, - "TlkIdleWatchClient" - }, - - { - tlTlkIdleWatchClientStare, - ARRAYSIZE ( tlTlkIdleWatchClientStare ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE | - bits_COND_HEAR_SOUND | - bits_COND_SMELL | - bits_COND_CLIENT_PUSH | - bits_COND_CLIENT_UNSEEN | - bits_COND_PROVOKED, - 0, - "TlkIdleWatchClientStare" - }, -}; - - -Task_t tlTlkIdleEyecontact[] = -{ - { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to - { TASK_FACE_IDEAL, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, - { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done -}; - -Schedule_t slTlkIdleEyecontact[] = -{ - { - tlTlkIdleEyecontact, - ARRAYSIZE ( tlTlkIdleEyecontact ), - bits_COND_NEW_ENEMY | - bits_COND_CLIENT_PUSH | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "TlkIdleEyecontact" - }, -}; - - -DEFINE_CUSTOM_SCHEDULES( CMTalkMonster ) -{ - slIdleResponse, - slIdleSpeak, - slIdleHello, - slIdleSpeakWait, - slIdleStopShooting, - slMoveAway, - slMoveAwayFollow, - slMoveAwayFail, - slTlkIdleWatchClient, - &slTlkIdleWatchClient[ 1 ], - slTlkIdleEyecontact, -}; - -IMPLEMENT_CUSTOM_SCHEDULES( CMTalkMonster, CMBaseMonster ); - - -void CMTalkMonster :: SetActivity ( Activity newActivity ) -{ - if (newActivity == ACT_IDLE && IsTalking() ) - newActivity = ACT_SIGNAL3; - - if ( newActivity == ACT_SIGNAL3 && (LookupActivity ( ACT_SIGNAL3 ) == ACTIVITY_NOT_AVAILABLE)) - newActivity = ACT_IDLE; - - CMBaseMonster::SetActivity( newActivity ); -} - - -void CMTalkMonster :: StartTask( Task_t *pTask ) -{ - switch ( pTask->iTask ) - { - case TASK_TLK_SPEAK: - // ask question or make statement - FIdleSpeak(); - TaskComplete(); - break; - - case TASK_TLK_RESPOND: - // respond to question - IdleRespond(); - TaskComplete(); - break; - - case TASK_TLK_HELLO: - // greet player - FIdleHello(); - TaskComplete(); - break; - - - case TASK_TLK_STARE: - // let the player know I know he's staring at me. - FIdleStare(); - TaskComplete(); - break; - - case TASK_FACE_PLAYER: - case TASK_TLK_LOOK_AT_CLIENT: - case TASK_TLK_CLIENT_STARE: - // track head to the client for a while. - m_flWaitFinished = gpGlobals->time + pTask->flData; - break; - - case TASK_TLK_EYECONTACT: - break; - - case TASK_TLK_IDEALYAW: - if (m_hTalkTarget != NULL) - { - pev->yaw_speed = 60; - float yaw = VecToYaw(m_hTalkTarget->v.origin - pev->origin) - pev->angles.y; - - if (yaw > 180) yaw -= 360; - if (yaw < -180) yaw += 360; - - if (yaw < 0) - { - pev->ideal_yaw = min( yaw + 45.0f, 0.0f ) + pev->angles.y; - } - else - { - pev->ideal_yaw = max( yaw - 45.0f, 0.0f ) + pev->angles.y; - } - } - TaskComplete(); - break; - - case TASK_TLK_HEADRESET: - // reset head position after looking at something - m_hTalkTarget = NULL; - TaskComplete(); - break; - - case TASK_TLK_STOPSHOOTING: - // tell player to stop shooting - PlaySentence( m_szGrp[TLK_NOSHOOT], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_NORM ); - TaskComplete(); - break; - - case TASK_CANT_FOLLOW: - StopFollowing( FALSE ); - PlaySentence( m_szGrp[TLK_STOP], RANDOM_FLOAT(2, 2.5), VOL_NORM, ATTN_NORM ); - TaskComplete(); - break; - - case TASK_WALK_PATH_FOR_UNITS: - m_movementActivity = ACT_WALK; - break; - - case TASK_MOVE_AWAY_PATH: - { - Vector dir = pev->angles; - dir.y = pev->ideal_yaw + 180; - Vector move; - - UTIL_MakeVectorsPrivate( dir, move, NULL, NULL ); - dir = pev->origin + move * pTask->flData; - if ( MoveToLocation( ACT_WALK, 2, dir ) ) - { - TaskComplete(); - } - else if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) - { - // then try for plain ole cover - m_flMoveWaitFinished = gpGlobals->time + 2; - TaskComplete(); - } - else - { - // nowhere to go? - TaskFail(); - } - } - break; - - case TASK_PLAY_SCRIPT: - m_hTalkTarget = NULL; - CMBaseMonster::StartTask( pTask ); - break; - - default: - CMBaseMonster::StartTask( pTask ); - } -} - - -void CMTalkMonster :: RunTask( Task_t *pTask ) -{ - switch( pTask->iTask ) - { - case TASK_TLK_CLIENT_STARE: - case TASK_TLK_LOOK_AT_CLIENT: - - edict_t *pPlayer; - - // track head to the client for a while. - if ( m_MonsterState == MONSTERSTATE_IDLE && - !IsMoving() && - !IsTalking() ) - { - // Get edict of nearest player - pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); - - if ( pPlayer ) - { - IdleHeadTurn( pPlayer->v.origin ); - } - } - else - { - // started moving or talking - TaskFail(); - return; - } - - if ( pTask->iTask == TASK_TLK_CLIENT_STARE ) - { - // fail out if the player looks away or moves away. - if ( ( pPlayer->v.origin - pev->origin ).Length2D() > TLK_STARE_DIST ) - { - // player moved away. - TaskFail(); - } - - UTIL_MakeVectors( pPlayer->v.angles ); - if ( UTIL_DotPoints( pPlayer->v.origin, pev->origin, gpGlobals->v_forward ) < m_flFieldOfView ) - { - // player looked away - TaskFail(); - } - } - - if ( gpGlobals->time > m_flWaitFinished ) - { - TaskComplete(); - } - break; - - case TASK_FACE_PLAYER: - { - // Get edict of nearest player - edict_t *pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); - - if ( pPlayer ) - { - MakeIdealYaw ( pPlayer->v.origin ); - ChangeYaw ( pev->yaw_speed ); - IdleHeadTurn( pPlayer->v.origin ); - if ( gpGlobals->time > m_flWaitFinished && FlYawDiff() < 10 ) - { - TaskComplete(); - } - } - else - { - TaskFail(); - } - } - break; - - case TASK_TLK_EYECONTACT: - if (!IsMoving() && IsTalking() && m_hTalkTarget != NULL) - { - // ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time ); - IdleHeadTurn( m_hTalkTarget->v.origin ); - } - else - { - TaskComplete(); - } - break; - - case TASK_WALK_PATH_FOR_UNITS: - { - float distance; - - distance = (m_vecLastPosition - pev->origin).Length2D(); - - // Walk path until far enough away - if ( distance > pTask->flData || MovementIsComplete() ) - { - TaskComplete(); - RouteClear(); // Stop moving - } - } - break; - case TASK_WAIT_FOR_MOVEMENT: - if (IsTalking() && m_hTalkTarget != NULL) - { - // ALERT(at_console, "walking, talking\n"); - IdleHeadTurn( m_hTalkTarget->v.origin ); - } - else - { - IdleHeadTurn( pev->origin ); - // override so that during walk, a scientist may talk and greet player - FIdleHello(); - if (RANDOM_LONG(0,m_nSpeak * 20) == 0) - { - FIdleSpeak(); - } - } - - CMBaseMonster::RunTask( pTask ); - if (TaskIsComplete()) - IdleHeadTurn( pev->origin ); - break; - - default: - if (IsTalking() && m_hTalkTarget != NULL) - { - IdleHeadTurn( m_hTalkTarget->v.origin ); - } - else - { - SetBoneController( 0, 0 ); - } - CMBaseMonster::RunTask( pTask ); - } -} - - -void CMTalkMonster :: Killed( entvars_t *pevAttacker, int iGib ) -{ - // If a client killed me (unless I was already Barnacle'd), make everyone else mad/afraid of him - if ( (pevAttacker->flags & FL_CLIENT) && m_MonsterState != MONSTERSTATE_PRONE ) - { - AlertFriends(); - LimitFollowers( ENT(pevAttacker), 0 ); - } - - m_hTargetEnt = NULL; - // Don't finish that sentence - StopTalking(); - SetUse( NULL ); - CMBaseMonster::Killed( pevAttacker, iGib ); -} - - - -edict_t *CMTalkMonster::EnumFriends( edict_t *pPrevious, int listNumber, BOOL bTrace ) -{ - edict_t *pFriend = pPrevious; - char *pszFriend; - TraceResult tr; - Vector vecCheck; - - pszFriend = m_szFriends[ FriendNumber(listNumber) ]; - while (pFriend = UTIL_FindEntityByClassname( pFriend, pszFriend )) - { - if (pFriend == this->edict() || !UTIL_IsAlive(pFriend)) - // don't talk to self or dead people - continue; - if ( bTrace ) - { - vecCheck = pFriend->v.origin; - vecCheck.z = pFriend->v.absmax.z; - - UTIL_TraceLine( pev->origin, vecCheck, ignore_monsters, ENT(pev), &tr); - } - else - tr.flFraction = 1.0; - - if (tr.flFraction == 1.0) - { - return pFriend; - } - } - - return NULL; -} - - -void CMTalkMonster::AlertFriends( void ) -{ -/*jlb - edict_t *pFriend = NULL; - int i; - // for each friend in this bsp... - for ( i = 0; i < TLK_CFRIENDS; i++ ) - { - while (pFriend = EnumFriends( pFriend, i, TRUE )) - { - CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); - if ( pMonster->IsAlive() ) - { - // don't provoke a friend that's playing a death animation. They're a goner - pMonster->m_afMemory |= bits_MEMORY_PROVOKED; - } - } - } -jlb*/ -} - - - -void CMTalkMonster::ShutUpFriends( void ) -{ -/*jlb - CBaseEntity *pFriend = NULL; - int i; - - // for each friend in this bsp... - for ( i = 0; i < TLK_CFRIENDS; i++ ) - { - while (pFriend = EnumFriends( pFriend, i, TRUE )) - { - CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); - if ( pMonster ) - { - pMonster->SentenceStop(); - } - } - } -jlb*/ -} - - -// UNDONE: Keep a follow time in each follower, make a list of followers in this function and do LRU -// UNDONE: Check this in Restore to keep restored monsters from joining a full list of followers -void CMTalkMonster::LimitFollowers( edict_t *pPlayer, int maxFollowers ) -{ -/*jlb - CBaseEntity *pFriend = NULL; - int i, count; - - count = 0; - // for each friend in this bsp... - for ( i = 0; i < TLK_CFRIENDS; i++ ) - { - while (pFriend = EnumFriends( pFriend, i, FALSE )) - { - CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); - if ( pMonster ) - { - if ( pMonster->m_hTargetEnt == pPlayer ) - { - count++; - if ( count > maxFollowers ) - pMonster->StopFollowing( TRUE ); - } - } - } - } -jlb*/ -} - - -float CMTalkMonster::TargetDistance( void ) -{ - // If we lose the player, or he dies, return a really large distance - if ( m_hTargetEnt == NULL || !UTIL_IsAlive(m_hEnemy) ) - return 1e6; - - return (m_hTargetEnt->v.origin - pev->origin).Length(); -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMTalkMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - CMBaseMonster::HandleAnimEvent( pEvent ); -} - -// monsters derived from CMTalkMonster should call this in precache() - -void CMTalkMonster :: TalkInit( void ) -{ - // every new talking monster must reset this global, otherwise - // when a level is loaded, nobody will talk (time is reset to 0) - - CMTalkMonster::g_talkWaitTime = 0; - - m_hTalkTarget = NULL; - - m_voicePitch = 100; -} -//========================================================= -// FindNearestFriend -// Scan for nearest, visible friend. If fPlayer is true, look for -// nearest player -//========================================================= -edict_t *CMTalkMonster :: FindNearestFriend(BOOL fPlayer) -{ - edict_t *pFriend = NULL; - edict_t *pNearest = NULL; -/*jlb - float range = 10000000.0; - TraceResult tr; - Vector vecStart = pev->origin; - Vector vecCheck; - int i; - char *pszFriend; - int cfriends; - - vecStart.z = pev->absmax.z; - - if (fPlayer) - cfriends = 1; - else - cfriends = TLK_CFRIENDS; - - // for each type of friend... - - for (i = cfriends-1; i > -1; i--) - { - if (fPlayer) - pszFriend = "player"; - else - pszFriend = m_szFriends[FriendNumber(i)]; - - if (!pszFriend) - continue; - - // for each friend in this bsp... - while (pFriend = UTIL_FindEntityByClassname( pFriend, pszFriend )) - { - if (pFriend == this || !pFriend->IsAlive()) - // don't talk to self or dead people - continue; - - CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); - - // If not a monster for some reason, or in a script, or prone - if ( !pMonster || pMonster->m_MonsterState == MONSTERSTATE_SCRIPT || pMonster->m_MonsterState == MONSTERSTATE_PRONE ) - continue; - - vecCheck = pFriend->pev->origin; - vecCheck.z = pFriend->pev->absmax.z; - - // if closer than previous friend, and in range, see if he's visible - - if (range > (vecStart - vecCheck).Length()) - { - UTIL_TraceLine(vecStart, vecCheck, ignore_monsters, ENT(pev), &tr); - - if (tr.flFraction == 1.0) - { - // visible and in range, this is the new nearest scientist - if ((vecStart - vecCheck).Length() < TALKRANGE_MIN) - { - pNearest = pFriend; - range = (vecStart - vecCheck).Length(); - } - } - } - } - } -jlb*/ - return pNearest; -} - -int CMTalkMonster :: GetVoicePitch( void ) -{ - return m_voicePitch + RANDOM_LONG(0,3); -} - - -void CMTalkMonster :: TalkTouch( edict_t *pOther ) -{ - // Did the player touch me? - if ( UTIL_IsPlayer(pOther) ) - { - // Ignore if pissed at player - if ( m_afMemory & bits_MEMORY_PROVOKED ) - return; - - // Stay put during speech - if ( IsTalking() ) - return; - - // Heuristic for determining if the player is pushing me away - float speed = fabs(pOther->v.velocity.x) + fabs(pOther->v.velocity.y); - if ( speed > 50 ) - { - SetConditions( bits_COND_CLIENT_PUSH ); - MakeIdealYaw( pOther->v.origin ); - } - } -} - - - -//========================================================= -// IdleRespond -// Respond to a previous question -//========================================================= -void CMTalkMonster :: IdleRespond( void ) -{ - int pitch = GetVoicePitch(); - - // play response - PlaySentence( m_szGrp[TLK_ANSWER], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); -} - -int CMTalkMonster :: FOkToSpeak( void ) -{ - // if in the grip of a barnacle, don't speak - if ( m_MonsterState == MONSTERSTATE_PRONE || m_IdealMonsterState == MONSTERSTATE_PRONE ) - { - return FALSE; - } - - // if not alive, certainly don't speak - if ( pev->deadflag != DEAD_NO ) - { - return FALSE; - } - - // if someone else is talking, don't speak - if (gpGlobals->time <= CMTalkMonster::g_talkWaitTime) - return FALSE; - - if ( pev->spawnflags & SF_MONSTER_GAG ) - return FALSE; - - if ( m_MonsterState == MONSTERSTATE_PRONE ) - return FALSE; - - // if player is not in pvs, don't speak - if (!IsAlive() || FNullEnt(FIND_CLIENT_IN_PVS(edict()))) - return FALSE; - - // don't talk if you're in combat - if (m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, this->edict() )) - return FALSE; - - return TRUE; -} - - -int CMTalkMonster::CanPlaySentence( BOOL fDisregardState ) -{ - if ( fDisregardState ) - return CMBaseMonster::CanPlaySentence( fDisregardState ); - return FOkToSpeak(); -} - -//========================================================= -// FIdleStare -//========================================================= -int CMTalkMonster :: FIdleStare( void ) -{ - if (!FOkToSpeak()) - return FALSE; - - PlaySentence( m_szGrp[TLK_STARE], RANDOM_FLOAT(5, 7.5), VOL_NORM, ATTN_IDLE ); - - m_hTalkTarget = FindNearestFriend( TRUE ); - return TRUE; -} - -//========================================================= -// IdleHello -// Try to greet player first time he's seen -//========================================================= -int CMTalkMonster :: FIdleHello( void ) -{ - if (!FOkToSpeak()) - return FALSE; - - // if this is first time scientist has seen player, greet him - if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) - { - // get a player - edict_t *pPlayer = FindNearestFriend(TRUE); - - if (pPlayer) - { - if (UTIL_FInViewCone(pPlayer, this->edict(), m_flFieldOfView) && UTIL_FVisible(pPlayer, this->edict())) - { - m_hTalkTarget = pPlayer; - - if (FBitSet(pev->spawnflags, SF_MONSTER_PREDISASTER)) - PlaySentence( m_szGrp[TLK_PHELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); - else - PlaySentence( m_szGrp[TLK_HELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); - - SetBits(m_bitsSaid, bit_saidHelloPlayer); - - return TRUE; - } - } - } - return FALSE; -} - - -// turn head towards supplied origin -void CMTalkMonster :: IdleHeadTurn( Vector &vecFriend ) -{ - // turn head in desired direction only if ent has a turnable head - if (m_afCapability & bits_CAP_TURN_HEAD) - { - float yaw = VecToYaw(vecFriend - pev->origin) - pev->angles.y; - - if (yaw > 180) yaw -= 360; - if (yaw < -180) yaw += 360; - - // turn towards vector - SetBoneController( 0, yaw ); - } -} - -//========================================================= -// FIdleSpeak -// ask question of nearby friend, or make statement -//========================================================= -int CMTalkMonster :: FIdleSpeak ( void ) -{ - // try to start a conversation, or make statement - int pitch; - const char *szIdleGroup; - const char *szQuestionGroup; - float duration; - - if (!FOkToSpeak()) - return FALSE; - - // set idle groups based on pre/post disaster - if (FBitSet(pev->spawnflags, SF_MONSTER_PREDISASTER)) - { - szIdleGroup = m_szGrp[TLK_PIDLE]; - szQuestionGroup = m_szGrp[TLK_PQUESTION]; - // set global min delay for next conversation - duration = RANDOM_FLOAT(4.8, 5.2); - } - else - { - szIdleGroup = m_szGrp[TLK_IDLE]; - szQuestionGroup = m_szGrp[TLK_QUESTION]; - // set global min delay for next conversation - duration = RANDOM_FLOAT(2.8, 3.2); - - } - - pitch = GetVoicePitch(); - - // player using this entity is alive and wounded? - edict_t *pTarget = m_hTargetEnt; - - if ( pTarget != NULL ) - { - if ( UTIL_IsPlayer(pTarget) ) - { - if ( UTIL_IsAlive(pTarget) ) - { - m_hTalkTarget = m_hTargetEnt; - if (!FBitSet(m_bitsSaid, bit_saidDamageHeavy) && - (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 8)) - { - //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT3], 1.0, ATTN_IDLE, 0, pitch); - PlaySentence( m_szGrp[TLK_PLHURT3], duration, VOL_NORM, ATTN_IDLE ); - SetBits(m_bitsSaid, bit_saidDamageHeavy); - return TRUE; - } - else if (!FBitSet(m_bitsSaid, bit_saidDamageMedium) && - (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 4)) - { - //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT2], 1.0, ATTN_IDLE, 0, pitch); - PlaySentence( m_szGrp[TLK_PLHURT2], duration, VOL_NORM, ATTN_IDLE ); - SetBits(m_bitsSaid, bit_saidDamageMedium); - return TRUE; - } - else if (!FBitSet(m_bitsSaid, bit_saidDamageLight) && - (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 2)) - { - //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT1], 1.0, ATTN_IDLE, 0, pitch); - PlaySentence( m_szGrp[TLK_PLHURT1], duration, VOL_NORM, ATTN_IDLE ); - SetBits(m_bitsSaid, bit_saidDamageLight); - return TRUE; - } - } - else - { - //!!!KELLY - here's a cool spot to have the talkmonster talk about the dead player if we want. - // "Oh dear, Gordon Freeman is dead!" -Scientist - // "Damn, I can't do this without you." -Barney - } - } - } - - // if there is a friend nearby to speak to, play sentence, set friend's response time, return - edict_t *pFriend = FindNearestFriend(FALSE); - - if (pFriend && !(UTIL_IsMoving(pFriend)) && (RANDOM_LONG(0,99) < 75)) - { - PlaySentence( szQuestionGroup, duration, VOL_NORM, ATTN_IDLE ); - //SENTENCEG_PlayRndSz( ENT(pev), szQuestionGroup, 1.0, ATTN_IDLE, 0, pitch ); - - // force friend to answer - CMTalkMonster *pTalkMonster = (CMTalkMonster *)pFriend; - m_hTalkTarget = pFriend; - pTalkMonster->SetAnswerQuestion( this->edict() ); // UNDONE: This is EVIL!!! - pTalkMonster->m_flStopTalkTime = m_flStopTalkTime; - - m_nSpeak++; - return TRUE; - } - - // otherwise, play an idle statement, try to face client when making a statement. - if ( RANDOM_LONG(0,1) ) - { - //SENTENCEG_PlayRndSz( ENT(pev), szIdleGroup, 1.0, ATTN_IDLE, 0, pitch ); - edict_t *pFriend = FindNearestFriend(TRUE); - - if ( pFriend ) - { - m_hTalkTarget = pFriend; - PlaySentence( szIdleGroup, duration, VOL_NORM, ATTN_IDLE ); - m_nSpeak++; - return TRUE; - } - } - - // didn't speak - Talk( 0 ); - CMTalkMonster::g_talkWaitTime = 0; - return FALSE; -} - -void CMTalkMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, edict_t *pListener ) -{ - if ( !bConcurrent ) - ShutUpFriends(); - - ClearConditions( bits_COND_CLIENT_PUSH ); // Forget about moving! I've got something to say! - m_useTime = gpGlobals->time + duration; - PlaySentence( pszSentence, duration, volume, attenuation ); - - m_hTalkTarget = pListener; -} - -void CMTalkMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) -{ - if ( !pszSentence ) - return; - - Talk ( duration ); - - CMTalkMonster::g_talkWaitTime = gpGlobals->time + duration + 2.0; - if ( pszSentence[0] == '!' ) - EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, GetVoicePitch()); - else - SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, GetVoicePitch() ); - - // If you say anything, don't greet the player - you may have already spoken to them - SetBits(m_bitsSaid, bit_saidHelloPlayer); -} - -//========================================================= -// Talk - set a timer that tells us when the monster is done -// talking. -//========================================================= -void CMTalkMonster :: Talk( float flDuration ) -{ - if ( flDuration <= 0 ) - { - // no duration :( - m_flStopTalkTime = gpGlobals->time + 3; - } - else - { - m_flStopTalkTime = gpGlobals->time + flDuration; - } -} - -// Prepare this talking monster to answer question -void CMTalkMonster :: SetAnswerQuestion( edict_t *pSpeaker ) -{ - m_hTalkTarget = pSpeaker; -} - -int CMTalkMonster :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) -{ - if ( IsAlive() ) - { - // if player damaged this entity, have other friends talk about it - if (pevAttacker && m_MonsterState != MONSTERSTATE_PRONE && FBitSet(pevAttacker->flags, FL_CLIENT)) - { - edict_t *pFriend = FindNearestFriend(FALSE); - - if (pFriend && UTIL_IsAlive(pFriend)) - { - // only if not dead or dying! - CMTalkMonster *pTalkMonster = (CMTalkMonster *)pFriend; - pTalkMonster->ChangeSchedule( slIdleStopShooting ); - } - } - } - return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - - -Schedule_t* CMTalkMonster :: GetScheduleOfType ( int Type ) -{ - switch( Type ) - { - case SCHED_MOVE_AWAY: - return slMoveAway; - - case SCHED_MOVE_AWAY_FOLLOW: - return slMoveAwayFollow; - - case SCHED_MOVE_AWAY_FAIL: - return slMoveAwayFail; - - case SCHED_TARGET_FACE: - // speak during 'use' - if (RANDOM_LONG(0,99) < 2) - //ALERT ( at_console, "target chase speak\n" ); - return slIdleSpeakWait; - else - return slIdleStand; - - case SCHED_IDLE_STAND: - { - // if never seen player, try to greet him - if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) - { - return slIdleHello; - } - - // sustained light wounds? - if (!FBitSet(m_bitsSaid, bit_saidWoundLight) && (pev->health <= (pev->max_health * 0.75))) - { - //SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_WOUND], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); - //CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); - PlaySentence( m_szGrp[TLK_WOUND], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - SetBits(m_bitsSaid, bit_saidWoundLight); - return slIdleStand; - } - // sustained heavy wounds? - else if (!FBitSet(m_bitsSaid, bit_saidWoundHeavy) && (pev->health <= (pev->max_health * 0.5))) - { - //SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_MORTAL], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); - //CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); - PlaySentence( m_szGrp[TLK_MORTAL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - SetBits(m_bitsSaid, bit_saidWoundHeavy); - return slIdleStand; - } - - // talk about world - if (FOkToSpeak() && RANDOM_LONG(0,m_nSpeak * 2) == 0) - { - //ALERT ( at_console, "standing idle speak\n" ); - return slIdleSpeak; - } - - if ( !IsTalking() && HasConditions ( bits_COND_SEE_CLIENT ) && RANDOM_LONG( 0, 6 ) == 0 ) - { - edict_t *pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); - - if ( pPlayer ) - { - // watch the client. - UTIL_MakeVectors ( pPlayer->v.angles ); - if ( ( pPlayer->v.origin - pev->origin ).Length2D() < TLK_STARE_DIST && - UTIL_DotPoints( pPlayer->v.origin, pev->origin, gpGlobals->v_forward ) >= m_flFieldOfView ) - { - // go into the special STARE schedule if the player is close, and looking at me too. - return &slTlkIdleWatchClient[ 1 ]; - } - - return slTlkIdleWatchClient; - } - } - else - { - if (IsTalking()) - // look at who we're talking to - return slTlkIdleEyecontact; - else - // regular standing idle - return slIdleStand; - } - - - // NOTE - caller must first CMTalkMonster::GetScheduleOfType, - // then check result and decide what to return ie: if sci gets back - // slIdleStand, return slIdleSciStand - } - break; - } - - return CMBaseMonster::GetScheduleOfType( Type ); -} - -//========================================================= -// IsTalking - am I saying a sentence right now? -//========================================================= -BOOL CMTalkMonster :: IsTalking( void ) -{ - if ( m_flStopTalkTime > gpGlobals->time ) - { - return TRUE; - } - - return FALSE; -} - -//========================================================= -// If there's a player around, watch him. -//========================================================= -void CMTalkMonster :: PrescheduleThink ( void ) -{ - if ( !HasConditions ( bits_COND_SEE_CLIENT ) ) - { - SetConditions ( bits_COND_CLIENT_UNSEEN ); - } -} - -// try to smell something -void CMTalkMonster :: TrySmellTalk( void ) -{ - if ( !FOkToSpeak() ) - return; - - // clear smell bits periodically - if ( gpGlobals->time > m_flLastSaidSmelled ) - { -// ALERT ( at_aiconsole, "Clear smell bits\n" ); - ClearBits(m_bitsSaid, bit_saidSmelled); - } - // smelled something? - if (!FBitSet(m_bitsSaid, bit_saidSmelled) && HasConditions ( bits_COND_SMELL )) - { - PlaySentence( m_szGrp[TLK_SMELL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - m_flLastSaidSmelled = gpGlobals->time + 60;// don't talk about the stinky for a while. - SetBits(m_bitsSaid, bit_saidSmelled); - } -} - - - -int CMTalkMonster::IRelationship( CMBaseEntity *pTarget ) -{ - if ( pTarget->IsPlayer() ) - if ( m_afMemory & bits_MEMORY_PROVOKED ) - return R_HT; - return CMBaseMonster::IRelationship( pTarget ); -} - - -void CMTalkMonster::StopFollowing( BOOL clearSchedule ) -{ - if ( IsFollowing() ) - { - if ( !(m_afMemory & bits_MEMORY_PROVOKED) ) - { - PlaySentence( m_szGrp[TLK_UNUSE], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - m_hTalkTarget = m_hTargetEnt; - } - - if ( m_movementGoal == MOVEGOAL_TARGETENT ) - RouteClear(); // Stop him from walking toward the player - m_hTargetEnt = NULL; - if ( clearSchedule ) - ClearSchedule(); - if ( m_hEnemy != NULL ) - m_IdealMonsterState = MONSTERSTATE_COMBAT; - } -} - - -void CMTalkMonster::StartFollowing( edict_t *pLeader ) -{ - if ( m_hEnemy != NULL ) - m_IdealMonsterState = MONSTERSTATE_ALERT; - - m_hTargetEnt = pLeader; - PlaySentence( m_szGrp[TLK_USE], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); - m_hTalkTarget = m_hTargetEnt; - ClearConditions( bits_COND_CLIENT_PUSH ); - ClearSchedule(); -} - - -BOOL CMTalkMonster::CanFollow( void ) -{ - if ( !IsAlive() ) - return FALSE; - - return !IsFollowing(); -} - - -void CMTalkMonster :: FollowerUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - // Don't allow use during a scripted_sentence - if ( m_useTime > gpGlobals->time ) - return; - - if ( pCaller != NULL && UTIL_IsPlayer(pCaller) ) - { - // Pre-disaster followers can't be used - if ( pev->spawnflags & SF_MONSTER_PREDISASTER ) - { - DeclineFollowing(); - } - else if ( CanFollow() ) - { - LimitFollowers( pCaller , 1 ); - - if ( m_afMemory & bits_MEMORY_PROVOKED ) - ALERT( at_console, "I'm not following you, you evil person!\n" ); - else - { - StartFollowing( pCaller ); - SetBits(m_bitsSaid, bit_saidHelloPlayer); // Don't say hi after you've started following - } - } - else - { - StopFollowing( TRUE ); - } - } -} - -void CMTalkMonster::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "UseSentence")) - { - m_iszUse = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "UnUseSentence")) - { - m_iszUnUse = ALLOC_STRING(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBaseMonster::KeyValue( pkvd ); -} - - -void CMTalkMonster::Precache( void ) -{ - if ( m_iszUse ) - m_szGrp[TLK_USE] = STRING( m_iszUse ); - if ( m_iszUnUse ) - m_szGrp[TLK_UNUSE] = STRING( m_iszUnUse ); -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "cmtalkmonster.h" +#include "defaultai.h" +#include "animation.h" + +//========================================================= +// Talking monster base class +// Used for scientists and barneys +//========================================================= +float CMTalkMonster::g_talkWaitTime = 0; // time delay until it's ok to speak: used so that two NPCs don't talk at once + +// NOTE: m_voicePitch & m_szGrp should be fixed up by precache each save/restore + +// array of friend names +char *CMTalkMonster::m_szFriends[TLK_CFRIENDS] = +{ + "monster_barney", + "monster_scientist", + "monster_sitting_scientist", +}; + + +//========================================================= +// AI Schedules Specific to talking monsters +//========================================================= + +Task_t tlIdleResponse[] = +{ + { TASK_SET_ACTIVITY, (float)ACT_IDLE },// Stop and listen + { TASK_WAIT, (float)0.5 },// Wait until sure it's me they are talking to + { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done + { TASK_TLK_RESPOND, (float)0 },// Wait and then say my response + { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, + { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done +}; + +Schedule_t slIdleResponse[] = +{ + { + tlIdleResponse, + ARRAYSIZE ( tlIdleResponse ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Idle Response" + + }, +}; + +Task_t tlIdleSpeak[] = +{ + { TASK_TLK_SPEAK, (float)0 },// question or remark + { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, + { TASK_TLK_EYECONTACT, (float)0 }, + { TASK_WAIT_RANDOM, (float)0.5 }, +}; + +Schedule_t slIdleSpeak[] = +{ + { + tlIdleSpeak, + ARRAYSIZE ( tlIdleSpeak ), + bits_COND_NEW_ENEMY | + bits_COND_CLIENT_PUSH | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Idle Speak" + }, +}; + +Task_t tlIdleSpeakWait[] = +{ + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 },// Stop and talk + { TASK_TLK_SPEAK, (float)0 },// question or remark + { TASK_TLK_EYECONTACT, (float)0 },// + { TASK_WAIT, (float)2 },// wait - used when sci is in 'use' mode to keep head turned +}; + +Schedule_t slIdleSpeakWait[] = +{ + { + tlIdleSpeakWait, + ARRAYSIZE ( tlIdleSpeakWait ), + bits_COND_NEW_ENEMY | + bits_COND_CLIENT_PUSH | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "Idle Speak Wait" + }, +}; + +Task_t tlIdleHello[] = +{ + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 },// Stop and talk + { TASK_TLK_HELLO, (float)0 },// Try to say hello to player + { TASK_TLK_EYECONTACT, (float)0 }, + { TASK_WAIT, (float)0.5 },// wait a bit + { TASK_TLK_HELLO, (float)0 },// Try to say hello to player + { TASK_TLK_EYECONTACT, (float)0 }, + { TASK_WAIT, (float)0.5 },// wait a bit + { TASK_TLK_HELLO, (float)0 },// Try to say hello to player + { TASK_TLK_EYECONTACT, (float)0 }, + { TASK_WAIT, (float)0.5 },// wait a bit + { TASK_TLK_HELLO, (float)0 },// Try to say hello to player + { TASK_TLK_EYECONTACT, (float)0 }, + { TASK_WAIT, (float)0.5 },// wait a bit + +}; + +Schedule_t slIdleHello[] = +{ + { + tlIdleHello, + ARRAYSIZE ( tlIdleHello ), + bits_COND_NEW_ENEMY | + bits_COND_CLIENT_PUSH | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_PROVOKED, + 0, + "Idle Hello" + }, +}; + +Task_t tlIdleStopShooting[] = +{ + { TASK_TLK_STOPSHOOTING, (float)0 },// tell player to stop shooting friend + // { TASK_TLK_EYECONTACT, (float)0 },// look at the player +}; + +Schedule_t slIdleStopShooting[] = +{ + { + tlIdleStopShooting, + ARRAYSIZE ( tlIdleStopShooting ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND, + 0, + "Idle Stop Shooting" + }, +}; + +Task_t tlMoveAway[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_MOVE_AWAY_FAIL }, + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_MOVE_AWAY_PATH, (float)100 }, + { TASK_WALK_PATH_FOR_UNITS, (float)100 }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_PLAYER, (float)0.5 }, +}; + +Schedule_t slMoveAway[] = +{ + { + tlMoveAway, + ARRAYSIZE ( tlMoveAway ), + 0, + 0, + "MoveAway" + }, +}; + + +Task_t tlMoveAwayFail[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_FACE_PLAYER, (float)0.5 }, +}; + +Schedule_t slMoveAwayFail[] = +{ + { + tlMoveAwayFail, + ARRAYSIZE ( tlMoveAwayFail ), + 0, + 0, + "MoveAwayFail" + }, +}; + + + +Task_t tlMoveAwayFollow[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_TARGET_FACE }, + { TASK_STORE_LASTPOSITION, (float)0 }, + { TASK_MOVE_AWAY_PATH, (float)100 }, + { TASK_WALK_PATH_FOR_UNITS, (float)100 }, + { TASK_STOP_MOVING, (float)0 }, + { TASK_SET_SCHEDULE, (float)SCHED_TARGET_FACE }, +}; + +Schedule_t slMoveAwayFollow[] = +{ + { + tlMoveAwayFollow, + ARRAYSIZE ( tlMoveAwayFollow ), + 0, + 0, + "MoveAwayFollow" + }, +}; + +Task_t tlTlkIdleWatchClient[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_TLK_LOOK_AT_CLIENT, (float)6 }, +}; + +Task_t tlTlkIdleWatchClientStare[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, + { TASK_TLK_CLIENT_STARE, (float)6 }, + { TASK_TLK_STARE, (float)0 }, + { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, + { TASK_TLK_EYECONTACT, (float)0 }, +}; + +Schedule_t slTlkIdleWatchClient[] = +{ + { + tlTlkIdleWatchClient, + ARRAYSIZE ( tlTlkIdleWatchClient ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL | + bits_COND_CLIENT_PUSH | + bits_COND_CLIENT_UNSEEN | + bits_COND_PROVOKED, + 0, + "TlkIdleWatchClient" + }, + + { + tlTlkIdleWatchClientStare, + ARRAYSIZE ( tlTlkIdleWatchClientStare ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE | + bits_COND_HEAR_SOUND | + bits_COND_SMELL | + bits_COND_CLIENT_PUSH | + bits_COND_CLIENT_UNSEEN | + bits_COND_PROVOKED, + 0, + "TlkIdleWatchClientStare" + }, +}; + + +Task_t tlTlkIdleEyecontact[] = +{ + { TASK_TLK_IDEALYAW, (float)0 },// look at who I'm talking to + { TASK_FACE_IDEAL, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_SIGNAL3 }, + { TASK_TLK_EYECONTACT, (float)0 },// Wait until speaker is done +}; + +Schedule_t slTlkIdleEyecontact[] = +{ + { + tlTlkIdleEyecontact, + ARRAYSIZE ( tlTlkIdleEyecontact ), + bits_COND_NEW_ENEMY | + bits_COND_CLIENT_PUSH | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "TlkIdleEyecontact" + }, +}; + + +DEFINE_CUSTOM_SCHEDULES( CMTalkMonster ) +{ + slIdleResponse, + slIdleSpeak, + slIdleHello, + slIdleSpeakWait, + slIdleStopShooting, + slMoveAway, + slMoveAwayFollow, + slMoveAwayFail, + slTlkIdleWatchClient, + &slTlkIdleWatchClient[ 1 ], + slTlkIdleEyecontact, +}; + +IMPLEMENT_CUSTOM_SCHEDULES( CMTalkMonster, CMBaseMonster ); + + +void CMTalkMonster :: SetActivity ( Activity newActivity ) +{ + if (newActivity == ACT_IDLE && IsTalking() ) + newActivity = ACT_SIGNAL3; + + if ( newActivity == ACT_SIGNAL3 && (LookupActivity ( ACT_SIGNAL3 ) == ACTIVITY_NOT_AVAILABLE)) + newActivity = ACT_IDLE; + + CMBaseMonster::SetActivity( newActivity ); +} + + +void CMTalkMonster :: StartTask( Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_TLK_SPEAK: + // ask question or make statement + FIdleSpeak(); + TaskComplete(); + break; + + case TASK_TLK_RESPOND: + // respond to question + IdleRespond(); + TaskComplete(); + break; + + case TASK_TLK_HELLO: + // greet player + FIdleHello(); + TaskComplete(); + break; + + + case TASK_TLK_STARE: + // let the player know I know he's staring at me. + FIdleStare(); + TaskComplete(); + break; + + case TASK_FACE_PLAYER: + case TASK_TLK_LOOK_AT_CLIENT: + case TASK_TLK_CLIENT_STARE: + // track head to the client for a while. + m_flWaitFinished = gpGlobals->time + pTask->flData; + break; + + case TASK_TLK_EYECONTACT: + break; + + case TASK_TLK_IDEALYAW: + if (m_hTalkTarget != NULL) + { + pev->yaw_speed = 60; + float yaw = VecToYaw(m_hTalkTarget->v.origin - pev->origin) - pev->angles.y; + + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + + if (yaw < 0) + { + pev->ideal_yaw = min( yaw + 45.0f, 0.0f ) + pev->angles.y; + } + else + { + pev->ideal_yaw = max( yaw - 45.0f, 0.0f ) + pev->angles.y; + } + } + TaskComplete(); + break; + + case TASK_TLK_HEADRESET: + // reset head position after looking at something + m_hTalkTarget = NULL; + TaskComplete(); + break; + + case TASK_TLK_STOPSHOOTING: + // tell player to stop shooting + PlaySentence( m_szGrp[TLK_NOSHOOT], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_NORM ); + TaskComplete(); + break; + + case TASK_CANT_FOLLOW: + StopFollowing( FALSE ); + PlaySentence( m_szGrp[TLK_STOP], RANDOM_FLOAT(2, 2.5), VOL_NORM, ATTN_NORM ); + TaskComplete(); + break; + + case TASK_WALK_PATH_FOR_UNITS: + m_movementActivity = ACT_WALK; + break; + + case TASK_MOVE_AWAY_PATH: + { + Vector dir = pev->angles; + dir.y = pev->ideal_yaw + 180; + Vector move; + + UTIL_MakeVectorsPrivate( dir, move, NULL, NULL ); + dir = pev->origin + move * pTask->flData; + if ( MoveToLocation( ACT_WALK, 2, dir ) ) + { + TaskComplete(); + } + else if ( FindCover( pev->origin, pev->view_ofs, 0, CoverRadius() ) ) + { + // then try for plain ole cover + m_flMoveWaitFinished = gpGlobals->time + 2; + TaskComplete(); + } + else + { + // nowhere to go? + TaskFail(); + } + } + break; + + case TASK_PLAY_SCRIPT: + m_hTalkTarget = NULL; + CMBaseMonster::StartTask( pTask ); + break; + + default: + CMBaseMonster::StartTask( pTask ); + } +} + + +void CMTalkMonster :: RunTask( Task_t *pTask ) +{ + switch( pTask->iTask ) + { + case TASK_TLK_CLIENT_STARE: + case TASK_TLK_LOOK_AT_CLIENT: + + edict_t *pPlayer; + + // track head to the client for a while. + if ( m_MonsterState == MONSTERSTATE_IDLE && + !IsMoving() && + !IsTalking() ) + { + // Get edict of nearest player + pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); + + if ( pPlayer ) + { + IdleHeadTurn( pPlayer->v.origin ); + } + } + else + { + // started moving or talking + TaskFail(); + return; + } + + if ( pTask->iTask == TASK_TLK_CLIENT_STARE ) + { + // fail out if the player looks away or moves away. + if ( ( pPlayer->v.origin - pev->origin ).Length2D() > TLK_STARE_DIST ) + { + // player moved away. + TaskFail(); + } + + UTIL_MakeVectors( pPlayer->v.angles ); + if ( UTIL_DotPoints( pPlayer->v.origin, pev->origin, gpGlobals->v_forward ) < m_flFieldOfView ) + { + // player looked away + TaskFail(); + } + } + + if ( gpGlobals->time > m_flWaitFinished ) + { + TaskComplete(); + } + break; + + case TASK_FACE_PLAYER: + { + // Get edict of nearest player + edict_t *pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); + + if ( pPlayer ) + { + MakeIdealYaw ( pPlayer->v.origin ); + ChangeYaw ( pev->yaw_speed ); + IdleHeadTurn( pPlayer->v.origin ); + if ( gpGlobals->time > m_flWaitFinished && FlYawDiff() < 10 ) + { + TaskComplete(); + } + } + else + { + TaskFail(); + } + } + break; + + case TASK_TLK_EYECONTACT: + if (!IsMoving() && IsTalking() && m_hTalkTarget != NULL) + { + // ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time ); + IdleHeadTurn( m_hTalkTarget->v.origin ); + } + else + { + TaskComplete(); + } + break; + + case TASK_WALK_PATH_FOR_UNITS: + { + float distance; + + distance = (m_vecLastPosition - pev->origin).Length2D(); + + // Walk path until far enough away + if ( distance > pTask->flData || MovementIsComplete() ) + { + TaskComplete(); + RouteClear(); // Stop moving + } + } + break; + case TASK_WAIT_FOR_MOVEMENT: + if (IsTalking() && m_hTalkTarget != NULL) + { + // ALERT(at_console, "walking, talking\n"); + IdleHeadTurn( m_hTalkTarget->v.origin ); + } + else + { + IdleHeadTurn( pev->origin ); + // override so that during walk, a scientist may talk and greet player + FIdleHello(); + if (RANDOM_LONG(0,m_nSpeak * 20) == 0) + { + FIdleSpeak(); + } + } + + CMBaseMonster::RunTask( pTask ); + if (TaskIsComplete()) + IdleHeadTurn( pev->origin ); + break; + + default: + if (IsTalking() && m_hTalkTarget != NULL) + { + IdleHeadTurn( m_hTalkTarget->v.origin ); + } + else + { + SetBoneController( 0, 0 ); + } + CMBaseMonster::RunTask( pTask ); + } +} + + +void CMTalkMonster :: Killed( entvars_t *pevAttacker, int iGib ) +{ + // If a client killed me (unless I was already Barnacle'd), make everyone else mad/afraid of him + if ( (pevAttacker->flags & FL_CLIENT) && m_MonsterState != MONSTERSTATE_PRONE ) + { + AlertFriends(); + LimitFollowers( ENT(pevAttacker), 0 ); + } + + m_hTargetEnt = NULL; + // Don't finish that sentence + StopTalking(); + SetUse( NULL ); + CMBaseMonster::Killed( pevAttacker, iGib ); +} + + + +edict_t *CMTalkMonster::EnumFriends( edict_t *pPrevious, int listNumber, BOOL bTrace ) +{ + edict_t *pFriend = pPrevious; + char *pszFriend; + TraceResult tr; + Vector vecCheck; + + pszFriend = m_szFriends[ FriendNumber(listNumber) ]; + while (pFriend = UTIL_FindEntityByClassname( pFriend, pszFriend )) + { + if (pFriend == this->edict() || !UTIL_IsAlive(pFriend)) + // don't talk to self or dead people + continue; + if ( bTrace ) + { + vecCheck = pFriend->v.origin; + vecCheck.z = pFriend->v.absmax.z; + + UTIL_TraceLine( pev->origin, vecCheck, ignore_monsters, ENT(pev), &tr); + } + else + tr.flFraction = 1.0; + + if (tr.flFraction == 1.0) + { + return pFriend; + } + } + + return NULL; +} + + +void CMTalkMonster::AlertFriends( void ) +{ +/*jlb + edict_t *pFriend = NULL; + int i; + // for each friend in this bsp... + for ( i = 0; i < TLK_CFRIENDS; i++ ) + { + while (pFriend = EnumFriends( pFriend, i, TRUE )) + { + CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); + if ( pMonster->IsAlive() ) + { + // don't provoke a friend that's playing a death animation. They're a goner + pMonster->m_afMemory |= bits_MEMORY_PROVOKED; + } + } + } +jlb*/ +} + + + +void CMTalkMonster::ShutUpFriends( void ) +{ +/*jlb + CBaseEntity *pFriend = NULL; + int i; + + // for each friend in this bsp... + for ( i = 0; i < TLK_CFRIENDS; i++ ) + { + while (pFriend = EnumFriends( pFriend, i, TRUE )) + { + CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); + if ( pMonster ) + { + pMonster->SentenceStop(); + } + } + } +jlb*/ +} + + +// UNDONE: Keep a follow time in each follower, make a list of followers in this function and do LRU +// UNDONE: Check this in Restore to keep restored monsters from joining a full list of followers +void CMTalkMonster::LimitFollowers( edict_t *pPlayer, int maxFollowers ) +{ +/*jlb + CBaseEntity *pFriend = NULL; + int i, count; + + count = 0; + // for each friend in this bsp... + for ( i = 0; i < TLK_CFRIENDS; i++ ) + { + while (pFriend = EnumFriends( pFriend, i, FALSE )) + { + CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); + if ( pMonster ) + { + if ( pMonster->m_hTargetEnt == pPlayer ) + { + count++; + if ( count > maxFollowers ) + pMonster->StopFollowing( TRUE ); + } + } + } + } +jlb*/ +} + + +float CMTalkMonster::TargetDistance( void ) +{ + // If we lose the player, or he dies, return a really large distance + if ( m_hTargetEnt == NULL || !UTIL_IsAlive(m_hEnemy) ) + return 1e6; + + return (m_hTargetEnt->v.origin - pev->origin).Length(); +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMTalkMonster :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + CMBaseMonster::HandleAnimEvent( pEvent ); +} + +// monsters derived from CMTalkMonster should call this in precache() + +void CMTalkMonster :: TalkInit( void ) +{ + // every new talking monster must reset this global, otherwise + // when a level is loaded, nobody will talk (time is reset to 0) + + CMTalkMonster::g_talkWaitTime = 0; + + m_hTalkTarget = NULL; + + m_voicePitch = 100; +} +//========================================================= +// FindNearestFriend +// Scan for nearest, visible friend. If fPlayer is true, look for +// nearest player +//========================================================= +edict_t *CMTalkMonster :: FindNearestFriend(BOOL fPlayer) +{ + edict_t *pFriend = NULL; + edict_t *pNearest = NULL; +/*jlb + float range = 10000000.0; + TraceResult tr; + Vector vecStart = pev->origin; + Vector vecCheck; + int i; + char *pszFriend; + int cfriends; + + vecStart.z = pev->absmax.z; + + if (fPlayer) + cfriends = 1; + else + cfriends = TLK_CFRIENDS; + + // for each type of friend... + + for (i = cfriends-1; i > -1; i--) + { + if (fPlayer) + pszFriend = "player"; + else + pszFriend = m_szFriends[FriendNumber(i)]; + + if (!pszFriend) + continue; + + // for each friend in this bsp... + while (pFriend = UTIL_FindEntityByClassname( pFriend, pszFriend )) + { + if (pFriend == this || !pFriend->IsAlive()) + // don't talk to self or dead people + continue; + + CMBaseMonster *pMonster = pFriend->MyMonsterPointer(); + + // If not a monster for some reason, or in a script, or prone + if ( !pMonster || pMonster->m_MonsterState == MONSTERSTATE_SCRIPT || pMonster->m_MonsterState == MONSTERSTATE_PRONE ) + continue; + + vecCheck = pFriend->pev->origin; + vecCheck.z = pFriend->pev->absmax.z; + + // if closer than previous friend, and in range, see if he's visible + + if (range > (vecStart - vecCheck).Length()) + { + UTIL_TraceLine(vecStart, vecCheck, ignore_monsters, ENT(pev), &tr); + + if (tr.flFraction == 1.0) + { + // visible and in range, this is the new nearest scientist + if ((vecStart - vecCheck).Length() < TALKRANGE_MIN) + { + pNearest = pFriend; + range = (vecStart - vecCheck).Length(); + } + } + } + } + } +jlb*/ + return pNearest; +} + +int CMTalkMonster :: GetVoicePitch( void ) +{ + return m_voicePitch + RANDOM_LONG(0,3); +} + + +void CMTalkMonster :: TalkTouch( edict_t *pOther ) +{ + // Did the player touch me? + if ( UTIL_IsPlayer(pOther) ) + { + // Ignore if pissed at player + if ( m_afMemory & bits_MEMORY_PROVOKED ) + return; + + // Stay put during speech + if ( IsTalking() ) + return; + + // Heuristic for determining if the player is pushing me away + float speed = fabs(pOther->v.velocity.x) + fabs(pOther->v.velocity.y); + if ( speed > 50 ) + { + SetConditions( bits_COND_CLIENT_PUSH ); + MakeIdealYaw( pOther->v.origin ); + } + } +} + + + +//========================================================= +// IdleRespond +// Respond to a previous question +//========================================================= +void CMTalkMonster :: IdleRespond( void ) +{ + int pitch = GetVoicePitch(); + + // play response + PlaySentence( m_szGrp[TLK_ANSWER], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); +} + +int CMTalkMonster :: FOkToSpeak( void ) +{ + // if in the grip of a barnacle, don't speak + if ( m_MonsterState == MONSTERSTATE_PRONE || m_IdealMonsterState == MONSTERSTATE_PRONE ) + { + return FALSE; + } + + // if not alive, certainly don't speak + if ( pev->deadflag != DEAD_NO ) + { + return FALSE; + } + + // if someone else is talking, don't speak + if (gpGlobals->time <= CMTalkMonster::g_talkWaitTime) + return FALSE; + + if ( pev->spawnflags & SF_MONSTER_GAG ) + return FALSE; + + if ( m_MonsterState == MONSTERSTATE_PRONE ) + return FALSE; + + // if player is not in pvs, don't speak + if (!IsAlive() || FNullEnt(FIND_CLIENT_IN_PVS(edict()))) + return FALSE; + + // don't talk if you're in combat + if (m_hEnemy != NULL && UTIL_FVisible( m_hEnemy, this->edict() )) + return FALSE; + + return TRUE; +} + + +int CMTalkMonster::CanPlaySentence( BOOL fDisregardState ) +{ + if ( fDisregardState ) + return CMBaseMonster::CanPlaySentence( fDisregardState ); + return FOkToSpeak(); +} + +//========================================================= +// FIdleStare +//========================================================= +int CMTalkMonster :: FIdleStare( void ) +{ + if (!FOkToSpeak()) + return FALSE; + + PlaySentence( m_szGrp[TLK_STARE], RANDOM_FLOAT(5, 7.5), VOL_NORM, ATTN_IDLE ); + + m_hTalkTarget = FindNearestFriend( TRUE ); + return TRUE; +} + +//========================================================= +// IdleHello +// Try to greet player first time he's seen +//========================================================= +int CMTalkMonster :: FIdleHello( void ) +{ + if (!FOkToSpeak()) + return FALSE; + + // if this is first time scientist has seen player, greet him + if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) + { + // get a player + edict_t *pPlayer = FindNearestFriend(TRUE); + + if (pPlayer) + { + if (UTIL_FInViewCone(pPlayer, this->edict(), m_flFieldOfView) && UTIL_FVisible(pPlayer, this->edict())) + { + m_hTalkTarget = pPlayer; + + if (FBitSet(pev->spawnflags, SF_MONSTER_PREDISASTER)) + PlaySentence( m_szGrp[TLK_PHELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); + else + PlaySentence( m_szGrp[TLK_HELLO], RANDOM_FLOAT(3, 3.5), VOL_NORM, ATTN_IDLE ); + + SetBits(m_bitsSaid, bit_saidHelloPlayer); + + return TRUE; + } + } + } + return FALSE; +} + + +// turn head towards supplied origin +void CMTalkMonster :: IdleHeadTurn( Vector &vecFriend ) +{ + // turn head in desired direction only if ent has a turnable head + if (m_afCapability & bits_CAP_TURN_HEAD) + { + float yaw = VecToYaw(vecFriend - pev->origin) - pev->angles.y; + + if (yaw > 180) yaw -= 360; + if (yaw < -180) yaw += 360; + + // turn towards vector + SetBoneController( 0, yaw ); + } +} + +//========================================================= +// FIdleSpeak +// ask question of nearby friend, or make statement +//========================================================= +int CMTalkMonster :: FIdleSpeak ( void ) +{ + // try to start a conversation, or make statement + int pitch; + const char *szIdleGroup; + const char *szQuestionGroup; + float duration; + + if (!FOkToSpeak()) + return FALSE; + + // set idle groups based on pre/post disaster + if (FBitSet(pev->spawnflags, SF_MONSTER_PREDISASTER)) + { + szIdleGroup = m_szGrp[TLK_PIDLE]; + szQuestionGroup = m_szGrp[TLK_PQUESTION]; + // set global min delay for next conversation + duration = RANDOM_FLOAT(4.8, 5.2); + } + else + { + szIdleGroup = m_szGrp[TLK_IDLE]; + szQuestionGroup = m_szGrp[TLK_QUESTION]; + // set global min delay for next conversation + duration = RANDOM_FLOAT(2.8, 3.2); + + } + + pitch = GetVoicePitch(); + + // player using this entity is alive and wounded? + edict_t *pTarget = m_hTargetEnt; + + if ( pTarget != NULL ) + { + if ( UTIL_IsPlayer(pTarget) ) + { + if ( UTIL_IsAlive(pTarget) ) + { + m_hTalkTarget = m_hTargetEnt; + if (!FBitSet(m_bitsSaid, bit_saidDamageHeavy) && + (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 8)) + { + //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT3], 1.0, ATTN_IDLE, 0, pitch); + PlaySentence( m_szGrp[TLK_PLHURT3], duration, VOL_NORM, ATTN_IDLE ); + SetBits(m_bitsSaid, bit_saidDamageHeavy); + return TRUE; + } + else if (!FBitSet(m_bitsSaid, bit_saidDamageMedium) && + (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 4)) + { + //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT2], 1.0, ATTN_IDLE, 0, pitch); + PlaySentence( m_szGrp[TLK_PLHURT2], duration, VOL_NORM, ATTN_IDLE ); + SetBits(m_bitsSaid, bit_saidDamageMedium); + return TRUE; + } + else if (!FBitSet(m_bitsSaid, bit_saidDamageLight) && + (m_hTargetEnt->v.health <= m_hTargetEnt->v.max_health / 2)) + { + //EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, m_szGrp[TLK_PLHURT1], 1.0, ATTN_IDLE, 0, pitch); + PlaySentence( m_szGrp[TLK_PLHURT1], duration, VOL_NORM, ATTN_IDLE ); + SetBits(m_bitsSaid, bit_saidDamageLight); + return TRUE; + } + } + else + { + //!!!KELLY - here's a cool spot to have the talkmonster talk about the dead player if we want. + // "Oh dear, Gordon Freeman is dead!" -Scientist + // "Damn, I can't do this without you." -Barney + } + } + } + + // if there is a friend nearby to speak to, play sentence, set friend's response time, return + edict_t *pFriend = FindNearestFriend(FALSE); + + if (pFriend && !(UTIL_IsMoving(pFriend)) && (RANDOM_LONG(0,99) < 75)) + { + PlaySentence( szQuestionGroup, duration, VOL_NORM, ATTN_IDLE ); + //SENTENCEG_PlayRndSz( ENT(pev), szQuestionGroup, 1.0, ATTN_IDLE, 0, pitch ); + + // force friend to answer + CMTalkMonster *pTalkMonster = (CMTalkMonster *)pFriend; + m_hTalkTarget = pFriend; + pTalkMonster->SetAnswerQuestion( this->edict() ); // UNDONE: This is EVIL!!! + pTalkMonster->m_flStopTalkTime = m_flStopTalkTime; + + m_nSpeak++; + return TRUE; + } + + // otherwise, play an idle statement, try to face client when making a statement. + if ( RANDOM_LONG(0,1) ) + { + //SENTENCEG_PlayRndSz( ENT(pev), szIdleGroup, 1.0, ATTN_IDLE, 0, pitch ); + edict_t *pFriend = FindNearestFriend(TRUE); + + if ( pFriend ) + { + m_hTalkTarget = pFriend; + PlaySentence( szIdleGroup, duration, VOL_NORM, ATTN_IDLE ); + m_nSpeak++; + return TRUE; + } + } + + // didn't speak + Talk( 0 ); + CMTalkMonster::g_talkWaitTime = 0; + return FALSE; +} + +void CMTalkMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, edict_t *pListener ) +{ + if ( !bConcurrent ) + ShutUpFriends(); + + ClearConditions( bits_COND_CLIENT_PUSH ); // Forget about moving! I've got something to say! + m_useTime = gpGlobals->time + duration; + PlaySentence( pszSentence, duration, volume, attenuation ); + + m_hTalkTarget = pListener; +} + +void CMTalkMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) +{ + if ( !pszSentence ) + return; + + Talk ( duration ); + + CMTalkMonster::g_talkWaitTime = gpGlobals->time + duration + 2.0; + if ( pszSentence[0] == '!' ) + EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, GetVoicePitch()); + else + SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, GetVoicePitch() ); + + // If you say anything, don't greet the player - you may have already spoken to them + SetBits(m_bitsSaid, bit_saidHelloPlayer); +} + +//========================================================= +// Talk - set a timer that tells us when the monster is done +// talking. +//========================================================= +void CMTalkMonster :: Talk( float flDuration ) +{ + if ( flDuration <= 0 ) + { + // no duration :( + m_flStopTalkTime = gpGlobals->time + 3; + } + else + { + m_flStopTalkTime = gpGlobals->time + flDuration; + } +} + +// Prepare this talking monster to answer question +void CMTalkMonster :: SetAnswerQuestion( edict_t *pSpeaker ) +{ + m_hTalkTarget = pSpeaker; +} + +int CMTalkMonster :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType) +{ + if ( IsAlive() ) + { + // if player damaged this entity, have other friends talk about it + if (pevAttacker && m_MonsterState != MONSTERSTATE_PRONE && FBitSet(pevAttacker->flags, FL_CLIENT)) + { + edict_t *pFriend = FindNearestFriend(FALSE); + + if (pFriend && UTIL_IsAlive(pFriend)) + { + // only if not dead or dying! + CMTalkMonster *pTalkMonster = (CMTalkMonster *)pFriend; + pTalkMonster->ChangeSchedule( slIdleStopShooting ); + } + } + } + return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + + +Schedule_t* CMTalkMonster :: GetScheduleOfType ( int Type ) +{ + switch( Type ) + { + case SCHED_MOVE_AWAY: + return slMoveAway; + + case SCHED_MOVE_AWAY_FOLLOW: + return slMoveAwayFollow; + + case SCHED_MOVE_AWAY_FAIL: + return slMoveAwayFail; + + case SCHED_TARGET_FACE: + // speak during 'use' + if (RANDOM_LONG(0,99) < 2) + //ALERT ( at_console, "target chase speak\n" ); + return slIdleSpeakWait; + else + return slIdleStand; + + case SCHED_IDLE_STAND: + { + // if never seen player, try to greet him + if (!FBitSet(m_bitsSaid, bit_saidHelloPlayer)) + { + return slIdleHello; + } + + // sustained light wounds? + if (!FBitSet(m_bitsSaid, bit_saidWoundLight) && (pev->health <= (pev->max_health * 0.75))) + { + //SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_WOUND], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); + //CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); + PlaySentence( m_szGrp[TLK_WOUND], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + SetBits(m_bitsSaid, bit_saidWoundLight); + return slIdleStand; + } + // sustained heavy wounds? + else if (!FBitSet(m_bitsSaid, bit_saidWoundHeavy) && (pev->health <= (pev->max_health * 0.5))) + { + //SENTENCEG_PlayRndSz( ENT(pev), m_szGrp[TLK_MORTAL], 1.0, ATTN_IDLE, 0, GetVoicePitch() ); + //CMTalkMonster::g_talkWaitTime = gpGlobals->time + RANDOM_FLOAT(2.8, 3.2); + PlaySentence( m_szGrp[TLK_MORTAL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + SetBits(m_bitsSaid, bit_saidWoundHeavy); + return slIdleStand; + } + + // talk about world + if (FOkToSpeak() && RANDOM_LONG(0,m_nSpeak * 2) == 0) + { + //ALERT ( at_console, "standing idle speak\n" ); + return slIdleSpeak; + } + + if ( !IsTalking() && HasConditions ( bits_COND_SEE_CLIENT ) && RANDOM_LONG( 0, 6 ) == 0 ) + { + edict_t *pPlayer = UTIL_FindNearestPlayer(this->edict(), m_flFieldOfView); + + if ( pPlayer ) + { + // watch the client. + UTIL_MakeVectors ( pPlayer->v.angles ); + if ( ( pPlayer->v.origin - pev->origin ).Length2D() < TLK_STARE_DIST && + UTIL_DotPoints( pPlayer->v.origin, pev->origin, gpGlobals->v_forward ) >= m_flFieldOfView ) + { + // go into the special STARE schedule if the player is close, and looking at me too. + return &slTlkIdleWatchClient[ 1 ]; + } + + return slTlkIdleWatchClient; + } + } + else + { + if (IsTalking()) + // look at who we're talking to + return slTlkIdleEyecontact; + else + // regular standing idle + return slIdleStand; + } + + + // NOTE - caller must first CMTalkMonster::GetScheduleOfType, + // then check result and decide what to return ie: if sci gets back + // slIdleStand, return slIdleSciStand + } + break; + } + + return CMBaseMonster::GetScheduleOfType( Type ); +} + +//========================================================= +// IsTalking - am I saying a sentence right now? +//========================================================= +BOOL CMTalkMonster :: IsTalking( void ) +{ + if ( m_flStopTalkTime > gpGlobals->time ) + { + return TRUE; + } + + return FALSE; +} + +//========================================================= +// If there's a player around, watch him. +//========================================================= +void CMTalkMonster :: PrescheduleThink ( void ) +{ + if ( !HasConditions ( bits_COND_SEE_CLIENT ) ) + { + SetConditions ( bits_COND_CLIENT_UNSEEN ); + } +} + +// try to smell something +void CMTalkMonster :: TrySmellTalk( void ) +{ + if ( !FOkToSpeak() ) + return; + + // clear smell bits periodically + if ( gpGlobals->time > m_flLastSaidSmelled ) + { +// ALERT ( at_aiconsole, "Clear smell bits\n" ); + ClearBits(m_bitsSaid, bit_saidSmelled); + } + // smelled something? + if (!FBitSet(m_bitsSaid, bit_saidSmelled) && HasConditions ( bits_COND_SMELL )) + { + PlaySentence( m_szGrp[TLK_SMELL], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + m_flLastSaidSmelled = gpGlobals->time + 60;// don't talk about the stinky for a while. + SetBits(m_bitsSaid, bit_saidSmelled); + } +} + + + +int CMTalkMonster::IRelationship( CMBaseEntity *pTarget ) +{ + if ( pTarget->IsPlayer() ) + if ( m_afMemory & bits_MEMORY_PROVOKED ) + return R_HT; + return CMBaseMonster::IRelationship( pTarget ); +} + + +void CMTalkMonster::StopFollowing( BOOL clearSchedule ) +{ + if ( IsFollowing() ) + { + if ( !(m_afMemory & bits_MEMORY_PROVOKED) ) + { + PlaySentence( m_szGrp[TLK_UNUSE], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + m_hTalkTarget = m_hTargetEnt; + } + + if ( m_movementGoal == MOVEGOAL_TARGETENT ) + RouteClear(); // Stop him from walking toward the player + m_hTargetEnt = NULL; + if ( clearSchedule ) + ClearSchedule(); + if ( m_hEnemy != NULL ) + m_IdealMonsterState = MONSTERSTATE_COMBAT; + } +} + + +void CMTalkMonster::StartFollowing( edict_t *pLeader ) +{ + if ( m_hEnemy != NULL ) + m_IdealMonsterState = MONSTERSTATE_ALERT; + + m_hTargetEnt = pLeader; + PlaySentence( m_szGrp[TLK_USE], RANDOM_FLOAT(2.8, 3.2), VOL_NORM, ATTN_IDLE ); + m_hTalkTarget = m_hTargetEnt; + ClearConditions( bits_COND_CLIENT_PUSH ); + ClearSchedule(); +} + + +BOOL CMTalkMonster::CanFollow( void ) +{ + if ( !IsAlive() ) + return FALSE; + + return !IsFollowing(); +} + + +void CMTalkMonster :: FollowerUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + // Don't allow use during a scripted_sentence + if ( m_useTime > gpGlobals->time ) + return; + + if ( pCaller != NULL && UTIL_IsPlayer(pCaller) ) + { + // Pre-disaster followers can't be used + if ( pev->spawnflags & SF_MONSTER_PREDISASTER ) + { + DeclineFollowing(); + } + else if ( CanFollow() ) + { + LimitFollowers( pCaller , 1 ); + + if ( m_afMemory & bits_MEMORY_PROVOKED ) + ALERT( at_console, "I'm not following you, you evil person!\n" ); + else + { + StartFollowing( pCaller ); + SetBits(m_bitsSaid, bit_saidHelloPlayer); // Don't say hi after you've started following + } + } + else + { + StopFollowing( TRUE ); + } + } +} + +void CMTalkMonster::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "UseSentence")) + { + m_iszUse = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "UnUseSentence")) + { + m_iszUnUse = ALLOC_STRING(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseMonster::KeyValue( pkvd ); +} + + +void CMTalkMonster::Precache( void ) +{ + if ( m_iszUse ) + m_szGrp[TLK_USE] = STRING( m_iszUse ); + if ( m_iszUnUse ) + m_szGrp[TLK_UNUSE] = STRING( m_iszUnUse ); +} + diff --git a/src/dlls/turret.cpp b/src/dlls/turret.cpp old mode 100755 new mode 100644 index 31b20ac..c7d97cd --- a/src/dlls/turret.cpp +++ b/src/dlls/turret.cpp @@ -1,1162 +1,1162 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -/* - -===== turret.cpp ======================================================== - -*/ - -// -// TODO: -// Take advantage of new monster fields like m_hEnemy and get rid of that OFFSET() stuff -// Revisit enemy validation stuff, maybe it's not necessary with the newest monster code -// - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "effects.h" -#include "skill.h" - -extern Vector VecBModelOrigin( entvars_t* pevBModel ); - -#define TURRET_SHOTS 2 -#define TURRET_RANGE (100 * 12) -#define TURRET_SPREAD Vector( 0, 0, 0 ) -#define TURRET_TURNRATE 30 //angles per 0.1 second -#define TURRET_MAXWAIT 15 // seconds turret will stay active w/o a target -#define TURRET_MAXSPIN 5 // seconds turret barrel will spin w/o a target -#define TURRET_MACHINE_VOLUME 0.5 - - -void CMBaseTurret::KeyValue( KeyValueData *pkvd ) -{ - if (FStrEq(pkvd->szKeyName, "maxsleep")) - { - m_flMaxWait = atof(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else if (FStrEq(pkvd->szKeyName, "orientation")) - { - m_iOrientation = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - - } - else if (FStrEq(pkvd->szKeyName, "searchspeed")) - { - m_iSearchSpeed = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - - } - else if (FStrEq(pkvd->szKeyName, "turnrate")) - { - m_iBaseTurnRate = atoi(pkvd->szValue); - pkvd->fHandled = TRUE; - } - else - CMBaseMonster::KeyValue( pkvd ); -} - - -void CMBaseTurret::Spawn() -{ - Precache( ); - pev->nextthink = gpGlobals->time + 1; - pev->movetype = MOVETYPE_FLY; - pev->sequence = 0; - pev->frame = 0; - pev->solid = SOLID_SLIDEBOX; - pev->takedamage = DAMAGE_AIM; - - SetBits (pev->flags, FL_MONSTER); - SetUse( &CMBaseTurret::TurretUse ); - - if (( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) - && !( pev->spawnflags & SF_MONSTER_TURRET_STARTINACTIVE )) - { - m_iAutoStart = TRUE; - } - - ResetSequenceInfo( ); - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - m_flFieldOfView = VIEW_FIELD_FULL; - // m_flSightRange = TURRET_RANGE; -} - - -void CMBaseTurret::Precache( ) -{ - PRECACHE_SOUND ("turret/tu_fire1.wav"); - PRECACHE_SOUND ("turret/tu_ping.wav"); - PRECACHE_SOUND ("turret/tu_active2.wav"); - PRECACHE_SOUND ("turret/tu_die.wav"); - PRECACHE_SOUND ("turret/tu_die2.wav"); - PRECACHE_SOUND ("turret/tu_die3.wav"); - // PRECACHE_SOUND ("turret/tu_retract.wav"); // just use deploy sound to save memory - PRECACHE_SOUND ("turret/tu_deploy.wav"); - PRECACHE_SOUND ("turret/tu_spinup.wav"); - PRECACHE_SOUND ("turret/tu_spindown.wav"); - PRECACHE_SOUND ("turret/tu_search.wav"); - PRECACHE_SOUND ("turret/tu_alert.wav"); -} - -#define TURRET_GLOW_SPRITE "sprites/flare3.spr" - -void CMTurret::Spawn() -{ - Precache( ); - SET_MODEL(ENT(pev), "models/turret.mdl"); - pev->health = gSkillData.turretHealth; - m_HackedGunPos = Vector( 0, 0, 12.75 ); - m_flMaxSpin = TURRET_MAXSPIN; - pev->view_ofs.z = 12.75; - - CMBaseTurret::Spawn( ); - - m_iRetractHeight = 16; - m_iDeployHeight = 32; - m_iMinPitch = -15; - UTIL_SetSize(pev, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight)); - - SetThink(&CMTurret::Initialize); - - m_pEyeGlow = CMSprite::SpriteCreate( TURRET_GLOW_SPRITE, pev->origin, FALSE ); - m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation ); - m_pEyeGlow->SetAttachment( edict(), 2 ); - m_eyeBrightness = 0; - - pev->nextthink = gpGlobals->time + 0.3; - - pev->classname = MAKE_STRING( "monster_turret" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Turret" ); - } -} - -void CMTurret::Precache() -{ - CMBaseTurret::Precache( ); - PRECACHE_MODEL ("models/turret.mdl"); - PRECACHE_MODEL (TURRET_GLOW_SPRITE); -} - -void CMMiniTurret::Spawn() -{ - Precache( ); - SET_MODEL(ENT(pev), "models/miniturret.mdl"); - pev->health = gSkillData.miniturretHealth; - m_HackedGunPos = Vector( 0, 0, 12.75 ); - m_flMaxSpin = 0; - pev->view_ofs.z = 12.75; - - CMBaseTurret::Spawn( ); - m_iRetractHeight = 16; - m_iDeployHeight = 32; - m_iMinPitch = -15; - UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); - - SetThink(&CMMiniTurret::Initialize); - pev->nextthink = gpGlobals->time + 0.3; - - pev->classname = MAKE_STRING( "monster_miniturret" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Mini-Turret" ); - } -} - - -void CMMiniTurret::Precache() -{ - CMBaseTurret::Precache( ); - PRECACHE_MODEL ("models/miniturret.mdl"); - PRECACHE_SOUND("weapons/hks1.wav"); - PRECACHE_SOUND("weapons/hks2.wav"); - PRECACHE_SOUND("weapons/hks3.wav"); -} - -void CMBaseTurret::Initialize(void) -{ - m_iOn = 0; - m_fBeserk = 0; - m_iSpin = 0; - - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - - if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE; - if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT; - m_flStartYaw = pev->angles.y; - if (m_iOrientation == 1) - { - pev->idealpitch = 180; - pev->angles.x = 180; - pev->view_ofs.z = -pev->view_ofs.z; - pev->effects |= EF_INVLIGHT; - pev->angles.y = pev->angles.y + 180; - if (pev->angles.y > 360) - pev->angles.y = pev->angles.y - 360; - } - - m_vecGoalAngles.x = 0; - - if (m_iAutoStart) - { - m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CMBaseTurret::AutoSearchThink); - pev->nextthink = gpGlobals->time + .1; - } - else - SetThink(&CMBaseTurret::SUB_DoNothing); -} - -void CMBaseTurret::TurretUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) -{ - if ( !ShouldToggle( useType, m_iOn ) ) - return; - - if (m_iOn) - { - m_hEnemy = NULL; - pev->nextthink = gpGlobals->time + 0.1; - m_iAutoStart = FALSE;// switching off a turret disables autostart - //!!!! this should spin down first!!BUGBUG - SetThink(&CMBaseTurret::Retire); - } - else - { - pev->nextthink = gpGlobals->time + 0.1; // turn on delay - - // if the turret is flagged as an autoactivate turret, re-enable it's ability open self. - if ( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) - { - m_iAutoStart = TRUE; - } - - SetThink(&CMBaseTurret::Deploy); - } -} - - -void CMBaseTurret::Ping( void ) -{ - // make the pinging noise every second while searching - if (m_flPingTime == 0) - m_flPingTime = gpGlobals->time + 1; - else if (m_flPingTime <= gpGlobals->time) - { - m_flPingTime = gpGlobals->time + 1; - EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_ping.wav", 1, ATTN_NORM); - EyeOn( ); - } - else if (m_eyeBrightness > 0) - { - EyeOff( ); - } -} - - -void CMBaseTurret::EyeOn( ) -{ - if (m_pEyeGlow) - { - if (m_eyeBrightness != 255) - { - m_eyeBrightness = 255; - } - m_pEyeGlow->SetBrightness( m_eyeBrightness ); - } -} - - -void CMBaseTurret::EyeOff( ) -{ - if (m_pEyeGlow) - { - if (m_eyeBrightness > 0) - { - m_eyeBrightness = max( 0, m_eyeBrightness - 30 ); - m_pEyeGlow->SetBrightness( m_eyeBrightness ); - } - } -} - - -void CMBaseTurret::ActiveThink(void) -{ - int fAttack = 0; - Vector vecDirToEnemy; - - pev->nextthink = gpGlobals->time + 0.1; - StudioFrameAdvance( ); - - if ((!m_iOn) || (m_hEnemy == NULL)) - { - m_hEnemy = NULL; - m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CMBaseTurret::SearchThink); - return; - } - - // if it's dead, look for something new - if ( !UTIL_IsAlive( m_hEnemy ) ) - { - if (!m_flLastSight) - { - m_flLastSight = gpGlobals->time + 0.5; // continue-shooting timeout - } - else - { - if (gpGlobals->time > m_flLastSight) - { - m_hEnemy = NULL; - m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CMBaseTurret::SearchThink); - return; - } - } - } - - Vector vecMid = pev->origin + pev->view_ofs; - Vector vecMidEnemy = UTIL_BodyTarget( m_hEnemy, vecMid ); // m_hEnemy->BodyTarget( vecMid ); - - // Look for our current enemy - int fEnemyVisible = FBoxVisible(pev, VARS(m_hEnemy), vecMidEnemy ); - - vecDirToEnemy = vecMidEnemy - vecMid; // calculate dir and dist to enemy - float flDistToEnemy = vecDirToEnemy.Length(); - - Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid); - - // Current enemy is not visible. - if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE)) - { - if (!m_flLastSight) - m_flLastSight = gpGlobals->time + 0.5; - else - { - // Should we look for a new target? - if (gpGlobals->time > m_flLastSight) - { - m_hEnemy = NULL; - m_flLastSight = gpGlobals->time + m_flMaxWait; - SetThink(&CMBaseTurret::SearchThink); - return; - } - } - fEnemyVisible = 0; - } - else - { - m_vecLastSight = vecMidEnemy; - } - - UTIL_MakeAimVectors(m_vecCurAngles); - - /* - ALERT( at_console, "%.0f %.0f : %.2f %.2f %.2f\n", - m_vecCurAngles.x, m_vecCurAngles.y, - gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_forward.z ); - */ - - Vector vecLOS = vecDirToEnemy; //vecMid - m_vecLastSight; - vecLOS = vecLOS.Normalize(); - - // Is the Gun looking at the target - if (DotProduct(vecLOS, gpGlobals->v_forward) <= 0.866) // 30 degree slop - fAttack = FALSE; - else - fAttack = TRUE; - - // fire the gun - if (m_iSpin && ((fAttack) || (m_fBeserk))) - { - Vector vecSrc, vecAng; - GetAttachment( 0, vecSrc, vecAng ); - SetTurretAnim(TURRET_ANIM_FIRE); - Shoot(vecSrc, gpGlobals->v_forward ); - } - else - { - SetTurretAnim(TURRET_ANIM_SPIN); - } - - //move the gun - if (m_fBeserk) - { - if (RANDOM_LONG(0,9) == 0) - { - m_vecGoalAngles.y = RANDOM_FLOAT(0,360); - m_vecGoalAngles.x = RANDOM_FLOAT(0,90) - 90 * m_iOrientation; - TakeDamage(pev,pev,1, DMG_GENERIC); // don't beserk forever - return; - } - } - else if (fEnemyVisible) - { - if (vec.y > 360) - vec.y -= 360; - - if (vec.y < 0) - vec.y += 360; - - //ALERT(at_console, "[%.2f]", vec.x); - - if (vec.x < -180) - vec.x += 360; - - if (vec.x > 180) - vec.x -= 360; - - // now all numbers should be in [1...360] - // pin to turret limitations to [-90...15] - - if (m_iOrientation == 0) - { - if (vec.x > 90) - vec.x = 90; - else if (vec.x < m_iMinPitch) - vec.x = m_iMinPitch; - } - else - { - if (vec.x < -90) - vec.x = -90; - else if (vec.x > -m_iMinPitch) - vec.x = -m_iMinPitch; - } - - // ALERT(at_console, "->[%.2f]\n", vec.x); - - m_vecGoalAngles.y = vec.y; - m_vecGoalAngles.x = vec.x; - - } - - SpinUpCall(); - MoveTurret(); -} - - -void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) -{ - FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1 ); - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.6); - pev->effects = pev->effects | EF_MUZZLEFLASH; -} - - -void CMMiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) -{ - FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_9MM, 1 ); - - switch(RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break; - } - pev->effects = pev->effects | EF_MUZZLEFLASH; -} - - -void CMBaseTurret::Deploy(void) -{ - pev->nextthink = gpGlobals->time + 0.1; - StudioFrameAdvance( ); - - if (pev->sequence != TURRET_ANIM_DEPLOY) - { - m_iOn = 1; - SetTurretAnim(TURRET_ANIM_DEPLOY); - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - SUB_UseTargets( this->edict(), USE_ON, 0 ); - } - - if (m_fSequenceFinished) - { - pev->maxs.z = m_iDeployHeight; - pev->mins.z = -m_iDeployHeight; - UTIL_SetSize(pev, pev->mins, pev->maxs); - - m_vecCurAngles.x = 0; - - if (m_iOrientation == 1) - { - m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y + 180 ); - } - else - { - m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y ); - } - - SetTurretAnim(TURRET_ANIM_SPIN); - pev->framerate = 0; - SetThink(&CMBaseTurret::SearchThink); - } - - m_flLastSight = gpGlobals->time + m_flMaxWait; -} - -void CMBaseTurret::Retire(void) -{ - // make the turret level - m_vecGoalAngles.x = 0; - m_vecGoalAngles.y = m_flStartYaw; - - pev->nextthink = gpGlobals->time + 0.1; - - StudioFrameAdvance( ); - - EyeOff( ); - - if (!MoveTurret()) - { - if (m_iSpin) - { - SpinDownCall(); - } - else if (pev->sequence != TURRET_ANIM_RETIRE) - { - SetTurretAnim(TURRET_ANIM_RETIRE); - EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM, 0, 120); - SUB_UseTargets( this->edict(), USE_OFF, 0 ); - } - else if (m_fSequenceFinished) - { - m_iOn = 0; - m_flLastSight = 0; - SetTurretAnim(TURRET_ANIM_NONE); - pev->maxs.z = m_iRetractHeight; - pev->mins.z = -m_iRetractHeight; - UTIL_SetSize(pev, pev->mins, pev->maxs); - if (m_iAutoStart) - { - SetThink(&CMBaseTurret::AutoSearchThink); - pev->nextthink = gpGlobals->time + .1; - } - else - SetThink(&CMBaseTurret::SUB_DoNothing); - } - } - else - { - SetTurretAnim(TURRET_ANIM_SPIN); - } -} - - -void CMTurret::SpinUpCall(void) -{ - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - // Are we already spun up? If not start the two stage process. - if (!m_iSpin) - { - SetTurretAnim( TURRET_ANIM_SPIN ); - // for the first pass, spin up the the barrel - if (!m_iStartSpin) - { - pev->nextthink = gpGlobals->time + 1.0; // spinup delay - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_spinup.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - m_iStartSpin = 1; - pev->framerate = 0.1; - } - // after the barrel is spun up, turn on the hum - else if (pev->framerate >= 1.0) - { - pev->nextthink = gpGlobals->time + 0.1; // retarget delay - EMIT_SOUND(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - SetThink(&CMTurret::ActiveThink); - m_iStartSpin = 0; - m_iSpin = 1; - } - else - { - pev->framerate += 0.075; - } - } - - if (m_iSpin) - { - SetThink(&CMTurret::ActiveThink); - } -} - - -void CMTurret::SpinDownCall(void) -{ - if (m_iSpin) - { - SetTurretAnim( TURRET_ANIM_SPIN ); - if (pev->framerate == 1.0) - { - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); - EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_spindown.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - } - pev->framerate -= 0.02; - if (pev->framerate <= 0) - { - pev->framerate = 0; - m_iSpin = 0; - } - } -} - - -void CMTurret::Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->health = 0; - pev->takedamage = DAMAGE_NO; - pev->dmgtime = gpGlobals->time; - - ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? - - SetUse(NULL); - SetThink(&CMBaseTurret::TurretDeath); - SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others - pev->nextthink = gpGlobals->time + 0.1; - - CMBaseMonster::Killed( pevAttacker, iGib ); -} - - -void CMMiniTurret::Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->health = 0; - pev->takedamage = DAMAGE_NO; - pev->dmgtime = gpGlobals->time; - - ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? - - SetUse(NULL); - SetThink(&CMBaseTurret::TurretDeath); - SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others - pev->nextthink = gpGlobals->time + 0.1; - - CMBaseMonster::Killed( pevAttacker, iGib ); -} - -void CMBaseTurret::SetTurretAnim(TURRET_ANIM anim) -{ - if (pev->sequence != anim) - { - switch(anim) - { - case TURRET_ANIM_FIRE: - case TURRET_ANIM_SPIN: - if (pev->sequence != TURRET_ANIM_FIRE && pev->sequence != TURRET_ANIM_SPIN) - { - pev->frame = 0; - } - break; - default: - pev->frame = 0; - break; - } - - pev->sequence = anim; - ResetSequenceInfo( ); - - switch(anim) - { - case TURRET_ANIM_RETIRE: - pev->frame = 255; - pev->framerate = -1.0; - break; - case TURRET_ANIM_DIE: - pev->framerate = 1.0; - break; - } - //ALERT(at_console, "Turret anim #%d\n", anim); - } -} - - -// -// This search function will sit with the turret deployed and look for a new target. -// After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will -// retact. -// -void CMBaseTurret::SearchThink(void) -{ - // ensure rethink - SetTurretAnim(TURRET_ANIM_SPIN); - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - if (m_flSpinUpTime == 0 && m_flMaxSpin) - m_flSpinUpTime = gpGlobals->time + m_flMaxSpin; - - Ping( ); - - // If we have a target and we're still healthy - if (m_hEnemy != NULL) - { - if (!UTIL_IsAlive( m_hEnemy ) ) - m_hEnemy = NULL;// Dead enemy forces a search for new one - } - - - // Acquire Target - if (m_hEnemy == NULL) - { - Look(TURRET_RANGE); - m_hEnemy = BestVisibleEnemy(); - } - - // If we've found a target, spin up the barrel and start to attack - if (m_hEnemy != NULL) - { - m_flLastSight = 0; - m_flSpinUpTime = 0; - SetThink(&CMBaseTurret::ActiveThink); - } - else - { - // Are we out of time, do we need to retract? - if (gpGlobals->time > m_flLastSight) - { - //Before we retrace, make sure that we are spun down. - m_flLastSight = 0; - m_flSpinUpTime = 0; - SetThink(&CMBaseTurret::Retire); - } - // should we stop the spin? - else if ((m_flSpinUpTime) && (gpGlobals->time > m_flSpinUpTime)) - { - SpinDownCall(); - } - - // generic hunt for new victims - m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate); - if (m_vecGoalAngles.y >= 360) - m_vecGoalAngles.y -= 360; - MoveTurret(); - } -} - - -// -// This think function will deploy the turret when something comes into range. This is for -// automatically activated turrets. -// -void CMBaseTurret::AutoSearchThink(void) -{ - // ensure rethink - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.3; - - // If we have a target and we're still healthy - - if (m_hEnemy != NULL) - { - if (!UTIL_IsAlive( m_hEnemy ) ) - m_hEnemy = NULL;// Dead enemy forces a search for new one - } - - // Acquire Target - - if (m_hEnemy == NULL) - { - Look( TURRET_RANGE ); - m_hEnemy = BestVisibleEnemy(); - } - - if (m_hEnemy != NULL) - { - SetThink(&CMBaseTurret::Deploy); - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_alert.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); - } -} - - -void CMBaseTurret :: TurretDeath( void ) -{ - BOOL iActive = FALSE; - - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - if (pev->deadflag != DEAD_DEAD) - { - pev->deadflag = DEAD_DEAD; - - float flRndSound = RANDOM_FLOAT ( 0 , 1 ); - - if ( flRndSound <= 0.33 ) - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM); - else if ( flRndSound <= 0.66 ) - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM); - else - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM); - - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); - - if (m_iOrientation == 0) - m_vecGoalAngles.x = -15; - else - m_vecGoalAngles.x = -90; - - SetTurretAnim(TURRET_ANIM_DIE); - - EyeOn( ); - } - - EyeOff( ); - - if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) - { - // lots of smoke - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) ); - WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) ); - WRITE_COORD( pev->origin.z - m_iOrientation * 64 ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( 25 ); // scale * 10 - WRITE_BYTE( 10 - m_iOrientation * 5); // framerate - MESSAGE_END(); - } - - if (pev->dmgtime + RANDOM_FLOAT( 0, 5 ) > gpGlobals->time) - { - Vector vecSrc = Vector( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ), RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ), 0 ); - if (m_iOrientation == 0) - vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->origin.z, pev->absmax.z ) ); - else - vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->absmin.z, pev->origin.z ) ); - - UTIL_Sparks( vecSrc ); - } - - if (m_fSequenceFinished && pev->dmgtime + 5 < gpGlobals->time) - { - pev->framerate = 0; - SetThink( NULL ); - SUB_StartFadeOut(); - } -} - - - -void CMBaseTurret :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - if ( ptr->iHitgroup == 10 ) - { - // hit armor - if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) - { - UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); - pev->dmgtime = gpGlobals->time; - } - - flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated - } - - if ( !pev->takedamage ) - return; - - AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); -} - -// take damage. bitsDamageType indicates type of damage sustained, ie: DMG_BULLET - -int CMBaseTurret::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - if ( !pev->takedamage ) - return 0; - - if (!m_iOn) - flDamage /= 10.0; - - pev->health -= flDamage; - - if (pev->health <= 10) - { - if (m_iOn && (1 || RANDOM_LONG(0, 0x7FFF) > 800)) - { - m_fBeserk = 1; - SetThink(&CMBaseTurret::SearchThink); - } - } - - return 1; -} - -int CMBaseTurret::MoveTurret(void) -{ - int state = 0; - // any x movement? - - if (m_vecCurAngles.x != m_vecGoalAngles.x) - { - float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ; - - m_vecCurAngles.x += 0.1 * m_fTurnRate * flDir; - - // if we started below the goal, and now we're past, peg to goal - if (flDir == 1) - { - if (m_vecCurAngles.x > m_vecGoalAngles.x) - m_vecCurAngles.x = m_vecGoalAngles.x; - } - else - { - if (m_vecCurAngles.x < m_vecGoalAngles.x) - m_vecCurAngles.x = m_vecGoalAngles.x; - } - - if (m_iOrientation == 0) - SetBoneController(1, -m_vecCurAngles.x); - else - SetBoneController(1, m_vecCurAngles.x); - state = 1; - } - - if (m_vecCurAngles.y != m_vecGoalAngles.y) - { - float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ; - float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y); - - if (flDist > 180) - { - flDist = 360 - flDist; - flDir = -flDir; - } - if (flDist > 30) - { - if (m_fTurnRate < m_iBaseTurnRate * 10) - { - m_fTurnRate += m_iBaseTurnRate; - } - } - else if (m_fTurnRate > 45) - { - m_fTurnRate -= m_iBaseTurnRate; - } - else - { - m_fTurnRate += m_iBaseTurnRate; - } - - m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir; - - if (m_vecCurAngles.y < 0) - m_vecCurAngles.y += 360; - else if (m_vecCurAngles.y >= 360) - m_vecCurAngles.y -= 360; - - if (flDist < (0.05 * m_iBaseTurnRate)) - m_vecCurAngles.y = m_vecGoalAngles.y; - - //ALERT(at_console, "%.2f -> %.2f\n", m_vecCurAngles.y, y); - if (m_iOrientation == 0) - SetBoneController(0, m_vecCurAngles.y - pev->angles.y ); - else - SetBoneController(0, pev->angles.y - 180 - m_vecCurAngles.y ); - state = 1; - } - - if (!state) - m_fTurnRate = m_iBaseTurnRate; - - //ALERT(at_console, "(%.2f, %.2f)->(%.2f, %.2f)\n", m_vecCurAngles.x, - // m_vecCurAngles.y, m_vecGoalAngles.x, m_vecGoalAngles.y); - return state; -} - -// -// ID as a machine -// -int CMBaseTurret::Classify ( void ) -{ - if (m_iOn || m_iAutoStart) - { - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_MACHINE; - } - - return CLASS_NONE; -} - - -void CMSentry::Precache() -{ - CMBaseTurret::Precache( ); - PRECACHE_MODEL ("models/sentry.mdl"); -} - -void CMSentry::Spawn() -{ - Precache( ); - SET_MODEL(ENT(pev), "models/sentry.mdl"); - pev->health = gSkillData.sentryHealth; - m_HackedGunPos = Vector( 0, 0, 48 ); - pev->view_ofs.z = 48; - m_flMaxWait = 1E6; - m_flMaxSpin = 1E6; - - CMBaseTurret::Spawn(); - m_iRetractHeight = 64; - m_iDeployHeight = 64; - m_iMinPitch = -60; - UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); - - SetTouch(&CMSentry::SentryTouch); - SetThink(&CMSentry::Initialize); - pev->nextthink = gpGlobals->time + 0.3; - - pev->classname = MAKE_STRING( "monster_sentry" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Sentry Turret" ); - } -} - -void CMSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) -{ - FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_MP5, 1 ); - - switch(RANDOM_LONG(0,2)) - { - case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break; - case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break; - case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break; - } - pev->effects = pev->effects | EF_MUZZLEFLASH; -} - -int CMSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - if ( !pev->takedamage ) - return 0; - - if (!m_iOn) - { - SetThink( &CMSentry::Deploy ); - SetUse( NULL ); - pev->nextthink = gpGlobals->time + 0.1; - } - - pev->health -= flDamage; - return 1; -} - - -void CMSentry::SentryTouch( edict_t *pOther ) -{ - if ( pOther && (UTIL_IsPlayer( pOther ) || (pOther->v.flags & FL_MONSTER)) ) - { - UTIL_TakeDamage(pOther, VARS(pOther), VARS(pOther), 0, 0 ); - } -} - - -void CMSentry::Killed( entvars_t *pevAttacker, int iGib ) -{ - pev->health = 0; - pev->takedamage = DAMAGE_NO; - pev->dmgtime = gpGlobals->time; - - ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? - - SetUse(NULL); - SetThink( &CMSentry::SentryDeath); - SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others - pev->nextthink = gpGlobals->time + 0.1; - - CMBaseMonster::Killed( pevAttacker, iGib ); -} - - -void CMSentry::SentryDeath( void ) -{ - BOOL iActive = FALSE; - - StudioFrameAdvance( ); - pev->nextthink = gpGlobals->time + 0.1; - - if (pev->deadflag != DEAD_DEAD) - { - pev->deadflag = DEAD_DEAD; - - float flRndSound = RANDOM_FLOAT ( 0 , 1 ); - - if ( flRndSound <= 0.33 ) - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM); - else if ( flRndSound <= 0.66 ) - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM); - else - EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM); - - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); - - SetBoneController( 0, 0 ); - SetBoneController( 1, 0 ); - - SetTurretAnim(TURRET_ANIM_DIE); - - pev->solid = SOLID_NOT; - pev->angles.y = UTIL_AngleMod( pev->angles.y + RANDOM_LONG( 0, 2 ) * 120 ); - - EyeOn( ); - } - - EyeOff( ); - - Vector vecSrc, vecAng; - GetAttachment( 1, vecSrc, vecAng ); - - if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) - { - // lots of smoke - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( vecSrc.x + RANDOM_FLOAT( -16, 16 ) ); - WRITE_COORD( vecSrc.y + RANDOM_FLOAT( -16, 16 ) ); - WRITE_COORD( vecSrc.z - 32 ); - WRITE_SHORT( g_sModelIndexSmoke ); - WRITE_BYTE( 15 ); // scale * 10 - WRITE_BYTE( 8 ); // framerate - MESSAGE_END(); - } - - if (pev->dmgtime + RANDOM_FLOAT( 0, 8 ) > gpGlobals->time) - { - UTIL_Sparks( vecSrc ); - } - - if (m_fSequenceFinished && pev->dmgtime + 5 < gpGlobals->time) - { - pev->framerate = 0; - SetThink( NULL ); - SUB_StartFadeOut(); - } -} - +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +/* + +===== turret.cpp ======================================================== + +*/ + +// +// TODO: +// Take advantage of new monster fields like m_hEnemy and get rid of that OFFSET() stuff +// Revisit enemy validation stuff, maybe it's not necessary with the newest monster code +// + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "effects.h" +#include "skill.h" + +extern Vector VecBModelOrigin( entvars_t* pevBModel ); + +#define TURRET_SHOTS 2 +#define TURRET_RANGE (100 * 12) +#define TURRET_SPREAD Vector( 0, 0, 0 ) +#define TURRET_TURNRATE 30 //angles per 0.1 second +#define TURRET_MAXWAIT 15 // seconds turret will stay active w/o a target +#define TURRET_MAXSPIN 5 // seconds turret barrel will spin w/o a target +#define TURRET_MACHINE_VOLUME 0.5 + + +void CMBaseTurret::KeyValue( KeyValueData *pkvd ) +{ + if (FStrEq(pkvd->szKeyName, "maxsleep")) + { + m_flMaxWait = atof(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else if (FStrEq(pkvd->szKeyName, "orientation")) + { + m_iOrientation = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + + } + else if (FStrEq(pkvd->szKeyName, "searchspeed")) + { + m_iSearchSpeed = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + + } + else if (FStrEq(pkvd->szKeyName, "turnrate")) + { + m_iBaseTurnRate = atoi(pkvd->szValue); + pkvd->fHandled = TRUE; + } + else + CMBaseMonster::KeyValue( pkvd ); +} + + +void CMBaseTurret::Spawn() +{ + Precache( ); + pev->nextthink = gpGlobals->time + 1; + pev->movetype = MOVETYPE_FLY; + pev->sequence = 0; + pev->frame = 0; + pev->solid = SOLID_SLIDEBOX; + pev->takedamage = DAMAGE_AIM; + + SetBits (pev->flags, FL_MONSTER); + SetUse( &CMBaseTurret::TurretUse ); + + if (( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) + && !( pev->spawnflags & SF_MONSTER_TURRET_STARTINACTIVE )) + { + m_iAutoStart = TRUE; + } + + ResetSequenceInfo( ); + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + m_flFieldOfView = VIEW_FIELD_FULL; + // m_flSightRange = TURRET_RANGE; +} + + +void CMBaseTurret::Precache( ) +{ + PRECACHE_SOUND ("turret/tu_fire1.wav"); + PRECACHE_SOUND ("turret/tu_ping.wav"); + PRECACHE_SOUND ("turret/tu_active2.wav"); + PRECACHE_SOUND ("turret/tu_die.wav"); + PRECACHE_SOUND ("turret/tu_die2.wav"); + PRECACHE_SOUND ("turret/tu_die3.wav"); + // PRECACHE_SOUND ("turret/tu_retract.wav"); // just use deploy sound to save memory + PRECACHE_SOUND ("turret/tu_deploy.wav"); + PRECACHE_SOUND ("turret/tu_spinup.wav"); + PRECACHE_SOUND ("turret/tu_spindown.wav"); + PRECACHE_SOUND ("turret/tu_search.wav"); + PRECACHE_SOUND ("turret/tu_alert.wav"); +} + +#define TURRET_GLOW_SPRITE "sprites/flare3.spr" + +void CMTurret::Spawn() +{ + Precache( ); + SET_MODEL(ENT(pev), "models/turret.mdl"); + pev->health = gSkillData.turretHealth; + m_HackedGunPos = Vector( 0, 0, 12.75 ); + m_flMaxSpin = TURRET_MAXSPIN; + pev->view_ofs.z = 12.75; + + CMBaseTurret::Spawn( ); + + m_iRetractHeight = 16; + m_iDeployHeight = 32; + m_iMinPitch = -15; + UTIL_SetSize(pev, Vector(-32, -32, -m_iRetractHeight), Vector(32, 32, m_iRetractHeight)); + + SetThink(&CMTurret::Initialize); + + m_pEyeGlow = CMSprite::SpriteCreate( TURRET_GLOW_SPRITE, pev->origin, FALSE ); + m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation ); + m_pEyeGlow->SetAttachment( edict(), 2 ); + m_eyeBrightness = 0; + + pev->nextthink = gpGlobals->time + 0.3; + + pev->classname = MAKE_STRING( "monster_turret" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Turret" ); + } +} + +void CMTurret::Precache() +{ + CMBaseTurret::Precache( ); + PRECACHE_MODEL ("models/turret.mdl"); + PRECACHE_MODEL (TURRET_GLOW_SPRITE); +} + +void CMMiniTurret::Spawn() +{ + Precache( ); + SET_MODEL(ENT(pev), "models/miniturret.mdl"); + pev->health = gSkillData.miniturretHealth; + m_HackedGunPos = Vector( 0, 0, 12.75 ); + m_flMaxSpin = 0; + pev->view_ofs.z = 12.75; + + CMBaseTurret::Spawn( ); + m_iRetractHeight = 16; + m_iDeployHeight = 32; + m_iMinPitch = -15; + UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); + + SetThink(&CMMiniTurret::Initialize); + pev->nextthink = gpGlobals->time + 0.3; + + pev->classname = MAKE_STRING( "monster_miniturret" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Mini-Turret" ); + } +} + + +void CMMiniTurret::Precache() +{ + CMBaseTurret::Precache( ); + PRECACHE_MODEL ("models/miniturret.mdl"); + PRECACHE_SOUND("weapons/hks1.wav"); + PRECACHE_SOUND("weapons/hks2.wav"); + PRECACHE_SOUND("weapons/hks3.wav"); +} + +void CMBaseTurret::Initialize(void) +{ + m_iOn = 0; + m_fBeserk = 0; + m_iSpin = 0; + + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + + if (m_iBaseTurnRate == 0) m_iBaseTurnRate = TURRET_TURNRATE; + if (m_flMaxWait == 0) m_flMaxWait = TURRET_MAXWAIT; + m_flStartYaw = pev->angles.y; + if (m_iOrientation == 1) + { + pev->idealpitch = 180; + pev->angles.x = 180; + pev->view_ofs.z = -pev->view_ofs.z; + pev->effects |= EF_INVLIGHT; + pev->angles.y = pev->angles.y + 180; + if (pev->angles.y > 360) + pev->angles.y = pev->angles.y - 360; + } + + m_vecGoalAngles.x = 0; + + if (m_iAutoStart) + { + m_flLastSight = gpGlobals->time + m_flMaxWait; + SetThink(&CMBaseTurret::AutoSearchThink); + pev->nextthink = gpGlobals->time + .1; + } + else + SetThink(&CMBaseTurret::SUB_DoNothing); +} + +void CMBaseTurret::TurretUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ) +{ + if ( !ShouldToggle( useType, m_iOn ) ) + return; + + if (m_iOn) + { + m_hEnemy = NULL; + pev->nextthink = gpGlobals->time + 0.1; + m_iAutoStart = FALSE;// switching off a turret disables autostart + //!!!! this should spin down first!!BUGBUG + SetThink(&CMBaseTurret::Retire); + } + else + { + pev->nextthink = gpGlobals->time + 0.1; // turn on delay + + // if the turret is flagged as an autoactivate turret, re-enable it's ability open self. + if ( pev->spawnflags & SF_MONSTER_TURRET_AUTOACTIVATE ) + { + m_iAutoStart = TRUE; + } + + SetThink(&CMBaseTurret::Deploy); + } +} + + +void CMBaseTurret::Ping( void ) +{ + // make the pinging noise every second while searching + if (m_flPingTime == 0) + m_flPingTime = gpGlobals->time + 1; + else if (m_flPingTime <= gpGlobals->time) + { + m_flPingTime = gpGlobals->time + 1; + EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_ping.wav", 1, ATTN_NORM); + EyeOn( ); + } + else if (m_eyeBrightness > 0) + { + EyeOff( ); + } +} + + +void CMBaseTurret::EyeOn( ) +{ + if (m_pEyeGlow) + { + if (m_eyeBrightness != 255) + { + m_eyeBrightness = 255; + } + m_pEyeGlow->SetBrightness( m_eyeBrightness ); + } +} + + +void CMBaseTurret::EyeOff( ) +{ + if (m_pEyeGlow) + { + if (m_eyeBrightness > 0) + { + m_eyeBrightness = max( 0, m_eyeBrightness - 30 ); + m_pEyeGlow->SetBrightness( m_eyeBrightness ); + } + } +} + + +void CMBaseTurret::ActiveThink(void) +{ + int fAttack = 0; + Vector vecDirToEnemy; + + pev->nextthink = gpGlobals->time + 0.1; + StudioFrameAdvance( ); + + if ((!m_iOn) || (m_hEnemy == NULL)) + { + m_hEnemy = NULL; + m_flLastSight = gpGlobals->time + m_flMaxWait; + SetThink(&CMBaseTurret::SearchThink); + return; + } + + // if it's dead, look for something new + if ( !UTIL_IsAlive( m_hEnemy ) ) + { + if (!m_flLastSight) + { + m_flLastSight = gpGlobals->time + 0.5; // continue-shooting timeout + } + else + { + if (gpGlobals->time > m_flLastSight) + { + m_hEnemy = NULL; + m_flLastSight = gpGlobals->time + m_flMaxWait; + SetThink(&CMBaseTurret::SearchThink); + return; + } + } + } + + Vector vecMid = pev->origin + pev->view_ofs; + Vector vecMidEnemy = UTIL_BodyTarget( m_hEnemy, vecMid ); // m_hEnemy->BodyTarget( vecMid ); + + // Look for our current enemy + int fEnemyVisible = FBoxVisible(pev, VARS(m_hEnemy), vecMidEnemy ); + + vecDirToEnemy = vecMidEnemy - vecMid; // calculate dir and dist to enemy + float flDistToEnemy = vecDirToEnemy.Length(); + + Vector vec = UTIL_VecToAngles(vecMidEnemy - vecMid); + + // Current enemy is not visible. + if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE)) + { + if (!m_flLastSight) + m_flLastSight = gpGlobals->time + 0.5; + else + { + // Should we look for a new target? + if (gpGlobals->time > m_flLastSight) + { + m_hEnemy = NULL; + m_flLastSight = gpGlobals->time + m_flMaxWait; + SetThink(&CMBaseTurret::SearchThink); + return; + } + } + fEnemyVisible = 0; + } + else + { + m_vecLastSight = vecMidEnemy; + } + + UTIL_MakeAimVectors(m_vecCurAngles); + + /* + ALERT( at_console, "%.0f %.0f : %.2f %.2f %.2f\n", + m_vecCurAngles.x, m_vecCurAngles.y, + gpGlobals->v_forward.x, gpGlobals->v_forward.y, gpGlobals->v_forward.z ); + */ + + Vector vecLOS = vecDirToEnemy; //vecMid - m_vecLastSight; + vecLOS = vecLOS.Normalize(); + + // Is the Gun looking at the target + if (DotProduct(vecLOS, gpGlobals->v_forward) <= 0.866) // 30 degree slop + fAttack = FALSE; + else + fAttack = TRUE; + + // fire the gun + if (m_iSpin && ((fAttack) || (m_fBeserk))) + { + Vector vecSrc, vecAng; + GetAttachment( 0, vecSrc, vecAng ); + SetTurretAnim(TURRET_ANIM_FIRE); + Shoot(vecSrc, gpGlobals->v_forward ); + } + else + { + SetTurretAnim(TURRET_ANIM_SPIN); + } + + //move the gun + if (m_fBeserk) + { + if (RANDOM_LONG(0,9) == 0) + { + m_vecGoalAngles.y = RANDOM_FLOAT(0,360); + m_vecGoalAngles.x = RANDOM_FLOAT(0,90) - 90 * m_iOrientation; + TakeDamage(pev,pev,1, DMG_GENERIC); // don't beserk forever + return; + } + } + else if (fEnemyVisible) + { + if (vec.y > 360) + vec.y -= 360; + + if (vec.y < 0) + vec.y += 360; + + //ALERT(at_console, "[%.2f]", vec.x); + + if (vec.x < -180) + vec.x += 360; + + if (vec.x > 180) + vec.x -= 360; + + // now all numbers should be in [1...360] + // pin to turret limitations to [-90...15] + + if (m_iOrientation == 0) + { + if (vec.x > 90) + vec.x = 90; + else if (vec.x < m_iMinPitch) + vec.x = m_iMinPitch; + } + else + { + if (vec.x < -90) + vec.x = -90; + else if (vec.x > -m_iMinPitch) + vec.x = -m_iMinPitch; + } + + // ALERT(at_console, "->[%.2f]\n", vec.x); + + m_vecGoalAngles.y = vec.y; + m_vecGoalAngles.x = vec.x; + + } + + SpinUpCall(); + MoveTurret(); +} + + +void CMTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_12MM, 1 ); + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "turret/tu_fire1.wav", 1, 0.6); + pev->effects = pev->effects | EF_MUZZLEFLASH; +} + + +void CMMiniTurret::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_9MM, 1 ); + + switch(RANDOM_LONG(0,2)) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break; + } + pev->effects = pev->effects | EF_MUZZLEFLASH; +} + + +void CMBaseTurret::Deploy(void) +{ + pev->nextthink = gpGlobals->time + 0.1; + StudioFrameAdvance( ); + + if (pev->sequence != TURRET_ANIM_DEPLOY) + { + m_iOn = 1; + SetTurretAnim(TURRET_ANIM_DEPLOY); + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); + SUB_UseTargets( this->edict(), USE_ON, 0 ); + } + + if (m_fSequenceFinished) + { + pev->maxs.z = m_iDeployHeight; + pev->mins.z = -m_iDeployHeight; + UTIL_SetSize(pev, pev->mins, pev->maxs); + + m_vecCurAngles.x = 0; + + if (m_iOrientation == 1) + { + m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y + 180 ); + } + else + { + m_vecCurAngles.y = UTIL_AngleMod( pev->angles.y ); + } + + SetTurretAnim(TURRET_ANIM_SPIN); + pev->framerate = 0; + SetThink(&CMBaseTurret::SearchThink); + } + + m_flLastSight = gpGlobals->time + m_flMaxWait; +} + +void CMBaseTurret::Retire(void) +{ + // make the turret level + m_vecGoalAngles.x = 0; + m_vecGoalAngles.y = m_flStartYaw; + + pev->nextthink = gpGlobals->time + 0.1; + + StudioFrameAdvance( ); + + EyeOff( ); + + if (!MoveTurret()) + { + if (m_iSpin) + { + SpinDownCall(); + } + else if (pev->sequence != TURRET_ANIM_RETIRE) + { + SetTurretAnim(TURRET_ANIM_RETIRE); + EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "turret/tu_deploy.wav", TURRET_MACHINE_VOLUME, ATTN_NORM, 0, 120); + SUB_UseTargets( this->edict(), USE_OFF, 0 ); + } + else if (m_fSequenceFinished) + { + m_iOn = 0; + m_flLastSight = 0; + SetTurretAnim(TURRET_ANIM_NONE); + pev->maxs.z = m_iRetractHeight; + pev->mins.z = -m_iRetractHeight; + UTIL_SetSize(pev, pev->mins, pev->maxs); + if (m_iAutoStart) + { + SetThink(&CMBaseTurret::AutoSearchThink); + pev->nextthink = gpGlobals->time + .1; + } + else + SetThink(&CMBaseTurret::SUB_DoNothing); + } + } + else + { + SetTurretAnim(TURRET_ANIM_SPIN); + } +} + + +void CMTurret::SpinUpCall(void) +{ + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + // Are we already spun up? If not start the two stage process. + if (!m_iSpin) + { + SetTurretAnim( TURRET_ANIM_SPIN ); + // for the first pass, spin up the the barrel + if (!m_iStartSpin) + { + pev->nextthink = gpGlobals->time + 1.0; // spinup delay + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_spinup.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); + m_iStartSpin = 1; + pev->framerate = 0.1; + } + // after the barrel is spun up, turn on the hum + else if (pev->framerate >= 1.0) + { + pev->nextthink = gpGlobals->time + 0.1; // retarget delay + EMIT_SOUND(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); + SetThink(&CMTurret::ActiveThink); + m_iStartSpin = 0; + m_iSpin = 1; + } + else + { + pev->framerate += 0.075; + } + } + + if (m_iSpin) + { + SetThink(&CMTurret::ActiveThink); + } +} + + +void CMTurret::SpinDownCall(void) +{ + if (m_iSpin) + { + SetTurretAnim( TURRET_ANIM_SPIN ); + if (pev->framerate == 1.0) + { + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); + EMIT_SOUND(ENT(pev), CHAN_ITEM, "turret/tu_spindown.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); + } + pev->framerate -= 0.02; + if (pev->framerate <= 0) + { + pev->framerate = 0; + m_iSpin = 0; + } + } +} + + +void CMTurret::Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->health = 0; + pev->takedamage = DAMAGE_NO; + pev->dmgtime = gpGlobals->time; + + ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? + + SetUse(NULL); + SetThink(&CMBaseTurret::TurretDeath); + SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others + pev->nextthink = gpGlobals->time + 0.1; + + CMBaseMonster::Killed( pevAttacker, iGib ); +} + + +void CMMiniTurret::Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->health = 0; + pev->takedamage = DAMAGE_NO; + pev->dmgtime = gpGlobals->time; + + ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? + + SetUse(NULL); + SetThink(&CMBaseTurret::TurretDeath); + SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others + pev->nextthink = gpGlobals->time + 0.1; + + CMBaseMonster::Killed( pevAttacker, iGib ); +} + +void CMBaseTurret::SetTurretAnim(TURRET_ANIM anim) +{ + if (pev->sequence != anim) + { + switch(anim) + { + case TURRET_ANIM_FIRE: + case TURRET_ANIM_SPIN: + if (pev->sequence != TURRET_ANIM_FIRE && pev->sequence != TURRET_ANIM_SPIN) + { + pev->frame = 0; + } + break; + default: + pev->frame = 0; + break; + } + + pev->sequence = anim; + ResetSequenceInfo( ); + + switch(anim) + { + case TURRET_ANIM_RETIRE: + pev->frame = 255; + pev->framerate = -1.0; + break; + case TURRET_ANIM_DIE: + pev->framerate = 1.0; + break; + } + //ALERT(at_console, "Turret anim #%d\n", anim); + } +} + + +// +// This search function will sit with the turret deployed and look for a new target. +// After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will +// retact. +// +void CMBaseTurret::SearchThink(void) +{ + // ensure rethink + SetTurretAnim(TURRET_ANIM_SPIN); + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + if (m_flSpinUpTime == 0 && m_flMaxSpin) + m_flSpinUpTime = gpGlobals->time + m_flMaxSpin; + + Ping( ); + + // If we have a target and we're still healthy + if (m_hEnemy != NULL) + { + if (!UTIL_IsAlive( m_hEnemy ) ) + m_hEnemy = NULL;// Dead enemy forces a search for new one + } + + + // Acquire Target + if (m_hEnemy == NULL) + { + Look(TURRET_RANGE); + m_hEnemy = BestVisibleEnemy(); + } + + // If we've found a target, spin up the barrel and start to attack + if (m_hEnemy != NULL) + { + m_flLastSight = 0; + m_flSpinUpTime = 0; + SetThink(&CMBaseTurret::ActiveThink); + } + else + { + // Are we out of time, do we need to retract? + if (gpGlobals->time > m_flLastSight) + { + //Before we retrace, make sure that we are spun down. + m_flLastSight = 0; + m_flSpinUpTime = 0; + SetThink(&CMBaseTurret::Retire); + } + // should we stop the spin? + else if ((m_flSpinUpTime) && (gpGlobals->time > m_flSpinUpTime)) + { + SpinDownCall(); + } + + // generic hunt for new victims + m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate); + if (m_vecGoalAngles.y >= 360) + m_vecGoalAngles.y -= 360; + MoveTurret(); + } +} + + +// +// This think function will deploy the turret when something comes into range. This is for +// automatically activated turrets. +// +void CMBaseTurret::AutoSearchThink(void) +{ + // ensure rethink + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.3; + + // If we have a target and we're still healthy + + if (m_hEnemy != NULL) + { + if (!UTIL_IsAlive( m_hEnemy ) ) + m_hEnemy = NULL;// Dead enemy forces a search for new one + } + + // Acquire Target + + if (m_hEnemy == NULL) + { + Look( TURRET_RANGE ); + m_hEnemy = BestVisibleEnemy(); + } + + if (m_hEnemy != NULL) + { + SetThink(&CMBaseTurret::Deploy); + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_alert.wav", TURRET_MACHINE_VOLUME, ATTN_NORM); + } +} + + +void CMBaseTurret :: TurretDeath( void ) +{ + BOOL iActive = FALSE; + + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + if (pev->deadflag != DEAD_DEAD) + { + pev->deadflag = DEAD_DEAD; + + float flRndSound = RANDOM_FLOAT ( 0 , 1 ); + + if ( flRndSound <= 0.33 ) + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM); + else if ( flRndSound <= 0.66 ) + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM); + else + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM); + + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); + + if (m_iOrientation == 0) + m_vecGoalAngles.x = -15; + else + m_vecGoalAngles.x = -90; + + SetTurretAnim(TURRET_ANIM_DIE); + + EyeOn( ); + } + + EyeOff( ); + + if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) + { + // lots of smoke + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ) ); + WRITE_COORD( RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ) ); + WRITE_COORD( pev->origin.z - m_iOrientation * 64 ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( 25 ); // scale * 10 + WRITE_BYTE( 10 - m_iOrientation * 5); // framerate + MESSAGE_END(); + } + + if (pev->dmgtime + RANDOM_FLOAT( 0, 5 ) > gpGlobals->time) + { + Vector vecSrc = Vector( RANDOM_FLOAT( pev->absmin.x, pev->absmax.x ), RANDOM_FLOAT( pev->absmin.y, pev->absmax.y ), 0 ); + if (m_iOrientation == 0) + vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->origin.z, pev->absmax.z ) ); + else + vecSrc = vecSrc + Vector( 0, 0, RANDOM_FLOAT( pev->absmin.z, pev->origin.z ) ); + + UTIL_Sparks( vecSrc ); + } + + if (m_fSequenceFinished && pev->dmgtime + 5 < gpGlobals->time) + { + pev->framerate = 0; + SetThink( NULL ); + SUB_StartFadeOut(); + } +} + + + +void CMBaseTurret :: TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + if ( ptr->iHitgroup == 10 ) + { + // hit armor + if ( pev->dmgtime != gpGlobals->time || (RANDOM_LONG(0,10) < 1) ) + { + UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT( 1, 2) ); + pev->dmgtime = gpGlobals->time; + } + + flDamage = 0.1;// don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated + } + + if ( !pev->takedamage ) + return; + + AddMultiDamage( pevAttacker, this->edict(), flDamage, bitsDamageType ); +} + +// take damage. bitsDamageType indicates type of damage sustained, ie: DMG_BULLET + +int CMBaseTurret::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + if ( !pev->takedamage ) + return 0; + + if (!m_iOn) + flDamage /= 10.0; + + pev->health -= flDamage; + + if (pev->health <= 10) + { + if (m_iOn && (1 || RANDOM_LONG(0, 0x7FFF) > 800)) + { + m_fBeserk = 1; + SetThink(&CMBaseTurret::SearchThink); + } + } + + return 1; +} + +int CMBaseTurret::MoveTurret(void) +{ + int state = 0; + // any x movement? + + if (m_vecCurAngles.x != m_vecGoalAngles.x) + { + float flDir = m_vecGoalAngles.x > m_vecCurAngles.x ? 1 : -1 ; + + m_vecCurAngles.x += 0.1 * m_fTurnRate * flDir; + + // if we started below the goal, and now we're past, peg to goal + if (flDir == 1) + { + if (m_vecCurAngles.x > m_vecGoalAngles.x) + m_vecCurAngles.x = m_vecGoalAngles.x; + } + else + { + if (m_vecCurAngles.x < m_vecGoalAngles.x) + m_vecCurAngles.x = m_vecGoalAngles.x; + } + + if (m_iOrientation == 0) + SetBoneController(1, -m_vecCurAngles.x); + else + SetBoneController(1, m_vecCurAngles.x); + state = 1; + } + + if (m_vecCurAngles.y != m_vecGoalAngles.y) + { + float flDir = m_vecGoalAngles.y > m_vecCurAngles.y ? 1 : -1 ; + float flDist = fabs(m_vecGoalAngles.y - m_vecCurAngles.y); + + if (flDist > 180) + { + flDist = 360 - flDist; + flDir = -flDir; + } + if (flDist > 30) + { + if (m_fTurnRate < m_iBaseTurnRate * 10) + { + m_fTurnRate += m_iBaseTurnRate; + } + } + else if (m_fTurnRate > 45) + { + m_fTurnRate -= m_iBaseTurnRate; + } + else + { + m_fTurnRate += m_iBaseTurnRate; + } + + m_vecCurAngles.y += 0.1 * m_fTurnRate * flDir; + + if (m_vecCurAngles.y < 0) + m_vecCurAngles.y += 360; + else if (m_vecCurAngles.y >= 360) + m_vecCurAngles.y -= 360; + + if (flDist < (0.05 * m_iBaseTurnRate)) + m_vecCurAngles.y = m_vecGoalAngles.y; + + //ALERT(at_console, "%.2f -> %.2f\n", m_vecCurAngles.y, y); + if (m_iOrientation == 0) + SetBoneController(0, m_vecCurAngles.y - pev->angles.y ); + else + SetBoneController(0, pev->angles.y - 180 - m_vecCurAngles.y ); + state = 1; + } + + if (!state) + m_fTurnRate = m_iBaseTurnRate; + + //ALERT(at_console, "(%.2f, %.2f)->(%.2f, %.2f)\n", m_vecCurAngles.x, + // m_vecCurAngles.y, m_vecGoalAngles.x, m_vecGoalAngles.y); + return state; +} + +// +// ID as a machine +// +int CMBaseTurret::Classify ( void ) +{ + if (m_iOn || m_iAutoStart) + { + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_MACHINE; + } + + return CLASS_NONE; +} + + +void CMSentry::Precache() +{ + CMBaseTurret::Precache( ); + PRECACHE_MODEL ("models/sentry.mdl"); +} + +void CMSentry::Spawn() +{ + Precache( ); + SET_MODEL(ENT(pev), "models/sentry.mdl"); + pev->health = gSkillData.sentryHealth; + m_HackedGunPos = Vector( 0, 0, 48 ); + pev->view_ofs.z = 48; + m_flMaxWait = 1E6; + m_flMaxSpin = 1E6; + + CMBaseTurret::Spawn(); + m_iRetractHeight = 64; + m_iDeployHeight = 64; + m_iMinPitch = -60; + UTIL_SetSize(pev, Vector(-16, -16, -m_iRetractHeight), Vector(16, 16, m_iRetractHeight)); + + SetTouch(&CMSentry::SentryTouch); + SetThink(&CMSentry::Initialize); + pev->nextthink = gpGlobals->time + 0.3; + + pev->classname = MAKE_STRING( "monster_sentry" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Sentry Turret" ); + } +} + +void CMSentry::Shoot(Vector &vecSrc, Vector &vecDirToEnemy) +{ + FireBullets( 1, vecSrc, vecDirToEnemy, TURRET_SPREAD, TURRET_RANGE, BULLET_MONSTER_MP5, 1 ); + + switch(RANDOM_LONG(0,2)) + { + case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM); break; + case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM); break; + case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "weapons/hks3.wav", 1, ATTN_NORM); break; + } + pev->effects = pev->effects | EF_MUZZLEFLASH; +} + +int CMSentry::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + if ( !pev->takedamage ) + return 0; + + if (!m_iOn) + { + SetThink( &CMSentry::Deploy ); + SetUse( NULL ); + pev->nextthink = gpGlobals->time + 0.1; + } + + pev->health -= flDamage; + return 1; +} + + +void CMSentry::SentryTouch( edict_t *pOther ) +{ + if ( pOther && (UTIL_IsPlayer( pOther ) || (pOther->v.flags & FL_MONSTER)) ) + { + UTIL_TakeDamage(pOther, VARS(pOther), VARS(pOther), 0, 0 ); + } +} + + +void CMSentry::Killed( entvars_t *pevAttacker, int iGib ) +{ + pev->health = 0; + pev->takedamage = DAMAGE_NO; + pev->dmgtime = gpGlobals->time; + + ClearBits (pev->flags, FL_MONSTER); // why are they set in the first place??? + + SetUse(NULL); + SetThink( &CMSentry::SentryDeath); + SUB_UseTargets( this->edict(), USE_ON, 0 ); // wake up others + pev->nextthink = gpGlobals->time + 0.1; + + CMBaseMonster::Killed( pevAttacker, iGib ); +} + + +void CMSentry::SentryDeath( void ) +{ + BOOL iActive = FALSE; + + StudioFrameAdvance( ); + pev->nextthink = gpGlobals->time + 0.1; + + if (pev->deadflag != DEAD_DEAD) + { + pev->deadflag = DEAD_DEAD; + + float flRndSound = RANDOM_FLOAT ( 0 , 1 ); + + if ( flRndSound <= 0.33 ) + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die.wav", 1.0, ATTN_NORM); + else if ( flRndSound <= 0.66 ) + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die2.wav", 1.0, ATTN_NORM); + else + EMIT_SOUND(ENT(pev), CHAN_BODY, "turret/tu_die3.wav", 1.0, ATTN_NORM); + + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, "turret/tu_active2.wav", 0, 0, SND_STOP, 100); + + SetBoneController( 0, 0 ); + SetBoneController( 1, 0 ); + + SetTurretAnim(TURRET_ANIM_DIE); + + pev->solid = SOLID_NOT; + pev->angles.y = UTIL_AngleMod( pev->angles.y + RANDOM_LONG( 0, 2 ) * 120 ); + + EyeOn( ); + } + + EyeOff( ); + + Vector vecSrc, vecAng; + GetAttachment( 1, vecSrc, vecAng ); + + if (pev->dmgtime + RANDOM_FLOAT( 0, 2 ) > gpGlobals->time) + { + // lots of smoke + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_SMOKE ); + WRITE_COORD( vecSrc.x + RANDOM_FLOAT( -16, 16 ) ); + WRITE_COORD( vecSrc.y + RANDOM_FLOAT( -16, 16 ) ); + WRITE_COORD( vecSrc.z - 32 ); + WRITE_SHORT( g_sModelIndexSmoke ); + WRITE_BYTE( 15 ); // scale * 10 + WRITE_BYTE( 8 ); // framerate + MESSAGE_END(); + } + + if (pev->dmgtime + RANDOM_FLOAT( 0, 8 ) > gpGlobals->time) + { + UTIL_Sparks( vecSrc ); + } + + if (m_fSequenceFinished && pev->dmgtime + 5 < gpGlobals->time) + { + pev->framerate = 0; + SetThink( NULL ); + SUB_StartFadeOut(); + } +} + diff --git a/src/dlls/util.cpp b/src/dlls/util.cpp index d36c6db..f34fff1 100644 --- a/src/dlls/util.cpp +++ b/src/dlls/util.cpp @@ -1,1970 +1,1970 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== util.cpp ======================================================== - - Utility code. Really not optional after all. - -*/ - -#include -#include // va_start, etc -#include - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "shake.h" -#include "decals.h" -#include "weapons.h" -#include "monsters.h" - -typedef struct { - DLL_FUNCTIONS *dllapi_table; - NEW_DLL_FUNCTIONS *newapi_table; -} gamedll_funcs_t; - -extern gamedll_funcs_t *gpGamedllFuncs; - - -// Print to console. -void META_CONS(char *fmt, ...) { - va_list ap; - char buf[1024]; - unsigned int len; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - len=strlen(buf); - if(len < sizeof(buf)-2) // -1 null, -1 for newline - strcat(buf, "\n"); - else - buf[len-1] = '\n'; - - (*g_engfuncs.pfnServerPrint)(buf); -} - -float UTIL_WeaponTimeBase( void ) -{ -#if defined( CLIENT_WEAPONS ) - return 0.0; -#else - return gpGlobals->time; -#endif -} - -static unsigned int glSeed = 0; - -unsigned int seed_table[ 256 ] = -{ - 28985, 27138, 26457, 9451, 17764, 10909, 28790, 8716, 6361, 4853, 17798, 21977, 19643, 20662, 10834, 20103, - 27067, 28634, 18623, 25849, 8576, 26234, 23887, 18228, 32587, 4836, 3306, 1811, 3035, 24559, 18399, 315, - 26766, 907, 24102, 12370, 9674, 2972, 10472, 16492, 22683, 11529, 27968, 30406, 13213, 2319, 23620, 16823, - 10013, 23772, 21567, 1251, 19579, 20313, 18241, 30130, 8402, 20807, 27354, 7169, 21211, 17293, 5410, 19223, - 10255, 22480, 27388, 9946, 15628, 24389, 17308, 2370, 9530, 31683, 25927, 23567, 11694, 26397, 32602, 15031, - 18255, 17582, 1422, 28835, 23607, 12597, 20602, 10138, 5212, 1252, 10074, 23166, 19823, 31667, 5902, 24630, - 18948, 14330, 14950, 8939, 23540, 21311, 22428, 22391, 3583, 29004, 30498, 18714, 4278, 2437, 22430, 3439, - 28313, 23161, 25396, 13471, 19324, 15287, 2563, 18901, 13103, 16867, 9714, 14322, 15197, 26889, 19372, 26241, - 31925, 14640, 11497, 8941, 10056, 6451, 28656, 10737, 13874, 17356, 8281, 25937, 1661, 4850, 7448, 12744, - 21826, 5477, 10167, 16705, 26897, 8839, 30947, 27978, 27283, 24685, 32298, 3525, 12398, 28726, 9475, 10208, - 617, 13467, 22287, 2376, 6097, 26312, 2974, 9114, 21787, 28010, 4725, 15387, 3274, 10762, 31695, 17320, - 18324, 12441, 16801, 27376, 22464, 7500, 5666, 18144, 15314, 31914, 31627, 6495, 5226, 31203, 2331, 4668, - 12650, 18275, 351, 7268, 31319, 30119, 7600, 2905, 13826, 11343, 13053, 15583, 30055, 31093, 5067, 761, - 9685, 11070, 21369, 27155, 3663, 26542, 20169, 12161, 15411, 30401, 7580, 31784, 8985, 29367, 20989, 14203, - 29694, 21167, 10337, 1706, 28578, 887, 3373, 19477, 14382, 675, 7033, 15111, 26138, 12252, 30996, 21409, - 25678, 18555, 13256, 23316, 22407, 16727, 991, 9236, 5373, 29402, 6117, 15241, 27715, 19291, 19888, 19847 -}; - -unsigned int U_Random( void ) -{ - glSeed *= 69069; - glSeed += seed_table[ glSeed & 0xff ]; - - return ( ++glSeed & 0x0fffffff ); -} - -void U_Srand( unsigned int seed ) -{ - glSeed = seed_table[ seed & 0xff ]; -} - -/* -===================== -UTIL_SharedRandomLong -===================== -*/ -int UTIL_SharedRandomLong( unsigned int seed, int low, int high ) -{ - unsigned int range; - - U_Srand( (int)seed + low + high ); - - range = high - low + 1; - if ( !(range - 1) ) - { - return low; - } - else - { - int offset; - int rnum; - - rnum = U_Random(); - - offset = rnum % range; - - return (low + offset); - } -} - -/* -===================== -UTIL_SharedRandomFloat -===================== -*/ -float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ) -{ - // - unsigned int range; - - U_Srand( (int)seed + *(int *)&low + *(int *)&high ); - - U_Random(); - U_Random(); - - range = high - low; - if ( !range ) - { - return low; - } - else - { - int tensixrand; - float offset; - - tensixrand = U_Random() & 65535; - - offset = (float)tensixrand / 65536.0; - - return (low + offset * range ); - } -} - -void UTIL_ParametricRocket( entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner ) -{ - pev->startpos = vecOrigin; - // Trace out line to end pos - TraceResult tr; - UTIL_MakeVectors( vecAngles ); - UTIL_TraceLine( pev->startpos, pev->startpos + gpGlobals->v_forward * 8192, ignore_monsters, owner, &tr); - pev->endpos = tr.vecEndPos; - - // Now compute how long it will take based on current velocity - Vector vecTravel = pev->endpos - pev->startpos; - float travelTime = 0.0; - if ( pev->velocity.Length() > 0 ) - { - travelTime = vecTravel.Length() / pev->velocity.Length(); - } - pev->starttime = gpGlobals->time; - pev->impacttime = gpGlobals->time + travelTime; -} - -int g_groupmask = 0; -int g_groupop = 0; - -// Normal overrides -void UTIL_SetGroupTrace( int groupmask, int op ) -{ - g_groupmask = groupmask; - g_groupop = op; - - ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); -} - -void UTIL_UnsetGroupTrace( void ) -{ - g_groupmask = 0; - g_groupop = 0; - - ENGINE_SETGROUPMASK( 0, 0 ); -} - -// Smart version, it'll clean itself up when it pops off stack -UTIL_GroupTrace::UTIL_GroupTrace( int groupmask, int op ) -{ - m_oldgroupmask = g_groupmask; - m_oldgroupop = g_groupop; - - g_groupmask = groupmask; - g_groupop = op; - - ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); -} - -UTIL_GroupTrace::~UTIL_GroupTrace( void ) -{ - g_groupmask = m_oldgroupmask; - g_groupop = m_oldgroupop; - - ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); -} - - -#ifdef DEBUG -edict_t *DBG_EntOfVars( const entvars_t *pev ) -{ - if (pev->pContainingEntity != NULL) - return pev->pContainingEntity; - ALERT(at_console, "entvars_t pContainingEntity is NULL, calling into engine"); - edict_t* pent = (*g_engfuncs.pfnFindEntityByVars)((entvars_t*)pev); - if (pent == NULL) - ALERT(at_console, "DAMN! Even the engine couldn't FindEntityByVars!"); - ((entvars_t *)pev)->pContainingEntity = pent; - return pent; -} -#endif //DEBUG - - -#ifdef DEBUG - void -DBG_AssertFunction( - BOOL fExpr, - const char* szExpr, - const char* szFile, - int szLine, - const char* szMessage) - { - if (fExpr) - return; - char szOut[512]; - if (szMessage != NULL) - sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); - else - sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine); - ALERT(at_console, szOut); - } -#endif // DEBUG - -// ripped this out of the engine -float UTIL_AngleMod(float a) -{ - if (a < 0) - { - a = a + 360 * ((int)(a / 360) + 1); - } - else if (a >= 360) - { - a = a - 360 * ((int)(a / 360)); - } - // a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); - return a; -} - -float UTIL_AngleDiff( float destAngle, float srcAngle ) -{ - float delta; - - delta = destAngle - srcAngle; - if ( destAngle > srcAngle ) - { - if ( delta >= 180 ) - delta -= 360; - } - else - { - if ( delta <= -180 ) - delta += 360; - } - return delta; -} - -Vector UTIL_VecToAngles( const Vector &vec ) -{ - float rgflVecOut[3]; - VEC_TO_ANGLES(vec, rgflVecOut); - return Vector(rgflVecOut); -} - -// float UTIL_MoveToOrigin( edict_t *pent, const Vector vecGoal, float flDist, int iMoveType ) -void UTIL_MoveToOrigin( edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType ) -{ - float rgfl[3]; - vecGoal.CopyToArray(rgfl); -// return MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType ); - MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType ); -} - - -int UTIL_EntitiesInBox( edict_t **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ) -{ - edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - int count; - - count = 0; - - if ( !pEdict ) - return count; - - for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) - { - if ( pEdict->free ) // Not in use - continue; - - if ( flagMask && !(pEdict->v.flags & flagMask) ) // Does it meet the criteria? - continue; - - if ( mins.x > pEdict->v.absmax.x || - mins.y > pEdict->v.absmax.y || - mins.z > pEdict->v.absmax.z || - maxs.x < pEdict->v.absmin.x || - maxs.y < pEdict->v.absmin.y || - maxs.z < pEdict->v.absmin.z ) - continue; - - pList[ count ] = pEdict; - count++; - - if ( count >= listMax ) - return count; - } - - return count; -} - - -int UTIL_MonstersInSphere( edict_t **pList, int listMax, const Vector ¢er, float radius ) -{ - edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); - int count; - float distance, delta; - - count = 0; - float radiusSquared = radius * radius; - - if ( !pEdict ) - return count; - - for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) - { - if ( pEdict->free ) // Not in use - continue; - - if ( !(pEdict->v.flags & (FL_CLIENT|FL_MONSTER)) ) // Not a client/monster ? - continue; - - // Use origin for X & Y since they are centered for all monsters - // Now X - delta = center.x - pEdict->v.origin.x;//(pEdict->v.absmin.x + pEdict->v.absmax.x)*0.5; - delta *= delta; - - if ( delta > radiusSquared ) - continue; - distance = delta; - - // Now Y - delta = center.y - pEdict->v.origin.y;//(pEdict->v.absmin.y + pEdict->v.absmax.y)*0.5; - delta *= delta; - - distance += delta; - if ( distance > radiusSquared ) - continue; - - // Now Z - delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z)*0.5; - delta *= delta; - - distance += delta; - if ( distance > radiusSquared ) - continue; - - pList[ count ] = pEdict; - count++; - - if ( count >= listMax ) - return count; - } - - - return count; -} - - -edict_t *UTIL_FindEntityInSphere( edict_t *pStartEntity, const Vector &vecCenter, float flRadius ) -{ - edict_t *pentEntity; - - if (pStartEntity) - pentEntity = pStartEntity; - else - pentEntity = NULL; - - pentEntity = FIND_ENTITY_IN_SPHERE( pentEntity, vecCenter, flRadius); - - if (!FNullEnt(pentEntity)) - return pentEntity; - - return NULL; -} - - -edict_t *UTIL_FindEntityByString( edict_t *pStartEntity, const char *szKeyword, const char *szValue ) -{ - edict_t *pentEntity; - - if (pStartEntity) - pentEntity = pStartEntity; - else - pentEntity = NULL; - - pentEntity = FIND_ENTITY_BY_STRING( pentEntity, szKeyword, szValue ); - - if (!FNullEnt(pentEntity)) - return pentEntity; - - return NULL; -} - -edict_t *UTIL_FindEntityByClassname( edict_t *pStartEntity, const char *szName ) -{ - return UTIL_FindEntityByString( pStartEntity, "classname", szName ); -} - -edict_t *UTIL_FindEntityByTargetname( edict_t *pStartEntity, const char *szName ) -{ - return UTIL_FindEntityByString( pStartEntity, "targetname", szName ); -} - - -edict_t *UTIL_FindEntityGeneric( const char *szWhatever, Vector &vecSrc, float flRadius ) -{ - edict_t *pEntity = NULL; - - pEntity = UTIL_FindEntityByTargetname( NULL, szWhatever ); - if (pEntity) - return pEntity; - - edict_t *pSearch = NULL; - float flMaxDist2 = flRadius * flRadius; - while ((pSearch = UTIL_FindEntityByClassname( pSearch, szWhatever )) != NULL) - { - float flDist2 = (pSearch->v.origin - vecSrc).Length(); - flDist2 = flDist2 * flDist2; - if (flMaxDist2 > flDist2) - { - pEntity = pSearch; - flMaxDist2 = flDist2; - } - } - return pEntity; -} - - -// returns a edict_t pointer to a player by index. Only returns if the player is spawned and connected -// otherwise returns NULL -// Index is 1 based -edict_t *UTIL_PlayerByIndex( int playerIndex ) -{ - edict_t *pPlayer = NULL; - - if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients ) - { - edict_t *pPlayerEdict = INDEXENT( playerIndex ); - if ( pPlayerEdict && !pPlayerEdict->free ) - { - pPlayer = pPlayerEdict; - } - } - - return pPlayer; -} - - -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); -} - - -#define SWAP(a,b,temp) ((temp)=(a),(a)=(b),(b)=(temp)) - -void UTIL_MakeInvVectors( const Vector &vec, globalvars_t *pgv ) -{ - MAKE_VECTORS(vec); - - float tmp; - pgv->v_right = pgv->v_right * -1; - - SWAP(pgv->v_forward.y, pgv->v_right.x, tmp); - SWAP(pgv->v_forward.z, pgv->v_up.x, tmp); - SWAP(pgv->v_right.z, pgv->v_up.y, tmp); -} - - -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); - -/*jlb - if (samp && *samp == '!') - { - char name[32]; - if (SENTENCEG_Lookup(samp, name) >= 0) - EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch); - } - else -jlb*/ - EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch); -} - -static unsigned short FixedUnsigned16( float value, float scale ) -{ - int output; - - output = value * scale; - if ( output < 0 ) - output = 0; - if ( output > 0xFFFF ) - output = 0xFFFF; - - return (unsigned short)output; -} - -static short FixedSigned16( float value, float scale ) -{ - int output; - - output = value * scale; - - if ( output > 32767 ) - output = 32767; - - if ( output < -32768 ) - output = -32768; - - return (short)output; -} - -// Shake the screen of all clients within radius -// radius == 0, shake all clients -// UNDONE: Allow caller to shake clients not ONGROUND? -// UNDONE: Fix falloff model (disabled)? -// UNDONE: Affect user controls? - -int gmsgShake = 0; - -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 ); // 4.12 fixed - shake.frequency = FixedUnsigned16( frequency, 1<<8 ); // 8.8 fixed - - for ( i = 1; i <= gpGlobals->maxClients; i++ ) - { - edict_t *pPlayer = UTIL_PlayerByIndex( i ); - - if ( !pPlayer || !(pPlayer->v.flags & FL_ONGROUND) ) // Don't shake if not onground - continue; - - localAmplitude = 0; - - if ( radius <= 0 ) - localAmplitude = amplitude; - else - { - Vector delta = center - pPlayer->v.origin; - float distance = delta.Length(); - - // Had to get rid of this falloff - it didn't work well - if ( distance < radius ) - localAmplitude = amplitude;//radius - distance; - } - if ( localAmplitude ) - { - shake.amplitude = FixedUnsigned16( localAmplitude, 1<<12 ); // 4.12 fixed - - if (gmsgShake == 0) - gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake)); - - MESSAGE_BEGIN( MSG_ONE, gmsgShake, NULL, pPlayer ); // use the magic #1 for "one client" - - WRITE_SHORT( shake.amplitude ); // shake amount - WRITE_SHORT( shake.duration ); // shake lasts this long - WRITE_SHORT( shake.frequency ); // shake noise frequency - - MESSAGE_END(); - } - } -} - - - -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 ); // 4.12 fixed - fade.holdTime = FixedUnsigned16( fadeHold, 1<<12 ); // 4.12 fixed - fade.r = (int)color.x; - fade.g = (int)color.y; - fade.b = (int)color.z; - fade.a = alpha; - fade.fadeFlags = flags; -} - - -int gmsgFade = 0; - -void UTIL_ScreenFadeWrite( const ScreenFade &fade, edict_t *pEntity ) -{ - if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) - return; - - if (gmsgFade == 0) - gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade)); - - MESSAGE_BEGIN( MSG_ONE, gmsgFade, NULL, pEntity ); // use the magic #1 for "one client" - - WRITE_SHORT( fade.duration ); // fade lasts this long - WRITE_SHORT( fade.holdTime ); // fade lasts this long - WRITE_SHORT( fade.fadeFlags ); // fade type (in / out) - WRITE_BYTE( fade.r ); // fade red - WRITE_BYTE( fade.g ); // fade green - WRITE_BYTE( fade.b ); // fade blue - WRITE_BYTE( fade.a ); // fade blue - - 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++ ) - { - edict_t *pPlayer = UTIL_PlayerByIndex( i ); - - UTIL_ScreenFadeWrite( fade, pPlayer ); - } -} - - -void UTIL_ScreenFade( edict_t *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( edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage ) -{ - if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) - return; - - MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pEntity ); - 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 ( strlen( pMessage ) < 512 ) - { - WRITE_STRING( pMessage ); - } - else - { - char tmp[512]; - strncpy( tmp, pMessage, 511 ); - tmp[511] = 0; - WRITE_STRING( tmp ); - } - MESSAGE_END(); -} - -void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ) -{ - int i; - - for ( i = 1; i <= gpGlobals->maxClients; i++ ) - { - edict_t *pPlayer = UTIL_PlayerByIndex( i ); - if ( pPlayer ) - UTIL_HudMessage( pPlayer, textparms, pMessage ); - } -} - - -int gmsgTextMsg = 0; -int gmsgSayText = 0; - -void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) -{ - if (gmsgTextMsg == 0) - gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); - - 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 ) -{ - if (gmsgTextMsg == 0) - gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); - - MESSAGE_BEGIN( MSG_ONE, gmsgTextMsg, NULL, 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_SayText( const char *pText, edict_t *pEntity ) -{ - if ( !(pEntity->v.flags & FL_CLIENT) ) - return; - - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG( "SayText", -1 ); - - MESSAGE_BEGIN( MSG_ONE, gmsgSayText, NULL, pEntity ); - WRITE_BYTE( ENTINDEX(pEntity) ); - WRITE_STRING( pText ); - MESSAGE_END(); -} - -void UTIL_SayTextAll( const char *pText, edict_t *pEntity ) -{ - if (gmsgSayText == 0) - gmsgSayText = REG_USER_MSG( "SayText", -1 ); - - MESSAGE_BEGIN( MSG_ALL, gmsgSayText, NULL ); - WRITE_BYTE( ENTINDEX(pEntity) ); - WRITE_STRING( pText ); - MESSAGE_END(); -} - - -char *UTIL_dtos1( int d ) -{ - static char buf[8]; - sprintf( buf, "%d", d ); - return buf; -} - -char *UTIL_dtos2( int d ) -{ - static char buf[8]; - sprintf( buf, "%d", d ); - return buf; -} - -char *UTIL_dtos3( int d ) -{ - static char buf[8]; - sprintf( buf, "%d", d ); - return buf; -} - -char *UTIL_dtos4( int d ) -{ - static char buf[8]; - sprintf( buf, "%d", d ); - return buf; -} - -int gmsgHudText = 0; - -void UTIL_ShowMessage( const char *pString, edict_t *pEntity ) -{ - if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) - return; - - if (gmsgHudText == 0) - gmsgHudText = REG_USER_MSG( "HudText", -1 ); - - MESSAGE_BEGIN( MSG_ONE, gmsgHudText, NULL, pEntity ); - WRITE_STRING( pString ); - MESSAGE_END(); -} - - -void UTIL_ShowMessageAll( const char *pString ) -{ - int i; - - // loop through all players - - for ( i = 1; i <= gpGlobals->maxClients; i++ ) - { - edict_t *pPlayer = UTIL_PlayerByIndex( i ); - if ( pPlayer ) - UTIL_ShowMessage( pString, pPlayer ); - } -} - -// Overloaded to add IGNORE_GLASS -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 ? TRUE : FALSE) | (ignoreGlass?0x100:0), pentIgnore, ptr ); -} - - -void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr ) -{ - TRACE_LINE( vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), 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 ? TRUE : FALSE), hullNumber, pentIgnore, ptr ); -} - -void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr ) -{ - g_engfuncs.pfnTraceModel( vecStart, vecEnd, hullNumber, pentModel, ptr ); -} - - -TraceResult UTIL_GetGlobalTrace( ) -{ - TraceResult tr; - - tr.fAllSolid = gpGlobals->trace_allsolid; - tr.fStartSolid = gpGlobals->trace_startsolid; - tr.fInOpen = gpGlobals->trace_inopen; - tr.fInWater = gpGlobals->trace_inwater; - tr.flFraction = gpGlobals->trace_fraction; - tr.flPlaneDist = gpGlobals->trace_plane_dist; - tr.pHit = gpGlobals->trace_ent; - tr.vecEndPos = gpGlobals->trace_endpos; - tr.vecPlaneNormal = gpGlobals->trace_plane_normal; - tr.iHitgroup = gpGlobals->trace_hitgroup; - 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 ) -{ - SET_ORIGIN(ENT(pev), vecOrigin ); -} - -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; -} - - -float UTIL_ApproachAngle( float target, float value, float speed ) -{ - target = UTIL_AngleMod( target ); - value = UTIL_AngleMod( target ); - - float delta = target - value; - - // Speed is assumed to be positive - if ( speed < 0 ) - speed = -speed; - - if ( delta < -180 ) - delta += 360; - else if ( delta > 180 ) - delta -= 360; - - if ( delta > speed ) - value += speed; - else if ( delta < -speed ) - value -= speed; - else - value = target; - - return value; -} - - -float UTIL_AngleDistance( float next, float cur ) -{ - float delta = next - cur; - - if ( delta < -180 ) - delta += 360; - else if ( delta > 180 ) - delta -= 360; - - return delta; -} - - -float UTIL_SplineFraction( float value, float scale ) -{ - value = scale * value; - float valueSquared = value * value; - - // Nice little ease-in, ease-out spline-like curve - return 3 * valueSquared - 2 * valueSquared * value; -} - - -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; -} - -Vector UTIL_GetAimVector( edict_t *pent, float flSpeed ) -{ - Vector tmp; - GET_AIM_VECTOR(pent, flSpeed, tmp); - return tmp; -} - -BOOL UTIL_ShouldShowBlood( int color ) -{ - if ( color != DONT_BLEED ) - { - if ( color == BLOOD_COLOR_RED ) - { - if ( CVAR_GET_FLOAT("violence_hblood") != 0 ) - return TRUE; - } - else - { - if ( CVAR_GET_FLOAT("violence_ablood") != 0 ) - 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; - - 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( 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; - - // scale up blood effect in multiplayer for better visibility - amount *= 2; - - if ( amount > 255 ) - amount = 255; - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); - WRITE_BYTE( TE_BLOODSPRITE ); - WRITE_COORD( origin.x); // pos - WRITE_COORD( origin.y); - WRITE_COORD( origin.z); - WRITE_SHORT( g_sModelIndexBloodSpray ); // initial sprite model - WRITE_SHORT( g_sModelIndexBloodDrop ); // droplet sprite models - WRITE_BYTE( color ); // color index into host_basepal - WRITE_BYTE( min( max( 3, amount / 10 ), 16 ) ); // size - MESSAGE_END(); -} - -Vector UTIL_RandomBloodVector( void ) -{ - 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) ); - } -} - - -bool IsBSPModel(edict_t *pEdict) -{ - return ((pEdict->v.solid == SOLID_BSP) || (pEdict->v.movetype == MOVETYPE_PUSHSTEP)); -} - - -void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ) -{ - short entityIndex; - int index; - int message; - - if ( decalNumber < 0 ) - return; - - index = gDecals[ decalNumber ].index; - - if ( index < 0 ) - return; - - if (pTrace->flFraction == 1.0) - return; - - // Only decal BSP models - if ( pTrace->pHit ) - { - edict_t *pEntity = pTrace->pHit; - if ( pEntity && !IsBSPModel(pEntity) ) - return; - entityIndex = ENTINDEX( pTrace->pHit ); - } - else - entityIndex = 0; - - message = TE_DECAL; - if ( entityIndex != 0 ) - { - 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(); -} - -/* -============== -UTIL_PlayerDecalTrace - -A player is trying to apply his custom decal for the spray can. -Tell connected clients to display it, or use the default spray can decal -if the custom can't be loaded. -============== -*/ -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.0) - return; - - 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( (short)ENTINDEX(pTrace->pHit) ); - WRITE_BYTE( index ); - MESSAGE_END(); -} - -void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ) -{ - if ( decalNumber < 0 ) - return; - - int index = gDecals[ decalNumber ].index; - if ( index < 0 ) - return; - - if (pTrace->flFraction == 1.0) - return; - - 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( (short)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) ); - MESSAGE_END(); -} - - -void UTIL_StringToVector( float *pVector, const char *pString ) -{ - char *pstr, *pfront, tempString[128]; - int j; - - strcpy( tempString, pString ); - pstr = pfront = tempString; - - for ( j = 0; j < 3; j++ ) // lifted from pr_edict.c - { - pVector[j] = atof( pfront ); - - while ( *pstr && *pstr != ' ' ) - pstr++; - if (!*pstr) - break; - pstr++; - pfront = pstr; - } - if (j < 2) - { - /* - ALERT( at_error, "Bad field in entity!! %s:%s == \"%s\"\n", - pkvd->szClassName, pkvd->szKeyName, pkvd->szValue ); - */ - for (j = j+1;j < 3; j++) - pVector[j] = 0; - } -} - - -void UTIL_StringToIntArray( int *pVector, int count, const char *pString ) -{ - char *pstr, *pfront, tempString[128]; - int j; - - strcpy( tempString, pString ); - pstr = pfront = tempString; - - for ( j = 0; j < count; j++ ) // lifted from pr_edict.c - { - pVector[j] = 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 = position; - midUp.z = minz; - - if (UTIL_PointContents(midUp) != CONTENTS_WATER) - return minz; - - midUp.z = maxz; - if (UTIL_PointContents(midUp) == CONTENTS_WATER) - return maxz; - - float diff = maxz - minz; - while (diff > 1.0) - { - midUp.z = minz + diff/2.0; - 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.5; - - float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 ); - flHeight = flHeight - mins.z; - - MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, mid ); - WRITE_BYTE( TE_BUBBLES ); - WRITE_COORD( mins.x ); // mins - WRITE_COORD( mins.y ); - WRITE_COORD( mins.z ); - WRITE_COORD( maxs.x ); // maxz - WRITE_COORD( maxs.y ); - WRITE_COORD( maxs.z ); - WRITE_COORD( flHeight ); // height - WRITE_SHORT( g_sModelIndexBubbles ); - WRITE_BYTE( count ); // count - WRITE_COORD( 8 ); // speed - MESSAGE_END(); -} - -void UTIL_BubbleTrail( Vector from, Vector to, int count ) -{ - float flHeight = UTIL_WaterLevel( from, from.z, from.z + 256 ); - flHeight = flHeight - from.z; - - if (flHeight < 8) - { - flHeight = UTIL_WaterLevel( to, to.z, to.z + 256 ); - flHeight = flHeight - to.z; - if (flHeight < 8) - return; - - // UNDONE: do a ploink sound - 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 ); // mins - WRITE_COORD( from.y ); - WRITE_COORD( from.z ); - WRITE_COORD( to.x ); // maxz - WRITE_COORD( to.y ); - WRITE_COORD( to.z ); - WRITE_COORD( flHeight ); // height - WRITE_SHORT( g_sModelIndexBubbles ); - WRITE_BYTE( count ); // count - WRITE_COORD( 8 ); // speed - MESSAGE_END(); -} - - -void UTIL_Remove( edict_t *pEntity ) -{ - if ( !pEntity ) - return; - -//jlb pEntity->UpdateOnRemove(); - pEntity->v.flags |= FL_KILLME; - pEntity->v.targetname = 0; -} - - -BOOL UTIL_IsValidEntity( edict_t *pent ) -{ - if ( !pent || pent->free || (pent->v.flags & FL_KILLME) ) - return FALSE; - return TRUE; -} - - -//========================================================= -// UTIL_LogPrintf - Prints a logged message to console. -// Preceded by LOG: ( timestamp ) < message > -//========================================================= -void UTIL_LogPrintf( char *fmt, ... ) -{ - va_list argptr; - static char string[1024]; - - va_start ( argptr, fmt ); - vsprintf ( string, fmt, argptr ); - va_end ( argptr ); - - // Print to server console - ALERT( at_logged, "%s", string ); -} - -//========================================================= -// UTIL_DotPoints - returns the dot product of a line from -// src to check and vecdir. -//========================================================= -float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ) -{ - Vector2D vec2LOS; - - vec2LOS = ( vecCheck - vecSrc ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - return DotProduct (vec2LOS , ( vecDir.Make2D() ) ); -} - - -//========================================================= -// UTIL_StripToken - for redundant keynames -//========================================================= -void UTIL_StripToken( const char *pKey, char *pDest ) -{ - int i = 0; - - while ( pKey[i] && pKey[i] != '#' ) - { - pDest[i] = pKey[i]; - i++; - } - pDest[i] = 0; -} - - -Vector VecBModelOrigin( entvars_t* pevBModel ) -{ - return pevBModel->absmin + ( pevBModel->size * 0.5 ); -} - - -bool UTIL_IsAlive(entvars_t *pev) -{ - return ((pev->deadflag == DEAD_NO) && (pev->health > 0) && - ((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0)); -} - - -bool UTIL_IsAlive(edict_t *pEdict) -{ - return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) && - ((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0)); -} - - -bool UTIL_IsPlayer(edict_t *pEdict) -{ - return ((pEdict->v.flags & FL_CLIENT) == FL_CLIENT); -} - - -Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc) -{ - if (pEdict->v.flags & FL_CLIENT) - return pEdict->v.origin + (pEdict->v.view_ofs * RANDOM_FLOAT(0.5, 1.1)); - else - return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5)); -} - -//========================================================= -// FVisible - returns true if a line can be traced from -// the caller's eyes to the target -//========================================================= -bool UTIL_FVisible ( edict_t *pEntity, edict_t *pLooker ) -{ - TraceResult tr; - Vector vecLookerOrigin; - Vector vecTargetOrigin; - - if (FBitSet( pEntity->v.flags, FL_NOTARGET )) - return FALSE; - - // don't look through water - if ((pLooker->v.waterlevel != 3 && pEntity->v.waterlevel == 3) - || (pLooker->v.waterlevel == 3 && pEntity->v.waterlevel == 0)) - return FALSE; - - vecLookerOrigin = pLooker->v.origin + pLooker->v.view_ofs;//look through the caller's 'eyes' - vecTargetOrigin = pEntity->v.origin + pEntity->v.view_ofs; - - UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, pLooker, &tr); - - if (tr.flFraction != 1.0) - { - return FALSE;// Line of sight is not established - } - else - { - return TRUE;// line of sight is valid. - } -} - -//========================================================= -// FVisible - returns true if a line can be traced from -// the caller's eyes to the target vector -//========================================================= -bool UTIL_FVisible ( const Vector &vecOrigin, edict_t *pLooker ) -{ - TraceResult tr; - Vector vecLookerOrigin; - - vecLookerOrigin = pLooker->v.origin + pLooker->v.view_ofs; - - UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, pLooker, &tr); - - if (tr.flFraction != 1.0) - { - return FALSE;// Line of sight is not established - } - else - { - return TRUE;// line of sight is valid. - } -} - - -//========================================================= -// FInViewCone - returns true is the passed ent is in -// the caller's forward view cone. The dot product is performed -// in 2d, making the view cone infinitely tall. -//========================================================= -bool UTIL_FInViewCone ( edict_t *pEntity, edict_t *pLooker, float fov ) -{ - Vector2D vec2LOS; - float flDot; - - UTIL_MakeVectors ( pLooker->v.angles ); - - vec2LOS = ( pEntity->v.origin - pLooker->v.origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - - if ( flDot > fov ) - { - return TRUE; - } - else - { - return FALSE; - } -} - -//========================================================= -// FInViewCone - returns true is the passed vector is in -// the caller's forward view cone. The dot product is performed -// in 2d, making the view cone infinitely tall. -//========================================================= -bool UTIL_FInViewCone ( Vector *pOrigin, edict_t *pLooker, float fov ) -{ - Vector2D vec2LOS; - float flDot; - - UTIL_MakeVectors ( pLooker->v.angles ); - - vec2LOS = ( *pOrigin - pLooker->v.origin ).Make2D(); - vec2LOS = vec2LOS.Normalize(); - - flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); - - if ( flDot > fov ) - { - return TRUE; - } - else - { - return FALSE; - } -} - - -#define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage -#define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health - -int gmsgDamage = 0; - -int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // note: This function should ONLY be used for players!!! - - int bitsDamage = bitsDamageType; - int ffound = TRUE; - int fTookDamage; - float flRatio; - float flBonus; - float flHealthPrev = pEdict->v.health; - - flBonus = ARMOR_BONUS; - flRatio = ARMOR_RATIO; - - if (!pEdict->v.takedamage) - return 0; - - if ( ( bitsDamageType & DMG_BLAST ) ) - { - // blasts damage armor more. - flBonus *= 2; - } - - // Already dead - if ( !UTIL_IsAlive(pEdict) ) - return 0; - - // Armor. - if (pEdict->v.armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! - { - float flNew = flDamage * flRatio; - - float flArmor; - - flArmor = (flDamage - flNew) * flBonus; - - // Does this use more armor than we have? - if (flArmor > pEdict->v.armorvalue) - { - flArmor = pEdict->v.armorvalue; - flArmor *= (1/flBonus); - flNew = flDamage - flArmor; - pEdict->v.armorvalue = 0; - } - else - pEdict->v.armorvalue -= flArmor; - - flDamage = flNew; - } - - float flTake; - Vector vecDir; - - if ( pEdict->v.deadflag == DEAD_NO ) - { - // no pain sound during death animation. -//jlb PainSound();// "Ouch!" - } - - // WARNING the cast to INT is critical!!! The player could wind up with 0.5 health - flTake = (int)flDamage; - - // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). - vecDir = Vector( 0, 0, 0 ); - - if (!FNullEnt( pevInflictor )) - { - edict_t *pInflictor = ENT(pevInflictor); - - if (pInflictor) - { - vecDir = ( UTIL_Center(pInflictor) - Vector ( 0, 0, 10 ) - UTIL_Center(pEdict) ).Normalize(); - vecDir = g_vecAttackDir = vecDir.Normalize(); - } - } - - fTookDamage = flTake; - - // if this is a player, move him around! - if ( ( !FNullEnt( pevInflictor ) ) && (pEdict->v.movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) ) - { - float force = flDamage * ((32 * 32 * 72.0) / (pEdict->v.size.x * pEdict->v.size.y * pEdict->v.size.z)) * 5; - - if ( force > 1000.0) - force = 1000.0; - - pEdict->v.velocity = pEdict->v.velocity + vecDir * -force; - } - - // do the damage - pEdict->v.health -= flTake; - - // store entity that hurt this player - pEdict->v.dmg_inflictor = ENT(pevAttacker); - - if ( pEdict->v.health <= 0 ) - { - pEdict->v.health = 1; // can't suicide if already dead! - gpGamedllFuncs->dllapi_table->pfnClientKill(pEdict); - - // Add 1 score to the monster that killed this player - if ( pevAttacker->flags & FL_MONSTER ) - pevAttacker->frags += 1.0; - } - - // tell director about it - MESSAGE_BEGIN( MSG_SPEC, SVC_HLTV ); - WRITE_BYTE ( DRC_EVENT ); // take damage event - WRITE_SHORT( ENTINDEX(pEdict) ); // index number of primary entity - WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity - WRITE_LONG( 5 ); // eventflags (priority and flags) - MESSAGE_END(); - - // handle all bits set in this damage message, - // let the suit give player the diagnosis - - // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) - - // UNDONE: still need to record damage and heal messages for the following types - - // DMG_BURN - // DMG_FREEZE - // DMG_BLAST - // DMG_SHOCK - - if (pEdict->v.health > 0) - { - pEdict->v.punchangle.x = -2; - - // only send down damage type that have hud art - int visibleDamageBits = bitsDamage & DMG_SHOWNHUD; - - if (gmsgDamage == 0) - gmsgDamage = REG_USER_MSG( "Damage", -1 ); - - MESSAGE_BEGIN( MSG_ONE, gmsgDamage, NULL, VARS(pEdict) ); - WRITE_BYTE( 0 ); - WRITE_BYTE( fTookDamage ); - WRITE_LONG( visibleDamageBits ); - WRITE_COORD( pevInflictor->origin.x ); - WRITE_COORD( pevInflictor->origin.y ); - WRITE_COORD( pevInflictor->origin.z ); - MESSAGE_END(); - } - - return fTookDamage; -} - - -int UTIL_TakeHealth (edict_t *pEdict, float flHealth, int bitsDamageType) -{ - if (!pEdict->v.takedamage) - return 0; - -// heal - if ( pEdict->v.health >= pEdict->v.max_health ) - return 0; - - pEdict->v.health += flHealth; - - if (pEdict->v.health > pEdict->v.max_health) - pEdict->v.health = pEdict->v.max_health; - - return 1; -} - - -void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) -{ - // note: This function should ONLY be used for players!!! - - if (flDamage == 0) - return; - - if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR))) - return; - - // make blood decal on the wall! - TraceResult Bloodtr; - Vector vecTraceDir; - float flNoise; - int cCount; - int i; - - if (flDamage < 10) - { - flNoise = 0.1; - cCount = 1; - } - else if (flDamage < 25) - { - flNoise = 0.2; - cCount = 2; - } - else - { - flNoise = 0.3; - cCount = 4; - } - - for ( i = 0 ; i < cCount ; i++ ) - { - vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) - - vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); - vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); - - UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, pEdict, &Bloodtr); - - if ( Bloodtr.flFraction != 1.0 ) - { - UTIL_BloodDecalTrace( &Bloodtr, BLOOD_COLOR_RED ); - } - } -} - -void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) -{ - // note: This function should ONLY be used for players!!! - - if ( pEdict->v.takedamage ) - { - AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType ); - - SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood. - - UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType ); - } -} - - -int UTIL_IsMoving(edict_t *pEdict) -{ - return (pEdict->v.velocity != g_vecZero); -} - - -Vector UTIL_EyePosition(edict_t *pEdict) -{ - return (pEdict->v.origin + pEdict->v.view_ofs); -} - - -Vector UTIL_Center(edict_t *pEdict) -{ - if (pEdict->v.flags & FL_CLIENT) - return pEdict->v.origin; - else - return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5)); -} - - -edict_t *UTIL_GetNextTarget( edict_t *pEntity ) -{ - if ( FStringNull( pEntity->v.target ) ) - return NULL; - edict_t *pTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pEntity->v.target) ); - if ( FNullEnt(pTarget) ) - return NULL; - - return pTarget; -} - -edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView) -{ - int playerIndex = 0; - float distance, nearest_distance; - edict_t *pNearestPlayer = NULL; - - nearest_distance = 9999.9f; - - while ( playerIndex <= gpGlobals->maxClients ) - { - edict_t *pPlayerEdict = INDEXENT( playerIndex ); - - if ( pPlayerEdict && !pPlayerEdict->free ) - { - if (UTIL_IsPlayer(pPlayerEdict) && UTIL_IsAlive(pPlayerEdict)) - { - if (UTIL_FInViewCone( pPlayerEdict, pEdict, m_flFieldOfView ) && - !FBitSet( pPlayerEdict->v.flags, FL_NOTARGET ) && UTIL_FVisible( pPlayerEdict, pEdict ) ) - { - distance = (pPlayerEdict->v.origin - pEdict->v.origin).Length(); - if (distance < nearest_distance) - { - nearest_distance = distance; - pNearestPlayer = pPlayerEdict; - } - } - } - - } - - playerIndex++; - } - - return pNearestPlayer; -} - - -bool UTIL_IsBSPModel( edict_t *pent ) -{ - return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP); -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== util.cpp ======================================================== + + Utility code. Really not optional after all. + +*/ + +#include +#include // va_start, etc +#include + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "shake.h" +#include "decals.h" +#include "weapons.h" +#include "monsters.h" + +typedef struct { + DLL_FUNCTIONS *dllapi_table; + NEW_DLL_FUNCTIONS *newapi_table; +} gamedll_funcs_t; + +extern gamedll_funcs_t *gpGamedllFuncs; + + +// Print to console. +void META_CONS(char *fmt, ...) { + va_list ap; + char buf[1024]; + unsigned int len; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + len=strlen(buf); + if(len < sizeof(buf)-2) // -1 null, -1 for newline + strcat(buf, "\n"); + else + buf[len-1] = '\n'; + + (*g_engfuncs.pfnServerPrint)(buf); +} + +float UTIL_WeaponTimeBase( void ) +{ +#if defined( CLIENT_WEAPONS ) + return 0.0; +#else + return gpGlobals->time; +#endif +} + +static unsigned int glSeed = 0; + +unsigned int seed_table[ 256 ] = +{ + 28985, 27138, 26457, 9451, 17764, 10909, 28790, 8716, 6361, 4853, 17798, 21977, 19643, 20662, 10834, 20103, + 27067, 28634, 18623, 25849, 8576, 26234, 23887, 18228, 32587, 4836, 3306, 1811, 3035, 24559, 18399, 315, + 26766, 907, 24102, 12370, 9674, 2972, 10472, 16492, 22683, 11529, 27968, 30406, 13213, 2319, 23620, 16823, + 10013, 23772, 21567, 1251, 19579, 20313, 18241, 30130, 8402, 20807, 27354, 7169, 21211, 17293, 5410, 19223, + 10255, 22480, 27388, 9946, 15628, 24389, 17308, 2370, 9530, 31683, 25927, 23567, 11694, 26397, 32602, 15031, + 18255, 17582, 1422, 28835, 23607, 12597, 20602, 10138, 5212, 1252, 10074, 23166, 19823, 31667, 5902, 24630, + 18948, 14330, 14950, 8939, 23540, 21311, 22428, 22391, 3583, 29004, 30498, 18714, 4278, 2437, 22430, 3439, + 28313, 23161, 25396, 13471, 19324, 15287, 2563, 18901, 13103, 16867, 9714, 14322, 15197, 26889, 19372, 26241, + 31925, 14640, 11497, 8941, 10056, 6451, 28656, 10737, 13874, 17356, 8281, 25937, 1661, 4850, 7448, 12744, + 21826, 5477, 10167, 16705, 26897, 8839, 30947, 27978, 27283, 24685, 32298, 3525, 12398, 28726, 9475, 10208, + 617, 13467, 22287, 2376, 6097, 26312, 2974, 9114, 21787, 28010, 4725, 15387, 3274, 10762, 31695, 17320, + 18324, 12441, 16801, 27376, 22464, 7500, 5666, 18144, 15314, 31914, 31627, 6495, 5226, 31203, 2331, 4668, + 12650, 18275, 351, 7268, 31319, 30119, 7600, 2905, 13826, 11343, 13053, 15583, 30055, 31093, 5067, 761, + 9685, 11070, 21369, 27155, 3663, 26542, 20169, 12161, 15411, 30401, 7580, 31784, 8985, 29367, 20989, 14203, + 29694, 21167, 10337, 1706, 28578, 887, 3373, 19477, 14382, 675, 7033, 15111, 26138, 12252, 30996, 21409, + 25678, 18555, 13256, 23316, 22407, 16727, 991, 9236, 5373, 29402, 6117, 15241, 27715, 19291, 19888, 19847 +}; + +unsigned int U_Random( void ) +{ + glSeed *= 69069; + glSeed += seed_table[ glSeed & 0xff ]; + + return ( ++glSeed & 0x0fffffff ); +} + +void U_Srand( unsigned int seed ) +{ + glSeed = seed_table[ seed & 0xff ]; +} + +/* +===================== +UTIL_SharedRandomLong +===================== +*/ +int UTIL_SharedRandomLong( unsigned int seed, int low, int high ) +{ + unsigned int range; + + U_Srand( (int)seed + low + high ); + + range = high - low + 1; + if ( !(range - 1) ) + { + return low; + } + else + { + int offset; + int rnum; + + rnum = U_Random(); + + offset = rnum % range; + + return (low + offset); + } +} + +/* +===================== +UTIL_SharedRandomFloat +===================== +*/ +float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ) +{ + // + unsigned int range; + + U_Srand( (int)seed + *(int *)&low + *(int *)&high ); + + U_Random(); + U_Random(); + + range = high - low; + if ( !range ) + { + return low; + } + else + { + int tensixrand; + float offset; + + tensixrand = U_Random() & 65535; + + offset = (float)tensixrand / 65536.0; + + return (low + offset * range ); + } +} + +void UTIL_ParametricRocket( entvars_t *pev, Vector vecOrigin, Vector vecAngles, edict_t *owner ) +{ + pev->startpos = vecOrigin; + // Trace out line to end pos + TraceResult tr; + UTIL_MakeVectors( vecAngles ); + UTIL_TraceLine( pev->startpos, pev->startpos + gpGlobals->v_forward * 8192, ignore_monsters, owner, &tr); + pev->endpos = tr.vecEndPos; + + // Now compute how long it will take based on current velocity + Vector vecTravel = pev->endpos - pev->startpos; + float travelTime = 0.0; + if ( pev->velocity.Length() > 0 ) + { + travelTime = vecTravel.Length() / pev->velocity.Length(); + } + pev->starttime = gpGlobals->time; + pev->impacttime = gpGlobals->time + travelTime; +} + +int g_groupmask = 0; +int g_groupop = 0; + +// Normal overrides +void UTIL_SetGroupTrace( int groupmask, int op ) +{ + g_groupmask = groupmask; + g_groupop = op; + + ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); +} + +void UTIL_UnsetGroupTrace( void ) +{ + g_groupmask = 0; + g_groupop = 0; + + ENGINE_SETGROUPMASK( 0, 0 ); +} + +// Smart version, it'll clean itself up when it pops off stack +UTIL_GroupTrace::UTIL_GroupTrace( int groupmask, int op ) +{ + m_oldgroupmask = g_groupmask; + m_oldgroupop = g_groupop; + + g_groupmask = groupmask; + g_groupop = op; + + ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); +} + +UTIL_GroupTrace::~UTIL_GroupTrace( void ) +{ + g_groupmask = m_oldgroupmask; + g_groupop = m_oldgroupop; + + ENGINE_SETGROUPMASK( g_groupmask, g_groupop ); +} + + +#ifdef DEBUG +edict_t *DBG_EntOfVars( const entvars_t *pev ) +{ + if (pev->pContainingEntity != NULL) + return pev->pContainingEntity; + ALERT(at_console, "entvars_t pContainingEntity is NULL, calling into engine"); + edict_t* pent = (*g_engfuncs.pfnFindEntityByVars)((entvars_t*)pev); + if (pent == NULL) + ALERT(at_console, "DAMN! Even the engine couldn't FindEntityByVars!"); + ((entvars_t *)pev)->pContainingEntity = pent; + return pent; +} +#endif //DEBUG + + +#ifdef DEBUG + void +DBG_AssertFunction( + BOOL fExpr, + const char* szExpr, + const char* szFile, + int szLine, + const char* szMessage) + { + if (fExpr) + return; + char szOut[512]; + if (szMessage != NULL) + sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); + else + sprintf(szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine); + ALERT(at_console, szOut); + } +#endif // DEBUG + +// ripped this out of the engine +float UTIL_AngleMod(float a) +{ + if (a < 0) + { + a = a + 360 * ((int)(a / 360) + 1); + } + else if (a >= 360) + { + a = a - 360 * ((int)(a / 360)); + } + // a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); + return a; +} + +float UTIL_AngleDiff( float destAngle, float srcAngle ) +{ + float delta; + + delta = destAngle - srcAngle; + if ( destAngle > srcAngle ) + { + if ( delta >= 180 ) + delta -= 360; + } + else + { + if ( delta <= -180 ) + delta += 360; + } + return delta; +} + +Vector UTIL_VecToAngles( const Vector &vec ) +{ + float rgflVecOut[3]; + VEC_TO_ANGLES(vec, rgflVecOut); + return Vector(rgflVecOut); +} + +// float UTIL_MoveToOrigin( edict_t *pent, const Vector vecGoal, float flDist, int iMoveType ) +void UTIL_MoveToOrigin( edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType ) +{ + float rgfl[3]; + vecGoal.CopyToArray(rgfl); +// return MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType ); + MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType ); +} + + +int UTIL_EntitiesInBox( edict_t **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ) +{ + edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + int count; + + count = 0; + + if ( !pEdict ) + return count; + + for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) + { + if ( pEdict->free ) // Not in use + continue; + + if ( flagMask && !(pEdict->v.flags & flagMask) ) // Does it meet the criteria? + continue; + + if ( mins.x > pEdict->v.absmax.x || + mins.y > pEdict->v.absmax.y || + mins.z > pEdict->v.absmax.z || + maxs.x < pEdict->v.absmin.x || + maxs.y < pEdict->v.absmin.y || + maxs.z < pEdict->v.absmin.z ) + continue; + + pList[ count ] = pEdict; + count++; + + if ( count >= listMax ) + return count; + } + + return count; +} + + +int UTIL_MonstersInSphere( edict_t **pList, int listMax, const Vector ¢er, float radius ) +{ + edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 ); + int count; + float distance, delta; + + count = 0; + float radiusSquared = radius * radius; + + if ( !pEdict ) + return count; + + for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ ) + { + if ( pEdict->free ) // Not in use + continue; + + if ( !(pEdict->v.flags & (FL_CLIENT|FL_MONSTER)) ) // Not a client/monster ? + continue; + + // Use origin for X & Y since they are centered for all monsters + // Now X + delta = center.x - pEdict->v.origin.x;//(pEdict->v.absmin.x + pEdict->v.absmax.x)*0.5; + delta *= delta; + + if ( delta > radiusSquared ) + continue; + distance = delta; + + // Now Y + delta = center.y - pEdict->v.origin.y;//(pEdict->v.absmin.y + pEdict->v.absmax.y)*0.5; + delta *= delta; + + distance += delta; + if ( distance > radiusSquared ) + continue; + + // Now Z + delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z)*0.5; + delta *= delta; + + distance += delta; + if ( distance > radiusSquared ) + continue; + + pList[ count ] = pEdict; + count++; + + if ( count >= listMax ) + return count; + } + + + return count; +} + + +edict_t *UTIL_FindEntityInSphere( edict_t *pStartEntity, const Vector &vecCenter, float flRadius ) +{ + edict_t *pentEntity; + + if (pStartEntity) + pentEntity = pStartEntity; + else + pentEntity = NULL; + + pentEntity = FIND_ENTITY_IN_SPHERE( pentEntity, vecCenter, flRadius); + + if (!FNullEnt(pentEntity)) + return pentEntity; + + return NULL; +} + + +edict_t *UTIL_FindEntityByString( edict_t *pStartEntity, const char *szKeyword, const char *szValue ) +{ + edict_t *pentEntity; + + if (pStartEntity) + pentEntity = pStartEntity; + else + pentEntity = NULL; + + pentEntity = FIND_ENTITY_BY_STRING( pentEntity, szKeyword, szValue ); + + if (!FNullEnt(pentEntity)) + return pentEntity; + + return NULL; +} + +edict_t *UTIL_FindEntityByClassname( edict_t *pStartEntity, const char *szName ) +{ + return UTIL_FindEntityByString( pStartEntity, "classname", szName ); +} + +edict_t *UTIL_FindEntityByTargetname( edict_t *pStartEntity, const char *szName ) +{ + return UTIL_FindEntityByString( pStartEntity, "targetname", szName ); +} + + +edict_t *UTIL_FindEntityGeneric( const char *szWhatever, Vector &vecSrc, float flRadius ) +{ + edict_t *pEntity = NULL; + + pEntity = UTIL_FindEntityByTargetname( NULL, szWhatever ); + if (pEntity) + return pEntity; + + edict_t *pSearch = NULL; + float flMaxDist2 = flRadius * flRadius; + while ((pSearch = UTIL_FindEntityByClassname( pSearch, szWhatever )) != NULL) + { + float flDist2 = (pSearch->v.origin - vecSrc).Length(); + flDist2 = flDist2 * flDist2; + if (flMaxDist2 > flDist2) + { + pEntity = pSearch; + flMaxDist2 = flDist2; + } + } + return pEntity; +} + + +// returns a edict_t pointer to a player by index. Only returns if the player is spawned and connected +// otherwise returns NULL +// Index is 1 based +edict_t *UTIL_PlayerByIndex( int playerIndex ) +{ + edict_t *pPlayer = NULL; + + if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients ) + { + edict_t *pPlayerEdict = INDEXENT( playerIndex ); + if ( pPlayerEdict && !pPlayerEdict->free ) + { + pPlayer = pPlayerEdict; + } + } + + return pPlayer; +} + + +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); +} + + +#define SWAP(a,b,temp) ((temp)=(a),(a)=(b),(b)=(temp)) + +void UTIL_MakeInvVectors( const Vector &vec, globalvars_t *pgv ) +{ + MAKE_VECTORS(vec); + + float tmp; + pgv->v_right = pgv->v_right * -1; + + SWAP(pgv->v_forward.y, pgv->v_right.x, tmp); + SWAP(pgv->v_forward.z, pgv->v_up.x, tmp); + SWAP(pgv->v_right.z, pgv->v_up.y, tmp); +} + + +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); + +/*jlb + if (samp && *samp == '!') + { + char name[32]; + if (SENTENCEG_Lookup(samp, name) >= 0) + EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch); + } + else +jlb*/ + EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch); +} + +static unsigned short FixedUnsigned16( float value, float scale ) +{ + int output; + + output = value * scale; + if ( output < 0 ) + output = 0; + if ( output > 0xFFFF ) + output = 0xFFFF; + + return (unsigned short)output; +} + +static short FixedSigned16( float value, float scale ) +{ + int output; + + output = value * scale; + + if ( output > 32767 ) + output = 32767; + + if ( output < -32768 ) + output = -32768; + + return (short)output; +} + +// Shake the screen of all clients within radius +// radius == 0, shake all clients +// UNDONE: Allow caller to shake clients not ONGROUND? +// UNDONE: Fix falloff model (disabled)? +// UNDONE: Affect user controls? + +int gmsgShake = 0; + +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 ); // 4.12 fixed + shake.frequency = FixedUnsigned16( frequency, 1<<8 ); // 8.8 fixed + + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + edict_t *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer || !(pPlayer->v.flags & FL_ONGROUND) ) // Don't shake if not onground + continue; + + localAmplitude = 0; + + if ( radius <= 0 ) + localAmplitude = amplitude; + else + { + Vector delta = center - pPlayer->v.origin; + float distance = delta.Length(); + + // Had to get rid of this falloff - it didn't work well + if ( distance < radius ) + localAmplitude = amplitude;//radius - distance; + } + if ( localAmplitude ) + { + shake.amplitude = FixedUnsigned16( localAmplitude, 1<<12 ); // 4.12 fixed + + if (gmsgShake == 0) + gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake)); + + MESSAGE_BEGIN( MSG_ONE, gmsgShake, NULL, pPlayer ); // use the magic #1 for "one client" + + WRITE_SHORT( shake.amplitude ); // shake amount + WRITE_SHORT( shake.duration ); // shake lasts this long + WRITE_SHORT( shake.frequency ); // shake noise frequency + + MESSAGE_END(); + } + } +} + + + +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 ); // 4.12 fixed + fade.holdTime = FixedUnsigned16( fadeHold, 1<<12 ); // 4.12 fixed + fade.r = (int)color.x; + fade.g = (int)color.y; + fade.b = (int)color.z; + fade.a = alpha; + fade.fadeFlags = flags; +} + + +int gmsgFade = 0; + +void UTIL_ScreenFadeWrite( const ScreenFade &fade, edict_t *pEntity ) +{ + if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) + return; + + if (gmsgFade == 0) + gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade)); + + MESSAGE_BEGIN( MSG_ONE, gmsgFade, NULL, pEntity ); // use the magic #1 for "one client" + + WRITE_SHORT( fade.duration ); // fade lasts this long + WRITE_SHORT( fade.holdTime ); // fade lasts this long + WRITE_SHORT( fade.fadeFlags ); // fade type (in / out) + WRITE_BYTE( fade.r ); // fade red + WRITE_BYTE( fade.g ); // fade green + WRITE_BYTE( fade.b ); // fade blue + WRITE_BYTE( fade.a ); // fade blue + + 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++ ) + { + edict_t *pPlayer = UTIL_PlayerByIndex( i ); + + UTIL_ScreenFadeWrite( fade, pPlayer ); + } +} + + +void UTIL_ScreenFade( edict_t *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( edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage ) +{ + if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) + return; + + MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pEntity ); + 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 ( strlen( pMessage ) < 512 ) + { + WRITE_STRING( pMessage ); + } + else + { + char tmp[512]; + strncpy( tmp, pMessage, 511 ); + tmp[511] = 0; + WRITE_STRING( tmp ); + } + MESSAGE_END(); +} + +void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ) +{ + int i; + + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + edict_t *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + UTIL_HudMessage( pPlayer, textparms, pMessage ); + } +} + + +int gmsgTextMsg = 0; +int gmsgSayText = 0; + +void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) +{ + if (gmsgTextMsg == 0) + gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); + + 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 ) +{ + if (gmsgTextMsg == 0) + gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 ); + + MESSAGE_BEGIN( MSG_ONE, gmsgTextMsg, NULL, 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_SayText( const char *pText, edict_t *pEntity ) +{ + if ( !(pEntity->v.flags & FL_CLIENT) ) + return; + + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG( "SayText", -1 ); + + MESSAGE_BEGIN( MSG_ONE, gmsgSayText, NULL, pEntity ); + WRITE_BYTE( ENTINDEX(pEntity) ); + WRITE_STRING( pText ); + MESSAGE_END(); +} + +void UTIL_SayTextAll( const char *pText, edict_t *pEntity ) +{ + if (gmsgSayText == 0) + gmsgSayText = REG_USER_MSG( "SayText", -1 ); + + MESSAGE_BEGIN( MSG_ALL, gmsgSayText, NULL ); + WRITE_BYTE( ENTINDEX(pEntity) ); + WRITE_STRING( pText ); + MESSAGE_END(); +} + + +char *UTIL_dtos1( int d ) +{ + static char buf[8]; + sprintf( buf, "%d", d ); + return buf; +} + +char *UTIL_dtos2( int d ) +{ + static char buf[8]; + sprintf( buf, "%d", d ); + return buf; +} + +char *UTIL_dtos3( int d ) +{ + static char buf[8]; + sprintf( buf, "%d", d ); + return buf; +} + +char *UTIL_dtos4( int d ) +{ + static char buf[8]; + sprintf( buf, "%d", d ); + return buf; +} + +int gmsgHudText = 0; + +void UTIL_ShowMessage( const char *pString, edict_t *pEntity ) +{ + if ( !pEntity || !(pEntity->v.flags & FL_CLIENT) ) + return; + + if (gmsgHudText == 0) + gmsgHudText = REG_USER_MSG( "HudText", -1 ); + + MESSAGE_BEGIN( MSG_ONE, gmsgHudText, NULL, pEntity ); + WRITE_STRING( pString ); + MESSAGE_END(); +} + + +void UTIL_ShowMessageAll( const char *pString ) +{ + int i; + + // loop through all players + + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + edict_t *pPlayer = UTIL_PlayerByIndex( i ); + if ( pPlayer ) + UTIL_ShowMessage( pString, pPlayer ); + } +} + +// Overloaded to add IGNORE_GLASS +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 ? TRUE : FALSE) | (ignoreGlass?0x100:0), pentIgnore, ptr ); +} + + +void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr ) +{ + TRACE_LINE( vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), 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 ? TRUE : FALSE), hullNumber, pentIgnore, ptr ); +} + +void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr ) +{ + g_engfuncs.pfnTraceModel( vecStart, vecEnd, hullNumber, pentModel, ptr ); +} + + +TraceResult UTIL_GetGlobalTrace( ) +{ + TraceResult tr; + + tr.fAllSolid = gpGlobals->trace_allsolid; + tr.fStartSolid = gpGlobals->trace_startsolid; + tr.fInOpen = gpGlobals->trace_inopen; + tr.fInWater = gpGlobals->trace_inwater; + tr.flFraction = gpGlobals->trace_fraction; + tr.flPlaneDist = gpGlobals->trace_plane_dist; + tr.pHit = gpGlobals->trace_ent; + tr.vecEndPos = gpGlobals->trace_endpos; + tr.vecPlaneNormal = gpGlobals->trace_plane_normal; + tr.iHitgroup = gpGlobals->trace_hitgroup; + 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 ) +{ + SET_ORIGIN(ENT(pev), vecOrigin ); +} + +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; +} + + +float UTIL_ApproachAngle( float target, float value, float speed ) +{ + target = UTIL_AngleMod( target ); + value = UTIL_AngleMod( target ); + + float delta = target - value; + + // Speed is assumed to be positive + if ( speed < 0 ) + speed = -speed; + + if ( delta < -180 ) + delta += 360; + else if ( delta > 180 ) + delta -= 360; + + if ( delta > speed ) + value += speed; + else if ( delta < -speed ) + value -= speed; + else + value = target; + + return value; +} + + +float UTIL_AngleDistance( float next, float cur ) +{ + float delta = next - cur; + + if ( delta < -180 ) + delta += 360; + else if ( delta > 180 ) + delta -= 360; + + return delta; +} + + +float UTIL_SplineFraction( float value, float scale ) +{ + value = scale * value; + float valueSquared = value * value; + + // Nice little ease-in, ease-out spline-like curve + return 3 * valueSquared - 2 * valueSquared * value; +} + + +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; +} + +Vector UTIL_GetAimVector( edict_t *pent, float flSpeed ) +{ + Vector tmp; + GET_AIM_VECTOR(pent, flSpeed, tmp); + return tmp; +} + +BOOL UTIL_ShouldShowBlood( int color ) +{ + if ( color != DONT_BLEED ) + { + if ( color == BLOOD_COLOR_RED ) + { + if ( CVAR_GET_FLOAT("violence_hblood") != 0 ) + return TRUE; + } + else + { + if ( CVAR_GET_FLOAT("violence_ablood") != 0 ) + 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; + + 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( 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; + + // scale up blood effect in multiplayer for better visibility + amount *= 2; + + if ( amount > 255 ) + amount = 255; + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); + WRITE_BYTE( TE_BLOODSPRITE ); + WRITE_COORD( origin.x); // pos + WRITE_COORD( origin.y); + WRITE_COORD( origin.z); + WRITE_SHORT( g_sModelIndexBloodSpray ); // initial sprite model + WRITE_SHORT( g_sModelIndexBloodDrop ); // droplet sprite models + WRITE_BYTE( color ); // color index into host_basepal + WRITE_BYTE( min( max( 3, amount / 10 ), 16 ) ); // size + MESSAGE_END(); +} + +Vector UTIL_RandomBloodVector( void ) +{ + 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) ); + } +} + + +bool IsBSPModel(edict_t *pEdict) +{ + return ((pEdict->v.solid == SOLID_BSP) || (pEdict->v.movetype == MOVETYPE_PUSHSTEP)); +} + + +void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ) +{ + short entityIndex; + int index; + int message; + + if ( decalNumber < 0 ) + return; + + index = gDecals[ decalNumber ].index; + + if ( index < 0 ) + return; + + if (pTrace->flFraction == 1.0) + return; + + // Only decal BSP models + if ( pTrace->pHit ) + { + edict_t *pEntity = pTrace->pHit; + if ( pEntity && !IsBSPModel(pEntity) ) + return; + entityIndex = ENTINDEX( pTrace->pHit ); + } + else + entityIndex = 0; + + message = TE_DECAL; + if ( entityIndex != 0 ) + { + 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(); +} + +/* +============== +UTIL_PlayerDecalTrace + +A player is trying to apply his custom decal for the spray can. +Tell connected clients to display it, or use the default spray can decal +if the custom can't be loaded. +============== +*/ +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.0) + return; + + 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( (short)ENTINDEX(pTrace->pHit) ); + WRITE_BYTE( index ); + MESSAGE_END(); +} + +void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ) +{ + if ( decalNumber < 0 ) + return; + + int index = gDecals[ decalNumber ].index; + if ( index < 0 ) + return; + + if (pTrace->flFraction == 1.0) + return; + + 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( (short)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) ); + MESSAGE_END(); +} + + +void UTIL_StringToVector( float *pVector, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + strcpy( tempString, pString ); + pstr = pfront = tempString; + + for ( j = 0; j < 3; j++ ) // lifted from pr_edict.c + { + pVector[j] = atof( pfront ); + + while ( *pstr && *pstr != ' ' ) + pstr++; + if (!*pstr) + break; + pstr++; + pfront = pstr; + } + if (j < 2) + { + /* + ALERT( at_error, "Bad field in entity!! %s:%s == \"%s\"\n", + pkvd->szClassName, pkvd->szKeyName, pkvd->szValue ); + */ + for (j = j+1;j < 3; j++) + pVector[j] = 0; + } +} + + +void UTIL_StringToIntArray( int *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + strcpy( tempString, pString ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = 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 = position; + midUp.z = minz; + + if (UTIL_PointContents(midUp) != CONTENTS_WATER) + return minz; + + midUp.z = maxz; + if (UTIL_PointContents(midUp) == CONTENTS_WATER) + return maxz; + + float diff = maxz - minz; + while (diff > 1.0) + { + midUp.z = minz + diff/2.0; + 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.5; + + float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 ); + flHeight = flHeight - mins.z; + + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, mid ); + WRITE_BYTE( TE_BUBBLES ); + WRITE_COORD( mins.x ); // mins + WRITE_COORD( mins.y ); + WRITE_COORD( mins.z ); + WRITE_COORD( maxs.x ); // maxz + WRITE_COORD( maxs.y ); + WRITE_COORD( maxs.z ); + WRITE_COORD( flHeight ); // height + WRITE_SHORT( g_sModelIndexBubbles ); + WRITE_BYTE( count ); // count + WRITE_COORD( 8 ); // speed + MESSAGE_END(); +} + +void UTIL_BubbleTrail( Vector from, Vector to, int count ) +{ + float flHeight = UTIL_WaterLevel( from, from.z, from.z + 256 ); + flHeight = flHeight - from.z; + + if (flHeight < 8) + { + flHeight = UTIL_WaterLevel( to, to.z, to.z + 256 ); + flHeight = flHeight - to.z; + if (flHeight < 8) + return; + + // UNDONE: do a ploink sound + 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 ); // mins + WRITE_COORD( from.y ); + WRITE_COORD( from.z ); + WRITE_COORD( to.x ); // maxz + WRITE_COORD( to.y ); + WRITE_COORD( to.z ); + WRITE_COORD( flHeight ); // height + WRITE_SHORT( g_sModelIndexBubbles ); + WRITE_BYTE( count ); // count + WRITE_COORD( 8 ); // speed + MESSAGE_END(); +} + + +void UTIL_Remove( edict_t *pEntity ) +{ + if ( !pEntity ) + return; + +//jlb pEntity->UpdateOnRemove(); + pEntity->v.flags |= FL_KILLME; + pEntity->v.targetname = 0; +} + + +BOOL UTIL_IsValidEntity( edict_t *pent ) +{ + if ( !pent || pent->free || (pent->v.flags & FL_KILLME) ) + return FALSE; + return TRUE; +} + + +//========================================================= +// UTIL_LogPrintf - Prints a logged message to console. +// Preceded by LOG: ( timestamp ) < message > +//========================================================= +void UTIL_LogPrintf( char *fmt, ... ) +{ + va_list argptr; + static char string[1024]; + + va_start ( argptr, fmt ); + vsprintf ( string, fmt, argptr ); + va_end ( argptr ); + + // Print to server console + ALERT( at_logged, "%s", string ); +} + +//========================================================= +// UTIL_DotPoints - returns the dot product of a line from +// src to check and vecdir. +//========================================================= +float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ) +{ + Vector2D vec2LOS; + + vec2LOS = ( vecCheck - vecSrc ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + return DotProduct (vec2LOS , ( vecDir.Make2D() ) ); +} + + +//========================================================= +// UTIL_StripToken - for redundant keynames +//========================================================= +void UTIL_StripToken( const char *pKey, char *pDest ) +{ + int i = 0; + + while ( pKey[i] && pKey[i] != '#' ) + { + pDest[i] = pKey[i]; + i++; + } + pDest[i] = 0; +} + + +Vector VecBModelOrigin( entvars_t* pevBModel ) +{ + return pevBModel->absmin + ( pevBModel->size * 0.5 ); +} + + +bool UTIL_IsAlive(entvars_t *pev) +{ + return ((pev->deadflag == DEAD_NO) && (pev->health > 0) && + ((pev->flags & FL_NOTARGET) == 0) && (pev->takedamage != 0)); +} + + +bool UTIL_IsAlive(edict_t *pEdict) +{ + return ((pEdict->v.deadflag == DEAD_NO) && (pEdict->v.health > 0) && + ((pEdict->v.flags & FL_NOTARGET) == 0) && (pEdict->v.takedamage != 0)); +} + + +bool UTIL_IsPlayer(edict_t *pEdict) +{ + return ((pEdict->v.flags & FL_CLIENT) == FL_CLIENT); +} + + +Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc) +{ + if (pEdict->v.flags & FL_CLIENT) + return pEdict->v.origin + (pEdict->v.view_ofs * RANDOM_FLOAT(0.5, 1.1)); + else + return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5)); +} + +//========================================================= +// FVisible - returns true if a line can be traced from +// the caller's eyes to the target +//========================================================= +bool UTIL_FVisible ( edict_t *pEntity, edict_t *pLooker ) +{ + TraceResult tr; + Vector vecLookerOrigin; + Vector vecTargetOrigin; + + if (FBitSet( pEntity->v.flags, FL_NOTARGET )) + return FALSE; + + // don't look through water + if ((pLooker->v.waterlevel != 3 && pEntity->v.waterlevel == 3) + || (pLooker->v.waterlevel == 3 && pEntity->v.waterlevel == 0)) + return FALSE; + + vecLookerOrigin = pLooker->v.origin + pLooker->v.view_ofs;//look through the caller's 'eyes' + vecTargetOrigin = pEntity->v.origin + pEntity->v.view_ofs; + + UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, pLooker, &tr); + + if (tr.flFraction != 1.0) + { + return FALSE;// Line of sight is not established + } + else + { + return TRUE;// line of sight is valid. + } +} + +//========================================================= +// FVisible - returns true if a line can be traced from +// the caller's eyes to the target vector +//========================================================= +bool UTIL_FVisible ( const Vector &vecOrigin, edict_t *pLooker ) +{ + TraceResult tr; + Vector vecLookerOrigin; + + vecLookerOrigin = pLooker->v.origin + pLooker->v.view_ofs; + + UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, pLooker, &tr); + + if (tr.flFraction != 1.0) + { + return FALSE;// Line of sight is not established + } + else + { + return TRUE;// line of sight is valid. + } +} + + +//========================================================= +// FInViewCone - returns true is the passed ent is in +// the caller's forward view cone. The dot product is performed +// in 2d, making the view cone infinitely tall. +//========================================================= +bool UTIL_FInViewCone ( edict_t *pEntity, edict_t *pLooker, float fov ) +{ + Vector2D vec2LOS; + float flDot; + + UTIL_MakeVectors ( pLooker->v.angles ); + + vec2LOS = ( pEntity->v.origin - pLooker->v.origin ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); + + if ( flDot > fov ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +//========================================================= +// FInViewCone - returns true is the passed vector is in +// the caller's forward view cone. The dot product is performed +// in 2d, making the view cone infinitely tall. +//========================================================= +bool UTIL_FInViewCone ( Vector *pOrigin, edict_t *pLooker, float fov ) +{ + Vector2D vec2LOS; + float flDot; + + UTIL_MakeVectors ( pLooker->v.angles ); + + vec2LOS = ( *pOrigin - pLooker->v.origin ).Make2D(); + vec2LOS = vec2LOS.Normalize(); + + flDot = DotProduct (vec2LOS , gpGlobals->v_forward.Make2D() ); + + if ( flDot > fov ) + { + return TRUE; + } + else + { + return FALSE; + } +} + + +#define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage +#define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health + +int gmsgDamage = 0; + +int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // note: This function should ONLY be used for players!!! + + int bitsDamage = bitsDamageType; + int ffound = TRUE; + int fTookDamage; + float flRatio; + float flBonus; + float flHealthPrev = pEdict->v.health; + + flBonus = ARMOR_BONUS; + flRatio = ARMOR_RATIO; + + if (!pEdict->v.takedamage) + return 0; + + if ( ( bitsDamageType & DMG_BLAST ) ) + { + // blasts damage armor more. + flBonus *= 2; + } + + // Already dead + if ( !UTIL_IsAlive(pEdict) ) + return 0; + + // Armor. + if (pEdict->v.armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage! + { + float flNew = flDamage * flRatio; + + float flArmor; + + flArmor = (flDamage - flNew) * flBonus; + + // Does this use more armor than we have? + if (flArmor > pEdict->v.armorvalue) + { + flArmor = pEdict->v.armorvalue; + flArmor *= (1/flBonus); + flNew = flDamage - flArmor; + pEdict->v.armorvalue = 0; + } + else + pEdict->v.armorvalue -= flArmor; + + flDamage = flNew; + } + + float flTake; + Vector vecDir; + + if ( pEdict->v.deadflag == DEAD_NO ) + { + // no pain sound during death animation. +//jlb PainSound();// "Ouch!" + } + + // WARNING the cast to INT is critical!!! The player could wind up with 0.5 health + flTake = (int)flDamage; + + // grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit). + vecDir = Vector( 0, 0, 0 ); + + if (!FNullEnt( pevInflictor )) + { + edict_t *pInflictor = ENT(pevInflictor); + + if (pInflictor) + { + vecDir = ( UTIL_Center(pInflictor) - Vector ( 0, 0, 10 ) - UTIL_Center(pEdict) ).Normalize(); + vecDir = g_vecAttackDir = vecDir.Normalize(); + } + } + + fTookDamage = flTake; + + // if this is a player, move him around! + if ( ( !FNullEnt( pevInflictor ) ) && (pEdict->v.movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) ) + { + float force = flDamage * ((32 * 32 * 72.0) / (pEdict->v.size.x * pEdict->v.size.y * pEdict->v.size.z)) * 5; + + if ( force > 1000.0) + force = 1000.0; + + pEdict->v.velocity = pEdict->v.velocity + vecDir * -force; + } + + // do the damage + pEdict->v.health -= flTake; + + // store entity that hurt this player + pEdict->v.dmg_inflictor = ENT(pevAttacker); + + if ( pEdict->v.health <= 0 ) + { + pEdict->v.health = 1; // can't suicide if already dead! + gpGamedllFuncs->dllapi_table->pfnClientKill(pEdict); + + // Add 1 score to the monster that killed this player + if ( pevAttacker->flags & FL_MONSTER ) + pevAttacker->frags += 1.0; + } + + // tell director about it + MESSAGE_BEGIN( MSG_SPEC, SVC_HLTV ); + WRITE_BYTE ( DRC_EVENT ); // take damage event + WRITE_SHORT( ENTINDEX(pEdict) ); // index number of primary entity + WRITE_SHORT( ENTINDEX(ENT(pevInflictor)) ); // index number of secondary entity + WRITE_LONG( 5 ); // eventflags (priority and flags) + MESSAGE_END(); + + // handle all bits set in this damage message, + // let the suit give player the diagnosis + + // UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash ) + + // UNDONE: still need to record damage and heal messages for the following types + + // DMG_BURN + // DMG_FREEZE + // DMG_BLAST + // DMG_SHOCK + + if (pEdict->v.health > 0) + { + pEdict->v.punchangle.x = -2; + + // only send down damage type that have hud art + int visibleDamageBits = bitsDamage & DMG_SHOWNHUD; + + if (gmsgDamage == 0) + gmsgDamage = REG_USER_MSG( "Damage", -1 ); + + MESSAGE_BEGIN( MSG_ONE, gmsgDamage, NULL, VARS(pEdict) ); + WRITE_BYTE( 0 ); + WRITE_BYTE( fTookDamage ); + WRITE_LONG( visibleDamageBits ); + WRITE_COORD( pevInflictor->origin.x ); + WRITE_COORD( pevInflictor->origin.y ); + WRITE_COORD( pevInflictor->origin.z ); + MESSAGE_END(); + } + + return fTookDamage; +} + + +int UTIL_TakeHealth (edict_t *pEdict, float flHealth, int bitsDamageType) +{ + if (!pEdict->v.takedamage) + return 0; + +// heal + if ( pEdict->v.health >= pEdict->v.max_health ) + return 0; + + pEdict->v.health += flHealth; + + if (pEdict->v.health > pEdict->v.max_health) + pEdict->v.health = pEdict->v.max_health; + + return 1; +} + + +void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ) +{ + // note: This function should ONLY be used for players!!! + + if (flDamage == 0) + return; + + if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR))) + return; + + // make blood decal on the wall! + TraceResult Bloodtr; + Vector vecTraceDir; + float flNoise; + int cCount; + int i; + + if (flDamage < 10) + { + flNoise = 0.1; + cCount = 1; + } + else if (flDamage < 25) + { + flNoise = 0.2; + cCount = 2; + } + else + { + flNoise = 0.3; + cCount = 4; + } + + for ( i = 0 ; i < cCount ; i++ ) + { + vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going) + + vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise ); + vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise ); + + UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, pEdict, &Bloodtr); + + if ( Bloodtr.flFraction != 1.0 ) + { + UTIL_BloodDecalTrace( &Bloodtr, BLOOD_COLOR_RED ); + } + } +} + +void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType) +{ + // note: This function should ONLY be used for players!!! + + if ( pEdict->v.takedamage ) + { + AddMultiDamage( pevAttacker, pEdict, flDamage, bitsDamageType ); + + SpawnBlood(ptr->vecEndPos, BLOOD_COLOR_RED, flDamage);// a little surface blood. + + UTIL_TraceBleed( pEdict, flDamage, vecDir, ptr, bitsDamageType ); + } +} + + +int UTIL_IsMoving(edict_t *pEdict) +{ + return (pEdict->v.velocity != g_vecZero); +} + + +Vector UTIL_EyePosition(edict_t *pEdict) +{ + return (pEdict->v.origin + pEdict->v.view_ofs); +} + + +Vector UTIL_Center(edict_t *pEdict) +{ + if (pEdict->v.flags & FL_CLIENT) + return pEdict->v.origin; + else + return (pEdict->v.origin + ((pEdict->v.mins + pEdict->v.maxs) * 0.5)); +} + + +edict_t *UTIL_GetNextTarget( edict_t *pEntity ) +{ + if ( FStringNull( pEntity->v.target ) ) + return NULL; + edict_t *pTarget = FIND_ENTITY_BY_TARGETNAME ( NULL, STRING(pEntity->v.target) ); + if ( FNullEnt(pTarget) ) + return NULL; + + return pTarget; +} + +edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView) +{ + int playerIndex = 0; + float distance, nearest_distance; + edict_t *pNearestPlayer = NULL; + + nearest_distance = 9999.9f; + + while ( playerIndex <= gpGlobals->maxClients ) + { + edict_t *pPlayerEdict = INDEXENT( playerIndex ); + + if ( pPlayerEdict && !pPlayerEdict->free ) + { + if (UTIL_IsPlayer(pPlayerEdict) && UTIL_IsAlive(pPlayerEdict)) + { + if (UTIL_FInViewCone( pPlayerEdict, pEdict, m_flFieldOfView ) && + !FBitSet( pPlayerEdict->v.flags, FL_NOTARGET ) && UTIL_FVisible( pPlayerEdict, pEdict ) ) + { + distance = (pPlayerEdict->v.origin - pEdict->v.origin).Length(); + if (distance < nearest_distance) + { + nearest_distance = distance; + pNearestPlayer = pPlayerEdict; + } + } + } + + } + + playerIndex++; + } + + return pNearestPlayer; +} + + +bool UTIL_IsBSPModel( edict_t *pent ) +{ + return (pent->v.solid == SOLID_BSP || pent->v.movetype == MOVETYPE_PUSHSTEP); +} diff --git a/src/dlls/util.h b/src/dlls/util.h index 6171d72..9070c11 100644 --- a/src/dlls/util.h +++ b/src/dlls/util.h @@ -1,540 +1,540 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -// -// Misc utility code -// -#ifndef ACTIVITY_H -#include "activity.h" -#endif - -#ifndef ENGINECALLBACK_H -#include "enginecallback.h" -#endif -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file - -extern globalvars_t *gpGlobals; -extern cvar_t *g_psv_gravity; - -// Use this instead of ALLOC_STRING on constant strings -#define STRING(offset) (const char *)(gpGlobals->pStringBase + (int)offset) -#define MAKE_STRING(str) ((int)str - (int)STRING(0)) - -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); -} - -// for doing a reverse lookup. Say you have a door, and want to find its button. -inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) -{ - return FIND_ENTITY_BY_STRING(entStart, "target", pszName); -} - -// Keeps clutter down a bit, when writing key-value pairs -#define WRITEKEY_INT(pf, szKeyName, iKeyValue) ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) -#define WRITEKEY_FLOAT(pf, szKeyName, flKeyValue) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%f\"\n", szKeyName, flKeyValue) -#define WRITEKEY_STRING(pf, szKeyName, szKeyValue) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%s\"\n", szKeyName, szKeyValue) -#define WRITEKEY_VECTOR(pf, szKeyName, flX, flY, flZ) \ - ENGINE_FPRINTF(pf, "\"%s\" \"%f %f %f\"\n", szKeyName, flX, flY, flZ) - -// Keeps clutter down a bit, when using a float as a bit-vector -#define SetBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) | (bits)) -#define ClearBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) & ~(bits)) -#define FBitSet(flBitVector, bit) ((int)(flBitVector) & (bit)) - -// Makes these more explicit, and easier to find -#define FILE_GLOBAL static -#define DLL_GLOBAL - -// Until we figure out why "const" gives the compiler problems, we'll just have to use -// this bogus "empty" define to mark things as constant. -#define CONSTANT - -// More explicit than "int" -typedef int EOFFSET; - -// In case it's not alread defined -typedef int BOOL; - -// In case this ever changes -#define M_PI 3.14159265358979323846 - -// Keeps clutter down a bit, when declaring external entity/global method prototypes -#define DECLARE_GLOBAL_METHOD(MethodName) extern void DLLEXPORT MethodName( void ) -#define GLOBAL_METHOD(funcname) void DLLEXPORT funcname(void) - -// -// Conversion among the three types of "entity", including identity-conversions. -// -#ifdef DEBUG - extern edict_t *DBG_EntOfVars(const entvars_t *pev); - inline edict_t *ENT(const entvars_t *pev) { return DBG_EntOfVars(pev); } -#else - inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } -#endif -inline edict_t *ENT(edict_t *pent) { return pent; } -inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } -inline EOFFSET OFFSET(EOFFSET eoffset) { return eoffset; } -inline EOFFSET OFFSET(const edict_t *pent) -{ -#if _DEBUG - if ( !pent ) - ALERT( at_error, "Bad ent in OFFSET()\n" ); -#endif - return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); -} -inline EOFFSET OFFSET(entvars_t *pev) -{ -#if _DEBUG - if ( !pev ) - ALERT( at_error, "Bad pev in OFFSET()\n" ); -#endif - return OFFSET(ENT(pev)); -} -inline entvars_t *VARS(entvars_t *pev) { return pev; } - -inline entvars_t *VARS(edict_t *pent) -{ - if ( !pent ) - return NULL; - - return &pent->v; -} - -inline entvars_t* VARS(EOFFSET eoffset) { return VARS(ENT(eoffset)); } -inline int ENTINDEX(edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } -inline edict_t* INDEXENT( int iEdictNum ) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } -inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ) { - (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ENT(ent)); -} - -// Testing the three types of "entity" for nullity -#define eoNullEntity 0 -inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } -inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } -inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } - -// Testing strings for nullity -#define iStringNull 0 -inline BOOL FStringNull(int iString) { return iString == iStringNull; } - -#define cchMapNameMost 32 - -// Dot products for view cone checking -#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees -#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks -#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks -#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks - -// All monsters need this data -#define DONT_BLEED -1 -#define BLOOD_COLOR_RED (BYTE)247 -#define BLOOD_COLOR_YELLOW (BYTE)195 -#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW - -typedef enum -{ - - MONSTERSTATE_NONE = 0, - MONSTERSTATE_IDLE, - MONSTERSTATE_COMBAT, - MONSTERSTATE_ALERT, - MONSTERSTATE_HUNT, - MONSTERSTATE_PRONE, - MONSTERSTATE_SCRIPT, - MONSTERSTATE_PLAYDEAD, - MONSTERSTATE_DEAD - -} MONSTERSTATE; - - - -// Things that toggle (buttons/triggers/doors) need this -typedef enum - { - TS_AT_TOP, - TS_AT_BOTTOM, - TS_GOING_UP, - TS_GOING_DOWN - } TOGGLE_STATE; - -// Misc useful -#include "string.h" -inline BOOL FStrEq(const char*sz1, const char*sz2) - { return (strcmp(sz1, sz2) == 0); } -inline BOOL FClassnameIs(edict_t* pent, const char* szClassname) - { return FStrEq(STRING(VARS(pent)->classname), szClassname); } -inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) - { return FStrEq(STRING(pev->classname), szClassname); } - -// Misc. Prototypes -extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); -extern float UTIL_VecToYaw (const Vector &vec); -extern Vector UTIL_VecToAngles (const Vector &vec); -extern float UTIL_AngleMod (float a); -extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); - -extern edict_t *UTIL_FindEntityInSphere(edict_t *pStartEntity, const Vector &vecCenter, float flRadius); -extern edict_t *UTIL_FindEntityByString(edict_t *pStartEntity, const char *szKeyword, const char *szValue ); -extern edict_t *UTIL_FindEntityByClassname(edict_t *pStartEntity, const char *szName ); -extern edict_t *UTIL_FindEntityByTargetname(edict_t *pStartEntity, const char *szName ); -extern edict_t *UTIL_FindEntityGeneric(const char *szName, Vector &vecSrc, float flRadius ); - -// returns a edict_t pointer to a player by index. Only returns if the player is spawned and connected -// otherwise returns NULL -// Index is 1 based -extern edict_t *UTIL_PlayerByIndex( int playerIndex ); - -#define UTIL_EntitiesInPVS(pent) (*g_engfuncs.pfnEntitiesInPVS)(pent) -extern void UTIL_MakeVectors (const Vector &vecAngles); - -// Pass in an array of pointers and an array size, it fills the array and returns the number inserted -extern int UTIL_MonstersInSphere( edict_t **pList, int listMax, const Vector ¢er, float radius ); -extern int UTIL_EntitiesInBox( edict_t **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ); - -inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, float *p_vRight, float *p_vUp ) -{ - g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); -} - -extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted -extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); - -extern void UTIL_SetOrigin ( entvars_t* pev, const Vector &vecOrigin ); -extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); -extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); -extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); -extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); -extern void UTIL_ShowMessage ( const char *pString, edict_t *pPlayer ); -extern void UTIL_ShowMessageAll ( const char *pString ); -extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); -extern void UTIL_ScreenFade ( edict_t *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); - -typedef enum { ignore_monsters=1, dont_ignore_monsters=0, missile=2 } IGNORE_MONSTERS; -typedef enum { ignore_glass=1, dont_ignore_glass=0 } IGNORE_GLASS; -extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); -extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); -typedef enum { point_hull=0, human_hull=1, large_hull=2, head_hull=3 }; -extern void UTIL_TraceHull (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); -extern TraceResult UTIL_GetGlobalTrace (void); -extern void UTIL_TraceModel (const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); -extern Vector UTIL_GetAimVector (edict_t* pent, float flSpeed); -extern int UTIL_PointContents (const Vector &vec); - -extern void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); -extern void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount ); -extern Vector UTIL_RandomBloodVector( void ); -extern BOOL UTIL_ShouldShowBlood( int bloodColor ); -extern void UTIL_BloodDecalTrace( TraceResult *pTrace, int bloodColor ); -extern void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ); -extern void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom ); -extern void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ); -extern void UTIL_Sparks( const Vector &position ); -extern void UTIL_Ricochet( const Vector &position, float scale ); -extern void UTIL_StringToVector( float *pVector, const char *pString ); -extern void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); -extern Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize ); -extern float UTIL_Approach( float target, float value, float speed ); -extern float UTIL_ApproachAngle( float target, float value, float speed ); -extern float UTIL_AngleDistance( float next, float cur ); - -extern char *UTIL_VarArgs( char *format, ... ); -extern void UTIL_Remove( edict_t *pEntity ); -extern BOOL UTIL_IsValidEntity( edict_t *pent ); - -// Use for ease-in, ease-out style interpolation (accel/decel) -extern float UTIL_SplineFraction( float value, float scale ); - -// Search for water transition along a vertical line -extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); -extern void UTIL_Bubbles( Vector mins, Vector maxs, int count ); -extern void UTIL_BubbleTrail( Vector from, Vector to, int count ); - -// prints a message to each client -extern void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); -inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) -{ - UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); -} - -// prints messages through the HUD -extern void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); - -// prints a message to the HUD say (chat) -extern void UTIL_SayText( const char *pText, edict_t *pEntity ); -extern void UTIL_SayTextAll( const char *pText, edict_t *pEntity ); - - -typedef struct hudtextparms_s -{ - float x; - float y; - int effect; - byte r1, g1, b1, a1; - byte r2, g2, b2, a2; - float fadeinTime; - float fadeoutTime; - float holdTime; - float fxTime; - int channel; -} hudtextparms_t; - -// prints as transparent 'title' to the HUD -extern void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); -extern void UTIL_HudMessage( edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage ); - -// for handy use with ClientPrint params -extern char *UTIL_dtos1( int d ); -extern char *UTIL_dtos2( int d ); -extern char *UTIL_dtos3( int d ); -extern char *UTIL_dtos4( int d ); - -// Writes message to console with timestamp and FragLog header. -extern void UTIL_LogPrintf( char *fmt, ... ); - -// Sorta like FInViewCone, but for nonmonsters. -extern float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); - -extern void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames - -// Misc functions -extern void SetMovedir(entvars_t* pev); -extern int BuildChangeList( LEVELLIST *pLevelList, int maxList ); - -// -// How did I ever live without ASSERT? -// -#ifdef DEBUG -void DBG_AssertFunction(BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); -#define ASSERT(f) DBG_AssertFunction(f, #f, __FILE__, __LINE__, NULL) -#define ASSERTSZ(f, sz) DBG_AssertFunction(f, #f, __FILE__, __LINE__, sz) -#else // !DEBUG -#define ASSERT(f) -#define ASSERTSZ(f, sz) -#endif // !DEBUG - - -extern DLL_GLOBAL const Vector g_vecZero; - -// -// Constants that were used only by QC (maybe not used at all now) -// -// Un-comment only as needed -// -#define LANGUAGE_ENGLISH 0 -#define LANGUAGE_GERMAN 1 -#define LANGUAGE_FRENCH 2 -#define LANGUAGE_BRITISH 3 - -#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation -#define AMBIENT_SOUND_EVERYWHERE 1 -#define AMBIENT_SOUND_SMALLRADIUS 2 -#define AMBIENT_SOUND_MEDIUMRADIUS 4 -#define AMBIENT_SOUND_LARGERADIUS 8 -#define AMBIENT_SOUND_START_SILENT 16 -#define AMBIENT_SOUND_NOT_LOOPING 32 - -#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements - -#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients -#define SND_STOP (1<<5) // duplicated in protocol.h stop sound -#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol -#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch - -#define LFO_SQUARE 1 -#define LFO_TRIANGLE 2 -#define LFO_RANDOM 3 - -// func_rotating -#define SF_BRUSH_ROTATE_Y_AXIS 0 -#define SF_BRUSH_ROTATE_INSTANT 1 -#define SF_BRUSH_ROTATE_BACKWARDS 2 -#define SF_BRUSH_ROTATE_Z_AXIS 4 -#define SF_BRUSH_ROTATE_X_AXIS 8 -#define SF_PENDULUM_AUTO_RETURN 16 -#define SF_PENDULUM_PASSABLE 32 - - -#define SF_BRUSH_ROTATE_SMALLRADIUS 128 -#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 -#define SF_BRUSH_ROTATE_LARGERADIUS 512 - -#define PUSH_BLOCK_ONLY_X 1 -#define PUSH_BLOCK_ONLY_Y 2 - -#define VEC_HULL_MIN Vector(-16, -16, -36) -#define VEC_HULL_MAX Vector( 16, 16, 36) -#define VEC_HUMAN_HULL_MIN Vector( -16, -16, 0 ) -#define VEC_HUMAN_HULL_MAX Vector( 16, 16, 72 ) -#define VEC_HUMAN_HULL_DUCK Vector( 16, 16, 36 ) - -#define VEC_VIEW Vector( 0, 0, 28 ) - -#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18 ) -#define VEC_DUCK_HULL_MAX Vector( 16, 16, 18) -#define VEC_DUCK_VIEW Vector( 0, 0, 12 ) - -#define SVC_TEMPENTITY 23 -#define SVC_INTERMISSION 30 -#define SVC_CDTRACK 32 -#define SVC_WEAPONANIM 35 -#define SVC_ROOMTYPE 37 -#define SVC_HLTV 50 - -// prxoy director stuff -#define DRC_EVENT 3 // informs the dircetor about ann important game event - -#define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important) -#define DRC_FLAG_DRAMATIC (1<<5) - -// triggers -#define SF_TRIGGER_ALLOWMONSTERS 1// monsters allowed to fire this trigger -#define SF_TRIGGER_NOCLIENTS 2// players not allowed to fire this trigger -#define SF_TRIGGER_PUSHABLES 4// only pushables can fire this trigger - -// func breakable -#define SF_BREAK_TRIGGER_ONLY 1// may only be broken by trigger -#define SF_BREAK_TOUCH 2// can be 'crashed through' by running player (plate glass) -#define SF_BREAK_PRESSURE 4// can be broken by a player standing on it -#define SF_BREAK_CROWBAR 256// instant break if hit with crowbar - -// func_pushable (it's also func_breakable, so don't collide with those flags) -#define SF_PUSH_BREAKABLE 128 - -#define SF_LIGHT_START_OFF 1 - -#define SPAWNFLAG_NOMESSAGE 1 -#define SPAWNFLAG_NOTOUCH 1 -#define SPAWNFLAG_DROIDONLY 4 - -#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) - -#define TELE_PLAYER_ONLY 1 -#define TELE_SILENT 2 - -#define SF_TRIG_PUSH_ONCE 1 - - -// Sound Utilities - -// sentence groups -#define CBSENTENCENAME_MAX 16 -#define CVOXFILESENTENCEMAX 1536 // max number of sentences in game. NOTE: this must match - // CVOXFILESENTENCEMAX in engine\sound.h!!! - -extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; -extern int gcallsentences; - -int USENTENCEG_Pick(int isentenceg, char *szfound); -int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset); -void USENTENCEG_InitLRU(unsigned char *plru, int count); - -void SENTENCEG_Init(); -void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); -int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float attenuation, int flags, int pitch); -int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch); -int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch, int ipick, int freset); -int SENTENCEG_GetIndex(const char *szrootname); -int SENTENCEG_Lookup(const char *sample, char *sentencenum); - -void TEXTURETYPE_Init(); -char TEXTURETYPE_Find(char *name); -float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType); - -// 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). - -void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, - int flags, int pitch); - - -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); -} - -void EMIT_SOUND_SUIT(edict_t *entity, const char *sample); -void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg); -void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname); - -#define PRECACHE_SOUND_ARRAY( a ) \ - { for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND((char *) a [i]); } - -#define EMIT_SOUND_ARRAY_DYN( chan, array ) \ - EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) ); - -#define RANDOM_SOUND_ARRAY( array ) (array) [ RANDOM_LONG(0,ARRAYSIZE( (array) )-1) ] - -#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 GROUP_OP_AND 0 -#define GROUP_OP_NAND 1 - -extern int g_groupmask; -extern int g_groupop; - -class UTIL_GroupTrace -{ -public: - UTIL_GroupTrace( int groupmask, int op ); - ~UTIL_GroupTrace( void ); - -private: - int m_oldgroupmask, m_oldgroupop; -}; - -void UTIL_SetGroupTrace( int groupmask, int op ); -void UTIL_UnsetGroupTrace( void ); - -int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); -float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); - -float UTIL_WeaponTimeBase( void ); - -Vector VecBModelOrigin( entvars_t* pevBModel ); -bool UTIL_IsAlive(entvars_t *pev); -bool UTIL_IsAlive(edict_t *pEdict); -bool UTIL_IsPlayer(edict_t *pEdict); -Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc); -bool UTIL_FVisible(edict_t *pEdict, edict_t *pLooker); -bool UTIL_FVisible ( const Vector &vecOrigin, edict_t *pLooker ); -bool UTIL_FInViewCone ( edict_t *pEntity, edict_t *pLooker, float fov ); -bool UTIL_FInViewCone ( Vector *pOrigin, edict_t *pLooker, float fov ); -int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); -int UTIL_TakeHealth (edict_t *pEdict, float flHealth, int bitsDamageType); -void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); -void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); -int UTIL_IsMoving(edict_t *pEdict); -Vector UTIL_EyePosition(edict_t *pEdict); -Vector UTIL_Center(edict_t *pEdict); -edict_t *UTIL_GetNextTarget( edict_t *pEntity ); -edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView); -bool UTIL_IsBSPModel( edict_t *pent ); +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +// +// Misc utility code +// +#ifndef ACTIVITY_H +#include "activity.h" +#endif + +#ifndef ENGINECALLBACK_H +#include "enginecallback.h" +#endif +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file + +extern globalvars_t *gpGlobals; +extern cvar_t *g_psv_gravity; + +// Use this instead of ALLOC_STRING on constant strings +#define STRING(offset) (const char *)(gpGlobals->pStringBase + (int)offset) +#define MAKE_STRING(str) ((int)str - (int)STRING(0)) + +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); +} + +// for doing a reverse lookup. Say you have a door, and want to find its button. +inline edict_t *FIND_ENTITY_BY_TARGET(edict_t *entStart, const char *pszName) +{ + return FIND_ENTITY_BY_STRING(entStart, "target", pszName); +} + +// Keeps clutter down a bit, when writing key-value pairs +#define WRITEKEY_INT(pf, szKeyName, iKeyValue) ENGINE_FPRINTF(pf, "\"%s\" \"%d\"\n", szKeyName, iKeyValue) +#define WRITEKEY_FLOAT(pf, szKeyName, flKeyValue) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%f\"\n", szKeyName, flKeyValue) +#define WRITEKEY_STRING(pf, szKeyName, szKeyValue) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%s\"\n", szKeyName, szKeyValue) +#define WRITEKEY_VECTOR(pf, szKeyName, flX, flY, flZ) \ + ENGINE_FPRINTF(pf, "\"%s\" \"%f %f %f\"\n", szKeyName, flX, flY, flZ) + +// Keeps clutter down a bit, when using a float as a bit-vector +#define SetBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) | (bits)) +#define ClearBits(flBitVector, bits) ((flBitVector) = (int)(flBitVector) & ~(bits)) +#define FBitSet(flBitVector, bit) ((int)(flBitVector) & (bit)) + +// Makes these more explicit, and easier to find +#define FILE_GLOBAL static +#define DLL_GLOBAL + +// Until we figure out why "const" gives the compiler problems, we'll just have to use +// this bogus "empty" define to mark things as constant. +#define CONSTANT + +// More explicit than "int" +typedef int EOFFSET; + +// In case it's not alread defined +typedef int BOOL; + +// In case this ever changes +#define M_PI 3.14159265358979323846 + +// Keeps clutter down a bit, when declaring external entity/global method prototypes +#define DECLARE_GLOBAL_METHOD(MethodName) extern void DLLEXPORT MethodName( void ) +#define GLOBAL_METHOD(funcname) void DLLEXPORT funcname(void) + +// +// Conversion among the three types of "entity", including identity-conversions. +// +#ifdef DEBUG + extern edict_t *DBG_EntOfVars(const entvars_t *pev); + inline edict_t *ENT(const entvars_t *pev) { return DBG_EntOfVars(pev); } +#else + inline edict_t *ENT(const entvars_t *pev) { return pev->pContainingEntity; } +#endif +inline edict_t *ENT(edict_t *pent) { return pent; } +inline edict_t *ENT(EOFFSET eoffset) { return (*g_engfuncs.pfnPEntityOfEntOffset)(eoffset); } +inline EOFFSET OFFSET(EOFFSET eoffset) { return eoffset; } +inline EOFFSET OFFSET(const edict_t *pent) +{ +#if _DEBUG + if ( !pent ) + ALERT( at_error, "Bad ent in OFFSET()\n" ); +#endif + return (*g_engfuncs.pfnEntOffsetOfPEntity)(pent); +} +inline EOFFSET OFFSET(entvars_t *pev) +{ +#if _DEBUG + if ( !pev ) + ALERT( at_error, "Bad pev in OFFSET()\n" ); +#endif + return OFFSET(ENT(pev)); +} +inline entvars_t *VARS(entvars_t *pev) { return pev; } + +inline entvars_t *VARS(edict_t *pent) +{ + if ( !pent ) + return NULL; + + return &pent->v; +} + +inline entvars_t* VARS(EOFFSET eoffset) { return VARS(ENT(eoffset)); } +inline int ENTINDEX(edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } +inline edict_t* INDEXENT( int iEdictNum ) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } +inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ) { + (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ENT(ent)); +} + +// Testing the three types of "entity" for nullity +#define eoNullEntity 0 +inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; } +inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); } +inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); } + +// Testing strings for nullity +#define iStringNull 0 +inline BOOL FStringNull(int iString) { return iString == iStringNull; } + +#define cchMapNameMost 32 + +// Dot products for view cone checking +#define VIEW_FIELD_FULL (float)-1.0 // +-180 degrees +#define VIEW_FIELD_WIDE (float)-0.7 // +-135 degrees 0.1 // +-85 degrees, used for full FOV checks +#define VIEW_FIELD_NARROW (float)0.7 // +-45 degrees, more narrow check used to set up ranged attacks +#define VIEW_FIELD_ULTRA_NARROW (float)0.9 // +-25 degrees, more narrow check used to set up ranged attacks + +// All monsters need this data +#define DONT_BLEED -1 +#define BLOOD_COLOR_RED (BYTE)247 +#define BLOOD_COLOR_YELLOW (BYTE)195 +#define BLOOD_COLOR_GREEN BLOOD_COLOR_YELLOW + +typedef enum +{ + + MONSTERSTATE_NONE = 0, + MONSTERSTATE_IDLE, + MONSTERSTATE_COMBAT, + MONSTERSTATE_ALERT, + MONSTERSTATE_HUNT, + MONSTERSTATE_PRONE, + MONSTERSTATE_SCRIPT, + MONSTERSTATE_PLAYDEAD, + MONSTERSTATE_DEAD + +} MONSTERSTATE; + + + +// Things that toggle (buttons/triggers/doors) need this +typedef enum + { + TS_AT_TOP, + TS_AT_BOTTOM, + TS_GOING_UP, + TS_GOING_DOWN + } TOGGLE_STATE; + +// Misc useful +#include "string.h" +inline BOOL FStrEq(const char*sz1, const char*sz2) + { return (strcmp(sz1, sz2) == 0); } +inline BOOL FClassnameIs(edict_t* pent, const char* szClassname) + { return FStrEq(STRING(VARS(pent)->classname), szClassname); } +inline BOOL FClassnameIs(entvars_t* pev, const char* szClassname) + { return FStrEq(STRING(pev->classname), szClassname); } + +// Misc. Prototypes +extern void UTIL_SetSize (entvars_t* pev, const Vector &vecMin, const Vector &vecMax); +extern float UTIL_VecToYaw (const Vector &vec); +extern Vector UTIL_VecToAngles (const Vector &vec); +extern float UTIL_AngleMod (float a); +extern float UTIL_AngleDiff ( float destAngle, float srcAngle ); + +extern edict_t *UTIL_FindEntityInSphere(edict_t *pStartEntity, const Vector &vecCenter, float flRadius); +extern edict_t *UTIL_FindEntityByString(edict_t *pStartEntity, const char *szKeyword, const char *szValue ); +extern edict_t *UTIL_FindEntityByClassname(edict_t *pStartEntity, const char *szName ); +extern edict_t *UTIL_FindEntityByTargetname(edict_t *pStartEntity, const char *szName ); +extern edict_t *UTIL_FindEntityGeneric(const char *szName, Vector &vecSrc, float flRadius ); + +// returns a edict_t pointer to a player by index. Only returns if the player is spawned and connected +// otherwise returns NULL +// Index is 1 based +extern edict_t *UTIL_PlayerByIndex( int playerIndex ); + +#define UTIL_EntitiesInPVS(pent) (*g_engfuncs.pfnEntitiesInPVS)(pent) +extern void UTIL_MakeVectors (const Vector &vecAngles); + +// Pass in an array of pointers and an array size, it fills the array and returns the number inserted +extern int UTIL_MonstersInSphere( edict_t **pList, int listMax, const Vector ¢er, float radius ); +extern int UTIL_EntitiesInBox( edict_t **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask ); + +inline void UTIL_MakeVectorsPrivate( const Vector &vecAngles, float *p_vForward, float *p_vRight, float *p_vUp ) +{ + g_engfuncs.pfnAngleVectors( vecAngles, p_vForward, p_vRight, p_vUp ); +} + +extern void UTIL_MakeAimVectors ( const Vector &vecAngles ); // like MakeVectors, but assumes pitch isn't inverted +extern void UTIL_MakeInvVectors ( const Vector &vec, globalvars_t *pgv ); + +extern void UTIL_SetOrigin ( entvars_t* pev, const Vector &vecOrigin ); +extern void UTIL_EmitAmbientSound ( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch ); +extern void UTIL_ParticleEffect ( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ); +extern void UTIL_ScreenShake ( const Vector ¢er, float amplitude, float frequency, float duration, float radius ); +extern void UTIL_ScreenShakeAll ( const Vector ¢er, float amplitude, float frequency, float duration ); +extern void UTIL_ShowMessage ( const char *pString, edict_t *pPlayer ); +extern void UTIL_ShowMessageAll ( const char *pString ); +extern void UTIL_ScreenFadeAll ( const Vector &color, float fadeTime, float holdTime, int alpha, int flags ); +extern void UTIL_ScreenFade ( edict_t *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags ); + +typedef enum { ignore_monsters=1, dont_ignore_monsters=0, missile=2 } IGNORE_MONSTERS; +typedef enum { ignore_glass=1, dont_ignore_glass=0 } IGNORE_GLASS; +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr); +extern void UTIL_TraceLine (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr); +typedef enum { point_hull=0, human_hull=1, large_hull=2, head_hull=3 }; +extern void UTIL_TraceHull (const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr); +extern TraceResult UTIL_GetGlobalTrace (void); +extern void UTIL_TraceModel (const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr); +extern Vector UTIL_GetAimVector (edict_t* pent, float flSpeed); +extern int UTIL_PointContents (const Vector &vec); + +extern void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ); +extern void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount ); +extern Vector UTIL_RandomBloodVector( void ); +extern BOOL UTIL_ShouldShowBlood( int bloodColor ); +extern void UTIL_BloodDecalTrace( TraceResult *pTrace, int bloodColor ); +extern void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber ); +extern void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom ); +extern void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber ); +extern void UTIL_Sparks( const Vector &position ); +extern void UTIL_Ricochet( const Vector &position, float scale ); +extern void UTIL_StringToVector( float *pVector, const char *pString ); +extern void UTIL_StringToIntArray( int *pVector, int count, const char *pString ); +extern Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize ); +extern float UTIL_Approach( float target, float value, float speed ); +extern float UTIL_ApproachAngle( float target, float value, float speed ); +extern float UTIL_AngleDistance( float next, float cur ); + +extern char *UTIL_VarArgs( char *format, ... ); +extern void UTIL_Remove( edict_t *pEntity ); +extern BOOL UTIL_IsValidEntity( edict_t *pent ); + +// Use for ease-in, ease-out style interpolation (accel/decel) +extern float UTIL_SplineFraction( float value, float scale ); + +// Search for water transition along a vertical line +extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); +extern void UTIL_Bubbles( Vector mins, Vector maxs, int count ); +extern void UTIL_BubbleTrail( Vector from, Vector to, int count ); + +// prints a message to each client +extern void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); +inline void UTIL_CenterPrintAll( const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) +{ + UTIL_ClientPrintAll( HUD_PRINTCENTER, msg_name, param1, param2, param3, param4 ); +} + +// prints messages through the HUD +extern void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ); + +// prints a message to the HUD say (chat) +extern void UTIL_SayText( const char *pText, edict_t *pEntity ); +extern void UTIL_SayTextAll( const char *pText, edict_t *pEntity ); + + +typedef struct hudtextparms_s +{ + float x; + float y; + int effect; + byte r1, g1, b1, a1; + byte r2, g2, b2, a2; + float fadeinTime; + float fadeoutTime; + float holdTime; + float fxTime; + int channel; +} hudtextparms_t; + +// prints as transparent 'title' to the HUD +extern void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); +extern void UTIL_HudMessage( edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage ); + +// for handy use with ClientPrint params +extern char *UTIL_dtos1( int d ); +extern char *UTIL_dtos2( int d ); +extern char *UTIL_dtos3( int d ); +extern char *UTIL_dtos4( int d ); + +// Writes message to console with timestamp and FragLog header. +extern void UTIL_LogPrintf( char *fmt, ... ); + +// Sorta like FInViewCone, but for nonmonsters. +extern float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ); + +extern void UTIL_StripToken( const char *pKey, char *pDest );// for redundant keynames + +// Misc functions +extern void SetMovedir(entvars_t* pev); +extern int BuildChangeList( LEVELLIST *pLevelList, int maxList ); + +// +// How did I ever live without ASSERT? +// +#ifdef DEBUG +void DBG_AssertFunction(BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage); +#define ASSERT(f) DBG_AssertFunction(f, #f, __FILE__, __LINE__, NULL) +#define ASSERTSZ(f, sz) DBG_AssertFunction(f, #f, __FILE__, __LINE__, sz) +#else // !DEBUG +#define ASSERT(f) +#define ASSERTSZ(f, sz) +#endif // !DEBUG + + +extern DLL_GLOBAL const Vector g_vecZero; + +// +// Constants that were used only by QC (maybe not used at all now) +// +// Un-comment only as needed +// +#define LANGUAGE_ENGLISH 0 +#define LANGUAGE_GERMAN 1 +#define LANGUAGE_FRENCH 2 +#define LANGUAGE_BRITISH 3 + +#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation +#define AMBIENT_SOUND_EVERYWHERE 1 +#define AMBIENT_SOUND_SMALLRADIUS 2 +#define AMBIENT_SOUND_MEDIUMRADIUS 4 +#define AMBIENT_SOUND_LARGERADIUS 8 +#define AMBIENT_SOUND_START_SILENT 16 +#define AMBIENT_SOUND_NOT_LOOPING 32 + +#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements + +#define SND_SPAWNING (1<<8) // duplicated in protocol.h we're spawing, used in some cases for ambients +#define SND_STOP (1<<5) // duplicated in protocol.h stop sound +#define SND_CHANGE_VOL (1<<6) // duplicated in protocol.h change sound vol +#define SND_CHANGE_PITCH (1<<7) // duplicated in protocol.h change sound pitch + +#define LFO_SQUARE 1 +#define LFO_TRIANGLE 2 +#define LFO_RANDOM 3 + +// func_rotating +#define SF_BRUSH_ROTATE_Y_AXIS 0 +#define SF_BRUSH_ROTATE_INSTANT 1 +#define SF_BRUSH_ROTATE_BACKWARDS 2 +#define SF_BRUSH_ROTATE_Z_AXIS 4 +#define SF_BRUSH_ROTATE_X_AXIS 8 +#define SF_PENDULUM_AUTO_RETURN 16 +#define SF_PENDULUM_PASSABLE 32 + + +#define SF_BRUSH_ROTATE_SMALLRADIUS 128 +#define SF_BRUSH_ROTATE_MEDIUMRADIUS 256 +#define SF_BRUSH_ROTATE_LARGERADIUS 512 + +#define PUSH_BLOCK_ONLY_X 1 +#define PUSH_BLOCK_ONLY_Y 2 + +#define VEC_HULL_MIN Vector(-16, -16, -36) +#define VEC_HULL_MAX Vector( 16, 16, 36) +#define VEC_HUMAN_HULL_MIN Vector( -16, -16, 0 ) +#define VEC_HUMAN_HULL_MAX Vector( 16, 16, 72 ) +#define VEC_HUMAN_HULL_DUCK Vector( 16, 16, 36 ) + +#define VEC_VIEW Vector( 0, 0, 28 ) + +#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18 ) +#define VEC_DUCK_HULL_MAX Vector( 16, 16, 18) +#define VEC_DUCK_VIEW Vector( 0, 0, 12 ) + +#define SVC_TEMPENTITY 23 +#define SVC_INTERMISSION 30 +#define SVC_CDTRACK 32 +#define SVC_WEAPONANIM 35 +#define SVC_ROOMTYPE 37 +#define SVC_HLTV 50 + +// prxoy director stuff +#define DRC_EVENT 3 // informs the dircetor about ann important game event + +#define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important) +#define DRC_FLAG_DRAMATIC (1<<5) + +// triggers +#define SF_TRIGGER_ALLOWMONSTERS 1// monsters allowed to fire this trigger +#define SF_TRIGGER_NOCLIENTS 2// players not allowed to fire this trigger +#define SF_TRIGGER_PUSHABLES 4// only pushables can fire this trigger + +// func breakable +#define SF_BREAK_TRIGGER_ONLY 1// may only be broken by trigger +#define SF_BREAK_TOUCH 2// can be 'crashed through' by running player (plate glass) +#define SF_BREAK_PRESSURE 4// can be broken by a player standing on it +#define SF_BREAK_CROWBAR 256// instant break if hit with crowbar + +// func_pushable (it's also func_breakable, so don't collide with those flags) +#define SF_PUSH_BREAKABLE 128 + +#define SF_LIGHT_START_OFF 1 + +#define SPAWNFLAG_NOMESSAGE 1 +#define SPAWNFLAG_NOTOUCH 1 +#define SPAWNFLAG_DROIDONLY 4 + +#define SPAWNFLAG_USEONLY 1 // can't be touched, must be used (buttons) + +#define TELE_PLAYER_ONLY 1 +#define TELE_SILENT 2 + +#define SF_TRIG_PUSH_ONCE 1 + + +// Sound Utilities + +// sentence groups +#define CBSENTENCENAME_MAX 16 +#define CVOXFILESENTENCEMAX 1536 // max number of sentences in game. NOTE: this must match + // CVOXFILESENTENCEMAX in engine\sound.h!!! + +extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX]; +extern int gcallsentences; + +int USENTENCEG_Pick(int isentenceg, char *szfound); +int USENTENCEG_PickSequential(int isentenceg, char *szfound, int ipick, int freset); +void USENTENCEG_InitLRU(unsigned char *plru, int count); + +void SENTENCEG_Init(); +void SENTENCEG_Stop(edict_t *entity, int isentenceg, int ipick); +int SENTENCEG_PlayRndI(edict_t *entity, int isentenceg, float volume, float attenuation, int flags, int pitch); +int SENTENCEG_PlayRndSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch); +int SENTENCEG_PlaySequentialSz(edict_t *entity, const char *szrootname, float volume, float attenuation, int flags, int pitch, int ipick, int freset); +int SENTENCEG_GetIndex(const char *szrootname); +int SENTENCEG_Lookup(const char *sample, char *sentencenum); + +void TEXTURETYPE_Init(); +char TEXTURETYPE_Find(char *name); +float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int iBulletType); + +// 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). + +void EMIT_SOUND_DYN(edict_t *entity, int channel, const char *sample, float volume, float attenuation, + int flags, int pitch); + + +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); +} + +void EMIT_SOUND_SUIT(edict_t *entity, const char *sample); +void EMIT_GROUPID_SUIT(edict_t *entity, int isentenceg); +void EMIT_GROUPNAME_SUIT(edict_t *entity, const char *groupname); + +#define PRECACHE_SOUND_ARRAY( a ) \ + { for (int i = 0; i < ARRAYSIZE( a ); i++ ) PRECACHE_SOUND((char *) a [i]); } + +#define EMIT_SOUND_ARRAY_DYN( chan, array ) \ + EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, ATTN_NORM, 0, RANDOM_LONG(95,105) ); + +#define RANDOM_SOUND_ARRAY( array ) (array) [ RANDOM_LONG(0,ARRAYSIZE( (array) )-1) ] + +#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 GROUP_OP_AND 0 +#define GROUP_OP_NAND 1 + +extern int g_groupmask; +extern int g_groupop; + +class UTIL_GroupTrace +{ +public: + UTIL_GroupTrace( int groupmask, int op ); + ~UTIL_GroupTrace( void ); + +private: + int m_oldgroupmask, m_oldgroupop; +}; + +void UTIL_SetGroupTrace( int groupmask, int op ); +void UTIL_UnsetGroupTrace( void ); + +int UTIL_SharedRandomLong( unsigned int seed, int low, int high ); +float UTIL_SharedRandomFloat( unsigned int seed, float low, float high ); + +float UTIL_WeaponTimeBase( void ); + +Vector VecBModelOrigin( entvars_t* pevBModel ); +bool UTIL_IsAlive(entvars_t *pev); +bool UTIL_IsAlive(edict_t *pEdict); +bool UTIL_IsPlayer(edict_t *pEdict); +Vector UTIL_BodyTarget(edict_t *pEdict, Vector posSrc); +bool UTIL_FVisible(edict_t *pEdict, edict_t *pLooker); +bool UTIL_FVisible ( const Vector &vecOrigin, edict_t *pLooker ); +bool UTIL_FInViewCone ( edict_t *pEntity, edict_t *pLooker, float fov ); +bool UTIL_FInViewCone ( Vector *pOrigin, edict_t *pLooker, float fov ); +int UTIL_TakeDamage( edict_t *pEdict, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); +int UTIL_TakeHealth (edict_t *pEdict, float flHealth, int bitsDamageType); +void UTIL_TraceBleed( edict_t *pEdict, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType ); +void UTIL_TraceAttack( edict_t *pEdict, entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType); +int UTIL_IsMoving(edict_t *pEdict); +Vector UTIL_EyePosition(edict_t *pEdict); +Vector UTIL_Center(edict_t *pEdict); +edict_t *UTIL_GetNextTarget( edict_t *pEntity ); +edict_t *UTIL_FindNearestPlayer(edict_t *pEdict, float m_flFieldOfView); +bool UTIL_IsBSPModel( edict_t *pent ); diff --git a/src/dlls/vector.h b/src/dlls/vector.h index cd5d735..282e3b9 100644 --- a/src/dlls/vector.h +++ b/src/dlls/vector.h @@ -1,112 +1,112 @@ -/*** -* -* 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. -* -****/ -#ifndef VECTOR_H -#define VECTOR_H - -//========================================================= -// 2DVector - used for many pathfinding and many other -// operations that are treated as planar rather than 3d. -//========================================================= -class Vector2D -{ -public: - inline Vector2D(void) { } - inline Vector2D(float X, float Y) { x = X; y = Y; } - inline Vector2D operator+(const Vector2D& v) const { return Vector2D(x+v.x, y+v.y); } - inline Vector2D operator-(const Vector2D& v) const { return Vector2D(x-v.x, y-v.y); } - inline Vector2D operator*(float fl) const { return Vector2D(x*fl, y*fl); } - inline Vector2D operator/(float fl) const { return Vector2D(x/fl, y/fl); } - - inline float Length(void) const { return sqrt(x*x + y*y ); } - - inline Vector2D Normalize ( void ) const - { - Vector2D vec2; - - float flLen = Length(); - if ( flLen == 0 ) - { - return Vector2D( 0, 0 ); - } - else - { - flLen = 1 / flLen; - return Vector2D( x * flLen, y * flLen ); - } - } - - vec_t x, y; -}; - -inline float 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 -//========================================================= -class Vector // same data-layout as engine's vec3_t, -{ // which is a vec_t[3] -public: - // Construction/destruction - inline Vector(void) { } - inline Vector(float X, float Y, float Z) { x = X; y = Y; z = Z; } - //inline Vector(double X, double Y, double Z) { x = (float)X; y = (float)Y; z = (float)Z; } - //inline Vector(int X, int Y, int Z) { x = (float)X; y = (float)Y; z = (float)Z; } - inline Vector(const Vector& v) { x = v.x; y = v.y; z = v.z; } - inline Vector(float rgfl[3]) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; } - - // Operators - inline Vector operator-(void) const { return Vector(-x,-y,-z); } - inline int operator==(const Vector& v) const { return x==v.x && y==v.y && z==v.z; } - inline int operator!=(const Vector& v) const { return !(*this==v); } - inline Vector operator+(const Vector& v) const { return Vector(x+v.x, y+v.y, z+v.z); } - inline Vector operator-(const Vector& v) const { return Vector(x-v.x, y-v.y, z-v.z); } - inline Vector operator*(float fl) const { return Vector(x*fl, y*fl, z*fl); } - inline Vector operator/(float fl) const { return Vector(x/fl, y/fl, z/fl); } - - // Methods - inline void CopyToArray(float* rgfl) const { rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; } - inline float Length(void) const { return sqrt(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 - inline Vector Normalize(void) const - { - float flLen = Length(); - if (flLen == 0) return Vector(0,0,1); // ???? - flLen = 1 / flLen; - return Vector(x * flLen, y * flLen, z * flLen); - } - - inline Vector2D Make2D ( void ) const - { - Vector2D Vec2; - - Vec2.x = x; - Vec2.y = y; - - return Vec2; - } - inline float Length2D(void) const { return sqrt(x*x + y*y); } - - // Members - vec_t x, y, z; -}; -inline Vector operator*(float fl, const Vector& v) { return v * fl; } -inline float DotProduct(const Vector& a, const Vector& b) { return(a.x*b.x+a.y*b.y+a.z*b.z); } -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 ); } - - - +/*** +* +* 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. +* +****/ +#ifndef VECTOR_H +#define VECTOR_H + +//========================================================= +// 2DVector - used for many pathfinding and many other +// operations that are treated as planar rather than 3d. +//========================================================= +class Vector2D +{ +public: + inline Vector2D(void) { } + inline Vector2D(float X, float Y) { x = X; y = Y; } + inline Vector2D operator+(const Vector2D& v) const { return Vector2D(x+v.x, y+v.y); } + inline Vector2D operator-(const Vector2D& v) const { return Vector2D(x-v.x, y-v.y); } + inline Vector2D operator*(float fl) const { return Vector2D(x*fl, y*fl); } + inline Vector2D operator/(float fl) const { return Vector2D(x/fl, y/fl); } + + inline float Length(void) const { return sqrt(x*x + y*y ); } + + inline Vector2D Normalize ( void ) const + { + Vector2D vec2; + + float flLen = Length(); + if ( flLen == 0 ) + { + return Vector2D( 0, 0 ); + } + else + { + flLen = 1 / flLen; + return Vector2D( x * flLen, y * flLen ); + } + } + + vec_t x, y; +}; + +inline float 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 +//========================================================= +class Vector // same data-layout as engine's vec3_t, +{ // which is a vec_t[3] +public: + // Construction/destruction + inline Vector(void) { } + inline Vector(float X, float Y, float Z) { x = X; y = Y; z = Z; } + //inline Vector(double X, double Y, double Z) { x = (float)X; y = (float)Y; z = (float)Z; } + //inline Vector(int X, int Y, int Z) { x = (float)X; y = (float)Y; z = (float)Z; } + inline Vector(const Vector& v) { x = v.x; y = v.y; z = v.z; } + inline Vector(float rgfl[3]) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; } + + // Operators + inline Vector operator-(void) const { return Vector(-x,-y,-z); } + inline int operator==(const Vector& v) const { return x==v.x && y==v.y && z==v.z; } + inline int operator!=(const Vector& v) const { return !(*this==v); } + inline Vector operator+(const Vector& v) const { return Vector(x+v.x, y+v.y, z+v.z); } + inline Vector operator-(const Vector& v) const { return Vector(x-v.x, y-v.y, z-v.z); } + inline Vector operator*(float fl) const { return Vector(x*fl, y*fl, z*fl); } + inline Vector operator/(float fl) const { return Vector(x/fl, y/fl, z/fl); } + + // Methods + inline void CopyToArray(float* rgfl) const { rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; } + inline float Length(void) const { return sqrt(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 + inline Vector Normalize(void) const + { + float flLen = Length(); + if (flLen == 0) return Vector(0,0,1); // ???? + flLen = 1 / flLen; + return Vector(x * flLen, y * flLen, z * flLen); + } + + inline Vector2D Make2D ( void ) const + { + Vector2D Vec2; + + Vec2.x = x; + Vec2.y = y; + + return Vec2; + } + inline float Length2D(void) const { return sqrt(x*x + y*y); } + + // Members + vec_t x, y, z; +}; +inline Vector operator*(float fl, const Vector& v) { return v * fl; } +inline float DotProduct(const Vector& a, const Vector& b) { return(a.x*b.x+a.y*b.y+a.z*b.z); } +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 ); } + + + #endif \ No newline at end of file diff --git a/src/dlls/voltigore.cpp b/src/dlls/voltigore.cpp index 6736f0f..c7459c9 100644 --- a/src/dlls/voltigore.cpp +++ b/src/dlls/voltigore.cpp @@ -1,1326 +1,1326 @@ -// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! - -/*** -* -* Copyright (c) 1996-2001, 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. -* -* This source code contains proprietary and confidential information of -* Valve LLC and its suppliers. Access to this code is restricted to -* persons who have executed a written SDK license with Valve. Any access, -* use or distribution of this code by or to any unlicensed person is illegal. -* -****/ -//========================================================= -// voltigore -//========================================================= - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "schedule.h" -#include "nodes.h" -#include "effects.h" -#include "decals.h" -#include "weapons.h" - -#define VOLTIGORE_SPRINT_DIST 256 // how close the voltigore has to get before starting to sprint and refusing to swerve - -#define VOLTIGORE_MAX_BEAMS 8 - -#define VOLTIGORE_CLASSNAME "monster_alien_voltigore" -#define VOLTIGORE_BABY_CLASSNAME "monster_alien_babyvoltigore" - -#define VOLTIGORE_ZAP_RED 180 -#define VOLTIGORE_ZAP_GREEN 16 -#define VOLTIGORE_ZAP_BLUE 255 -#define VOLTIGORE_ZAP_BEAM "sprites/lgtning.spr" -#define VOLTIGORE_ZAP_NOISE 80 -#define VOLTIGORE_ZAP_WIDTH 30 -#define VOLTIGORE_ZAP_BRIGHTNESS 255 -#define VOLTIGORE_ZAP_DISTANCE 512 -#define VOLTIGORE_GLOW_SCALE 0.75f -#define VOLTIGORE_GIB_COUNT 9 -#define VOLTIGORE_GLOW_SPRITE "sprites/blueflare2.spr" - -//========================================================= -// monster-specific schedule types -//========================================================= - - -//========================================================= -// monster-specific tasks -//========================================================= - -enum { - TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1 -}; - - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::Spawn(void) -{ - pev->movetype = MOVETYPE_FLY; - pev->classname = MAKE_STRING("voltigore_energy_ball"); - - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAdd; - pev->renderamt = 255; - - SET_MODEL(ENT(pev), VOLTIGORE_GLOW_SPRITE); - pev->frame = 0; - pev->scale = VOLTIGORE_GLOW_SCALE; - - UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); - - m_iBeams = 0; -} - -//========================================================= -// Purpose: -//========================================================= -edict_t *CMVoltigoreEnergyBall::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity) -{ - CMVoltigoreEnergyBall *pEnergyBall = CreateClassPtr((CMVoltigoreEnergyBall *)NULL); - if (pEnergyBall == NULL) - return NULL; - - pEnergyBall->Spawn(); - - UTIL_SetOrigin(pEnergyBall->pev, vecStart); - pEnergyBall->pev->velocity = vecVelocity; - pEnergyBall->pev->owner = ENT(pevOwner); - - pEnergyBall->SetTouch(&CMVoltigoreEnergyBall::BallTouch); - pEnergyBall->SetThink(&CMVoltigoreEnergyBall::FlyThink); - pEnergyBall->pev->nextthink = gpGlobals->time + 0.1; - - return pEnergyBall->edict(); -} - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::BallTouch(edict_t *pOther) -{ - if (m_timeToDie) { - return; - } - TraceResult tr; - if (!pOther->v.takedamage) - { - - // make a splat on the wall - UTIL_TraceLine(pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT(pev), &tr); - UTIL_DecalTrace(&tr, DECAL_SCORCH1 + RANDOM_LONG(0, 1)); - } - else - { - if ( UTIL_IsPlayer( pOther ) ) - UTIL_TakeDamage( pOther, pev, VARS( pev->owner ), gSkillData.voltigoreDmgBeam, DMG_SHOCK|DMG_ALWAYSGIB ); - else if (pOther->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); - pMonster->TakeDamage( pev, VARS( pev->owner ), gSkillData.voltigoreDmgBeam, DMG_SHOCK|DMG_ALWAYSGIB ); - } - } - pev->velocity = Vector(0,0,0); - - m_timeToDie = gpGlobals->time + 0.3; - SetTouch( NULL ); -} - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::FlyThink(void) -{ - pev->nextthink = gpGlobals->time + 0.1; - if (m_timeToDie) - { - edict_t *pEntity = NULL; - while ((pEntity = UTIL_FindEntityInSphere(pEntity, pev->origin, 32)) != NULL) - { - if (pEntity->v.takedamage && !FClassnameIs(pEntity, "monster_alien_voltigore") && !FClassnameIs(pEntity, "monster_alien_babyvoltigore")) - { - if ( UTIL_IsPlayer( pEntity ) ) - UTIL_TakeDamage( pEntity, pev, pev, gSkillData.voltigoreDmgBeam/5, DMG_SHOCK ); - else if (pEntity->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TakeDamage( pev, pev, gSkillData.voltigoreDmgBeam/5, DMG_SHOCK ); - } - } - } - - if (m_timeToDie <= gpGlobals->time) - { - ClearBeams(); - SetThink(&CMVoltigoreEnergyBall::SUB_Remove); - pev->nextthink = gpGlobals->time; - } - } - else - { - if (m_iBeams) - UpdateBeams(); - else - CreateBeams(); - } -} - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness) -{ - m_pBeam[nIndex] = CMBeam::BeamCreate(VOLTIGORE_ZAP_BEAM, width); - if (!m_pBeam[nIndex]) - return; - - m_pBeam[nIndex]->PointEntInit(vecPos, entindex()); - m_pBeam[nIndex]->SetColor(VOLTIGORE_ZAP_RED, VOLTIGORE_ZAP_GREEN, VOLTIGORE_ZAP_BLUE); - m_pBeam[nIndex]->SetBrightness(brightness); - m_pBeam[nIndex]->SetNoise(VOLTIGORE_ZAP_NOISE); - //m_pBeam[nIndex]->SetFlags( SF_BEAM_SHADEIN ); -} - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::UpdateBeam(int nIndex, const Vector& vecPos, bool show) -{ - if (!m_pBeam[nIndex]) - return; - m_pBeam[nIndex]->SetBrightness(show ? VOLTIGORE_ZAP_BRIGHTNESS : 0); - m_pBeam[nIndex]->SetStartPos(vecPos); - m_pBeam[nIndex]->SetEndEntity(entindex()); - m_pBeam[nIndex]->RelinkBeam(); -} - -//========================================================= -// Purpose: -//========================================================= -void CMVoltigoreEnergyBall::ClearBeam(int nIndex) -{ - if (m_pBeam[nIndex]) - { - UTIL_Remove(m_pBeam[nIndex]->edict()); - m_pBeam[nIndex] = NULL; - } -} - -//========================================================= -// CreateBeams - create all beams -//========================================================= -void CMVoltigoreEnergyBall::CreateBeams() -{ - for (int i = 0; i < VOLTIGORE_MAX_BEAMS; ++i) - { - CreateBeam(i, pev->origin, VOLTIGORE_ZAP_WIDTH, VOLTIGORE_ZAP_BRIGHTNESS ); - } - m_iBeams = VOLTIGORE_MAX_BEAMS; -} - -//========================================================= -// ClearBeams - remove all beams -//========================================================= -void CMVoltigoreEnergyBall::ClearBeams() -{ - for (int i = 0; i < VOLTIGORE_MAX_BEAMS; ++i) - { - ClearBeam( i ); - } - m_iBeams = 0; -} - - -void CMVoltigoreEnergyBall::UpdateBeams() -{ - int i, j; - - TraceResult tr; - const Vector vecSrc = pev->origin; - const Vector directionVector = pev->velocity.Normalize(); - const int baseDistance = VOLTIGORE_ZAP_DISTANCE; - for (i = 0; i < m_iBeams; ++i) - { - for (j = 0; j < 3; ++j) - { - const float randomX = RANDOM_FLOAT(-1, 0.1); - const float randomY = RANDOM_FLOAT(-1, 0.1); - //ALERT(at_console, "Randomize: %f %f\n", randomX, randomY); - Vector vecTarget = vecSrc + Vector( - directionVector.x * randomX, - directionVector.y * randomY, - RANDOM_LONG(0, 1) ? 1 : -1 - ) * baseDistance; - TraceResult tr1; - UTIL_TraceLine(vecSrc, vecTarget, ignore_monsters, ENT(pev), &tr1); - if (tr1.flFraction != 1.0f) { - tr = tr1; - break; - } - } - - // Update the target position of the beam. - UpdateBeam(i, tr.vecEndPos, tr.flFraction != 1.0f); - } -} - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define VOLTIGORE_AE_THROW ( 1 ) -#define VOLTIGORE_AE_PUNCH_BOTH ( 12 ) -#define VOLTIGORE_AE_PUNCH_SINGLE ( 13 ) -#define VOLTIGORE_AE_GIB ( 2002 ) - -const char* CMVoltigore::pAlertSounds[] = -{ - "voltigore/voltigore_alert1.wav", - "voltigore/voltigore_alert2.wav", - "voltigore/voltigore_alert3.wav", -}; - -const char* CMVoltigore::pAttackMeleeSounds[] = -{ - "voltigore/voltigore_attack_melee1.wav", - "voltigore/voltigore_attack_melee2.wav", -}; - -const char* CMVoltigore::pMeleeHitSounds[] = -{ - "zombie/claw_strike1.wav", - "zombie/claw_strike2.wav", - "zombie/claw_strike3.wav", -}; - -const char* CMVoltigore::pMeleeMissSounds[] = -{ - "zombie/claw_miss1.wav", - "zombie/claw_miss2.wav", -}; - -const char* CMVoltigore::pComSounds[] = -{ - "voltigore/voltigore_communicate1.wav", - "voltigore/voltigore_communicate2.wav", - "voltigore/voltigore_communicate3.wav", -}; - - -const char* CMVoltigore::pDeathSounds[] = -{ - "voltigore/voltigore_die1.wav", - "voltigore/voltigore_die2.wav", - "voltigore/voltigore_die3.wav", -}; - -const char* CMVoltigore::pFootstepSounds[] = -{ - "voltigore/voltigore_footstep1.wav", - "voltigore/voltigore_footstep2.wav", - "voltigore/voltigore_footstep3.wav", -}; - -const char* CMVoltigore::pIdleSounds[] = -{ - "voltigore/voltigore_idle1.wav", - "voltigore/voltigore_idle2.wav", - "voltigore/voltigore_idle3.wav", -}; - -const char* CMVoltigore::pPainSounds[] = -{ - "voltigore/voltigore_pain1.wav", - "voltigore/voltigore_pain2.wav", - "voltigore/voltigore_pain3.wav", - "voltigore/voltigore_pain4.wav", -}; - -const char* CMVoltigore::pGruntSounds[] = -{ - "voltigore/voltigore_run_grunt1.wav", - "voltigore/voltigore_run_grunt2.wav", -}; - -//========================================================= -// TakeDamage - overridden for voltigore so we can keep track -// of how much time has passed since it was last injured -//========================================================= -int CMVoltigore::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) -{ - float flDist; - Vector vecApex; - - // if the voltigore is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy, - // it will swerve. (whew). - if (m_hEnemy != 0 && IsMoving() && pevAttacker == VARS(m_hEnemy)) - { - flDist = (pev->origin - m_hEnemy->v.origin).Length2D(); - - if (flDist > VOLTIGORE_SPRINT_DIST) - { - flDist = (pev->origin - m_Route[m_iRouteIndex].vecLocation).Length2D();// reusing flDist. - - if (FTriangulate(pev->origin, m_Route[m_iRouteIndex].vecLocation, flDist * 0.5, m_hEnemy, &vecApex)) - { - InsertWaypoint(vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY); - } - } - } - - // Ain't something missing here? -Giegue - - return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); -} - -//========================================================= -// CheckRangeAttack1 -//========================================================= -BOOL CMVoltigore::CheckRangeAttack1(float flDot, float flDist) -{ - if (IsMoving() && flDist >= 512) - { - // voltigore will far too far behind if he stops running to spit at this distance from the enemy. - return FALSE; - } - - if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextZapTime) - { - if (m_hEnemy != 0) - { - if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256) - { - // don't try to spit at someone up really high or down really low. - return FALSE; - } - } - - if (IsMoving()) - { - // don't spit again for a long time, resume chasing enemy. - m_flNextZapTime = gpGlobals->time + 5; - } - else - { - // not moving, so spit again pretty soon. - m_flNextZapTime = gpGlobals->time + 2; - } - - return TRUE; - } - - return FALSE; -} - -//========================================================= -//========================================================= -void CMVoltigore::RunAI(void) -{ - CMBaseMonster::RunAI(); - - if (m_fShouldUpdateBeam) - { - UpdateBeams(); - } - - GlowUpdate(); -} - -void CMVoltigore::GibMonster() -{ - GibBeamDamage(); - EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); - if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly - { - CMGib::SpawnRandomGibs( pev, VOLTIGORE_GIB_COUNT, "models/vgibs.mdl", 0 ); // Throw alien gibs - } - SetThink( &CMBaseEntity::SUB_Remove ); - pev->nextthink = gpGlobals->time; -} - -//========================================================= -// CheckMeleeAttack1 - voltigore is a big guy, so has a longer -// melee range than most monsters. This is the tailwhip attack -//========================================================= -BOOL CMVoltigore::CheckMeleeAttack1(float flDot, float flDist) -{ - if (flDist <= 120 && flDot >= 0.7) - { - return TRUE; - } - return FALSE; -} - -//========================================================= -// Classify - indicates this monster's place in the -// relationship table. -//========================================================= -int CMVoltigore::Classify(void) -{ - if ( m_iClassifyOverride == -1 ) // helper - return CLASS_NONE; - else if ( m_iClassifyOverride > 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// IdleSound -//========================================================= -void CMVoltigore::IdleSound(void) -{ - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM); -} - -//========================================================= -// PainSound -//========================================================= -void CMVoltigore::PainSound(void) -{ - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1, ATTN_NORM, 0, RANDOM_LONG(85, 120)); -} - -//========================================================= -// AlertSound -//========================================================= -void CMVoltigore::AlertSound(void) -{ - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), 1, ATTN_NORM, 0, RANDOM_LONG(140, 160)); -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMVoltigore::SetYawSpeed(void) -{ - int ys; - - ys = 0; - - switch (m_Activity) - { - case ACT_WALK: ys = 90; break; - case ACT_RUN: ys = 90; break; - case ACT_IDLE: ys = 90; break; - case ACT_RANGE_ATTACK1: ys = 90; break; - default: - ys = 90; - break; - } - - pev->yaw_speed = ys; -} - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMVoltigore::HandleAnimEvent(MonsterEvent_t *pEvent) -{ - switch (pEvent->event) - { - case VOLTIGORE_AE_THROW: - { - // SOUND HERE! - Vector vecSpitDir; - - UTIL_MakeVectors(pev->angles); - - Vector vecSpitOrigin, vecAngles; - GetAttachment(3, vecSpitOrigin, vecAngles); - vecSpitDir = ShootAtEnemy(vecSpitOrigin); - - // do stuff for this event. - //AttackSound(); - - CMVoltigoreEnergyBall::Shoot(pev, vecSpitOrigin, vecSpitDir * 1000); - - // turn the beam glow off. - DestroyBeams(); - - GlowOff(); - - m_fShouldUpdateBeam = FALSE; - } - break; - - - case VOLTIGORE_AE_PUNCH_SINGLE: - { - // SOUND HERE! - edict_t *pHurt = CheckTraceHullAttack(120, gSkillData.voltigoreDmgPunch, DMG_CLUB); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.z = -15; - pHurt->v.punchangle.x = 15; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -150; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; - } - - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. - } - else - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - } - } - break; - - case VOLTIGORE_AE_PUNCH_BOTH: - { - // SOUND HERE! - edict_t *pHurt = CheckTraceHullAttack(120, gSkillData.voltigoreDmgPunch, DMG_CLUB); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.x = 20; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 150; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; - } - - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. - } - else - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - } - } - break; - - case VOLTIGORE_AE_GIB: - { - pev->health = 0; - GibMonster(); - } - break; - - default: - CMBaseMonster::HandleAnimEvent(pEvent); - } -} - -//========================================================= -// Spawn -//========================================================= -void CMVoltigore::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/voltigore.mdl"); - UTIL_SetSize(pev, Vector(-80, -80, 0), Vector(80, 80, 90)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.voltigoreHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_flNextZapTime = gpGlobals->time; - - - m_fShouldUpdateBeam = FALSE; - m_pBeamGlow = NULL; - - GlowOff(); - - // Create glow. - CreateGlow(); - - MonsterInit(); - pev->view_ofs = Vector(0, 0, 84); - - pev->classname = MAKE_STRING("monster_alien_voltigore"); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Voltigore" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMVoltigore::Precache() -{ - PrecacheImpl("models/voltigore.mdl"); - PRECACHE_MODEL("models/vgibs.mdl"); -} - -void CMVoltigore::PrecacheImpl(char *modelName) -{ - PRECACHE_MODEL(modelName); - - PRECACHE_SOUND_ARRAY(pAlertSounds); - PRECACHE_SOUND_ARRAY(pAttackMeleeSounds); - PRECACHE_SOUND_ARRAY(pMeleeHitSounds); - PRECACHE_SOUND_ARRAY(pMeleeMissSounds); - PRECACHE_SOUND_ARRAY(pComSounds); - PRECACHE_SOUND_ARRAY(pDeathSounds); - PRECACHE_SOUND_ARRAY(pFootstepSounds); - PRECACHE_SOUND_ARRAY(pIdleSounds); - PRECACHE_SOUND_ARRAY(pPainSounds); - PRECACHE_SOUND_ARRAY(pGruntSounds); - - PRECACHE_SOUND("voltigore/voltigore_attack_shock.wav"); - PRECACHE_SOUND("voltigore/voltigore_eat.wav"); - - PRECACHE_SOUND("debris/beamstart1.wav"); - - m_beamTexture = PRECACHE_MODEL(VOLTIGORE_ZAP_BEAM); - PRECACHE_MODEL(VOLTIGORE_GLOW_SPRITE); - - PRECACHE_MODEL("sprites/lgtning.spr"); - PRECACHE_MODEL("sprites/blueflare2.spr"); -} - -//========================================================= -// DeathSound -//========================================================= -void CMVoltigore::DeathSound(void) -{ - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1, ATTN_NORM); -} - -//========================================================= -// AttackSound -//========================================================= -void CMVoltigore::AttackSound(void) -{ - EMIT_SOUND(ENT(pev), CHAN_WEAPON, "voltigore/voltigore_attack_shock.wav", 1, ATTN_NORM); -} - -//======================================================== -// AI Schedules Specific to this monster -//========================================================= - -// primary range attack -Task_t tlVoltigoreRangeAttack1[] = -{ - { TASK_STOP_MOVING, 0 }, - { TASK_FACE_IDEAL, (float)0 }, - { TASK_RANGE_ATTACK1, (float)0 }, - { TASK_SET_ACTIVITY, (float)ACT_IDLE }, -}; - -Schedule_t slVoltigoreRangeAttack1[] = -{ - { - tlVoltigoreRangeAttack1, - ARRAYSIZE(tlVoltigoreRangeAttack1), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_HEAVY_DAMAGE | - // Attack animation is quite long, so it's better to not stop it when enemy hides - //bits_COND_ENEMY_OCCLUDED | - bits_COND_NO_AMMO_LOADED, - 0, - "Voltigore Range Attack1" - }, -}; - -// Chase enemy schedule -Task_t tlVoltigoreChaseEnemy1[] = -{ - { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CHASE_ENEMY_FAILED }, - { TASK_GET_PATH_TO_ENEMY, (float)0 }, - { TASK_RUN_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, -}; - -Schedule_t slVoltigoreChaseEnemy[] = -{ - { - tlVoltigoreChaseEnemy1, - ARRAYSIZE(tlVoltigoreChaseEnemy1), - bits_COND_NEW_ENEMY | - bits_COND_ENEMY_DEAD | - bits_COND_SMELL_FOOD | - bits_COND_CAN_RANGE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK1 | - bits_COND_CAN_MELEE_ATTACK2 | - bits_COND_TASK_FAILED | - bits_COND_HEAR_SOUND, - 0, - "Voltigore Chase Enemy" - }, -}; - -//========================================================= -// Victory dance! -//========================================================= -Task_t tlVoltigoreVictoryDance[] = -{ - { TASK_STOP_MOVING, (float)0 }, - { TASK_WAIT, (float)0.2 }, - { TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, - { TASK_WALK_PATH, (float)0 }, - { TASK_WAIT_FOR_MOVEMENT, (float)0 }, - { TASK_FACE_ENEMY, (float)0 }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, - { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, -}; - -Schedule_t slVoltigoreVictoryDance[] = -{ - { - tlVoltigoreVictoryDance, - ARRAYSIZE( tlVoltigoreVictoryDance ), - bits_COND_NEW_ENEMY | - bits_COND_LIGHT_DAMAGE | - bits_COND_HEAVY_DAMAGE, - 0, - "VoltigoreVictoryDance" - }, -}; - -DEFINE_CUSTOM_SCHEDULES(CMVoltigore) -{ - slVoltigoreRangeAttack1, - slVoltigoreChaseEnemy, - slVoltigoreVictoryDance -}; - -IMPLEMENT_CUSTOM_SCHEDULES(CMVoltigore, CMBaseMonster) - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMVoltigore::GetSchedule(void) -{ - switch (m_MonsterState) - { - case MONSTERSTATE_COMBAT: - { - // dead enemy - if (HasConditions(bits_COND_ENEMY_DEAD)) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster::GetSchedule(); - } - - if (HasConditions(bits_COND_NEW_ENEMY)) - { - return GetScheduleOfType(SCHED_WAKE_ANGRY); - } - - if( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) - { - return GetScheduleOfType(SCHED_CHASE_ENEMY); - } - - if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) - { - return GetScheduleOfType(SCHED_RANGE_ATTACK1); - } - - if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) - { - return GetScheduleOfType(SCHED_MELEE_ATTACK1); - } - - return GetScheduleOfType(SCHED_CHASE_ENEMY); - - break; - } - } - - return CMBaseMonster::GetSchedule(); -} - -//========================================================= -// GetScheduleOfType -//========================================================= -Schedule_t* CMVoltigore::GetScheduleOfType(int Type) -{ - switch (Type) - { - case SCHED_RANGE_ATTACK1: - return &slVoltigoreRangeAttack1[0]; - break; - case SCHED_CHASE_ENEMY: - return &slVoltigoreChaseEnemy[0]; - break; - case SCHED_VICTORY_DANCE: - return &slVoltigoreVictoryDance[0]; - break; - } - - return CMBaseMonster::GetScheduleOfType(Type); -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. OVERRIDDEN for voltigore because it needs to -// know explicitly when the last attempt to chase the enemy -// failed, since that impacts its attack choices. -//========================================================= -void CMVoltigore::StartTask(Task_t *pTask) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - GlowOff(); - DestroyBeams(); - m_fShouldUpdateBeam = FALSE; - - switch (pTask->iTask) - { - case TASK_RANGE_ATTACK1: - { - CreateBeams(); - - GlowOn( 255 ); - m_fShouldUpdateBeam = TRUE; - - // Play the beam 'glow' sound. - EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 1, ATTN_NORM, 0, PITCH_HIGH); - - CMBaseMonster::StartTask(pTask); - } - break; - case TASK_GET_PATH_TO_ENEMY: - { - if (BuildRoute(m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy)) - { - m_iTaskStatus = TASKSTATUS_COMPLETE; - } - else - { - ALERT(at_aiconsole, "GetPathToEnemy failed!!\n"); - TaskFail(); - } - } - break; - case TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE: - UTIL_MakeVectors( pev->angles ); - if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) - { - TaskComplete(); - } - else - { - ALERT( at_aiconsole, "VoltigoreGetPathToEnemyCorpse failed!!\n" ); - TaskFail(); - } - default: - CMBaseMonster::StartTask(pTask); - break; - } -} - -void CMVoltigore::Killed(entvars_t *pevAttacker, int iGib) -{ - DestroyBeams(); - DestroyGlow(); - - int iTimes = 0; - int iDrawn = 0; - const int iBeams = VOLTIGORE_MAX_BEAMS; - while( iDrawn < iBeams && iTimes < ( iBeams * 3 ) ) - { - TraceResult tr; - const Vector vecOrigin = Center(); - const Vector vecDest = VOLTIGORE_ZAP_DISTANCE * ( Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize() ); - UTIL_TraceLine( vecOrigin, vecOrigin + vecDest, ignore_monsters, ENT( pev ), &tr ); - if( tr.flFraction != 1.0 ) - { - // we hit something. - iDrawn++; - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMPOINTS ); - WRITE_COORD( vecOrigin.x ); - WRITE_COORD( vecOrigin.y ); - WRITE_COORD( vecOrigin.z ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_SHORT( m_beamTexture ); - WRITE_BYTE( 0 ); // framestart - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( RANDOM_LONG( 8, 10 ) ); // life - WRITE_BYTE( VOLTIGORE_ZAP_WIDTH ); // width - WRITE_BYTE( VOLTIGORE_ZAP_NOISE ); // noise - WRITE_BYTE( VOLTIGORE_ZAP_RED ); // r, g, b - WRITE_BYTE( VOLTIGORE_ZAP_GREEN); // r, g, b - WRITE_BYTE( VOLTIGORE_ZAP_BLUE ); // r, g, b - WRITE_BYTE( VOLTIGORE_ZAP_BRIGHTNESS ); // brightness - WRITE_BYTE( 35 ); // speed - MESSAGE_END(); - } - iTimes++; - } - - CMBaseMonster::Killed(pevAttacker, iGib); -} - -void CMVoltigore::GibBeamDamage() -{ - edict_t *pEntity = NULL; - // iterate on all entities in the vicinity. - const float attackRadius = gSkillData.voltigoreDmgBeam * 10; - float flAdjustedDamage = gSkillData.voltigoreDmgBeam/2; - while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, attackRadius ) ) != NULL ) - { - if( pEntity->v.takedamage != DAMAGE_NO ) - { - if( pEntity->v.classname != pev->classname && !FClassnameIs( pEntity, "monster_alien_babyvoltigore" ) ) - { - // voltigores don't hurt other (baby) voltigores on death - const float flDist = ( UTIL_Center( pEntity ) - pev->origin ).Length(); - - flAdjustedDamage -= ( flDist / attackRadius ) * flAdjustedDamage; - - if( !UTIL_FVisible( pEntity, this->edict() ) ) - { - if( UTIL_IsPlayer( pEntity ) ) - { - // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still - // take the residual damage if they don't totally leave the voltigore's effective radius. We restrict it to clients - // so that monsters in other parts of the level don't take the damage and get pissed. - flAdjustedDamage *= 0.5; - } - else if( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) - { - // do not hurt nonclients through walls, but allow damage to be done to breakables - flAdjustedDamage = 0; - } - } - - if( flAdjustedDamage > 0 ) - { - if ( UTIL_IsPlayer( pEntity ) ) - UTIL_TakeDamage( pEntity, pev, pev, flAdjustedDamage, DMG_SHOCK ); - else if ( pEntity->v.euser4 != NULL ) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); - pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SHOCK ); - } - } - } - } - } -} - -void CMVoltigore::CreateBeams() -{ - Vector vecStart, vecEnd, vecAngles; - GetAttachment(3, vecStart, vecAngles); - - for (int i = 0; i < 3; i++) - { - m_pBeam[i] = CMBeam::BeamCreate(VOLTIGORE_ZAP_BEAM, VOLTIGORE_ZAP_WIDTH); - if (!m_pBeam[i]) - return; - - GetAttachment(i, vecEnd, vecAngles); - - m_pBeam[i]->PointsInit(vecStart, vecEnd); - m_pBeam[i]->SetColor(VOLTIGORE_ZAP_RED, VOLTIGORE_ZAP_GREEN, VOLTIGORE_ZAP_BLUE); - m_pBeam[i]->SetBrightness(VOLTIGORE_ZAP_BRIGHTNESS); - m_pBeam[i]->SetNoise(VOLTIGORE_ZAP_NOISE); - } -} - -void CMVoltigore::DestroyBeams() -{ - for (int i = 0; i < 3; i++) - { - if (m_pBeam[i]) - { - UTIL_Remove(m_pBeam[i]->edict()); - m_pBeam[i] = NULL; - } - } -} - -void CMVoltigore::UpdateBeams() -{ - Vector vecStart, vecEnd, vecAngles; - GetAttachment(3, vecStart, vecAngles); - - for (int i = 0; i < 3; i++) - { - if (!m_pBeam[i]) { - continue; - } - GetAttachment(i, vecEnd, vecAngles); - m_pBeam[i]->SetStartPos(vecStart); - m_pBeam[i]->SetEndPos(vecEnd); - m_pBeam[i]->RelinkBeam(); - } -} - -void CMVoltigore::CreateGlow() -{ - m_pBeamGlow = CMSprite::SpriteCreate(VOLTIGORE_GLOW_SPRITE, pev->origin, FALSE); - m_pBeamGlow->SetTransparency(kRenderTransAdd, 255, 255, 255, 0, kRenderFxNoDissipation); - m_pBeamGlow->SetAttachment(edict(), 4); - m_pBeamGlow->SetScale(VOLTIGORE_GLOW_SCALE); -} - -void CMVoltigore::DestroyGlow() -{ - if (m_pBeamGlow) - { - UTIL_Remove(m_pBeamGlow->edict()); - m_pBeamGlow = NULL; - } -} - -void CMVoltigore::GlowUpdate() -{ - if (m_pBeamGlow) - { - m_pBeamGlow->pev->renderamt = UTIL_Approach(m_glowBrightness, m_pBeamGlow->pev->renderamt, 100); - if (m_pBeamGlow->pev->renderamt == 0) - m_pBeamGlow->pev->effects |= EF_NODRAW; - else - m_pBeamGlow->pev->effects &= ~EF_NODRAW; - UTIL_SetOrigin(m_pBeamGlow->pev, pev->origin); - } -} - -void CMVoltigore::GlowOff(void) -{ - m_glowBrightness = 0; -} - -void CMVoltigore::GlowOn(int level) -{ - m_glowBrightness = level; -} - - -//========================================================= -// CBabyAlienVoltigore -//========================================================= - -//========================================================= -// Monster's Anim Events Go Here -//========================================================= -#define BABY_VOLTIGORE_AE_RUN ( 14 ) - -//========================================================= -// Spawn -//========================================================= -void CMBabyVoltigore::Spawn() -{ - Precache(); - - SET_MODEL(ENT(pev), "models/baby_voltigore.mdl"); - UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 36)); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->effects = 0; - pev->health = gSkillData.babyVoltigoreHealth; - m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - - m_flNextZapTime = gpGlobals->time; - - MonsterInit(); - pev->view_ofs = Vector(0, 0, 32); - - pev->classname = MAKE_STRING("monster_alien_babyvoltigore"); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Baby Voltigore" ); - } -} - -//========================================================= -//========================================================= -void CMBabyVoltigore::Precache(void) -{ - PrecacheImpl("models/baby_voltigore.mdl"); -} - -void CMBabyVoltigore::HandleAnimEvent(MonsterEvent_t* pEvent) -{ - switch (pEvent->event) - { - case BABY_VOLTIGORE_AE_RUN: - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pFootstepSounds), RANDOM_FLOAT(0.5, 0.6), ATTN_NORM, 0, RANDOM_LONG(85, 120)); - break; - - case VOLTIGORE_AE_PUNCH_SINGLE: - { - edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.babyVoltigoreDmgPunch, DMG_CLUB | DMG_ALWAYSGIB); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.z = -10; - pHurt->v.punchangle.x = 10; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -100; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 50; - } - - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. - } - else - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - } - } - break; - - case VOLTIGORE_AE_PUNCH_BOTH: - { - edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.babyVoltigoreDmgPunch, DMG_CLUB | DMG_ALWAYSGIB); - if (pHurt) - { - if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) - { - pHurt->v.punchangle.x = 15; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 50; - } - - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - - Vector vecArmPos, vecArmAng; - GetAttachment( 0, vecArmPos, vecArmAng ); - SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. - } - else - { - EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); - } - } - break; - default: - CMVoltigore::HandleAnimEvent(pEvent); - break; - } -} - -BOOL CMBabyVoltigore::CheckMeleeAttack1(float flDot, float flDist) -{ - return CMBaseMonster::CheckMeleeAttack1(flDot, flDist); -} - -//========================================================= -// Start task - selects the correct activity and performs -// any necessary calculations to start the next task on the -// schedule. OVERRIDDEN for voltigore because it needs to -// know explicitly when the last attempt to chase the enemy -// failed, since that impacts its attack choices. -//========================================================= -void CMBabyVoltigore::StartTask(Task_t *pTask) -{ - m_iTaskStatus = TASKSTATUS_RUNNING; - - switch (pTask->iTask) - { - case TASK_MELEE_ATTACK1: - { - EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackMeleeSounds), RANDOM_FLOAT(0.5, 0.6), ATTN_NONE, 0, RANDOM_LONG(110, 120)); - CMBaseMonster::StartTask(pTask); - } - break; - default: - CMBaseMonster::StartTask(pTask); - break; - } -} - -void CMBabyVoltigore::Killed(entvars_t* pevAttacker, int iGib) -{ - DestroyBeams(); - CMBaseMonster::Killed(pevAttacker, iGib); -} - -void CMBabyVoltigore::GibMonster() -{ - CMBaseMonster::GibMonster(); -} - -BOOL CMBabyVoltigore::CheckRangeAttack1(float flDot, float flDist) -{ - return FALSE; -} - -//========================================================= -// GetSchedule -//========================================================= -Schedule_t *CMBabyVoltigore::GetSchedule(void) -{ - switch (m_MonsterState) - { - case MONSTERSTATE_COMBAT: - { - // dead enemy - if (HasConditions(bits_COND_ENEMY_DEAD)) - { - // call base class, all code to handle dead enemies is centralized there. - return CMBaseMonster::GetSchedule(); - } - - if (HasConditions(bits_COND_NEW_ENEMY)) - { - return GetScheduleOfType(SCHED_WAKE_ANGRY); - } - - if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) - { - return GetScheduleOfType(SCHED_MELEE_ATTACK1); - } - - return GetScheduleOfType(SCHED_CHASE_ENEMY); - - break; - } - } - - return CMBaseMonster::GetSchedule(); -} - -Schedule_t *CMBabyVoltigore::GetScheduleOfType(int Type) -{ - switch (Type) { - // For some cryptic reason baby voltigore tries to start the range attack even though its model does not have sequence with range attack activity. - // This hack is for preventing baby voltigore to do this. - case SCHED_RANGE_ATTACK1: - return &slVoltigoreChaseEnemy[0]; - break; - default: - return CMVoltigore::GetScheduleOfType(Type); - break; - } -} +// HUGE thanks to DrBeef for his hlsdk-xash3d-opfor repository! + +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// voltigore +//========================================================= + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" +#include "nodes.h" +#include "effects.h" +#include "decals.h" +#include "weapons.h" + +#define VOLTIGORE_SPRINT_DIST 256 // how close the voltigore has to get before starting to sprint and refusing to swerve + +#define VOLTIGORE_MAX_BEAMS 8 + +#define VOLTIGORE_CLASSNAME "monster_alien_voltigore" +#define VOLTIGORE_BABY_CLASSNAME "monster_alien_babyvoltigore" + +#define VOLTIGORE_ZAP_RED 180 +#define VOLTIGORE_ZAP_GREEN 16 +#define VOLTIGORE_ZAP_BLUE 255 +#define VOLTIGORE_ZAP_BEAM "sprites/lgtning.spr" +#define VOLTIGORE_ZAP_NOISE 80 +#define VOLTIGORE_ZAP_WIDTH 30 +#define VOLTIGORE_ZAP_BRIGHTNESS 255 +#define VOLTIGORE_ZAP_DISTANCE 512 +#define VOLTIGORE_GLOW_SCALE 0.75f +#define VOLTIGORE_GIB_COUNT 9 +#define VOLTIGORE_GLOW_SPRITE "sprites/blueflare2.spr" + +//========================================================= +// monster-specific schedule types +//========================================================= + + +//========================================================= +// monster-specific tasks +//========================================================= + +enum { + TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE = LAST_COMMON_TASK + 1 +}; + + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::Spawn(void) +{ + pev->movetype = MOVETYPE_FLY; + pev->classname = MAKE_STRING("voltigore_energy_ball"); + + pev->solid = SOLID_BBOX; + pev->rendermode = kRenderTransAdd; + pev->renderamt = 255; + + SET_MODEL(ENT(pev), VOLTIGORE_GLOW_SPRITE); + pev->frame = 0; + pev->scale = VOLTIGORE_GLOW_SCALE; + + UTIL_SetSize(pev, Vector(0, 0, 0), Vector(0, 0, 0)); + + m_iBeams = 0; +} + +//========================================================= +// Purpose: +//========================================================= +edict_t *CMVoltigoreEnergyBall::Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity) +{ + CMVoltigoreEnergyBall *pEnergyBall = CreateClassPtr((CMVoltigoreEnergyBall *)NULL); + if (pEnergyBall == NULL) + return NULL; + + pEnergyBall->Spawn(); + + UTIL_SetOrigin(pEnergyBall->pev, vecStart); + pEnergyBall->pev->velocity = vecVelocity; + pEnergyBall->pev->owner = ENT(pevOwner); + + pEnergyBall->SetTouch(&CMVoltigoreEnergyBall::BallTouch); + pEnergyBall->SetThink(&CMVoltigoreEnergyBall::FlyThink); + pEnergyBall->pev->nextthink = gpGlobals->time + 0.1; + + return pEnergyBall->edict(); +} + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::BallTouch(edict_t *pOther) +{ + if (m_timeToDie) { + return; + } + TraceResult tr; + if (!pOther->v.takedamage) + { + + // make a splat on the wall + UTIL_TraceLine(pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT(pev), &tr); + UTIL_DecalTrace(&tr, DECAL_SCORCH1 + RANDOM_LONG(0, 1)); + } + else + { + if ( UTIL_IsPlayer( pOther ) ) + UTIL_TakeDamage( pOther, pev, VARS( pev->owner ), gSkillData.voltigoreDmgBeam, DMG_SHOCK|DMG_ALWAYSGIB ); + else if (pOther->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pOther)); + pMonster->TakeDamage( pev, VARS( pev->owner ), gSkillData.voltigoreDmgBeam, DMG_SHOCK|DMG_ALWAYSGIB ); + } + } + pev->velocity = Vector(0,0,0); + + m_timeToDie = gpGlobals->time + 0.3; + SetTouch( NULL ); +} + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::FlyThink(void) +{ + pev->nextthink = gpGlobals->time + 0.1; + if (m_timeToDie) + { + edict_t *pEntity = NULL; + while ((pEntity = UTIL_FindEntityInSphere(pEntity, pev->origin, 32)) != NULL) + { + if (pEntity->v.takedamage && !FClassnameIs(pEntity, "monster_alien_voltigore") && !FClassnameIs(pEntity, "monster_alien_babyvoltigore")) + { + if ( UTIL_IsPlayer( pEntity ) ) + UTIL_TakeDamage( pEntity, pev, pev, gSkillData.voltigoreDmgBeam/5, DMG_SHOCK ); + else if (pEntity->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TakeDamage( pev, pev, gSkillData.voltigoreDmgBeam/5, DMG_SHOCK ); + } + } + } + + if (m_timeToDie <= gpGlobals->time) + { + ClearBeams(); + SetThink(&CMVoltigoreEnergyBall::SUB_Remove); + pev->nextthink = gpGlobals->time; + } + } + else + { + if (m_iBeams) + UpdateBeams(); + else + CreateBeams(); + } +} + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::CreateBeam(int nIndex, const Vector& vecPos, int width, int brightness) +{ + m_pBeam[nIndex] = CMBeam::BeamCreate(VOLTIGORE_ZAP_BEAM, width); + if (!m_pBeam[nIndex]) + return; + + m_pBeam[nIndex]->PointEntInit(vecPos, entindex()); + m_pBeam[nIndex]->SetColor(VOLTIGORE_ZAP_RED, VOLTIGORE_ZAP_GREEN, VOLTIGORE_ZAP_BLUE); + m_pBeam[nIndex]->SetBrightness(brightness); + m_pBeam[nIndex]->SetNoise(VOLTIGORE_ZAP_NOISE); + //m_pBeam[nIndex]->SetFlags( SF_BEAM_SHADEIN ); +} + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::UpdateBeam(int nIndex, const Vector& vecPos, bool show) +{ + if (!m_pBeam[nIndex]) + return; + m_pBeam[nIndex]->SetBrightness(show ? VOLTIGORE_ZAP_BRIGHTNESS : 0); + m_pBeam[nIndex]->SetStartPos(vecPos); + m_pBeam[nIndex]->SetEndEntity(entindex()); + m_pBeam[nIndex]->RelinkBeam(); +} + +//========================================================= +// Purpose: +//========================================================= +void CMVoltigoreEnergyBall::ClearBeam(int nIndex) +{ + if (m_pBeam[nIndex]) + { + UTIL_Remove(m_pBeam[nIndex]->edict()); + m_pBeam[nIndex] = NULL; + } +} + +//========================================================= +// CreateBeams - create all beams +//========================================================= +void CMVoltigoreEnergyBall::CreateBeams() +{ + for (int i = 0; i < VOLTIGORE_MAX_BEAMS; ++i) + { + CreateBeam(i, pev->origin, VOLTIGORE_ZAP_WIDTH, VOLTIGORE_ZAP_BRIGHTNESS ); + } + m_iBeams = VOLTIGORE_MAX_BEAMS; +} + +//========================================================= +// ClearBeams - remove all beams +//========================================================= +void CMVoltigoreEnergyBall::ClearBeams() +{ + for (int i = 0; i < VOLTIGORE_MAX_BEAMS; ++i) + { + ClearBeam( i ); + } + m_iBeams = 0; +} + + +void CMVoltigoreEnergyBall::UpdateBeams() +{ + int i, j; + + TraceResult tr; + const Vector vecSrc = pev->origin; + const Vector directionVector = pev->velocity.Normalize(); + const int baseDistance = VOLTIGORE_ZAP_DISTANCE; + for (i = 0; i < m_iBeams; ++i) + { + for (j = 0; j < 3; ++j) + { + const float randomX = RANDOM_FLOAT(-1, 0.1); + const float randomY = RANDOM_FLOAT(-1, 0.1); + //ALERT(at_console, "Randomize: %f %f\n", randomX, randomY); + Vector vecTarget = vecSrc + Vector( + directionVector.x * randomX, + directionVector.y * randomY, + RANDOM_LONG(0, 1) ? 1 : -1 + ) * baseDistance; + TraceResult tr1; + UTIL_TraceLine(vecSrc, vecTarget, ignore_monsters, ENT(pev), &tr1); + if (tr1.flFraction != 1.0f) { + tr = tr1; + break; + } + } + + // Update the target position of the beam. + UpdateBeam(i, tr.vecEndPos, tr.flFraction != 1.0f); + } +} + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define VOLTIGORE_AE_THROW ( 1 ) +#define VOLTIGORE_AE_PUNCH_BOTH ( 12 ) +#define VOLTIGORE_AE_PUNCH_SINGLE ( 13 ) +#define VOLTIGORE_AE_GIB ( 2002 ) + +const char* CMVoltigore::pAlertSounds[] = +{ + "voltigore/voltigore_alert1.wav", + "voltigore/voltigore_alert2.wav", + "voltigore/voltigore_alert3.wav", +}; + +const char* CMVoltigore::pAttackMeleeSounds[] = +{ + "voltigore/voltigore_attack_melee1.wav", + "voltigore/voltigore_attack_melee2.wav", +}; + +const char* CMVoltigore::pMeleeHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char* CMVoltigore::pMeleeMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char* CMVoltigore::pComSounds[] = +{ + "voltigore/voltigore_communicate1.wav", + "voltigore/voltigore_communicate2.wav", + "voltigore/voltigore_communicate3.wav", +}; + + +const char* CMVoltigore::pDeathSounds[] = +{ + "voltigore/voltigore_die1.wav", + "voltigore/voltigore_die2.wav", + "voltigore/voltigore_die3.wav", +}; + +const char* CMVoltigore::pFootstepSounds[] = +{ + "voltigore/voltigore_footstep1.wav", + "voltigore/voltigore_footstep2.wav", + "voltigore/voltigore_footstep3.wav", +}; + +const char* CMVoltigore::pIdleSounds[] = +{ + "voltigore/voltigore_idle1.wav", + "voltigore/voltigore_idle2.wav", + "voltigore/voltigore_idle3.wav", +}; + +const char* CMVoltigore::pPainSounds[] = +{ + "voltigore/voltigore_pain1.wav", + "voltigore/voltigore_pain2.wav", + "voltigore/voltigore_pain3.wav", + "voltigore/voltigore_pain4.wav", +}; + +const char* CMVoltigore::pGruntSounds[] = +{ + "voltigore/voltigore_run_grunt1.wav", + "voltigore/voltigore_run_grunt2.wav", +}; + +//========================================================= +// TakeDamage - overridden for voltigore so we can keep track +// of how much time has passed since it was last injured +//========================================================= +int CMVoltigore::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) +{ + float flDist; + Vector vecApex; + + // if the voltigore is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy, + // it will swerve. (whew). + if (m_hEnemy != 0 && IsMoving() && pevAttacker == VARS(m_hEnemy)) + { + flDist = (pev->origin - m_hEnemy->v.origin).Length2D(); + + if (flDist > VOLTIGORE_SPRINT_DIST) + { + flDist = (pev->origin - m_Route[m_iRouteIndex].vecLocation).Length2D();// reusing flDist. + + if (FTriangulate(pev->origin, m_Route[m_iRouteIndex].vecLocation, flDist * 0.5, m_hEnemy, &vecApex)) + { + InsertWaypoint(vecApex, bits_MF_TO_DETOUR | bits_MF_DONT_SIMPLIFY); + } + } + } + + // Ain't something missing here? -Giegue + + return CMBaseMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); +} + +//========================================================= +// CheckRangeAttack1 +//========================================================= +BOOL CMVoltigore::CheckRangeAttack1(float flDot, float flDist) +{ + if (IsMoving() && flDist >= 512) + { + // voltigore will far too far behind if he stops running to spit at this distance from the enemy. + return FALSE; + } + + if (flDist > 64 && flDist <= 784 && flDot >= 0.5 && gpGlobals->time >= m_flNextZapTime) + { + if (m_hEnemy != 0) + { + if (fabs(pev->origin.z - m_hEnemy->v.origin.z) > 256) + { + // don't try to spit at someone up really high or down really low. + return FALSE; + } + } + + if (IsMoving()) + { + // don't spit again for a long time, resume chasing enemy. + m_flNextZapTime = gpGlobals->time + 5; + } + else + { + // not moving, so spit again pretty soon. + m_flNextZapTime = gpGlobals->time + 2; + } + + return TRUE; + } + + return FALSE; +} + +//========================================================= +//========================================================= +void CMVoltigore::RunAI(void) +{ + CMBaseMonster::RunAI(); + + if (m_fShouldUpdateBeam) + { + UpdateBeams(); + } + + GlowUpdate(); +} + +void CMVoltigore::GibMonster() +{ + GibBeamDamage(); + EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM ); + if( CVAR_GET_FLOAT( "violence_agibs" ) != 0 ) // Should never get here, but someone might call it directly + { + CMGib::SpawnRandomGibs( pev, VOLTIGORE_GIB_COUNT, "models/vgibs.mdl", 0 ); // Throw alien gibs + } + SetThink( &CMBaseEntity::SUB_Remove ); + pev->nextthink = gpGlobals->time; +} + +//========================================================= +// CheckMeleeAttack1 - voltigore is a big guy, so has a longer +// melee range than most monsters. This is the tailwhip attack +//========================================================= +BOOL CMVoltigore::CheckMeleeAttack1(float flDot, float flDist) +{ + if (flDist <= 120 && flDot >= 0.7) + { + return TRUE; + } + return FALSE; +} + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMVoltigore::Classify(void) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// IdleSound +//========================================================= +void CMVoltigore::IdleSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pIdleSounds), 1, ATTN_NORM); +} + +//========================================================= +// PainSound +//========================================================= +void CMVoltigore::PainSound(void) +{ + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pPainSounds), 1, ATTN_NORM, 0, RANDOM_LONG(85, 120)); +} + +//========================================================= +// AlertSound +//========================================================= +void CMVoltigore::AlertSound(void) +{ + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAlertSounds), 1, ATTN_NORM, 0, RANDOM_LONG(140, 160)); +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMVoltigore::SetYawSpeed(void) +{ + int ys; + + ys = 0; + + switch (m_Activity) + { + case ACT_WALK: ys = 90; break; + case ACT_RUN: ys = 90; break; + case ACT_IDLE: ys = 90; break; + case ACT_RANGE_ATTACK1: ys = 90; break; + default: + ys = 90; + break; + } + + pev->yaw_speed = ys; +} + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMVoltigore::HandleAnimEvent(MonsterEvent_t *pEvent) +{ + switch (pEvent->event) + { + case VOLTIGORE_AE_THROW: + { + // SOUND HERE! + Vector vecSpitDir; + + UTIL_MakeVectors(pev->angles); + + Vector vecSpitOrigin, vecAngles; + GetAttachment(3, vecSpitOrigin, vecAngles); + vecSpitDir = ShootAtEnemy(vecSpitOrigin); + + // do stuff for this event. + //AttackSound(); + + CMVoltigoreEnergyBall::Shoot(pev, vecSpitOrigin, vecSpitDir * 1000); + + // turn the beam glow off. + DestroyBeams(); + + GlowOff(); + + m_fShouldUpdateBeam = FALSE; + } + break; + + + case VOLTIGORE_AE_PUNCH_SINGLE: + { + // SOUND HERE! + edict_t *pHurt = CheckTraceHullAttack(120, gSkillData.voltigoreDmgPunch, DMG_CLUB); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.z = -15; + pHurt->v.punchangle.x = 15; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -150; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; + } + + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. + } + else + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + } + } + break; + + case VOLTIGORE_AE_PUNCH_BOTH: + { + // SOUND HERE! + edict_t *pHurt = CheckTraceHullAttack(120, gSkillData.voltigoreDmgPunch, DMG_CLUB); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.x = 20; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 150; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 100; + } + + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. + } + else + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + } + } + break; + + case VOLTIGORE_AE_GIB: + { + pev->health = 0; + GibMonster(); + } + break; + + default: + CMBaseMonster::HandleAnimEvent(pEvent); + } +} + +//========================================================= +// Spawn +//========================================================= +void CMVoltigore::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/voltigore.mdl"); + UTIL_SetSize(pev, Vector(-80, -80, 0), Vector(80, 80, 90)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.voltigoreHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextZapTime = gpGlobals->time; + + + m_fShouldUpdateBeam = FALSE; + m_pBeamGlow = NULL; + + GlowOff(); + + // Create glow. + CreateGlow(); + + MonsterInit(); + pev->view_ofs = Vector(0, 0, 84); + + pev->classname = MAKE_STRING("monster_alien_voltigore"); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Voltigore" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMVoltigore::Precache() +{ + PrecacheImpl("models/voltigore.mdl"); + PRECACHE_MODEL("models/vgibs.mdl"); +} + +void CMVoltigore::PrecacheImpl(char *modelName) +{ + PRECACHE_MODEL(modelName); + + PRECACHE_SOUND_ARRAY(pAlertSounds); + PRECACHE_SOUND_ARRAY(pAttackMeleeSounds); + PRECACHE_SOUND_ARRAY(pMeleeHitSounds); + PRECACHE_SOUND_ARRAY(pMeleeMissSounds); + PRECACHE_SOUND_ARRAY(pComSounds); + PRECACHE_SOUND_ARRAY(pDeathSounds); + PRECACHE_SOUND_ARRAY(pFootstepSounds); + PRECACHE_SOUND_ARRAY(pIdleSounds); + PRECACHE_SOUND_ARRAY(pPainSounds); + PRECACHE_SOUND_ARRAY(pGruntSounds); + + PRECACHE_SOUND("voltigore/voltigore_attack_shock.wav"); + PRECACHE_SOUND("voltigore/voltigore_eat.wav"); + + PRECACHE_SOUND("debris/beamstart1.wav"); + + m_beamTexture = PRECACHE_MODEL(VOLTIGORE_ZAP_BEAM); + PRECACHE_MODEL(VOLTIGORE_GLOW_SPRITE); + + PRECACHE_MODEL("sprites/lgtning.spr"); + PRECACHE_MODEL("sprites/blueflare2.spr"); +} + +//========================================================= +// DeathSound +//========================================================= +void CMVoltigore::DeathSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pDeathSounds), 1, ATTN_NORM); +} + +//========================================================= +// AttackSound +//========================================================= +void CMVoltigore::AttackSound(void) +{ + EMIT_SOUND(ENT(pev), CHAN_WEAPON, "voltigore/voltigore_attack_shock.wav", 1, ATTN_NORM); +} + +//======================================================== +// AI Schedules Specific to this monster +//========================================================= + +// primary range attack +Task_t tlVoltigoreRangeAttack1[] = +{ + { TASK_STOP_MOVING, 0 }, + { TASK_FACE_IDEAL, (float)0 }, + { TASK_RANGE_ATTACK1, (float)0 }, + { TASK_SET_ACTIVITY, (float)ACT_IDLE }, +}; + +Schedule_t slVoltigoreRangeAttack1[] = +{ + { + tlVoltigoreRangeAttack1, + ARRAYSIZE(tlVoltigoreRangeAttack1), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_HEAVY_DAMAGE | + // Attack animation is quite long, so it's better to not stop it when enemy hides + //bits_COND_ENEMY_OCCLUDED | + bits_COND_NO_AMMO_LOADED, + 0, + "Voltigore Range Attack1" + }, +}; + +// Chase enemy schedule +Task_t tlVoltigoreChaseEnemy1[] = +{ + { TASK_SET_FAIL_SCHEDULE, (float)SCHED_CHASE_ENEMY_FAILED }, + { TASK_GET_PATH_TO_ENEMY, (float)0 }, + { TASK_RUN_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, +}; + +Schedule_t slVoltigoreChaseEnemy[] = +{ + { + tlVoltigoreChaseEnemy1, + ARRAYSIZE(tlVoltigoreChaseEnemy1), + bits_COND_NEW_ENEMY | + bits_COND_ENEMY_DEAD | + bits_COND_SMELL_FOOD | + bits_COND_CAN_RANGE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK1 | + bits_COND_CAN_MELEE_ATTACK2 | + bits_COND_TASK_FAILED | + bits_COND_HEAR_SOUND, + 0, + "Voltigore Chase Enemy" + }, +}; + +//========================================================= +// Victory dance! +//========================================================= +Task_t tlVoltigoreVictoryDance[] = +{ + { TASK_STOP_MOVING, (float)0 }, + { TASK_WAIT, (float)0.2 }, + { TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE, (float)0 }, + { TASK_WALK_PATH, (float)0 }, + { TASK_WAIT_FOR_MOVEMENT, (float)0 }, + { TASK_FACE_ENEMY, (float)0 }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_VICTORY_DANCE }, + { TASK_PLAY_SEQUENCE, (float)ACT_STAND }, +}; + +Schedule_t slVoltigoreVictoryDance[] = +{ + { + tlVoltigoreVictoryDance, + ARRAYSIZE( tlVoltigoreVictoryDance ), + bits_COND_NEW_ENEMY | + bits_COND_LIGHT_DAMAGE | + bits_COND_HEAVY_DAMAGE, + 0, + "VoltigoreVictoryDance" + }, +}; + +DEFINE_CUSTOM_SCHEDULES(CMVoltigore) +{ + slVoltigoreRangeAttack1, + slVoltigoreChaseEnemy, + slVoltigoreVictoryDance +}; + +IMPLEMENT_CUSTOM_SCHEDULES(CMVoltigore, CMBaseMonster) + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMVoltigore::GetSchedule(void) +{ + switch (m_MonsterState) + { + case MONSTERSTATE_COMBAT: + { + // dead enemy + if (HasConditions(bits_COND_ENEMY_DEAD)) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster::GetSchedule(); + } + + if (HasConditions(bits_COND_NEW_ENEMY)) + { + return GetScheduleOfType(SCHED_WAKE_ANGRY); + } + + if( HasConditions( bits_COND_ENEMY_OCCLUDED ) ) + { + return GetScheduleOfType(SCHED_CHASE_ENEMY); + } + + if (HasConditions(bits_COND_CAN_RANGE_ATTACK1)) + { + return GetScheduleOfType(SCHED_RANGE_ATTACK1); + } + + if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) + { + return GetScheduleOfType(SCHED_MELEE_ATTACK1); + } + + return GetScheduleOfType(SCHED_CHASE_ENEMY); + + break; + } + } + + return CMBaseMonster::GetSchedule(); +} + +//========================================================= +// GetScheduleOfType +//========================================================= +Schedule_t* CMVoltigore::GetScheduleOfType(int Type) +{ + switch (Type) + { + case SCHED_RANGE_ATTACK1: + return &slVoltigoreRangeAttack1[0]; + break; + case SCHED_CHASE_ENEMY: + return &slVoltigoreChaseEnemy[0]; + break; + case SCHED_VICTORY_DANCE: + return &slVoltigoreVictoryDance[0]; + break; + } + + return CMBaseMonster::GetScheduleOfType(Type); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. OVERRIDDEN for voltigore because it needs to +// know explicitly when the last attempt to chase the enemy +// failed, since that impacts its attack choices. +//========================================================= +void CMVoltigore::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + GlowOff(); + DestroyBeams(); + m_fShouldUpdateBeam = FALSE; + + switch (pTask->iTask) + { + case TASK_RANGE_ATTACK1: + { + CreateBeams(); + + GlowOn( 255 ); + m_fShouldUpdateBeam = TRUE; + + // Play the beam 'glow' sound. + EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, "debris/beamstart1.wav", 1, ATTN_NORM, 0, PITCH_HIGH); + + CMBaseMonster::StartTask(pTask); + } + break; + case TASK_GET_PATH_TO_ENEMY: + { + if (BuildRoute(m_hEnemy->v.origin, bits_MF_TO_ENEMY, m_hEnemy)) + { + m_iTaskStatus = TASKSTATUS_COMPLETE; + } + else + { + ALERT(at_aiconsole, "GetPathToEnemy failed!!\n"); + TaskFail(); + } + } + break; + case TASK_VOLTIGORE_GET_PATH_TO_ENEMY_CORPSE: + UTIL_MakeVectors( pev->angles ); + if( BuildRoute( m_vecEnemyLKP - gpGlobals->v_forward * 50, bits_MF_TO_LOCATION, NULL ) ) + { + TaskComplete(); + } + else + { + ALERT( at_aiconsole, "VoltigoreGetPathToEnemyCorpse failed!!\n" ); + TaskFail(); + } + default: + CMBaseMonster::StartTask(pTask); + break; + } +} + +void CMVoltigore::Killed(entvars_t *pevAttacker, int iGib) +{ + DestroyBeams(); + DestroyGlow(); + + int iTimes = 0; + int iDrawn = 0; + const int iBeams = VOLTIGORE_MAX_BEAMS; + while( iDrawn < iBeams && iTimes < ( iBeams * 3 ) ) + { + TraceResult tr; + const Vector vecOrigin = Center(); + const Vector vecDest = VOLTIGORE_ZAP_DISTANCE * ( Vector( RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ), RANDOM_FLOAT( -1, 1 ) ).Normalize() ); + UTIL_TraceLine( vecOrigin, vecOrigin + vecDest, ignore_monsters, ENT( pev ), &tr ); + if( tr.flFraction != 1.0 ) + { + // we hit something. + iDrawn++; + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMPOINTS ); + WRITE_COORD( vecOrigin.x ); + WRITE_COORD( vecOrigin.y ); + WRITE_COORD( vecOrigin.z ); + WRITE_COORD( tr.vecEndPos.x ); + WRITE_COORD( tr.vecEndPos.y ); + WRITE_COORD( tr.vecEndPos.z ); + WRITE_SHORT( m_beamTexture ); + WRITE_BYTE( 0 ); // framestart + WRITE_BYTE( 10 ); // framerate + WRITE_BYTE( RANDOM_LONG( 8, 10 ) ); // life + WRITE_BYTE( VOLTIGORE_ZAP_WIDTH ); // width + WRITE_BYTE( VOLTIGORE_ZAP_NOISE ); // noise + WRITE_BYTE( VOLTIGORE_ZAP_RED ); // r, g, b + WRITE_BYTE( VOLTIGORE_ZAP_GREEN); // r, g, b + WRITE_BYTE( VOLTIGORE_ZAP_BLUE ); // r, g, b + WRITE_BYTE( VOLTIGORE_ZAP_BRIGHTNESS ); // brightness + WRITE_BYTE( 35 ); // speed + MESSAGE_END(); + } + iTimes++; + } + + CMBaseMonster::Killed(pevAttacker, iGib); +} + +void CMVoltigore::GibBeamDamage() +{ + edict_t *pEntity = NULL; + // iterate on all entities in the vicinity. + const float attackRadius = gSkillData.voltigoreDmgBeam * 10; + float flAdjustedDamage = gSkillData.voltigoreDmgBeam/2; + while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, attackRadius ) ) != NULL ) + { + if( pEntity->v.takedamage != DAMAGE_NO ) + { + if( pEntity->v.classname != pev->classname && !FClassnameIs( pEntity, "monster_alien_babyvoltigore" ) ) + { + // voltigores don't hurt other (baby) voltigores on death + const float flDist = ( UTIL_Center( pEntity ) - pev->origin ).Length(); + + flAdjustedDamage -= ( flDist / attackRadius ) * flAdjustedDamage; + + if( !UTIL_FVisible( pEntity, this->edict() ) ) + { + if( UTIL_IsPlayer( pEntity ) ) + { + // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still + // take the residual damage if they don't totally leave the voltigore's effective radius. We restrict it to clients + // so that monsters in other parts of the level don't take the damage and get pissed. + flAdjustedDamage *= 0.5; + } + else if( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) + { + // do not hurt nonclients through walls, but allow damage to be done to breakables + flAdjustedDamage = 0; + } + } + + if( flAdjustedDamage > 0 ) + { + if ( UTIL_IsPlayer( pEntity ) ) + UTIL_TakeDamage( pEntity, pev, pev, flAdjustedDamage, DMG_SHOCK ); + else if ( pEntity->v.euser4 != NULL ) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(pEntity)); + pMonster->TakeDamage( pev, pev, flAdjustedDamage, DMG_SHOCK ); + } + } + } + } + } +} + +void CMVoltigore::CreateBeams() +{ + Vector vecStart, vecEnd, vecAngles; + GetAttachment(3, vecStart, vecAngles); + + for (int i = 0; i < 3; i++) + { + m_pBeam[i] = CMBeam::BeamCreate(VOLTIGORE_ZAP_BEAM, VOLTIGORE_ZAP_WIDTH); + if (!m_pBeam[i]) + return; + + GetAttachment(i, vecEnd, vecAngles); + + m_pBeam[i]->PointsInit(vecStart, vecEnd); + m_pBeam[i]->SetColor(VOLTIGORE_ZAP_RED, VOLTIGORE_ZAP_GREEN, VOLTIGORE_ZAP_BLUE); + m_pBeam[i]->SetBrightness(VOLTIGORE_ZAP_BRIGHTNESS); + m_pBeam[i]->SetNoise(VOLTIGORE_ZAP_NOISE); + } +} + +void CMVoltigore::DestroyBeams() +{ + for (int i = 0; i < 3; i++) + { + if (m_pBeam[i]) + { + UTIL_Remove(m_pBeam[i]->edict()); + m_pBeam[i] = NULL; + } + } +} + +void CMVoltigore::UpdateBeams() +{ + Vector vecStart, vecEnd, vecAngles; + GetAttachment(3, vecStart, vecAngles); + + for (int i = 0; i < 3; i++) + { + if (!m_pBeam[i]) { + continue; + } + GetAttachment(i, vecEnd, vecAngles); + m_pBeam[i]->SetStartPos(vecStart); + m_pBeam[i]->SetEndPos(vecEnd); + m_pBeam[i]->RelinkBeam(); + } +} + +void CMVoltigore::CreateGlow() +{ + m_pBeamGlow = CMSprite::SpriteCreate(VOLTIGORE_GLOW_SPRITE, pev->origin, FALSE); + m_pBeamGlow->SetTransparency(kRenderTransAdd, 255, 255, 255, 0, kRenderFxNoDissipation); + m_pBeamGlow->SetAttachment(edict(), 4); + m_pBeamGlow->SetScale(VOLTIGORE_GLOW_SCALE); +} + +void CMVoltigore::DestroyGlow() +{ + if (m_pBeamGlow) + { + UTIL_Remove(m_pBeamGlow->edict()); + m_pBeamGlow = NULL; + } +} + +void CMVoltigore::GlowUpdate() +{ + if (m_pBeamGlow) + { + m_pBeamGlow->pev->renderamt = UTIL_Approach(m_glowBrightness, m_pBeamGlow->pev->renderamt, 100); + if (m_pBeamGlow->pev->renderamt == 0) + m_pBeamGlow->pev->effects |= EF_NODRAW; + else + m_pBeamGlow->pev->effects &= ~EF_NODRAW; + UTIL_SetOrigin(m_pBeamGlow->pev, pev->origin); + } +} + +void CMVoltigore::GlowOff(void) +{ + m_glowBrightness = 0; +} + +void CMVoltigore::GlowOn(int level) +{ + m_glowBrightness = level; +} + + +//========================================================= +// CBabyAlienVoltigore +//========================================================= + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define BABY_VOLTIGORE_AE_RUN ( 14 ) + +//========================================================= +// Spawn +//========================================================= +void CMBabyVoltigore::Spawn() +{ + Precache(); + + SET_MODEL(ENT(pev), "models/baby_voltigore.mdl"); + UTIL_SetSize(pev, Vector(-32, -32, 0), Vector(32, 32, 36)); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->effects = 0; + pev->health = gSkillData.babyVoltigoreHealth; + m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + + m_flNextZapTime = gpGlobals->time; + + MonsterInit(); + pev->view_ofs = Vector(0, 0, 32); + + pev->classname = MAKE_STRING("monster_alien_babyvoltigore"); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Baby Voltigore" ); + } +} + +//========================================================= +//========================================================= +void CMBabyVoltigore::Precache(void) +{ + PrecacheImpl("models/baby_voltigore.mdl"); +} + +void CMBabyVoltigore::HandleAnimEvent(MonsterEvent_t* pEvent) +{ + switch (pEvent->event) + { + case BABY_VOLTIGORE_AE_RUN: + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pFootstepSounds), RANDOM_FLOAT(0.5, 0.6), ATTN_NORM, 0, RANDOM_LONG(85, 120)); + break; + + case VOLTIGORE_AE_PUNCH_SINGLE: + { + edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.babyVoltigoreDmgPunch, DMG_CLUB | DMG_ALWAYSGIB); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.z = -10; + pHurt->v.punchangle.x = 10; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * -100; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 50; + } + + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. + } + else + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + } + } + break; + + case VOLTIGORE_AE_PUNCH_BOTH: + { + edict_t *pHurt = CheckTraceHullAttack(70, gSkillData.babyVoltigoreDmgPunch, DMG_CLUB | DMG_ALWAYSGIB); + if (pHurt) + { + if (FBitSet(pHurt->v.flags, FL_MONSTER|FL_CLIENT)) + { + pHurt->v.punchangle.x = 15; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * 100; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_up * 50; + } + + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeHitSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + + Vector vecArmPos, vecArmAng; + GetAttachment( 0, vecArmPos, vecArmAng ); + SpawnBlood( vecArmPos, BloodColor(), 25 );// a little surface blood. + } + else + { + EMIT_SOUND(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pMeleeMissSounds), RANDOM_FLOAT(0.8, 0.9), ATTN_NORM); + } + } + break; + default: + CMVoltigore::HandleAnimEvent(pEvent); + break; + } +} + +BOOL CMBabyVoltigore::CheckMeleeAttack1(float flDot, float flDist) +{ + return CMBaseMonster::CheckMeleeAttack1(flDot, flDist); +} + +//========================================================= +// Start task - selects the correct activity and performs +// any necessary calculations to start the next task on the +// schedule. OVERRIDDEN for voltigore because it needs to +// know explicitly when the last attempt to chase the enemy +// failed, since that impacts its attack choices. +//========================================================= +void CMBabyVoltigore::StartTask(Task_t *pTask) +{ + m_iTaskStatus = TASKSTATUS_RUNNING; + + switch (pTask->iTask) + { + case TASK_MELEE_ATTACK1: + { + EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, RANDOM_SOUND_ARRAY(pAttackMeleeSounds), RANDOM_FLOAT(0.5, 0.6), ATTN_NONE, 0, RANDOM_LONG(110, 120)); + CMBaseMonster::StartTask(pTask); + } + break; + default: + CMBaseMonster::StartTask(pTask); + break; + } +} + +void CMBabyVoltigore::Killed(entvars_t* pevAttacker, int iGib) +{ + DestroyBeams(); + CMBaseMonster::Killed(pevAttacker, iGib); +} + +void CMBabyVoltigore::GibMonster() +{ + CMBaseMonster::GibMonster(); +} + +BOOL CMBabyVoltigore::CheckRangeAttack1(float flDot, float flDist) +{ + return FALSE; +} + +//========================================================= +// GetSchedule +//========================================================= +Schedule_t *CMBabyVoltigore::GetSchedule(void) +{ + switch (m_MonsterState) + { + case MONSTERSTATE_COMBAT: + { + // dead enemy + if (HasConditions(bits_COND_ENEMY_DEAD)) + { + // call base class, all code to handle dead enemies is centralized there. + return CMBaseMonster::GetSchedule(); + } + + if (HasConditions(bits_COND_NEW_ENEMY)) + { + return GetScheduleOfType(SCHED_WAKE_ANGRY); + } + + if (HasConditions(bits_COND_CAN_MELEE_ATTACK1)) + { + return GetScheduleOfType(SCHED_MELEE_ATTACK1); + } + + return GetScheduleOfType(SCHED_CHASE_ENEMY); + + break; + } + } + + return CMBaseMonster::GetSchedule(); +} + +Schedule_t *CMBabyVoltigore::GetScheduleOfType(int Type) +{ + switch (Type) { + // For some cryptic reason baby voltigore tries to start the range attack even though its model does not have sequence with range attack activity. + // This hack is for preventing baby voltigore to do this. + case SCHED_RANGE_ATTACK1: + return &slVoltigoreChaseEnemy[0]; + break; + default: + return CMVoltigore::GetScheduleOfType(Type); + break; + } +} diff --git a/src/dlls/weapons.cpp b/src/dlls/weapons.cpp index 02a0f96..16d1950 100644 --- a/src/dlls/weapons.cpp +++ b/src/dlls/weapons.cpp @@ -1,232 +1,232 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -/* - -===== weapons.cpp ======================================================== - - functions governing the selection/use of weapons for players - -*/ - -#include "extdll.h" -#include "util.h" -#include "cmbase.h" -#include "cmbasemonster.h" -#include "monsters.h" -#include "weapons.h" -#include "nodes.h" -#include "decals.h" - -extern CGraph WorldGraph; - -MULTIDAMAGE gMultiDamage; - -#define TRACER_FREQ 4 // Tracers fire every fourth bullet - - -/* -============================================================================== - -MULTI-DAMAGE - -Collects multiple small damages into a single damage - -============================================================================== -*/ - -// -// ClearMultiDamage - resets the global multi damage accumulator -// -void ClearMultiDamage(void) -{ - gMultiDamage.pEntity = NULL; - gMultiDamage.amount = 0; - gMultiDamage.type = 0; -} - - -// -// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity -// -// GLOBALS USED: -// gMultiDamage - -void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker ) -{ - Vector vecSpot1;//where blood comes from - Vector vecDir;//direction blood should go - TraceResult tr; - - if ( !gMultiDamage.pEntity ) - return; - - if (UTIL_IsPlayer(gMultiDamage.pEntity)) - UTIL_TakeDamage(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); - else if (gMultiDamage.pEntity->v.euser4 != NULL) - { - CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity)); - pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); - } -} - - -// GLOBALS USED: -// gMultiDamage - -void AddMultiDamage( entvars_t *pevInflictor, edict_t *pEntity, float flDamage, int bitsDamageType) -{ - if ( !pEntity ) - return; - - gMultiDamage.type |= bitsDamageType; - - if ( pEntity != gMultiDamage.pEntity ) - { - ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker! - gMultiDamage.pEntity = pEntity; - gMultiDamage.amount = 0; - } - - gMultiDamage.amount += flDamage; -} - -/* -================ -SpawnBlood -================ -*/ -void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) -{ - UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage ); -} - - -int DamageDecal( CMBaseEntity *pEntity, int bitsDamageType ) -{ - if ( !pEntity ) - return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4)); - - return pEntity->DamageDecal( bitsDamageType ); -} - -void DecalGunshot( TraceResult *pTrace, int iBulletType ) -{ - // Is the entity valid - if ( !UTIL_IsValidEntity( pTrace->pHit ) ) - return; - - if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP ) - { - CMBaseEntity *pEntity = NULL; - // Decal the wall with a gunshot - if ( !FNullEnt(pTrace->pHit) ) - pEntity = CMBaseEntity::Instance(pTrace->pHit); - - switch( iBulletType ) - { - case BULLET_PLAYER_CROWBAR: - { - // wall decal - UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); - break; - } - default: - { - // smoke and decal - UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); - break; - } - /* why the duplicate case? - case BULLET_PLAYER_9MM: - case BULLET_MONSTER_9MM: - case BULLET_PLAYER_MP5: - case BULLET_MONSTER_MP5: - case BULLET_PLAYER_BUCKSHOT: - case BULLET_PLAYER_357: - default: - // smoke and decal - UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); - break; - case BULLET_MONSTER_12MM: - // smoke and decal - UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); - break; - case BULLET_PLAYER_CROWBAR: - // wall decal - UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); - break; - */ - } - } -} - - - -// -// EjectBrass - tosses a brass shell from passed origin at passed velocity -// -void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ) -{ - // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. - - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); - 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 ( soundtype); - WRITE_BYTE ( 25 );// 2.5 seconds - MESSAGE_END(); -} - - -#if 0 -// UNDONE: This is no longer used? -void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) -{ - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); - WRITE_BYTE ( TE_EXPLODEMODEL ); - WRITE_COORD( vecOrigin.x ); - WRITE_COORD( vecOrigin.y ); - WRITE_COORD( vecOrigin.z ); - WRITE_COORD( speed ); - WRITE_SHORT( model ); - WRITE_SHORT( count ); - WRITE_BYTE ( 15 );// 1.5 seconds - MESSAGE_END(); -} -#endif - - -BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) -{ -#if defined( CLIENT_WEAPONS ) - if ( !isPredicted ) -#else - if ( 1 ) -#endif - { - return ( attack_time <= curtime ) ? TRUE : FALSE; - } - else - { - return ( attack_time <= 0.0 ) ? TRUE : FALSE; - } -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +/* + +===== weapons.cpp ======================================================== + + functions governing the selection/use of weapons for players + +*/ + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "weapons.h" +#include "nodes.h" +#include "decals.h" + +extern CGraph WorldGraph; + +MULTIDAMAGE gMultiDamage; + +#define TRACER_FREQ 4 // Tracers fire every fourth bullet + + +/* +============================================================================== + +MULTI-DAMAGE + +Collects multiple small damages into a single damage + +============================================================================== +*/ + +// +// ClearMultiDamage - resets the global multi damage accumulator +// +void ClearMultiDamage(void) +{ + gMultiDamage.pEntity = NULL; + gMultiDamage.amount = 0; + gMultiDamage.type = 0; +} + + +// +// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity +// +// GLOBALS USED: +// gMultiDamage + +void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker ) +{ + Vector vecSpot1;//where blood comes from + Vector vecDir;//direction blood should go + TraceResult tr; + + if ( !gMultiDamage.pEntity ) + return; + + if (UTIL_IsPlayer(gMultiDamage.pEntity)) + UTIL_TakeDamage(gMultiDamage.pEntity, pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); + else if (gMultiDamage.pEntity->v.euser4 != NULL) + { + CMBaseMonster *pMonster = GetClassPtr((CMBaseMonster *)VARS(gMultiDamage.pEntity)); + pMonster->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type ); + } +} + + +// GLOBALS USED: +// gMultiDamage + +void AddMultiDamage( entvars_t *pevInflictor, edict_t *pEntity, float flDamage, int bitsDamageType) +{ + if ( !pEntity ) + return; + + gMultiDamage.type |= bitsDamageType; + + if ( pEntity != gMultiDamage.pEntity ) + { + ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker! + gMultiDamage.pEntity = pEntity; + gMultiDamage.amount = 0; + } + + gMultiDamage.amount += flDamage; +} + +/* +================ +SpawnBlood +================ +*/ +void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage) +{ + UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage ); +} + + +int DamageDecal( CMBaseEntity *pEntity, int bitsDamageType ) +{ + if ( !pEntity ) + return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4)); + + return pEntity->DamageDecal( bitsDamageType ); +} + +void DecalGunshot( TraceResult *pTrace, int iBulletType ) +{ + // Is the entity valid + if ( !UTIL_IsValidEntity( pTrace->pHit ) ) + return; + + if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP ) + { + CMBaseEntity *pEntity = NULL; + // Decal the wall with a gunshot + if ( !FNullEnt(pTrace->pHit) ) + pEntity = CMBaseEntity::Instance(pTrace->pHit); + + switch( iBulletType ) + { + case BULLET_PLAYER_CROWBAR: + { + // wall decal + UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); + break; + } + default: + { + // smoke and decal + UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); + break; + } + /* why the duplicate case? + case BULLET_PLAYER_9MM: + case BULLET_MONSTER_9MM: + case BULLET_PLAYER_MP5: + case BULLET_MONSTER_MP5: + case BULLET_PLAYER_BUCKSHOT: + case BULLET_PLAYER_357: + default: + // smoke and decal + UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); + break; + case BULLET_MONSTER_12MM: + // smoke and decal + UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) ); + break; + case BULLET_PLAYER_CROWBAR: + // wall decal + UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) ); + break; + */ + } + } +} + + + +// +// EjectBrass - tosses a brass shell from passed origin at passed velocity +// +void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype ) +{ + // FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see. + + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); + 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 ( soundtype); + WRITE_BYTE ( 25 );// 2.5 seconds + MESSAGE_END(); +} + + +#if 0 +// UNDONE: This is no longer used? +void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count ) +{ + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); + WRITE_BYTE ( TE_EXPLODEMODEL ); + WRITE_COORD( vecOrigin.x ); + WRITE_COORD( vecOrigin.y ); + WRITE_COORD( vecOrigin.z ); + WRITE_COORD( speed ); + WRITE_SHORT( model ); + WRITE_SHORT( count ); + WRITE_BYTE ( 15 );// 1.5 seconds + MESSAGE_END(); +} +#endif + + +BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted ) +{ +#if defined( CLIENT_WEAPONS ) + if ( !isPredicted ) +#else + if ( 1 ) +#endif + { + return ( attack_time <= curtime ) ? TRUE : FALSE; + } + else + { + return ( attack_time <= 0.0 ) ? TRUE : FALSE; + } +} diff --git a/src/dlls/weapons.h b/src/dlls/weapons.h index 65d1fee..3fc94dd 100644 --- a/src/dlls/weapons.h +++ b/src/dlls/weapons.h @@ -1,287 +1,287 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#ifndef WEAPONS_H -#define WEAPONS_H - -#include "effects.h" - -// Contact Grenade / Timed grenade / Satchel Charge -class CMGrenade : public CMBaseMonster -{ -public: - void Spawn( void ); - - typedef enum { SATCHEL_DETONATE = 0, SATCHEL_RELEASE } SATCHELCODE; - - static CMGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time ); - static CMGrenade *ShootContact( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - static CMGrenade *ShootSatchelCharge( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity ); - - void Explode( Vector vecSrc, Vector vecAim ); - void Explode( TraceResult *pTrace, int bitsDamageType ); - void EXPORT Smoke( void ); - - void EXPORT BounceTouch( edict_t *pOther ); - void EXPORT SlideTouch( edict_t *pOther ); - void EXPORT ExplodeTouch( edict_t *pOther ); - void EXPORT DangerSoundThink( void ); - void EXPORT PreDetonate( void ); - void EXPORT Detonate( void ); - void EXPORT DetonateUse( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value ); - void EXPORT TumbleThink( void ); - - virtual void BounceSound( void ); - virtual int BloodColor( void ) { return DONT_BLEED; } - virtual void Killed( entvars_t *pevAttacker, int iGib ); - - BOOL m_fRegisteredSound;// whether or not this grenade has issued its DANGER sound to the world sound list yet. -}; - -// Contact/Timed spore grenade -class CMSporeGrenade : public CMBaseMonster -{ -public: - void Precache(void); - void Spawn(void); - - static CMSporeGrenade *ShootTimed(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, bool ai); - static CMSporeGrenade *ShootContact(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity); - - void Explode(TraceResult *pTrace); - - void EXPORT BounceTouch(edict_t *pOther); - void EXPORT ExplodeTouch(edict_t *pOther); - void EXPORT DangerSoundThink(void); - void EXPORT Detonate(void); - void EXPORT TumbleThink(void); - - void BounceSound(void); - void DangerSound(); - static void SpawnTrailParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise); - static void SpawnExplosionParticles(const Vector& origin, const Vector& direction, int modelindex, int count, float speed, float noise); - - void UpdateOnRemove(); - - CMSprite* m_pSporeGlow; -}; - -// constant items -#define ITEM_HEALTHKIT 1 -#define ITEM_ANTIDOTE 2 -#define ITEM_SECURITY 3 -#define ITEM_BATTERY 4 - -#define WEAPON_NONE 0 -#define WEAPON_CROWBAR 1 -#define WEAPON_GLOCK 2 -#define WEAPON_PYTHON 3 -#define WEAPON_MP5 4 -#define WEAPON_CHAINGUN 5 -#define WEAPON_CROSSBOW 6 -#define WEAPON_SHOTGUN 7 -#define WEAPON_RPG 8 -#define WEAPON_GAUSS 9 -#define WEAPON_EGON 10 -#define WEAPON_HORNETGUN 11 -#define WEAPON_HANDGRENADE 12 -#define WEAPON_TRIPMINE 13 -#define WEAPON_SATCHEL 14 -#define WEAPON_SNARK 15 - -#define WEAPON_ALLWEAPONS (~(1< 0 ) - return m_iClassifyOverride; // override - - return CLASS_ALIEN_MONSTER; -} - -//========================================================= -// SetYawSpeed - allows each sequence to have a different -// turn rate associated with it. -//========================================================= -void CMZombie :: SetYawSpeed ( void ) -{ - int ys; - - ys = 120; - -#if 0 - switch ( m_Activity ) - { - } -#endif - - pev->yaw_speed = ys; -} - -int CMZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) -{ - // Take 30% damage from bullets - if ( bitsDamageType == DMG_BULLET ) - { - Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; - vecDir = vecDir.Normalize(); - float flForce = DamageForce( flDamage ); - pev->velocity = pev->velocity + vecDir * flForce; - flDamage *= 0.3; - } - - // HACK HACK -- until we fix this. - if ( IsAlive() ) - PainSound(); - return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); -} - -void CMZombie :: PainSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - if (RANDOM_LONG(0,5) < 2) - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); -} - -void CMZombie :: AlertSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); -} - -void CMZombie :: IdleSound( void ) -{ - int pitch = 95 + RANDOM_LONG(0,9); - - // Play a random idle sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); -} - -void CMZombie :: AttackSound( void ) -{ - // Play a random attack sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); -} - - -//========================================================= -// HandleAnimEvent - catches the monster-specific messages -// that occur when tagged animation frames are played. -//========================================================= -void CMZombie :: HandleAnimEvent( MonsterEvent_t *pEvent ) -{ - switch( pEvent->event ) - { - case ZOMBIE_AE_ATTACK_RIGHT: - { - // do stuff for this event. - // ALERT( at_console, "Slash right!\n" ); - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.z = -18; - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 100; - } - // Play a random attack hit sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else // Play a random attack miss sound - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - case ZOMBIE_AE_ATTACK_LEFT: - { - // do stuff for this event. - // ALERT( at_console, "Slash left!\n" ); - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.z = 18; - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 100; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - case ZOMBIE_AE_ATTACK_BOTH: - { - // do stuff for this event. - edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgBothSlash, DMG_SLASH ); - if ( pHurt ) - { - if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) - { - pHurt->v.punchangle.x = 5; - pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * -100; - } - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - } - else - EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); - - if (RANDOM_LONG(0,1)) - AttackSound(); - } - break; - - default: - CMBaseMonster::HandleAnimEvent( pEvent ); - break; - } -} - -//========================================================= -// Spawn -//========================================================= -void CMZombie :: Spawn() -{ - Precache( ); - - SET_MODEL(ENT(pev), "models/zombie.mdl"); - UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); - - pev->solid = SOLID_SLIDEBOX; - pev->movetype = MOVETYPE_STEP; - m_bloodColor = BLOOD_COLOR_GREEN; - pev->health = gSkillData.zombieHealth; - pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. - m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) - m_MonsterState = MONSTERSTATE_NONE; - m_afCapability = bits_CAP_DOORS_GROUP; - - MonsterInit(); - - pev->classname = MAKE_STRING( "monster_zombie" ); - if ( strlen( STRING( m_szMonsterName ) ) == 0 ) - { - // default name - m_szMonsterName = MAKE_STRING( "Zombie" ); - } -} - -//========================================================= -// Precache - precaches all resources this monster needs -//========================================================= -void CMZombie :: Precache() -{ - int i; - - PRECACHE_MODEL("models/zombie.mdl"); - - for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackHitSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackMissSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) - PRECACHE_SOUND((char *)pAttackSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) - PRECACHE_SOUND((char *)pIdleSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) - PRECACHE_SOUND((char *)pAlertSounds[i]); - - for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) - PRECACHE_SOUND((char *)pPainSounds[i]); -} - -//========================================================= -// AI Schedules Specific to this monster -//========================================================= - - - -int CMZombie::IgnoreConditions ( void ) -{ - int iIgnore = CMBaseMonster::IgnoreConditions(); - - if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK1)) - { -#if 0 - if (pev->health < 20) - iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); - else -#endif - if (m_flNextFlinch >= gpGlobals->time) - iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); - } - - if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) - { - if (m_flNextFlinch < gpGlobals->time) - m_flNextFlinch = gpGlobals->time + ZOMBIE_FLINCH_DELAY; - } - - return iIgnore; - -} +/*** +* +* Copyright (c) 1996-2001, 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. +* +* This source code contains proprietary and confidential information of +* Valve LLC and its suppliers. Access to this code is restricted to +* persons who have executed a written SDK license with Valve. Any access, +* use or distribution of this code by or to any unlicensed person is illegal. +* +****/ +//========================================================= +// Zombie +//========================================================= + +// UNDONE: Don't flinch every time you get hit + +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "monsters.h" +#include "schedule.h" + + +//========================================================= +// Monster's Anim Events Go Here +//========================================================= +#define ZOMBIE_AE_ATTACK_RIGHT 0x01 +#define ZOMBIE_AE_ATTACK_LEFT 0x02 +#define ZOMBIE_AE_ATTACK_BOTH 0x03 + +#define ZOMBIE_FLINCH_DELAY 2 // at most one flinch every n secs + + +const char *CMZombie::pAttackHitSounds[] = +{ + "zombie/claw_strike1.wav", + "zombie/claw_strike2.wav", + "zombie/claw_strike3.wav", +}; + +const char *CMZombie::pAttackMissSounds[] = +{ + "zombie/claw_miss1.wav", + "zombie/claw_miss2.wav", +}; + +const char *CMZombie::pAttackSounds[] = +{ + "zombie/zo_attack1.wav", + "zombie/zo_attack2.wav", +}; + +const char *CMZombie::pIdleSounds[] = +{ + "zombie/zo_idle1.wav", + "zombie/zo_idle2.wav", + "zombie/zo_idle3.wav", + "zombie/zo_idle4.wav", +}; + +const char *CMZombie::pAlertSounds[] = +{ + "zombie/zo_alert10.wav", + "zombie/zo_alert20.wav", + "zombie/zo_alert30.wav", +}; + +const char *CMZombie::pPainSounds[] = +{ + "zombie/zo_pain1.wav", + "zombie/zo_pain2.wav", +}; + +//========================================================= +// Classify - indicates this monster's place in the +// relationship table. +//========================================================= +int CMZombie :: Classify ( void ) +{ + if ( m_iClassifyOverride == -1 ) // helper + return CLASS_NONE; + else if ( m_iClassifyOverride > 0 ) + return m_iClassifyOverride; // override + + return CLASS_ALIEN_MONSTER; +} + +//========================================================= +// SetYawSpeed - allows each sequence to have a different +// turn rate associated with it. +//========================================================= +void CMZombie :: SetYawSpeed ( void ) +{ + int ys; + + ys = 120; + +#if 0 + switch ( m_Activity ) + { + } +#endif + + pev->yaw_speed = ys; +} + +int CMZombie :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) +{ + // Take 30% damage from bullets + if ( bitsDamageType == DMG_BULLET ) + { + Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5; + vecDir = vecDir.Normalize(); + float flForce = DamageForce( flDamage ); + pev->velocity = pev->velocity + vecDir * flForce; + flDamage *= 0.3; + } + + // HACK HACK -- until we fix this. + if ( IsAlive() ) + PainSound(); + return CMBaseMonster::TakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType ); +} + +void CMZombie :: PainSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + if (RANDOM_LONG(0,5) < 2) + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pPainSounds[ RANDOM_LONG(0,ARRAYSIZE(pPainSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); +} + +void CMZombie :: AlertSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAlertSounds[ RANDOM_LONG(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM, 0, pitch ); +} + +void CMZombie :: IdleSound( void ) +{ + int pitch = 95 + RANDOM_LONG(0,9); + + // Play a random idle sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pIdleSounds[ RANDOM_LONG(0,ARRAYSIZE(pIdleSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); +} + +void CMZombie :: AttackSound( void ) +{ + // Play a random attack sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_VOICE, pAttackSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); +} + + +//========================================================= +// HandleAnimEvent - catches the monster-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CMZombie :: HandleAnimEvent( MonsterEvent_t *pEvent ) +{ + switch( pEvent->event ) + { + case ZOMBIE_AE_ATTACK_RIGHT: + { + // do stuff for this event. + // ALERT( at_console, "Slash right!\n" ); + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.z = -18; + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity - gpGlobals->v_right * 100; + } + // Play a random attack hit sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else // Play a random attack miss sound + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + case ZOMBIE_AE_ATTACK_LEFT: + { + // do stuff for this event. + // ALERT( at_console, "Slash left!\n" ); + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgOneSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.z = 18; + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_right * 100; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + case ZOMBIE_AE_ATTACK_BOTH: + { + // do stuff for this event. + edict_t *pHurt = CheckTraceHullAttack( 70, gSkillData.zombieDmgBothSlash, DMG_SLASH ); + if ( pHurt ) + { + if ( pHurt->v.flags & (FL_MONSTER|FL_CLIENT) ) + { + pHurt->v.punchangle.x = 5; + pHurt->v.velocity = pHurt->v.velocity + gpGlobals->v_forward * -100; + } + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackHitSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackHitSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + } + else + EMIT_SOUND_DYN ( ENT(pev), CHAN_WEAPON, pAttackMissSounds[ RANDOM_LONG(0,ARRAYSIZE(pAttackMissSounds)-1) ], 1.0, ATTN_NORM, 0, 100 + RANDOM_LONG(-5,5) ); + + if (RANDOM_LONG(0,1)) + AttackSound(); + } + break; + + default: + CMBaseMonster::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// Spawn +//========================================================= +void CMZombie :: Spawn() +{ + Precache( ); + + SET_MODEL(ENT(pev), "models/zombie.mdl"); + UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX ); + + pev->solid = SOLID_SLIDEBOX; + pev->movetype = MOVETYPE_STEP; + m_bloodColor = BLOOD_COLOR_GREEN; + pev->health = gSkillData.zombieHealth; + pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin. + m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result ) + m_MonsterState = MONSTERSTATE_NONE; + m_afCapability = bits_CAP_DOORS_GROUP; + + MonsterInit(); + + pev->classname = MAKE_STRING( "monster_zombie" ); + if ( strlen( STRING( m_szMonsterName ) ) == 0 ) + { + // default name + m_szMonsterName = MAKE_STRING( "Zombie" ); + } +} + +//========================================================= +// Precache - precaches all resources this monster needs +//========================================================= +void CMZombie :: Precache() +{ + int i; + + PRECACHE_MODEL("models/zombie.mdl"); + + for ( i = 0; i < ARRAYSIZE( pAttackHitSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackHitSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackMissSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackMissSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAttackSounds ); i++ ) + PRECACHE_SOUND((char *)pAttackSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pIdleSounds ); i++ ) + PRECACHE_SOUND((char *)pIdleSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pAlertSounds ); i++ ) + PRECACHE_SOUND((char *)pAlertSounds[i]); + + for ( i = 0; i < ARRAYSIZE( pPainSounds ); i++ ) + PRECACHE_SOUND((char *)pPainSounds[i]); +} + +//========================================================= +// AI Schedules Specific to this monster +//========================================================= + + + +int CMZombie::IgnoreConditions ( void ) +{ + int iIgnore = CMBaseMonster::IgnoreConditions(); + + if ((m_Activity == ACT_MELEE_ATTACK1) || (m_Activity == ACT_MELEE_ATTACK1)) + { +#if 0 + if (pev->health < 20) + iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); + else +#endif + if (m_flNextFlinch >= gpGlobals->time) + iIgnore |= (bits_COND_LIGHT_DAMAGE|bits_COND_HEAVY_DAMAGE); + } + + if ((m_Activity == ACT_SMALL_FLINCH) || (m_Activity == ACT_BIG_FLINCH)) + { + if (m_flNextFlinch < gpGlobals->time) + m_flNextFlinch = gpGlobals->time + ZOMBIE_FLINCH_DELAY; + } + + return iIgnore; + +} diff --git a/src/engine/Sequence.h b/src/engine/Sequence.h index 6253018..a321b84 100644 --- a/src/engine/Sequence.h +++ b/src/engine/Sequence.h @@ -1,201 +1,201 @@ -//--------------------------------------------------------------------------- -// -// S c r i p t e d S e q u e n c e s -// -//--------------------------------------------------------------------------- -#ifndef _INCLUDE_SEQUENCE_H_ -#define _INCLUDE_SEQUENCE_H_ - - -#ifndef _DEF_BYTE_ -typedef unsigned char byte; -#endif - -//--------------------------------------------------------------------------- -// client_textmessage_t -//--------------------------------------------------------------------------- -typedef struct client_textmessage_s -{ - int effect; - byte r1, g1, b1, a1; // 2 colors for effects - byte r2, g2, b2, a2; - float x; - float y; - float fadein; - float fadeout; - float holdtime; - float fxtime; - const char *pName; - const char *pMessage; -} client_textmessage_t; - - -//-------------------------------------------------------------------------- -// sequenceDefaultBits_e -// -// Enumerated list of possible modifiers for a command. This enumeration -// is used in a bitarray controlling what modifiers are specified for a command. -//--------------------------------------------------------------------------- -enum sequenceModifierBits -{ - SEQUENCE_MODIFIER_EFFECT_BIT = (1 << 1), - SEQUENCE_MODIFIER_POSITION_BIT = (1 << 2), - SEQUENCE_MODIFIER_COLOR_BIT = (1 << 3), - SEQUENCE_MODIFIER_COLOR2_BIT = (1 << 4), - SEQUENCE_MODIFIER_FADEIN_BIT = (1 << 5), - SEQUENCE_MODIFIER_FADEOUT_BIT = (1 << 6), - SEQUENCE_MODIFIER_HOLDTIME_BIT = (1 << 7), - SEQUENCE_MODIFIER_FXTIME_BIT = (1 << 8), - SEQUENCE_MODIFIER_SPEAKER_BIT = (1 << 9), - SEQUENCE_MODIFIER_LISTENER_BIT = (1 << 10), - SEQUENCE_MODIFIER_TEXTCHANNEL_BIT = (1 << 11), -}; -typedef enum sequenceModifierBits sequenceModifierBits_e ; - - -//--------------------------------------------------------------------------- -// sequenceCommandEnum_e -// -// Enumerated sequence command types. -//--------------------------------------------------------------------------- -enum sequenceCommandEnum_ -{ - SEQUENCE_COMMAND_ERROR = -1, - SEQUENCE_COMMAND_PAUSE = 0, - SEQUENCE_COMMAND_FIRETARGETS, - SEQUENCE_COMMAND_KILLTARGETS, - SEQUENCE_COMMAND_TEXT, - SEQUENCE_COMMAND_SOUND, - SEQUENCE_COMMAND_GOSUB, - SEQUENCE_COMMAND_SENTENCE, - SEQUENCE_COMMAND_REPEAT, - SEQUENCE_COMMAND_SETDEFAULTS, - SEQUENCE_COMMAND_MODIFIER, - SEQUENCE_COMMAND_POSTMODIFIER, - SEQUENCE_COMMAND_NOOP, - - SEQUENCE_MODIFIER_EFFECT, - SEQUENCE_MODIFIER_POSITION, - SEQUENCE_MODIFIER_COLOR, - SEQUENCE_MODIFIER_COLOR2, - SEQUENCE_MODIFIER_FADEIN, - SEQUENCE_MODIFIER_FADEOUT, - SEQUENCE_MODIFIER_HOLDTIME, - SEQUENCE_MODIFIER_FXTIME, - SEQUENCE_MODIFIER_SPEAKER, - SEQUENCE_MODIFIER_LISTENER, - SEQUENCE_MODIFIER_TEXTCHANNEL, -}; -typedef enum sequenceCommandEnum_ sequenceCommandEnum_e; - - -//--------------------------------------------------------------------------- -// sequenceCommandType_e -// -// Typeerated sequence command types. -//--------------------------------------------------------------------------- -enum sequenceCommandType_ -{ - SEQUENCE_TYPE_COMMAND, - SEQUENCE_TYPE_MODIFIER, -}; -typedef enum sequenceCommandType_ sequenceCommandType_e; - - -//--------------------------------------------------------------------------- -// sequenceCommandMapping_s -// -// A mapping of a command enumerated-value to its name. -//--------------------------------------------------------------------------- -typedef struct sequenceCommandMapping_ sequenceCommandMapping_s; -struct sequenceCommandMapping_ -{ - sequenceCommandEnum_e commandEnum; - const char* commandName; - sequenceCommandType_e commandType; -}; - - -//--------------------------------------------------------------------------- -// sequenceCommandLine_s -// -// Structure representing a single command (usually 1 line) from a -// .SEQ file entry. -//--------------------------------------------------------------------------- -typedef struct sequenceCommandLine_ sequenceCommandLine_s; -struct sequenceCommandLine_ -{ - int commandType; // Specifies the type of command - client_textmessage_t clientMessage; // Text HUD message struct - char* speakerName; // Targetname of speaking entity - char* listenerName; // Targetname of entity being spoken to - char* soundFileName; // Name of sound file to play - char* sentenceName; // Name of sentences.txt to play - char* fireTargetNames; // List of targetnames to fire - char* killTargetNames; // List of targetnames to remove - float delay; // Seconds 'till next command - int repeatCount; // If nonzero, reset execution pointer to top of block (N times, -1 = infinite) - int textChannel; // Display channel on which text message is sent - int modifierBitField; // Bit field to specify what clientmessage fields are valid - sequenceCommandLine_s* nextCommandLine; // Next command (linked list) -}; - - -//--------------------------------------------------------------------------- -// sequenceEntry_s -// -// Structure representing a single command (usually 1 line) from a -// .SEQ file entry. -//--------------------------------------------------------------------------- -typedef struct sequenceEntry_ sequenceEntry_s; -struct sequenceEntry_ -{ - char* fileName; // Name of sequence file without .SEQ extension - char* entryName; // Name of entry label in file - sequenceCommandLine_s* firstCommand; // Linked list of commands in entry - sequenceEntry_s* nextEntry; // Next loaded entry - qboolean isGlobal; // Is entry retained over level transitions? -}; - - - -//--------------------------------------------------------------------------- -// sentenceEntry_s -// Structure representing a single sentence of a group from a .SEQ -// file entry. Sentences are identical to entries in sentences.txt, but -// can be unique per level and are loaded/unloaded with the level. -//--------------------------------------------------------------------------- -typedef struct sentenceEntry_ sentenceEntry_s; -struct sentenceEntry_ -{ - char* data; // sentence data (ie "We have hostiles" ) - sentenceEntry_s* nextEntry; // Next loaded entry - qboolean isGlobal; // Is entry retained over level transitions? - unsigned int index; // this entry's position in the file. -}; - -//-------------------------------------------------------------------------- -// sentenceGroupEntry_s -// Structure representing a group of sentences found in a .SEQ file. -// A sentence group is defined by all sentences with the same name, ignoring -// the number at the end of the sentence name. Groups enable a sentence -// to be picked at random across a group. -//-------------------------------------------------------------------------- -typedef struct sentenceGroupEntry_ sentenceGroupEntry_s; -struct sentenceGroupEntry_ -{ - char* groupName; // name of the group (ie CT_ALERT ) - unsigned int numSentences; // number of sentences in group - sentenceEntry_s* firstSentence; // head of linked list of sentences in group - sentenceGroupEntry_s* nextEntry; // next loaded group -}; - -//--------------------------------------------------------------------------- -// Function declarations -//--------------------------------------------------------------------------- -sequenceEntry_s* SequenceGet( const char* fileName, const char* entryName ); -void Sequence_ParseFile( const char* fileName, qboolean isGlobal ); -void Sequence_OnLevelLoad( const char* mapName ); -sentenceEntry_s* SequencePickSentence( const char *groupName, int pickMethod, int *picked ); - -#endif /* _INCLUDE_SEQUENCE_H_ */ +//--------------------------------------------------------------------------- +// +// S c r i p t e d S e q u e n c e s +// +//--------------------------------------------------------------------------- +#ifndef _INCLUDE_SEQUENCE_H_ +#define _INCLUDE_SEQUENCE_H_ + + +#ifndef _DEF_BYTE_ +typedef unsigned char byte; +#endif + +//--------------------------------------------------------------------------- +// client_textmessage_t +//--------------------------------------------------------------------------- +typedef struct client_textmessage_s +{ + int effect; + byte r1, g1, b1, a1; // 2 colors for effects + byte r2, g2, b2, a2; + float x; + float y; + float fadein; + float fadeout; + float holdtime; + float fxtime; + const char *pName; + const char *pMessage; +} client_textmessage_t; + + +//-------------------------------------------------------------------------- +// sequenceDefaultBits_e +// +// Enumerated list of possible modifiers for a command. This enumeration +// is used in a bitarray controlling what modifiers are specified for a command. +//--------------------------------------------------------------------------- +enum sequenceModifierBits +{ + SEQUENCE_MODIFIER_EFFECT_BIT = (1 << 1), + SEQUENCE_MODIFIER_POSITION_BIT = (1 << 2), + SEQUENCE_MODIFIER_COLOR_BIT = (1 << 3), + SEQUENCE_MODIFIER_COLOR2_BIT = (1 << 4), + SEQUENCE_MODIFIER_FADEIN_BIT = (1 << 5), + SEQUENCE_MODIFIER_FADEOUT_BIT = (1 << 6), + SEQUENCE_MODIFIER_HOLDTIME_BIT = (1 << 7), + SEQUENCE_MODIFIER_FXTIME_BIT = (1 << 8), + SEQUENCE_MODIFIER_SPEAKER_BIT = (1 << 9), + SEQUENCE_MODIFIER_LISTENER_BIT = (1 << 10), + SEQUENCE_MODIFIER_TEXTCHANNEL_BIT = (1 << 11), +}; +typedef enum sequenceModifierBits sequenceModifierBits_e ; + + +//--------------------------------------------------------------------------- +// sequenceCommandEnum_e +// +// Enumerated sequence command types. +//--------------------------------------------------------------------------- +enum sequenceCommandEnum_ +{ + SEQUENCE_COMMAND_ERROR = -1, + SEQUENCE_COMMAND_PAUSE = 0, + SEQUENCE_COMMAND_FIRETARGETS, + SEQUENCE_COMMAND_KILLTARGETS, + SEQUENCE_COMMAND_TEXT, + SEQUENCE_COMMAND_SOUND, + SEQUENCE_COMMAND_GOSUB, + SEQUENCE_COMMAND_SENTENCE, + SEQUENCE_COMMAND_REPEAT, + SEQUENCE_COMMAND_SETDEFAULTS, + SEQUENCE_COMMAND_MODIFIER, + SEQUENCE_COMMAND_POSTMODIFIER, + SEQUENCE_COMMAND_NOOP, + + SEQUENCE_MODIFIER_EFFECT, + SEQUENCE_MODIFIER_POSITION, + SEQUENCE_MODIFIER_COLOR, + SEQUENCE_MODIFIER_COLOR2, + SEQUENCE_MODIFIER_FADEIN, + SEQUENCE_MODIFIER_FADEOUT, + SEQUENCE_MODIFIER_HOLDTIME, + SEQUENCE_MODIFIER_FXTIME, + SEQUENCE_MODIFIER_SPEAKER, + SEQUENCE_MODIFIER_LISTENER, + SEQUENCE_MODIFIER_TEXTCHANNEL, +}; +typedef enum sequenceCommandEnum_ sequenceCommandEnum_e; + + +//--------------------------------------------------------------------------- +// sequenceCommandType_e +// +// Typeerated sequence command types. +//--------------------------------------------------------------------------- +enum sequenceCommandType_ +{ + SEQUENCE_TYPE_COMMAND, + SEQUENCE_TYPE_MODIFIER, +}; +typedef enum sequenceCommandType_ sequenceCommandType_e; + + +//--------------------------------------------------------------------------- +// sequenceCommandMapping_s +// +// A mapping of a command enumerated-value to its name. +//--------------------------------------------------------------------------- +typedef struct sequenceCommandMapping_ sequenceCommandMapping_s; +struct sequenceCommandMapping_ +{ + sequenceCommandEnum_e commandEnum; + const char* commandName; + sequenceCommandType_e commandType; +}; + + +//--------------------------------------------------------------------------- +// sequenceCommandLine_s +// +// Structure representing a single command (usually 1 line) from a +// .SEQ file entry. +//--------------------------------------------------------------------------- +typedef struct sequenceCommandLine_ sequenceCommandLine_s; +struct sequenceCommandLine_ +{ + int commandType; // Specifies the type of command + client_textmessage_t clientMessage; // Text HUD message struct + char* speakerName; // Targetname of speaking entity + char* listenerName; // Targetname of entity being spoken to + char* soundFileName; // Name of sound file to play + char* sentenceName; // Name of sentences.txt to play + char* fireTargetNames; // List of targetnames to fire + char* killTargetNames; // List of targetnames to remove + float delay; // Seconds 'till next command + int repeatCount; // If nonzero, reset execution pointer to top of block (N times, -1 = infinite) + int textChannel; // Display channel on which text message is sent + int modifierBitField; // Bit field to specify what clientmessage fields are valid + sequenceCommandLine_s* nextCommandLine; // Next command (linked list) +}; + + +//--------------------------------------------------------------------------- +// sequenceEntry_s +// +// Structure representing a single command (usually 1 line) from a +// .SEQ file entry. +//--------------------------------------------------------------------------- +typedef struct sequenceEntry_ sequenceEntry_s; +struct sequenceEntry_ +{ + char* fileName; // Name of sequence file without .SEQ extension + char* entryName; // Name of entry label in file + sequenceCommandLine_s* firstCommand; // Linked list of commands in entry + sequenceEntry_s* nextEntry; // Next loaded entry + qboolean isGlobal; // Is entry retained over level transitions? +}; + + + +//--------------------------------------------------------------------------- +// sentenceEntry_s +// Structure representing a single sentence of a group from a .SEQ +// file entry. Sentences are identical to entries in sentences.txt, but +// can be unique per level and are loaded/unloaded with the level. +//--------------------------------------------------------------------------- +typedef struct sentenceEntry_ sentenceEntry_s; +struct sentenceEntry_ +{ + char* data; // sentence data (ie "We have hostiles" ) + sentenceEntry_s* nextEntry; // Next loaded entry + qboolean isGlobal; // Is entry retained over level transitions? + unsigned int index; // this entry's position in the file. +}; + +//-------------------------------------------------------------------------- +// sentenceGroupEntry_s +// Structure representing a group of sentences found in a .SEQ file. +// A sentence group is defined by all sentences with the same name, ignoring +// the number at the end of the sentence name. Groups enable a sentence +// to be picked at random across a group. +//-------------------------------------------------------------------------- +typedef struct sentenceGroupEntry_ sentenceGroupEntry_s; +struct sentenceGroupEntry_ +{ + char* groupName; // name of the group (ie CT_ALERT ) + unsigned int numSentences; // number of sentences in group + sentenceEntry_s* firstSentence; // head of linked list of sentences in group + sentenceGroupEntry_s* nextEntry; // next loaded group +}; + +//--------------------------------------------------------------------------- +// Function declarations +//--------------------------------------------------------------------------- +sequenceEntry_s* SequenceGet( const char* fileName, const char* entryName ); +void Sequence_ParseFile( const char* fileName, qboolean isGlobal ); +void Sequence_OnLevelLoad( const char* mapName ); +sentenceEntry_s* SequencePickSentence( const char *groupName, int pickMethod, int *picked ); + +#endif /* _INCLUDE_SEQUENCE_H_ */ diff --git a/src/engine/anorms.h b/src/engine/anorms.h index d147aea..c90f8d6 100644 --- a/src/engine/anorms.h +++ b/src/engine/anorms.h @@ -1,177 +1,177 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ - -{-0.525731, 0.000000, 0.850651}, -{-0.442863, 0.238856, 0.864188}, -{-0.295242, 0.000000, 0.955423}, -{-0.309017, 0.500000, 0.809017}, -{-0.162460, 0.262866, 0.951056}, -{0.000000, 0.000000, 1.000000}, -{0.000000, 0.850651, 0.525731}, -{-0.147621, 0.716567, 0.681718}, -{0.147621, 0.716567, 0.681718}, -{0.000000, 0.525731, 0.850651}, -{0.309017, 0.500000, 0.809017}, -{0.525731, 0.000000, 0.850651}, -{0.295242, 0.000000, 0.955423}, -{0.442863, 0.238856, 0.864188}, -{0.162460, 0.262866, 0.951056}, -{-0.681718, 0.147621, 0.716567}, -{-0.809017, 0.309017, 0.500000}, -{-0.587785, 0.425325, 0.688191}, -{-0.850651, 0.525731, 0.000000}, -{-0.864188, 0.442863, 0.238856}, -{-0.716567, 0.681718, 0.147621}, -{-0.688191, 0.587785, 0.425325}, -{-0.500000, 0.809017, 0.309017}, -{-0.238856, 0.864188, 0.442863}, -{-0.425325, 0.688191, 0.587785}, -{-0.716567, 0.681718, -0.147621}, -{-0.500000, 0.809017, -0.309017}, -{-0.525731, 0.850651, 0.000000}, -{0.000000, 0.850651, -0.525731}, -{-0.238856, 0.864188, -0.442863}, -{0.000000, 0.955423, -0.295242}, -{-0.262866, 0.951056, -0.162460}, -{0.000000, 1.000000, 0.000000}, -{0.000000, 0.955423, 0.295242}, -{-0.262866, 0.951056, 0.162460}, -{0.238856, 0.864188, 0.442863}, -{0.262866, 0.951056, 0.162460}, -{0.500000, 0.809017, 0.309017}, -{0.238856, 0.864188, -0.442863}, -{0.262866, 0.951056, -0.162460}, -{0.500000, 0.809017, -0.309017}, -{0.850651, 0.525731, 0.000000}, -{0.716567, 0.681718, 0.147621}, -{0.716567, 0.681718, -0.147621}, -{0.525731, 0.850651, 0.000000}, -{0.425325, 0.688191, 0.587785}, -{0.864188, 0.442863, 0.238856}, -{0.688191, 0.587785, 0.425325}, -{0.809017, 0.309017, 0.500000}, -{0.681718, 0.147621, 0.716567}, -{0.587785, 0.425325, 0.688191}, -{0.955423, 0.295242, 0.000000}, -{1.000000, 0.000000, 0.000000}, -{0.951056, 0.162460, 0.262866}, -{0.850651, -0.525731, 0.000000}, -{0.955423, -0.295242, 0.000000}, -{0.864188, -0.442863, 0.238856}, -{0.951056, -0.162460, 0.262866}, -{0.809017, -0.309017, 0.500000}, -{0.681718, -0.147621, 0.716567}, -{0.850651, 0.000000, 0.525731}, -{0.864188, 0.442863, -0.238856}, -{0.809017, 0.309017, -0.500000}, -{0.951056, 0.162460, -0.262866}, -{0.525731, 0.000000, -0.850651}, -{0.681718, 0.147621, -0.716567}, -{0.681718, -0.147621, -0.716567}, -{0.850651, 0.000000, -0.525731}, -{0.809017, -0.309017, -0.500000}, -{0.864188, -0.442863, -0.238856}, -{0.951056, -0.162460, -0.262866}, -{0.147621, 0.716567, -0.681718}, -{0.309017, 0.500000, -0.809017}, -{0.425325, 0.688191, -0.587785}, -{0.442863, 0.238856, -0.864188}, -{0.587785, 0.425325, -0.688191}, -{0.688191, 0.587785, -0.425325}, -{-0.147621, 0.716567, -0.681718}, -{-0.309017, 0.500000, -0.809017}, -{0.000000, 0.525731, -0.850651}, -{-0.525731, 0.000000, -0.850651}, -{-0.442863, 0.238856, -0.864188}, -{-0.295242, 0.000000, -0.955423}, -{-0.162460, 0.262866, -0.951056}, -{0.000000, 0.000000, -1.000000}, -{0.295242, 0.000000, -0.955423}, -{0.162460, 0.262866, -0.951056}, -{-0.442863, -0.238856, -0.864188}, -{-0.309017, -0.500000, -0.809017}, -{-0.162460, -0.262866, -0.951056}, -{0.000000, -0.850651, -0.525731}, -{-0.147621, -0.716567, -0.681718}, -{0.147621, -0.716567, -0.681718}, -{0.000000, -0.525731, -0.850651}, -{0.309017, -0.500000, -0.809017}, -{0.442863, -0.238856, -0.864188}, -{0.162460, -0.262866, -0.951056}, -{0.238856, -0.864188, -0.442863}, -{0.500000, -0.809017, -0.309017}, -{0.425325, -0.688191, -0.587785}, -{0.716567, -0.681718, -0.147621}, -{0.688191, -0.587785, -0.425325}, -{0.587785, -0.425325, -0.688191}, -{0.000000, -0.955423, -0.295242}, -{0.000000, -1.000000, 0.000000}, -{0.262866, -0.951056, -0.162460}, -{0.000000, -0.850651, 0.525731}, -{0.000000, -0.955423, 0.295242}, -{0.238856, -0.864188, 0.442863}, -{0.262866, -0.951056, 0.162460}, -{0.500000, -0.809017, 0.309017}, -{0.716567, -0.681718, 0.147621}, -{0.525731, -0.850651, 0.000000}, -{-0.238856, -0.864188, -0.442863}, -{-0.500000, -0.809017, -0.309017}, -{-0.262866, -0.951056, -0.162460}, -{-0.850651, -0.525731, 0.000000}, -{-0.716567, -0.681718, -0.147621}, -{-0.716567, -0.681718, 0.147621}, -{-0.525731, -0.850651, 0.000000}, -{-0.500000, -0.809017, 0.309017}, -{-0.238856, -0.864188, 0.442863}, -{-0.262866, -0.951056, 0.162460}, -{-0.864188, -0.442863, 0.238856}, -{-0.809017, -0.309017, 0.500000}, -{-0.688191, -0.587785, 0.425325}, -{-0.681718, -0.147621, 0.716567}, -{-0.442863, -0.238856, 0.864188}, -{-0.587785, -0.425325, 0.688191}, -{-0.309017, -0.500000, 0.809017}, -{-0.147621, -0.716567, 0.681718}, -{-0.425325, -0.688191, 0.587785}, -{-0.162460, -0.262866, 0.951056}, -{0.442863, -0.238856, 0.864188}, -{0.162460, -0.262866, 0.951056}, -{0.309017, -0.500000, 0.809017}, -{0.147621, -0.716567, 0.681718}, -{0.000000, -0.525731, 0.850651}, -{0.425325, -0.688191, 0.587785}, -{0.587785, -0.425325, 0.688191}, -{0.688191, -0.587785, 0.425325}, -{-0.955423, 0.295242, 0.000000}, -{-0.951056, 0.162460, 0.262866}, -{-1.000000, 0.000000, 0.000000}, -{-0.850651, 0.000000, 0.525731}, -{-0.955423, -0.295242, 0.000000}, -{-0.951056, -0.162460, 0.262866}, -{-0.864188, 0.442863, -0.238856}, -{-0.951056, 0.162460, -0.262866}, -{-0.809017, 0.309017, -0.500000}, -{-0.864188, -0.442863, -0.238856}, -{-0.951056, -0.162460, -0.262866}, -{-0.809017, -0.309017, -0.500000}, -{-0.681718, 0.147621, -0.716567}, -{-0.681718, -0.147621, -0.716567}, -{-0.850651, 0.000000, -0.525731}, -{-0.688191, 0.587785, -0.425325}, -{-0.587785, 0.425325, -0.688191}, -{-0.425325, 0.688191, -0.587785}, -{-0.425325, -0.688191, -0.587785}, -{-0.587785, -0.425325, -0.688191}, -{-0.688191, -0.587785, -0.425325}, +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ + +{-0.525731, 0.000000, 0.850651}, +{-0.442863, 0.238856, 0.864188}, +{-0.295242, 0.000000, 0.955423}, +{-0.309017, 0.500000, 0.809017}, +{-0.162460, 0.262866, 0.951056}, +{0.000000, 0.000000, 1.000000}, +{0.000000, 0.850651, 0.525731}, +{-0.147621, 0.716567, 0.681718}, +{0.147621, 0.716567, 0.681718}, +{0.000000, 0.525731, 0.850651}, +{0.309017, 0.500000, 0.809017}, +{0.525731, 0.000000, 0.850651}, +{0.295242, 0.000000, 0.955423}, +{0.442863, 0.238856, 0.864188}, +{0.162460, 0.262866, 0.951056}, +{-0.681718, 0.147621, 0.716567}, +{-0.809017, 0.309017, 0.500000}, +{-0.587785, 0.425325, 0.688191}, +{-0.850651, 0.525731, 0.000000}, +{-0.864188, 0.442863, 0.238856}, +{-0.716567, 0.681718, 0.147621}, +{-0.688191, 0.587785, 0.425325}, +{-0.500000, 0.809017, 0.309017}, +{-0.238856, 0.864188, 0.442863}, +{-0.425325, 0.688191, 0.587785}, +{-0.716567, 0.681718, -0.147621}, +{-0.500000, 0.809017, -0.309017}, +{-0.525731, 0.850651, 0.000000}, +{0.000000, 0.850651, -0.525731}, +{-0.238856, 0.864188, -0.442863}, +{0.000000, 0.955423, -0.295242}, +{-0.262866, 0.951056, -0.162460}, +{0.000000, 1.000000, 0.000000}, +{0.000000, 0.955423, 0.295242}, +{-0.262866, 0.951056, 0.162460}, +{0.238856, 0.864188, 0.442863}, +{0.262866, 0.951056, 0.162460}, +{0.500000, 0.809017, 0.309017}, +{0.238856, 0.864188, -0.442863}, +{0.262866, 0.951056, -0.162460}, +{0.500000, 0.809017, -0.309017}, +{0.850651, 0.525731, 0.000000}, +{0.716567, 0.681718, 0.147621}, +{0.716567, 0.681718, -0.147621}, +{0.525731, 0.850651, 0.000000}, +{0.425325, 0.688191, 0.587785}, +{0.864188, 0.442863, 0.238856}, +{0.688191, 0.587785, 0.425325}, +{0.809017, 0.309017, 0.500000}, +{0.681718, 0.147621, 0.716567}, +{0.587785, 0.425325, 0.688191}, +{0.955423, 0.295242, 0.000000}, +{1.000000, 0.000000, 0.000000}, +{0.951056, 0.162460, 0.262866}, +{0.850651, -0.525731, 0.000000}, +{0.955423, -0.295242, 0.000000}, +{0.864188, -0.442863, 0.238856}, +{0.951056, -0.162460, 0.262866}, +{0.809017, -0.309017, 0.500000}, +{0.681718, -0.147621, 0.716567}, +{0.850651, 0.000000, 0.525731}, +{0.864188, 0.442863, -0.238856}, +{0.809017, 0.309017, -0.500000}, +{0.951056, 0.162460, -0.262866}, +{0.525731, 0.000000, -0.850651}, +{0.681718, 0.147621, -0.716567}, +{0.681718, -0.147621, -0.716567}, +{0.850651, 0.000000, -0.525731}, +{0.809017, -0.309017, -0.500000}, +{0.864188, -0.442863, -0.238856}, +{0.951056, -0.162460, -0.262866}, +{0.147621, 0.716567, -0.681718}, +{0.309017, 0.500000, -0.809017}, +{0.425325, 0.688191, -0.587785}, +{0.442863, 0.238856, -0.864188}, +{0.587785, 0.425325, -0.688191}, +{0.688191, 0.587785, -0.425325}, +{-0.147621, 0.716567, -0.681718}, +{-0.309017, 0.500000, -0.809017}, +{0.000000, 0.525731, -0.850651}, +{-0.525731, 0.000000, -0.850651}, +{-0.442863, 0.238856, -0.864188}, +{-0.295242, 0.000000, -0.955423}, +{-0.162460, 0.262866, -0.951056}, +{0.000000, 0.000000, -1.000000}, +{0.295242, 0.000000, -0.955423}, +{0.162460, 0.262866, -0.951056}, +{-0.442863, -0.238856, -0.864188}, +{-0.309017, -0.500000, -0.809017}, +{-0.162460, -0.262866, -0.951056}, +{0.000000, -0.850651, -0.525731}, +{-0.147621, -0.716567, -0.681718}, +{0.147621, -0.716567, -0.681718}, +{0.000000, -0.525731, -0.850651}, +{0.309017, -0.500000, -0.809017}, +{0.442863, -0.238856, -0.864188}, +{0.162460, -0.262866, -0.951056}, +{0.238856, -0.864188, -0.442863}, +{0.500000, -0.809017, -0.309017}, +{0.425325, -0.688191, -0.587785}, +{0.716567, -0.681718, -0.147621}, +{0.688191, -0.587785, -0.425325}, +{0.587785, -0.425325, -0.688191}, +{0.000000, -0.955423, -0.295242}, +{0.000000, -1.000000, 0.000000}, +{0.262866, -0.951056, -0.162460}, +{0.000000, -0.850651, 0.525731}, +{0.000000, -0.955423, 0.295242}, +{0.238856, -0.864188, 0.442863}, +{0.262866, -0.951056, 0.162460}, +{0.500000, -0.809017, 0.309017}, +{0.716567, -0.681718, 0.147621}, +{0.525731, -0.850651, 0.000000}, +{-0.238856, -0.864188, -0.442863}, +{-0.500000, -0.809017, -0.309017}, +{-0.262866, -0.951056, -0.162460}, +{-0.850651, -0.525731, 0.000000}, +{-0.716567, -0.681718, -0.147621}, +{-0.716567, -0.681718, 0.147621}, +{-0.525731, -0.850651, 0.000000}, +{-0.500000, -0.809017, 0.309017}, +{-0.238856, -0.864188, 0.442863}, +{-0.262866, -0.951056, 0.162460}, +{-0.864188, -0.442863, 0.238856}, +{-0.809017, -0.309017, 0.500000}, +{-0.688191, -0.587785, 0.425325}, +{-0.681718, -0.147621, 0.716567}, +{-0.442863, -0.238856, 0.864188}, +{-0.587785, -0.425325, 0.688191}, +{-0.309017, -0.500000, 0.809017}, +{-0.147621, -0.716567, 0.681718}, +{-0.425325, -0.688191, 0.587785}, +{-0.162460, -0.262866, 0.951056}, +{0.442863, -0.238856, 0.864188}, +{0.162460, -0.262866, 0.951056}, +{0.309017, -0.500000, 0.809017}, +{0.147621, -0.716567, 0.681718}, +{0.000000, -0.525731, 0.850651}, +{0.425325, -0.688191, 0.587785}, +{0.587785, -0.425325, 0.688191}, +{0.688191, -0.587785, 0.425325}, +{-0.955423, 0.295242, 0.000000}, +{-0.951056, 0.162460, 0.262866}, +{-1.000000, 0.000000, 0.000000}, +{-0.850651, 0.000000, 0.525731}, +{-0.955423, -0.295242, 0.000000}, +{-0.951056, -0.162460, 0.262866}, +{-0.864188, 0.442863, -0.238856}, +{-0.951056, 0.162460, -0.262866}, +{-0.809017, 0.309017, -0.500000}, +{-0.864188, -0.442863, -0.238856}, +{-0.951056, -0.162460, -0.262866}, +{-0.809017, -0.309017, -0.500000}, +{-0.681718, 0.147621, -0.716567}, +{-0.681718, -0.147621, -0.716567}, +{-0.850651, 0.000000, -0.525731}, +{-0.688191, 0.587785, -0.425325}, +{-0.587785, 0.425325, -0.688191}, +{-0.425325, 0.688191, -0.587785}, +{-0.425325, -0.688191, -0.587785}, +{-0.587785, -0.425325, -0.688191}, +{-0.688191, -0.587785, -0.425325}, diff --git a/src/engine/archtypes.h b/src/engine/archtypes.h index 3315135..ea143ce 100644 --- a/src/engine/archtypes.h +++ b/src/engine/archtypes.h @@ -1,41 +1,41 @@ -// -// Word size dependent definitions -// DAL 1/03 -// -#ifndef ARCHTYPES_H -#define ARCHTYPES_H - -#ifdef __x86_64__ -#define X64BITS -#endif - -#if defined( _WIN32 ) && (! defined( __MINGW32__ )) - -typedef __int16 int16; -typedef unsigned __int16 uint16; -typedef __int32 int32; -typedef unsigned __int32 uint32; -typedef __int64 int64; -typedef unsigned __int64 uint64; -typedef __int32 intp; // intp is an integer that can accomodate a pointer -typedef unsigned __int32 uintp; // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *) - -#else /* _WIN32 */ - -typedef short int16; -typedef unsigned short uint16; -typedef int int32; -typedef unsigned int uint32; -typedef long long int64; -typedef unsigned long long uint64; -#ifdef X64BITS -typedef long long intp; -typedef unsigned long long uintp; -#else -typedef int intp; -typedef unsigned int uintp; -#endif - -#endif /* else _WIN32 */ - -#endif /* ARCHTYPES_H */ +// +// Word size dependent definitions +// DAL 1/03 +// +#ifndef ARCHTYPES_H +#define ARCHTYPES_H + +#ifdef __x86_64__ +#define X64BITS +#endif + +#if defined( _WIN32 ) && (! defined( __MINGW32__ )) + +typedef __int16 int16; +typedef unsigned __int16 uint16; +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +typedef __int32 intp; // intp is an integer that can accomodate a pointer +typedef unsigned __int32 uintp; // (ie, sizeof(intp) >= sizeof(int) && sizeof(intp) >= sizeof(void *) + +#else /* _WIN32 */ + +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int64; +typedef unsigned long long uint64; +#ifdef X64BITS +typedef long long intp; +typedef unsigned long long uintp; +#else +typedef int intp; +typedef unsigned int uintp; +#endif + +#endif /* else _WIN32 */ + +#endif /* ARCHTYPES_H */ diff --git a/src/engine/cdll_int.h b/src/engine/cdll_int.h index 18db1f7..2c70fa5 100644 --- a/src/engine/cdll_int.h +++ b/src/engine/cdll_int.h @@ -1,311 +1,311 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -// -// cdll_int.h -// -// 4-23-98 -// JOHN: client dll interface declarations -// - -#ifndef CDLL_INT_H -#define CDLL_INT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "const.h" - - -// this file is included by both the engine and the client-dll, -// so make sure engine declarations aren't done twice - -typedef int HSPRITE; // handle to a graphic - -#define SCRINFO_SCREENFLASH 1 -#define SCRINFO_STRETCHED 2 - -typedef struct SCREENINFO_s -{ - int iSize; - int iWidth; - int iHeight; - int iFlags; - int iCharHeight; - short charWidths[256]; -} SCREENINFO; - - -typedef struct client_data_s -{ - // fields that cannot be modified (ie. have no effect if changed) - vec3_t origin; - - // fields that can be changed by the cldll - vec3_t viewangles; - int iWeaponBits; - float fov; // field of view -} client_data_t; - -typedef struct client_sprite_s -{ - char szName[64]; - char szSprite[64]; - int hspr; - int iRes; - wrect_t rc; -} client_sprite_t; - -typedef struct client_textmessage_s -{ - int effect; - byte r1, g1, b1, a1; // 2 colors for effects - byte r2, g2, b2, a2; - float x; - float y; - float fadein; - float fadeout; - float holdtime; - float fxtime; - const char *pName; - const char *pMessage; -} client_textmessage_t; - -typedef struct hud_player_info_s -{ - char *name; - short ping; - byte thisplayer; // TRUE if this is the calling player - - // stuff that's unused at the moment, but should be done - byte spectator; - byte packetloss; - - char *model; - short topcolor; - short bottomcolor; - -} hud_player_info_t; - - -typedef struct cl_enginefuncs_s -{ - // sprite handlers - HSPRITE ( *pfnSPR_Load ) ( const char *szPicName ); - int ( *pfnSPR_Frames ) ( HSPRITE hPic ); - int ( *pfnSPR_Height ) ( HSPRITE hPic, int frame ); - int ( *pfnSPR_Width ) ( HSPRITE hPic, int frame ); - void ( *pfnSPR_Set ) ( HSPRITE hPic, int r, int g, int b ); - void ( *pfnSPR_Draw ) ( int frame, int x, int y, const wrect_t *prc ); - void ( *pfnSPR_DrawHoles ) ( int frame, int x, int y, const wrect_t *prc ); - void ( *pfnSPR_DrawAdditive ) ( int frame, int x, int y, const wrect_t *prc ); - void ( *pfnSPR_EnableScissor ) ( int x, int y, int width, int height ); - void ( *pfnSPR_DisableScissor ) ( void ); - client_sprite_t *( *pfnSPR_GetList ) ( char *psz, int *piCount ); - - // screen handlers - void ( *pfnFillRGBA ) ( int x, int y, int width, int height, int r, int g, int b, int a ); - int ( *pfnGetScreenInfo ) ( SCREENINFO *pscrinfo ); - void ( *pfnSetCrosshair ) ( HSPRITE hspr, wrect_t rc, int r, int g, int b ); - - // cvar handlers - struct cvar_s *( *pfnRegisterVariable ) ( char *szName, char *szValue, int flags ); - float ( *pfnGetCvarFloat ) ( char *szName ); - char* ( *pfnGetCvarString ) ( char *szName ); - - // command handlers - int ( *pfnAddCommand ) ( char *cmd_name, void (*function)(void) ); - int ( *pfnHookUserMsg ) ( char *szMsgName, pfnUserMsgHook pfn ); - int ( *pfnServerCmd ) ( char *szCmdString ); - int ( *pfnClientCmd ) ( char *szCmdString ); - - void ( *pfnGetPlayerInfo ) ( int ent_num, hud_player_info_t *pinfo ); - - // sound handlers - void ( *pfnPlaySoundByName ) ( char *szSound, float volume ); - void ( *pfnPlaySoundByIndex ) ( int iSound, float volume ); - - // vector helpers - void ( *pfnAngleVectors ) ( const float * vecAngles, float * forward, float * right, float * up ); - - // text message system - client_textmessage_t *( *pfnTextMessageGet ) ( const char *pName ); - int ( *pfnDrawCharacter ) ( int x, int y, int number, int r, int g, int b ); - int ( *pfnDrawConsoleString ) ( int x, int y, char *string ); - void ( *pfnDrawSetTextColor ) ( float r, float g, float b ); - void ( *pfnDrawConsoleStringLen )( const char *string, int *length, int *height ); - - void ( *pfnConsolePrint ) ( const char *string ); - void ( *pfnCenterPrint ) ( const char *string ); - - -// Added for user input processing - int ( *GetWindowCenterX ) ( void ); - int ( *GetWindowCenterY ) ( void ); - void ( *GetViewAngles ) ( float * ); - void ( *SetViewAngles ) ( float * ); - int ( *GetMaxClients ) ( void ); - void ( *Cvar_SetValue ) ( char *cvar, float value ); - - int (*Cmd_Argc) (void); - char *( *Cmd_Argv ) ( int arg ); - void ( *Con_Printf ) ( char *fmt, ... ); - void ( *Con_DPrintf ) ( char *fmt, ... ); - void ( *Con_NPrintf ) ( int pos, char *fmt, ... ); - void ( *Con_NXPrintf ) ( struct con_nprint_s *info, char *fmt, ... ); - - const char *( *PhysInfo_ValueForKey ) ( const char *key ); - const char *( *ServerInfo_ValueForKey )( const char *key ); - float ( *GetClientMaxspeed ) ( void ); - int ( *CheckParm ) ( char *parm, char **ppnext ); - void ( *Key_Event ) ( int key, int down ); - void ( *GetMousePosition ) ( int *mx, int *my ); - int ( *IsNoClipping ) ( void ); - - struct cl_entity_s *( *GetLocalPlayer ) ( void ); - struct cl_entity_s *( *GetViewModel ) ( void ); - struct cl_entity_s *( *GetEntityByIndex ) ( int idx ); - - float ( *GetClientTime ) ( void ); - void ( *V_CalcShake ) ( void ); - void ( *V_ApplyShake ) ( float *origin, float *angles, float factor ); - - int ( *PM_PointContents ) ( float *point, int *truecontents ); - int ( *PM_WaterEntity ) ( float *p ); - struct pmtrace_s *( *PM_TraceLine ) ( float *start, float *end, int flags, int usehull, int ignore_pe ); - - struct model_s *( *CL_LoadModel ) ( const char *modelname, int *index ); - int ( *CL_CreateVisibleEntity ) ( int type, struct cl_entity_s *ent ); - - const struct model_s * ( *GetSpritePointer ) ( HSPRITE hSprite ); - void ( *pfnPlaySoundByNameAtLocation ) ( char *szSound, float volume, float *origin ); - - unsigned short ( *pfnPrecacheEvent ) ( int type, const char* psz ); - void ( *pfnPlaybackEvent ) ( int flags, const struct edict_s *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); - void ( *pfnWeaponAnim ) ( int iAnim, int body ); - float ( *pfnRandomFloat ) ( float flLow, float flHigh ); - long ( *pfnRandomLong ) ( long lLow, long lHigh ); - void ( *pfnHookEvent ) ( char *name, void ( *pfnEvent )( struct event_args_s *args ) ); - int (*Con_IsVisible) (); - const char *( *pfnGetGameDirectory ) ( void ); - struct cvar_s *( *pfnGetCvarPointer ) ( const char *szName ); - const char *( *Key_LookupBinding ) ( const char *pBinding ); - const char *( *pfnGetLevelName ) ( void ); - void ( *pfnGetScreenFade ) ( struct screenfade_s *fade ); - void ( *pfnSetScreenFade ) ( struct screenfade_s *fade ); - void *( *VGui_GetPanel ) ( ); - void ( *VGui_ViewportPaintBackground ) (int extents[4]); - - byte* (*COM_LoadFile) ( char *path, int usehunk, int *pLength ); - char* (*COM_ParseFile) ( char *data, char *token ); - void (*COM_FreeFile) ( void *buffer ); - - struct triangleapi_s *pTriAPI; - struct efx_api_s *pEfxAPI; - struct event_api_s *pEventAPI; - struct demo_api_s *pDemoAPI; - struct net_api_s *pNetAPI; - struct IVoiceTweak_s *pVoiceTweak; - - // returns 1 if the client is a spectator only (connected to a proxy), 0 otherwise or 2 if in dev_overview mode - int ( *IsSpectateOnly ) ( void ); - struct model_s *( *LoadMapSprite ) ( const char *filename ); - - // file search functions - void ( *COM_AddAppDirectoryToSearchPath ) ( const char *pszBaseDir, const char *appName ); - int ( *COM_ExpandFilename) ( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ); - - // User info - // playerNum is in the range (1, MaxClients) - // returns NULL if player doesn't exit - // returns "" if no value is set - const char *( *PlayerInfo_ValueForKey )( int playerNum, const char *key ); - void ( *PlayerInfo_SetValueForKey )( const char *key, const char *value ); - - // Gets a unique ID for the specified player. This is the same even if you see the player on a different server. - // iPlayer is an entity index, so client 0 would use iPlayer=1. - // Returns false if there is no player on the server in the specified slot. - qboolean (*GetPlayerUniqueID)(int iPlayer, char playerID[16]); - - // TrackerID access - int (*GetTrackerIDForPlayer)(int playerSlot); - int (*GetPlayerForTrackerID)(int trackerID); - - // Same as pfnServerCmd, but the message goes in the unreliable stream so it can't clog the net stream - // (but it might not get there). - int ( *pfnServerCmdUnreliable )( char *szCmdString ); - - void ( *pfnGetMousePos )( struct tagPOINT *ppt ); - void ( *pfnSetMousePos )( int x, int y ); - void ( *pfnSetMouseEnable )( qboolean fEnable ); -} cl_enginefunc_t; - -#ifndef IN_BUTTONS_H -#include "in_buttons.h" -#endif - -#define CLDLL_INTERFACE_VERSION 7 - -extern void ClientDLL_Init( void ); // from cdll_int.c -extern void ClientDLL_Shutdown( void ); -extern void ClientDLL_HudInit( void ); -extern void ClientDLL_HudVidInit( void ); -extern void ClientDLL_UpdateClientData( void ); -extern void ClientDLL_Frame( double time ); -extern void ClientDLL_HudRedraw( int intermission ); -extern void ClientDLL_MoveClient( struct playermove_s *ppmove ); -extern void ClientDLL_ClientMoveInit( struct playermove_s *ppmove ); -extern char ClientDLL_ClientTextureType( char *name ); - -extern void ClientDLL_CreateMove( float frametime, struct usercmd_s *cmd, int active ); -extern void ClientDLL_ActivateMouse( void ); -extern void ClientDLL_DeactivateMouse( void ); -extern void ClientDLL_MouseEvent( int mstate ); -extern void ClientDLL_ClearStates( void ); -extern int ClientDLL_IsThirdPerson( void ); -extern void ClientDLL_GetCameraOffsets( float *ofs ); -extern int ClientDLL_GraphKeyDown( void ); -extern struct kbutton_s *ClientDLL_FindKey( const char *name ); -extern void ClientDLL_CAM_Think( void ); -extern void ClientDLL_IN_Accumulate( void ); -extern void ClientDLL_CalcRefdef( struct ref_params_s *pparams ); -extern int ClientDLL_AddEntity( int type, struct cl_entity_s *ent ); -extern void ClientDLL_CreateEntities( void ); - -extern void ClientDLL_DrawNormalTriangles( void ); -extern void ClientDLL_DrawTransparentTriangles( void ); -extern void ClientDLL_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity ); -extern void ClientDLL_PostRunCmd( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed ); -extern void ClientDLL_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client ); -extern void ClientDLL_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src ); -extern void ClientDLL_TxferPredictionData ( struct entity_state_s *ps, const struct entity_state_s *pps, struct clientdata_s *pcd, const struct clientdata_s *ppcd, struct weapon_data_s *wd, const struct weapon_data_s *pwd ); -extern void ClientDLL_ReadDemoBuffer( int size, unsigned char *buffer ); -extern int ClientDLL_ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); -extern int ClientDLL_GetHullBounds( int hullnumber, float *mins, float *maxs ); - -extern void ClientDLL_VGui_ConsolePrint(const char* text); - -extern int ClientDLL_Key_Event( int down, int keynum, const char *pszCurrentBinding ); -extern void ClientDLL_TempEntUpdate( double ft, double ct, double grav, struct tempent_s **ppFreeTE, struct tempent_s **ppActiveTE, int ( *addTEntity )( struct cl_entity_s *pEntity ), void ( *playTESound )( struct tempent_s *pTemp, float damp ) ); -extern struct cl_entity_s *ClientDLL_GetUserEntity( int index ); -extern void ClientDLL_VoiceStatus(int entindex, qboolean bTalking); -extern void ClientDLL_DirectorMessage( int iSize, void *pbuf ); - - -#ifdef __cplusplus -} -#endif - -#endif // CDLL_INT_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +// +// cdll_int.h +// +// 4-23-98 +// JOHN: client dll interface declarations +// + +#ifndef CDLL_INT_H +#define CDLL_INT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "const.h" + + +// this file is included by both the engine and the client-dll, +// so make sure engine declarations aren't done twice + +typedef int HSPRITE; // handle to a graphic + +#define SCRINFO_SCREENFLASH 1 +#define SCRINFO_STRETCHED 2 + +typedef struct SCREENINFO_s +{ + int iSize; + int iWidth; + int iHeight; + int iFlags; + int iCharHeight; + short charWidths[256]; +} SCREENINFO; + + +typedef struct client_data_s +{ + // fields that cannot be modified (ie. have no effect if changed) + vec3_t origin; + + // fields that can be changed by the cldll + vec3_t viewangles; + int iWeaponBits; + float fov; // field of view +} client_data_t; + +typedef struct client_sprite_s +{ + char szName[64]; + char szSprite[64]; + int hspr; + int iRes; + wrect_t rc; +} client_sprite_t; + +typedef struct client_textmessage_s +{ + int effect; + byte r1, g1, b1, a1; // 2 colors for effects + byte r2, g2, b2, a2; + float x; + float y; + float fadein; + float fadeout; + float holdtime; + float fxtime; + const char *pName; + const char *pMessage; +} client_textmessage_t; + +typedef struct hud_player_info_s +{ + char *name; + short ping; + byte thisplayer; // TRUE if this is the calling player + + // stuff that's unused at the moment, but should be done + byte spectator; + byte packetloss; + + char *model; + short topcolor; + short bottomcolor; + +} hud_player_info_t; + + +typedef struct cl_enginefuncs_s +{ + // sprite handlers + HSPRITE ( *pfnSPR_Load ) ( const char *szPicName ); + int ( *pfnSPR_Frames ) ( HSPRITE hPic ); + int ( *pfnSPR_Height ) ( HSPRITE hPic, int frame ); + int ( *pfnSPR_Width ) ( HSPRITE hPic, int frame ); + void ( *pfnSPR_Set ) ( HSPRITE hPic, int r, int g, int b ); + void ( *pfnSPR_Draw ) ( int frame, int x, int y, const wrect_t *prc ); + void ( *pfnSPR_DrawHoles ) ( int frame, int x, int y, const wrect_t *prc ); + void ( *pfnSPR_DrawAdditive ) ( int frame, int x, int y, const wrect_t *prc ); + void ( *pfnSPR_EnableScissor ) ( int x, int y, int width, int height ); + void ( *pfnSPR_DisableScissor ) ( void ); + client_sprite_t *( *pfnSPR_GetList ) ( char *psz, int *piCount ); + + // screen handlers + void ( *pfnFillRGBA ) ( int x, int y, int width, int height, int r, int g, int b, int a ); + int ( *pfnGetScreenInfo ) ( SCREENINFO *pscrinfo ); + void ( *pfnSetCrosshair ) ( HSPRITE hspr, wrect_t rc, int r, int g, int b ); + + // cvar handlers + struct cvar_s *( *pfnRegisterVariable ) ( char *szName, char *szValue, int flags ); + float ( *pfnGetCvarFloat ) ( char *szName ); + char* ( *pfnGetCvarString ) ( char *szName ); + + // command handlers + int ( *pfnAddCommand ) ( char *cmd_name, void (*function)(void) ); + int ( *pfnHookUserMsg ) ( char *szMsgName, pfnUserMsgHook pfn ); + int ( *pfnServerCmd ) ( char *szCmdString ); + int ( *pfnClientCmd ) ( char *szCmdString ); + + void ( *pfnGetPlayerInfo ) ( int ent_num, hud_player_info_t *pinfo ); + + // sound handlers + void ( *pfnPlaySoundByName ) ( char *szSound, float volume ); + void ( *pfnPlaySoundByIndex ) ( int iSound, float volume ); + + // vector helpers + void ( *pfnAngleVectors ) ( const float * vecAngles, float * forward, float * right, float * up ); + + // text message system + client_textmessage_t *( *pfnTextMessageGet ) ( const char *pName ); + int ( *pfnDrawCharacter ) ( int x, int y, int number, int r, int g, int b ); + int ( *pfnDrawConsoleString ) ( int x, int y, char *string ); + void ( *pfnDrawSetTextColor ) ( float r, float g, float b ); + void ( *pfnDrawConsoleStringLen )( const char *string, int *length, int *height ); + + void ( *pfnConsolePrint ) ( const char *string ); + void ( *pfnCenterPrint ) ( const char *string ); + + +// Added for user input processing + int ( *GetWindowCenterX ) ( void ); + int ( *GetWindowCenterY ) ( void ); + void ( *GetViewAngles ) ( float * ); + void ( *SetViewAngles ) ( float * ); + int ( *GetMaxClients ) ( void ); + void ( *Cvar_SetValue ) ( char *cvar, float value ); + + int (*Cmd_Argc) (void); + char *( *Cmd_Argv ) ( int arg ); + void ( *Con_Printf ) ( char *fmt, ... ); + void ( *Con_DPrintf ) ( char *fmt, ... ); + void ( *Con_NPrintf ) ( int pos, char *fmt, ... ); + void ( *Con_NXPrintf ) ( struct con_nprint_s *info, char *fmt, ... ); + + const char *( *PhysInfo_ValueForKey ) ( const char *key ); + const char *( *ServerInfo_ValueForKey )( const char *key ); + float ( *GetClientMaxspeed ) ( void ); + int ( *CheckParm ) ( char *parm, char **ppnext ); + void ( *Key_Event ) ( int key, int down ); + void ( *GetMousePosition ) ( int *mx, int *my ); + int ( *IsNoClipping ) ( void ); + + struct cl_entity_s *( *GetLocalPlayer ) ( void ); + struct cl_entity_s *( *GetViewModel ) ( void ); + struct cl_entity_s *( *GetEntityByIndex ) ( int idx ); + + float ( *GetClientTime ) ( void ); + void ( *V_CalcShake ) ( void ); + void ( *V_ApplyShake ) ( float *origin, float *angles, float factor ); + + int ( *PM_PointContents ) ( float *point, int *truecontents ); + int ( *PM_WaterEntity ) ( float *p ); + struct pmtrace_s *( *PM_TraceLine ) ( float *start, float *end, int flags, int usehull, int ignore_pe ); + + struct model_s *( *CL_LoadModel ) ( const char *modelname, int *index ); + int ( *CL_CreateVisibleEntity ) ( int type, struct cl_entity_s *ent ); + + const struct model_s * ( *GetSpritePointer ) ( HSPRITE hSprite ); + void ( *pfnPlaySoundByNameAtLocation ) ( char *szSound, float volume, float *origin ); + + unsigned short ( *pfnPrecacheEvent ) ( int type, const char* psz ); + void ( *pfnPlaybackEvent ) ( int flags, const struct edict_s *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); + void ( *pfnWeaponAnim ) ( int iAnim, int body ); + float ( *pfnRandomFloat ) ( float flLow, float flHigh ); + long ( *pfnRandomLong ) ( long lLow, long lHigh ); + void ( *pfnHookEvent ) ( char *name, void ( *pfnEvent )( struct event_args_s *args ) ); + int (*Con_IsVisible) (); + const char *( *pfnGetGameDirectory ) ( void ); + struct cvar_s *( *pfnGetCvarPointer ) ( const char *szName ); + const char *( *Key_LookupBinding ) ( const char *pBinding ); + const char *( *pfnGetLevelName ) ( void ); + void ( *pfnGetScreenFade ) ( struct screenfade_s *fade ); + void ( *pfnSetScreenFade ) ( struct screenfade_s *fade ); + void *( *VGui_GetPanel ) ( ); + void ( *VGui_ViewportPaintBackground ) (int extents[4]); + + byte* (*COM_LoadFile) ( char *path, int usehunk, int *pLength ); + char* (*COM_ParseFile) ( char *data, char *token ); + void (*COM_FreeFile) ( void *buffer ); + + struct triangleapi_s *pTriAPI; + struct efx_api_s *pEfxAPI; + struct event_api_s *pEventAPI; + struct demo_api_s *pDemoAPI; + struct net_api_s *pNetAPI; + struct IVoiceTweak_s *pVoiceTweak; + + // returns 1 if the client is a spectator only (connected to a proxy), 0 otherwise or 2 if in dev_overview mode + int ( *IsSpectateOnly ) ( void ); + struct model_s *( *LoadMapSprite ) ( const char *filename ); + + // file search functions + void ( *COM_AddAppDirectoryToSearchPath ) ( const char *pszBaseDir, const char *appName ); + int ( *COM_ExpandFilename) ( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ); + + // User info + // playerNum is in the range (1, MaxClients) + // returns NULL if player doesn't exit + // returns "" if no value is set + const char *( *PlayerInfo_ValueForKey )( int playerNum, const char *key ); + void ( *PlayerInfo_SetValueForKey )( const char *key, const char *value ); + + // Gets a unique ID for the specified player. This is the same even if you see the player on a different server. + // iPlayer is an entity index, so client 0 would use iPlayer=1. + // Returns false if there is no player on the server in the specified slot. + qboolean (*GetPlayerUniqueID)(int iPlayer, char playerID[16]); + + // TrackerID access + int (*GetTrackerIDForPlayer)(int playerSlot); + int (*GetPlayerForTrackerID)(int trackerID); + + // Same as pfnServerCmd, but the message goes in the unreliable stream so it can't clog the net stream + // (but it might not get there). + int ( *pfnServerCmdUnreliable )( char *szCmdString ); + + void ( *pfnGetMousePos )( struct tagPOINT *ppt ); + void ( *pfnSetMousePos )( int x, int y ); + void ( *pfnSetMouseEnable )( qboolean fEnable ); +} cl_enginefunc_t; + +#ifndef IN_BUTTONS_H +#include "in_buttons.h" +#endif + +#define CLDLL_INTERFACE_VERSION 7 + +extern void ClientDLL_Init( void ); // from cdll_int.c +extern void ClientDLL_Shutdown( void ); +extern void ClientDLL_HudInit( void ); +extern void ClientDLL_HudVidInit( void ); +extern void ClientDLL_UpdateClientData( void ); +extern void ClientDLL_Frame( double time ); +extern void ClientDLL_HudRedraw( int intermission ); +extern void ClientDLL_MoveClient( struct playermove_s *ppmove ); +extern void ClientDLL_ClientMoveInit( struct playermove_s *ppmove ); +extern char ClientDLL_ClientTextureType( char *name ); + +extern void ClientDLL_CreateMove( float frametime, struct usercmd_s *cmd, int active ); +extern void ClientDLL_ActivateMouse( void ); +extern void ClientDLL_DeactivateMouse( void ); +extern void ClientDLL_MouseEvent( int mstate ); +extern void ClientDLL_ClearStates( void ); +extern int ClientDLL_IsThirdPerson( void ); +extern void ClientDLL_GetCameraOffsets( float *ofs ); +extern int ClientDLL_GraphKeyDown( void ); +extern struct kbutton_s *ClientDLL_FindKey( const char *name ); +extern void ClientDLL_CAM_Think( void ); +extern void ClientDLL_IN_Accumulate( void ); +extern void ClientDLL_CalcRefdef( struct ref_params_s *pparams ); +extern int ClientDLL_AddEntity( int type, struct cl_entity_s *ent ); +extern void ClientDLL_CreateEntities( void ); + +extern void ClientDLL_DrawNormalTriangles( void ); +extern void ClientDLL_DrawTransparentTriangles( void ); +extern void ClientDLL_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity ); +extern void ClientDLL_PostRunCmd( struct local_state_s *from, struct local_state_s *to, struct usercmd_s *cmd, int runfuncs, double time, unsigned int random_seed ); +extern void ClientDLL_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client ); +extern void ClientDLL_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src ); +extern void ClientDLL_TxferPredictionData ( struct entity_state_s *ps, const struct entity_state_s *pps, struct clientdata_s *pcd, const struct clientdata_s *ppcd, struct weapon_data_s *wd, const struct weapon_data_s *pwd ); +extern void ClientDLL_ReadDemoBuffer( int size, unsigned char *buffer ); +extern int ClientDLL_ConnectionlessPacket( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); +extern int ClientDLL_GetHullBounds( int hullnumber, float *mins, float *maxs ); + +extern void ClientDLL_VGui_ConsolePrint(const char* text); + +extern int ClientDLL_Key_Event( int down, int keynum, const char *pszCurrentBinding ); +extern void ClientDLL_TempEntUpdate( double ft, double ct, double grav, struct tempent_s **ppFreeTE, struct tempent_s **ppActiveTE, int ( *addTEntity )( struct cl_entity_s *pEntity ), void ( *playTESound )( struct tempent_s *pTemp, float damp ) ); +extern struct cl_entity_s *ClientDLL_GetUserEntity( int index ); +extern void ClientDLL_VoiceStatus(int entindex, qboolean bTalking); +extern void ClientDLL_DirectorMessage( int iSize, void *pbuf ); + + +#ifdef __cplusplus +} +#endif + +#endif // CDLL_INT_H diff --git a/src/engine/custom.h b/src/engine/custom.h index dd7a70a..61fba7b 100644 --- a/src/engine/custom.h +++ b/src/engine/custom.h @@ -1,104 +1,104 @@ -/*** -* -* 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. -* -****/ -// Customization.h - -#ifndef CUSTOM_H -#define CUSTOM_H -#ifdef _WIN32 -#ifndef __MINGW32__ -#pragma once -#endif /* not __MINGW32__ */ -#endif - -#include "const.h" - -#define MAX_QPATH 64 // Must match value in quakedefs.h - -///////////////// -// Customization -// passed to pfnPlayerCustomization -// For automatic downloading. -typedef enum -{ - t_sound = 0, - t_skin, - t_model, - t_decal, - t_generic, - t_eventscript -} resourcetype_t; - -// Fake type for world -#define t_world 6 - -typedef struct -{ - int size; -} _resourceinfo_t; - -typedef struct resourceinfo_s -{ - _resourceinfo_t info[ 7 ]; -} resourceinfo_t; - -#define RES_FATALIFMISSING (1<<0) // Disconnect if we can't get this file. -#define RES_WASMISSING (1<<1) // Do we have the file locally, did we get it ok? -#define RES_CUSTOM (1<<2) // Is this resource one that corresponds to another player's customization - // or is it a server startup resource. -#define RES_REQUESTED (1<<3) // Already requested a download of this one -#define RES_PRECACHED (1<<4) // Already precached - -#include "crc.h" - -typedef struct resource_s -{ - char szFileName[MAX_QPATH]; // File name to download/precache. - resourcetype_t type; // t_sound, t_skin, t_model, t_decal. - int nIndex; // For t_decals - int nDownloadSize; // Size in Bytes if this must be downloaded. - unsigned char ucFlags; - -// For handling client to client resource propagation - unsigned char rgucMD5_hash[16]; // To determine if we already have it. - unsigned char playernum; // Which player index this resource is associated with, if it's a custom resource. - - unsigned char rguc_reserved[ 32 ]; // For future expansion - struct resource_s *pNext; // Next in chain. - struct resource_s *pPrev; -} resource_t; - -typedef struct customization_s -{ - qboolean bInUse; // Is this customization in use; - resource_t resource; // The resource_t for this customization - qboolean bTranslated; // Has the raw data been translated into a useable format? - // (e.g., raw decal .wad make into texture_t *) - int nUserData1; // Customization specific data - int nUserData2; // Customization specific data - void *pInfo; // Buffer that holds the data structure that references the data (e.g., the cachewad_t) - void *pBuffer; // Buffer that holds the data for the customization (the raw .wad data) - struct customization_s *pNext; // Next in chain -} customization_t; - -#define FCUST_FROMHPAK ( 1<<0 ) -#define FCUST_WIPEDATA ( 1<<1 ) -#define FCUST_IGNOREINIT ( 1<<2 ) - -void COM_ClearCustomizationList( struct customization_s *pHead, qboolean bCleanDecals); -qboolean COM_CreateCustomization( struct customization_s *pListHead, struct resource_s *pResource, int playernumber, int flags, - struct customization_s **pCustomization, int *nLumps ); -int COM_SizeofResourceList ( struct resource_s *pList, struct resourceinfo_s *ri ); - -#endif // CUSTOM_H +/*** +* +* 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. +* +****/ +// Customization.h + +#ifndef CUSTOM_H +#define CUSTOM_H +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma once +#endif /* not __MINGW32__ */ +#endif + +#include "const.h" + +#define MAX_QPATH 64 // Must match value in quakedefs.h + +///////////////// +// Customization +// passed to pfnPlayerCustomization +// For automatic downloading. +typedef enum +{ + t_sound = 0, + t_skin, + t_model, + t_decal, + t_generic, + t_eventscript +} resourcetype_t; + +// Fake type for world +#define t_world 6 + +typedef struct +{ + int size; +} _resourceinfo_t; + +typedef struct resourceinfo_s +{ + _resourceinfo_t info[ 7 ]; +} resourceinfo_t; + +#define RES_FATALIFMISSING (1<<0) // Disconnect if we can't get this file. +#define RES_WASMISSING (1<<1) // Do we have the file locally, did we get it ok? +#define RES_CUSTOM (1<<2) // Is this resource one that corresponds to another player's customization + // or is it a server startup resource. +#define RES_REQUESTED (1<<3) // Already requested a download of this one +#define RES_PRECACHED (1<<4) // Already precached + +#include "crc.h" + +typedef struct resource_s +{ + char szFileName[MAX_QPATH]; // File name to download/precache. + resourcetype_t type; // t_sound, t_skin, t_model, t_decal. + int nIndex; // For t_decals + int nDownloadSize; // Size in Bytes if this must be downloaded. + unsigned char ucFlags; + +// For handling client to client resource propagation + unsigned char rgucMD5_hash[16]; // To determine if we already have it. + unsigned char playernum; // Which player index this resource is associated with, if it's a custom resource. + + unsigned char rguc_reserved[ 32 ]; // For future expansion + struct resource_s *pNext; // Next in chain. + struct resource_s *pPrev; +} resource_t; + +typedef struct customization_s +{ + qboolean bInUse; // Is this customization in use; + resource_t resource; // The resource_t for this customization + qboolean bTranslated; // Has the raw data been translated into a useable format? + // (e.g., raw decal .wad make into texture_t *) + int nUserData1; // Customization specific data + int nUserData2; // Customization specific data + void *pInfo; // Buffer that holds the data structure that references the data (e.g., the cachewad_t) + void *pBuffer; // Buffer that holds the data for the customization (the raw .wad data) + struct customization_s *pNext; // Next in chain +} customization_t; + +#define FCUST_FROMHPAK ( 1<<0 ) +#define FCUST_WIPEDATA ( 1<<1 ) +#define FCUST_IGNOREINIT ( 1<<2 ) + +void COM_ClearCustomizationList( struct customization_s *pHead, qboolean bCleanDecals); +qboolean COM_CreateCustomization( struct customization_s *pListHead, struct resource_s *pResource, int playernumber, int flags, + struct customization_s **pCustomization, int *nLumps ); +int COM_SizeofResourceList ( struct resource_s *pList, struct resourceinfo_s *ri ); + +#endif // CUSTOM_H diff --git a/src/engine/customentity.h b/src/engine/customentity.h index 0895bee..2c4eb5e 100644 --- a/src/engine/customentity.h +++ b/src/engine/customentity.h @@ -1,38 +1,38 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef CUSTOMENTITY_H -#define CUSTOMENTITY_H - -// Custom Entities - -// Start/End Entity is encoded as 12 bits of entity index, and 4 bits of attachment (4:12) -#define BEAMENT_ENTITY(x) ((x)&0xFFF) -#define BEAMENT_ATTACHMENT(x) (((x)>>12)&0xF) - -// Beam types, encoded as a byte -enum -{ - BEAM_POINTS = 0, - BEAM_ENTPOINT, - BEAM_ENTS, - BEAM_HOSE, -}; - -#define BEAM_FSINE 0x10 -#define BEAM_FSOLID 0x20 -#define BEAM_FSHADEIN 0x40 -#define BEAM_FSHADEOUT 0x80 - -#endif //CUSTOMENTITY_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef CUSTOMENTITY_H +#define CUSTOMENTITY_H + +// Custom Entities + +// Start/End Entity is encoded as 12 bits of entity index, and 4 bits of attachment (4:12) +#define BEAMENT_ENTITY(x) ((x)&0xFFF) +#define BEAMENT_ATTACHMENT(x) (((x)>>12)&0xF) + +// Beam types, encoded as a byte +enum +{ + BEAM_POINTS = 0, + BEAM_ENTPOINT, + BEAM_ENTS, + BEAM_HOSE, +}; + +#define BEAM_FSINE 0x10 +#define BEAM_FSOLID 0x20 +#define BEAM_FSHADEIN 0x40 +#define BEAM_FSHADEOUT 0x80 + +#endif //CUSTOMENTITY_H diff --git a/src/engine/edict.h b/src/engine/edict.h index 27797b4..fa6285c 100644 --- a/src/engine/edict.h +++ b/src/engine/edict.h @@ -1,31 +1,31 @@ -#if !defined EDICT_H -#define EDICT_H -#ifdef _WIN32 -#ifndef __MINGW32__ -#pragma once -#endif /* not __MINGW32__ */ -#endif -#define MAX_ENT_LEAFS 48 - -#include "progdefs.h" - -struct edict_s -{ - qboolean free; - int serialnumber; - link_t area; // linked to a division node or leaf - - int headnode; // -1 to use normal leaf check - int num_leafs; - short leafnums[MAX_ENT_LEAFS]; - - float freetime; // sv.time when the object was freed - - void* pvPrivateData; // Alloced and freed by engine, used by DLLs - - entvars_t v; // C exported fields from progs - - // other fields from progs come immediately after -}; - -#endif +#if !defined EDICT_H +#define EDICT_H +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma once +#endif /* not __MINGW32__ */ +#endif +#define MAX_ENT_LEAFS 48 + +#include "progdefs.h" + +struct edict_s +{ + qboolean free; + int serialnumber; + link_t area; // linked to a division node or leaf + + int headnode; // -1 to use normal leaf check + int num_leafs; + short leafnums[MAX_ENT_LEAFS]; + + float freetime; // sv.time when the object was freed + + void* pvPrivateData; // Alloced and freed by engine, used by DLLs + + entvars_t v; // C exported fields from progs + + // other fields from progs come immediately after +}; + +#endif diff --git a/src/engine/eiface.h b/src/engine/eiface.h index 35f95a0..596aa9b 100644 --- a/src/engine/eiface.h +++ b/src/engine/eiface.h @@ -1,575 +1,575 @@ -/*** -* -* 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. -* -****/ -#ifndef EIFACE_H -#define EIFACE_H - -#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 -#else -#define DLLEXPORT /* */ -#endif - -typedef enum - { - 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 ). - } ALERT_TYPE; - -// 4-22-98 JOHN: added for use in pfnClientPrintf -typedef enum - { - print_console, - print_center, - print_chat, - } PRINT_TYPE; - -// For integrity checking of content on clients -typedef enum -{ - 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) -} FORCE_TYPE; - -// Returned by TraceLine -typedef struct - { - 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 - } TraceResult; - -// 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) (char* s); - int (*pfnPrecacheSound) (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) (char* s1, 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) (char* str); - void (*pfnServerExecute) (void); - void (*pfnClientCommand) (edict_t* pEdict, char* szFmt, ...); - void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); - void (*pfnLightStyle) (int style, 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, char *szFmt, ...); -#ifdef HLSDK_3_2_OLD_EIFACE - void (*pfnEngineFprintf) (FILE *pfile, char *szFmt, ...); - void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, long cb); -#else - void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...); - void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, int32 cb); -#endif - 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 ); -#ifdef HLSDK_3_2_OLD_EIFACE - unsigned long (*pfnFunctionFromName) ( const char *pName ); - const char *(*pfnNameForFunction) ( unsigned long function ); -#else - uint32 (*pfnFunctionFromName) ( const char *pName ); - const char *(*pfnNameForFunction) ( uint32 function ); -#endif - 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); -#ifdef HLSDK_3_2_OLD_EIFACE - long (*pfnRandomLong) (long lLow, long lHigh); -#else - int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); -#endif - 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) (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) (const 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, char *key); - void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value); - void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, char *key, char *value); - int (*pfnIsMapValid) (char *filename); - void (*pfnStaticDecal) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); - int (*pfnPrecacheGeneric) (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 ) ( const 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) ( 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, 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) ( 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) ( 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 **pchNextVal); - -#ifdef __METAMOD_BUILD__ - //extra (future updates) - void * extra_functions[16]; -#endif /*__METAMOD_BUILD__*/ -} 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 -#ifdef HLSDK_3_2_OLD_EIFACE - long fHandled; // out: DLL sets to true if key-value pair was understood -#else - int32 fHandled; // out: DLL sets to true if key-value pair was understood -#endif -} 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; - -#ifndef linux -#ifndef offsetof -#define offsetof(s,m) (size_t)&(((s *)0)->m) -#endif -#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; - -// Fixed MSVC compiling, by Nikolay "The Storm" Baklicharov. -#if defined _MSC_VER && _MSC_VER >= 1400 - #ifndef ARRAYSIZE - #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) - #endif -#else /* MSVC 8.0 */ - #ifndef ARRAYSIZE - #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) - #endif -#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 ); - - // Added 2005/08/11 (no SDK update): - void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); - - // Added 2005/11/21 (no SDK update): - // value is "Bad CVAR request" on failure (i.e that user is not connected or the cvar does not exist). - // value is "Bad Player" if invalid player edict. - 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 ); - -#endif /* EIFACE_H */ +/*** +* +* 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. +* +****/ +#ifndef EIFACE_H +#define EIFACE_H + +#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 +#else +#define DLLEXPORT /* */ +#endif + +typedef enum + { + 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 ). + } ALERT_TYPE; + +// 4-22-98 JOHN: added for use in pfnClientPrintf +typedef enum + { + print_console, + print_center, + print_chat, + } PRINT_TYPE; + +// For integrity checking of content on clients +typedef enum +{ + 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) +} FORCE_TYPE; + +// Returned by TraceLine +typedef struct + { + 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 + } TraceResult; + +// 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) (char* s); + int (*pfnPrecacheSound) (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) (char* s1, 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) (char* str); + void (*pfnServerExecute) (void); + void (*pfnClientCommand) (edict_t* pEdict, char* szFmt, ...); + void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count); + void (*pfnLightStyle) (int style, 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, char *szFmt, ...); +#ifdef HLSDK_3_2_OLD_EIFACE + void (*pfnEngineFprintf) (FILE *pfile, char *szFmt, ...); + void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, long cb); +#else + void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...); + void* (*pfnPvAllocEntPrivateData) (edict_t *pEdict, int32 cb); +#endif + 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 ); +#ifdef HLSDK_3_2_OLD_EIFACE + unsigned long (*pfnFunctionFromName) ( const char *pName ); + const char *(*pfnNameForFunction) ( unsigned long function ); +#else + uint32 (*pfnFunctionFromName) ( const char *pName ); + const char *(*pfnNameForFunction) ( uint32 function ); +#endif + 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); +#ifdef HLSDK_3_2_OLD_EIFACE + long (*pfnRandomLong) (long lLow, long lHigh); +#else + int32 (*pfnRandomLong) (int32 lLow, int32 lHigh); +#endif + 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) (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) (const 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, char *key); + void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value); + void (*pfnSetClientKeyValue) (int clientIndex, char *infobuffer, char *key, char *value); + int (*pfnIsMapValid) (char *filename); + void (*pfnStaticDecal) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); + int (*pfnPrecacheGeneric) (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 ) ( const 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) ( 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, 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) ( 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) ( 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 **pchNextVal); + +#ifdef __METAMOD_BUILD__ + //extra (future updates) + void * extra_functions[16]; +#endif /*__METAMOD_BUILD__*/ +} 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 +#ifdef HLSDK_3_2_OLD_EIFACE + long fHandled; // out: DLL sets to true if key-value pair was understood +#else + int32 fHandled; // out: DLL sets to true if key-value pair was understood +#endif +} 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; + +#ifndef linux +#ifndef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif +#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; + +// Fixed MSVC compiling, by Nikolay "The Storm" Baklicharov. +#if defined _MSC_VER && _MSC_VER >= 1400 + #ifndef ARRAYSIZE + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + #endif +#else /* MSVC 8.0 */ + #ifndef ARRAYSIZE + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + #endif +#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 ); + + // Added 2005/08/11 (no SDK update): + void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); + + // Added 2005/11/21 (no SDK update): + // value is "Bad CVAR request" on failure (i.e that user is not connected or the cvar does not exist). + // value is "Bad Player" if invalid player edict. + 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 ); + +#endif /* EIFACE_H */ diff --git a/src/engine/keydefs.h b/src/engine/keydefs.h index ef9b2fc..4adeeab 100644 --- a/src/engine/keydefs.h +++ b/src/engine/keydefs.h @@ -1,131 +1,131 @@ -//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= - -// keydefs.h -#ifndef KEYDEFS_H -#define KEYDEFS_H -#ifdef _WIN32 -#ifndef __MINGW32__ -#pragma once -#endif /* not __MINGW32__ */ -#endif - -// -// these are the key numbers that should be passed to Key_Event -// -#define K_TAB 9 -#define K_ENTER 13 -#define K_ESCAPE 27 -#define K_SPACE 32 - -// normal keys should be passed as lowercased ascii - -#define K_BACKSPACE 127 -#define K_UPARROW 128 -#define K_DOWNARROW 129 -#define K_LEFTARROW 130 -#define K_RIGHTARROW 131 - -#define K_ALT 132 -#define K_CTRL 133 -#define K_SHIFT 134 -#define K_F1 135 -#define K_F2 136 -#define K_F3 137 -#define K_F4 138 -#define K_F5 139 -#define K_F6 140 -#define K_F7 141 -#define K_F8 142 -#define K_F9 143 -#define K_F10 144 -#define K_F11 145 -#define K_F12 146 -#define K_INS 147 -#define K_DEL 148 -#define K_PGDN 149 -#define K_PGUP 150 -#define K_HOME 151 -#define K_END 152 - -#define K_KP_HOME 160 -#define K_KP_UPARROW 161 -#define K_KP_PGUP 162 -#define K_KP_LEFTARROW 163 -#define K_KP_5 164 -#define K_KP_RIGHTARROW 165 -#define K_KP_END 166 -#define K_KP_DOWNARROW 167 -#define K_KP_PGDN 168 -#define K_KP_ENTER 169 -#define K_KP_INS 170 -#define K_KP_DEL 171 -#define K_KP_SLASH 172 -#define K_KP_MINUS 173 -#define K_KP_PLUS 174 -#define K_CAPSLOCK 175 - - -// -// joystick buttons -// -#define K_JOY1 203 -#define K_JOY2 204 -#define K_JOY3 205 -#define K_JOY4 206 - -// -// aux keys are for multi-buttoned joysticks to generate so they can use -// the normal binding process -// -#define K_AUX1 207 -#define K_AUX2 208 -#define K_AUX3 209 -#define K_AUX4 210 -#define K_AUX5 211 -#define K_AUX6 212 -#define K_AUX7 213 -#define K_AUX8 214 -#define K_AUX9 215 -#define K_AUX10 216 -#define K_AUX11 217 -#define K_AUX12 218 -#define K_AUX13 219 -#define K_AUX14 220 -#define K_AUX15 221 -#define K_AUX16 222 -#define K_AUX17 223 -#define K_AUX18 224 -#define K_AUX19 225 -#define K_AUX20 226 -#define K_AUX21 227 -#define K_AUX22 228 -#define K_AUX23 229 -#define K_AUX24 230 -#define K_AUX25 231 -#define K_AUX26 232 -#define K_AUX27 233 -#define K_AUX28 234 -#define K_AUX29 235 -#define K_AUX30 236 -#define K_AUX31 237 -#define K_AUX32 238 -#define K_MWHEELDOWN 239 -#define K_MWHEELUP 240 - -#define K_PAUSE 255 - -// -// mouse buttons generate virtual keys -// -#define K_MOUSE1 241 -#define K_MOUSE2 242 -#define K_MOUSE3 243 -#define K_MOUSE4 244 -#define K_MOUSE5 245 - -#endif // KEYDEFS_H +//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============ +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +// keydefs.h +#ifndef KEYDEFS_H +#define KEYDEFS_H +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma once +#endif /* not __MINGW32__ */ +#endif + +// +// these are the key numbers that should be passed to Key_Event +// +#define K_TAB 9 +#define K_ENTER 13 +#define K_ESCAPE 27 +#define K_SPACE 32 + +// normal keys should be passed as lowercased ascii + +#define K_BACKSPACE 127 +#define K_UPARROW 128 +#define K_DOWNARROW 129 +#define K_LEFTARROW 130 +#define K_RIGHTARROW 131 + +#define K_ALT 132 +#define K_CTRL 133 +#define K_SHIFT 134 +#define K_F1 135 +#define K_F2 136 +#define K_F3 137 +#define K_F4 138 +#define K_F5 139 +#define K_F6 140 +#define K_F7 141 +#define K_F8 142 +#define K_F9 143 +#define K_F10 144 +#define K_F11 145 +#define K_F12 146 +#define K_INS 147 +#define K_DEL 148 +#define K_PGDN 149 +#define K_PGUP 150 +#define K_HOME 151 +#define K_END 152 + +#define K_KP_HOME 160 +#define K_KP_UPARROW 161 +#define K_KP_PGUP 162 +#define K_KP_LEFTARROW 163 +#define K_KP_5 164 +#define K_KP_RIGHTARROW 165 +#define K_KP_END 166 +#define K_KP_DOWNARROW 167 +#define K_KP_PGDN 168 +#define K_KP_ENTER 169 +#define K_KP_INS 170 +#define K_KP_DEL 171 +#define K_KP_SLASH 172 +#define K_KP_MINUS 173 +#define K_KP_PLUS 174 +#define K_CAPSLOCK 175 + + +// +// joystick buttons +// +#define K_JOY1 203 +#define K_JOY2 204 +#define K_JOY3 205 +#define K_JOY4 206 + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// +#define K_AUX1 207 +#define K_AUX2 208 +#define K_AUX3 209 +#define K_AUX4 210 +#define K_AUX5 211 +#define K_AUX6 212 +#define K_AUX7 213 +#define K_AUX8 214 +#define K_AUX9 215 +#define K_AUX10 216 +#define K_AUX11 217 +#define K_AUX12 218 +#define K_AUX13 219 +#define K_AUX14 220 +#define K_AUX15 221 +#define K_AUX16 222 +#define K_AUX17 223 +#define K_AUX18 224 +#define K_AUX19 225 +#define K_AUX20 226 +#define K_AUX21 227 +#define K_AUX22 228 +#define K_AUX23 229 +#define K_AUX24 230 +#define K_AUX25 231 +#define K_AUX26 232 +#define K_AUX27 233 +#define K_AUX28 234 +#define K_AUX29 235 +#define K_AUX30 236 +#define K_AUX31 237 +#define K_AUX32 238 +#define K_MWHEELDOWN 239 +#define K_MWHEELUP 240 + +#define K_PAUSE 255 + +// +// mouse buttons generate virtual keys +// +#define K_MOUSE1 241 +#define K_MOUSE2 242 +#define K_MOUSE3 243 +#define K_MOUSE4 244 +#define K_MOUSE5 245 + +#endif // KEYDEFS_H diff --git a/src/engine/progdefs.h b/src/engine/progdefs.h index 97caf2e..bb0e864 100644 --- a/src/engine/progdefs.h +++ b/src/engine/progdefs.h @@ -1,226 +1,226 @@ -/*** -* -* 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. -* -****/ -#ifndef PROGDEFS_H -#define PROGDEFS_H -#ifdef _WIN32 -#ifndef __MINGW32__ -#pragma once -#endif /* not __MINGW32__ */ -#endif - -typedef struct -{ - float time; - float frametime; - float force_retouch; - string_t mapname; - string_t startspot; - float deathmatch; - float coop; - float teamplay; - float serverflags; - float found_secrets; - vec3_t v_forward; - vec3_t v_up; - vec3_t v_right; - float trace_allsolid; - float trace_startsolid; - float trace_fraction; - vec3_t trace_endpos; - vec3_t trace_plane_normal; - float trace_plane_dist; - edict_t *trace_ent; - float trace_inopen; - float trace_inwater; - int trace_hitgroup; - int trace_flags; - int msg_entity; - int cdAudioTrack; - int maxClients; - int maxEntities; - const char *pStringBase; - - void *pSaveData; - vec3_t vecLandmarkOffset; -} globalvars_t; - - -typedef struct entvars_s -{ - string_t classname; - string_t globalname; - - vec3_t origin; - vec3_t oldorigin; - vec3_t velocity; - vec3_t basevelocity; - vec3_t clbasevelocity; // Base velocity that was passed in to server physics so - // client can predict conveyors correctly. Server zeroes it, so we need to store here, too. - vec3_t movedir; - - vec3_t angles; // Model angles - vec3_t avelocity; // angle velocity (degrees per second) - vec3_t punchangle; // auto-decaying view angle adjustment - vec3_t v_angle; // Viewing angle (player only) - - // For parametric entities - vec3_t endpos; - vec3_t startpos; - float impacttime; - float starttime; - - int fixangle; // 0:nothing, 1:force view angles, 2:add avelocity - float idealpitch; - float pitch_speed; - float ideal_yaw; - float yaw_speed; - - int modelindex; - string_t model; - - int viewmodel; // player's viewmodel - int weaponmodel; // what other players see - - vec3_t absmin; // BB max translated to world coord - vec3_t absmax; // BB max translated to world coord - vec3_t mins; // local BB min - vec3_t maxs; // local BB max - vec3_t size; // maxs - mins - - float ltime; - float nextthink; - - int movetype; - int solid; - - int skin; - int body; // sub-model selection for studiomodels - int effects; - - float gravity; // % of "normal" gravity - float friction; // inverse elasticity of MOVETYPE_BOUNCE - - int light_level; - - int sequence; // animation sequence - int gaitsequence; // movement animation sequence for player (0 for none) - float frame; // % playback position in animation sequences (0..255) - float animtime; // world time when frame was set - float framerate; // animation playback rate (-8x to 8x) - byte controller[4]; // bone controller setting (0..255) - byte blending[2]; // blending amount between sub-sequences (0..255) - - float scale; // sprite rendering scale (0..255) - - int rendermode; - float renderamt; - vec3_t rendercolor; - int renderfx; - - float health; - float frags; - int weapons; // bit mask for available weapons - float takedamage; - - int deadflag; - vec3_t view_ofs; // eye position - - int button; - int impulse; - - edict_t *chain; // Entity pointer when linked into a linked list - edict_t *dmg_inflictor; - edict_t *enemy; - edict_t *aiment; // entity pointer when MOVETYPE_FOLLOW - edict_t *owner; - edict_t *groundentity; - - int spawnflags; - int flags; - - int colormap; // lowbyte topcolor, highbyte bottomcolor - int team; - - float max_health; - float teleport_time; - float armortype; - float armorvalue; - int waterlevel; - int watertype; - - string_t target; - string_t targetname; - string_t netname; - string_t message; - - float dmg_take; - float dmg_save; - float dmg; - float dmgtime; - - string_t noise; - string_t noise1; - string_t noise2; - string_t noise3; - - float speed; - float air_finished; - float pain_finished; - float radsuit_finished; - - edict_t *pContainingEntity; - - int playerclass; - float maxspeed; - - float fov; - int weaponanim; - - int pushmsec; - - int bInDuck; - int flTimeStepSound; - int flSwimTime; - int flDuckTime; - int iStepLeft; - float flFallVelocity; - - int gamestate; - - int oldbuttons; - - int groupinfo; - - // For mods - int iuser1; - int iuser2; - int iuser3; - int iuser4; - float fuser1; - float fuser2; - float fuser3; - float fuser4; - vec3_t vuser1; - vec3_t vuser2; - vec3_t vuser3; - vec3_t vuser4; - edict_t *euser1; - edict_t *euser2; - edict_t *euser3; - edict_t *euser4; -} entvars_t; - - -#endif // PROGDEFS_H +/*** +* +* 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. +* +****/ +#ifndef PROGDEFS_H +#define PROGDEFS_H +#ifdef _WIN32 +#ifndef __MINGW32__ +#pragma once +#endif /* not __MINGW32__ */ +#endif + +typedef struct +{ + float time; + float frametime; + float force_retouch; + string_t mapname; + string_t startspot; + float deathmatch; + float coop; + float teamplay; + float serverflags; + float found_secrets; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + edict_t *trace_ent; + float trace_inopen; + float trace_inwater; + int trace_hitgroup; + int trace_flags; + int msg_entity; + int cdAudioTrack; + int maxClients; + int maxEntities; + const char *pStringBase; + + void *pSaveData; + vec3_t vecLandmarkOffset; +} globalvars_t; + + +typedef struct entvars_s +{ + string_t classname; + string_t globalname; + + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t basevelocity; + vec3_t clbasevelocity; // Base velocity that was passed in to server physics so + // client can predict conveyors correctly. Server zeroes it, so we need to store here, too. + vec3_t movedir; + + vec3_t angles; // Model angles + vec3_t avelocity; // angle velocity (degrees per second) + vec3_t punchangle; // auto-decaying view angle adjustment + vec3_t v_angle; // Viewing angle (player only) + + // For parametric entities + vec3_t endpos; + vec3_t startpos; + float impacttime; + float starttime; + + int fixangle; // 0:nothing, 1:force view angles, 2:add avelocity + float idealpitch; + float pitch_speed; + float ideal_yaw; + float yaw_speed; + + int modelindex; + string_t model; + + int viewmodel; // player's viewmodel + int weaponmodel; // what other players see + + vec3_t absmin; // BB max translated to world coord + vec3_t absmax; // BB max translated to world coord + vec3_t mins; // local BB min + vec3_t maxs; // local BB max + vec3_t size; // maxs - mins + + float ltime; + float nextthink; + + int movetype; + int solid; + + int skin; + int body; // sub-model selection for studiomodels + int effects; + + float gravity; // % of "normal" gravity + float friction; // inverse elasticity of MOVETYPE_BOUNCE + + int light_level; + + int sequence; // animation sequence + int gaitsequence; // movement animation sequence for player (0 for none) + float frame; // % playback position in animation sequences (0..255) + float animtime; // world time when frame was set + float framerate; // animation playback rate (-8x to 8x) + byte controller[4]; // bone controller setting (0..255) + byte blending[2]; // blending amount between sub-sequences (0..255) + + float scale; // sprite rendering scale (0..255) + + int rendermode; + float renderamt; + vec3_t rendercolor; + int renderfx; + + float health; + float frags; + int weapons; // bit mask for available weapons + float takedamage; + + int deadflag; + vec3_t view_ofs; // eye position + + int button; + int impulse; + + edict_t *chain; // Entity pointer when linked into a linked list + edict_t *dmg_inflictor; + edict_t *enemy; + edict_t *aiment; // entity pointer when MOVETYPE_FOLLOW + edict_t *owner; + edict_t *groundentity; + + int spawnflags; + int flags; + + int colormap; // lowbyte topcolor, highbyte bottomcolor + int team; + + float max_health; + float teleport_time; + float armortype; + float armorvalue; + int waterlevel; + int watertype; + + string_t target; + string_t targetname; + string_t netname; + string_t message; + + float dmg_take; + float dmg_save; + float dmg; + float dmgtime; + + string_t noise; + string_t noise1; + string_t noise2; + string_t noise3; + + float speed; + float air_finished; + float pain_finished; + float radsuit_finished; + + edict_t *pContainingEntity; + + int playerclass; + float maxspeed; + + float fov; + int weaponanim; + + int pushmsec; + + int bInDuck; + int flTimeStepSound; + int flSwimTime; + int flDuckTime; + int iStepLeft; + float flFallVelocity; + + int gamestate; + + int oldbuttons; + + int groupinfo; + + // For mods + int iuser1; + int iuser2; + int iuser3; + int iuser4; + float fuser1; + float fuser2; + float fuser3; + float fuser4; + vec3_t vuser1; + vec3_t vuser2; + vec3_t vuser3; + vec3_t vuser4; + edict_t *euser1; + edict_t *euser2; + edict_t *euser3; + edict_t *euser4; +} entvars_t; + + +#endif // PROGDEFS_H diff --git a/src/engine/progs.h b/src/engine/progs.h index fe4796e..1d1f885 100644 --- a/src/engine/progs.h +++ b/src/engine/progs.h @@ -1,82 +1,82 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef PROGS_H -#define PROGS_H - -#include "progdefs.h" - -// 16 simultaneous events, max -#define MAX_EVENT_QUEUE 64 - -#define DEFAULT_EVENT_RESENDS 1 - -#include "event_flags.h" - -typedef struct event_info_s event_info_t; - -#include "event_args.h" - -struct event_info_s -{ - unsigned short index; // 0 implies not in use - - short packet_index; // Use data from state info for entity in delta_packet . -1 implies separate info based on event - // parameter signature - short entity_index; // The edict this event is associated with - - float fire_time; // if non-zero, the time when the event should be fired ( fixed up on the client ) - - event_args_t args; - -// CLIENT ONLY - int flags; // Reliable or not, etc. - -}; - -typedef struct event_state_s event_state_t; - -struct event_state_s -{ - struct event_info_s ei[ MAX_EVENT_QUEUE ]; -}; - -#if !defined( ENTITY_STATEH ) -#include "entity_state.h" -#endif - -#if !defined( EDICT_H ) -#include "edict.h" -#endif - -#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) -#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) - -//============================================================================ - -extern char *pr_strings; -extern globalvars_t gGlobalVariables; - -//============================================================================ - -edict_t *ED_Alloc (void); -void ED_Free (edict_t *ed); -void ED_LoadFromFile (char *data); - -edict_t *EDICT_NUM(int n); -int NUM_FOR_EDICT(const edict_t *e); - -#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) - -#endif // PROGS_H +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef PROGS_H +#define PROGS_H + +#include "progdefs.h" + +// 16 simultaneous events, max +#define MAX_EVENT_QUEUE 64 + +#define DEFAULT_EVENT_RESENDS 1 + +#include "event_flags.h" + +typedef struct event_info_s event_info_t; + +#include "event_args.h" + +struct event_info_s +{ + unsigned short index; // 0 implies not in use + + short packet_index; // Use data from state info for entity in delta_packet . -1 implies separate info based on event + // parameter signature + short entity_index; // The edict this event is associated with + + float fire_time; // if non-zero, the time when the event should be fired ( fixed up on the client ) + + event_args_t args; + +// CLIENT ONLY + int flags; // Reliable or not, etc. + +}; + +typedef struct event_state_s event_state_t; + +struct event_state_s +{ + struct event_info_s ei[ MAX_EVENT_QUEUE ]; +}; + +#if !defined( ENTITY_STATEH ) +#include "entity_state.h" +#endif + +#if !defined( EDICT_H ) +#include "edict.h" +#endif + +#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) +#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) + +//============================================================================ + +extern char *pr_strings; +extern globalvars_t gGlobalVariables; + +//============================================================================ + +edict_t *ED_Alloc (void); +void ED_Free (edict_t *ed); +void ED_LoadFromFile (char *data); + +edict_t *EDICT_NUM(int n); +int NUM_FOR_EDICT(const edict_t *e); + +#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) + +#endif // PROGS_H diff --git a/src/engine/shake.h b/src/engine/shake.h index a18ddc2..8e405db 100644 --- a/src/engine/shake.h +++ b/src/engine/shake.h @@ -1,55 +1,55 @@ -/*** -* -* Copyright (c) 1996-2002, 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. -* -****/ -#ifndef SHAKE_H -#define SHAKE_H - -// Screen / View effects - -// screen shake -extern int gmsgShake; - -// This structure is sent over the net to describe a screen shake event -typedef struct -{ - unsigned short amplitude; // FIXED 4.12 amount of shake - unsigned short duration; // FIXED 4.12 seconds duration - unsigned short frequency; // FIXED 8.8 noise frequency (low frequency is a jerk,high frequency is a rumble) -} ScreenShake; - -extern void V_ApplyShake( float *origin, float *angles, float factor ); -extern void V_CalcShake( void ); -extern int V_ScreenShake( const char *pszName, int iSize, void *pbuf ); -extern int V_ScreenFade( const char *pszName, int iSize, void *pbuf ); - - -// Fade in/out -extern int gmsgFade; - -#define FFADE_IN 0x0000 // Just here so we don't pass 0 into the function -#define FFADE_OUT 0x0001 // Fade out (not in) -#define FFADE_MODULATE 0x0002 // Modulate (don't blend) -#define FFADE_STAYOUT 0x0004 // ignores the duration, stays faded out until new ScreenFade message received - -// This structure is sent over the net to describe a screen fade event -typedef struct -{ - unsigned short duration; // FIXED 4.12 seconds duration - unsigned short holdTime; // FIXED 4.12 seconds duration until reset (fade & hold) - short fadeFlags; // flags - byte r, g, b, a; // fade to color ( max alpha ) -} ScreenFade; - -#endif // SHAKE_H - +/*** +* +* Copyright (c) 1996-2002, 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. +* +****/ +#ifndef SHAKE_H +#define SHAKE_H + +// Screen / View effects + +// screen shake +extern int gmsgShake; + +// This structure is sent over the net to describe a screen shake event +typedef struct +{ + unsigned short amplitude; // FIXED 4.12 amount of shake + unsigned short duration; // FIXED 4.12 seconds duration + unsigned short frequency; // FIXED 8.8 noise frequency (low frequency is a jerk,high frequency is a rumble) +} ScreenShake; + +extern void V_ApplyShake( float *origin, float *angles, float factor ); +extern void V_CalcShake( void ); +extern int V_ScreenShake( const char *pszName, int iSize, void *pbuf ); +extern int V_ScreenFade( const char *pszName, int iSize, void *pbuf ); + + +// Fade in/out +extern int gmsgFade; + +#define FFADE_IN 0x0000 // Just here so we don't pass 0 into the function +#define FFADE_OUT 0x0001 // Fade out (not in) +#define FFADE_MODULATE 0x0002 // Modulate (don't blend) +#define FFADE_STAYOUT 0x0004 // ignores the duration, stays faded out until new ScreenFade message received + +// This structure is sent over the net to describe a screen fade event +typedef struct +{ + unsigned short duration; // FIXED 4.12 seconds duration + unsigned short holdTime; // FIXED 4.12 seconds duration until reset (fade & hold) + short fadeFlags; // flags + byte r, g, b, a; // fade to color ( max alpha ) +} ScreenFade; + +#endif // SHAKE_H + diff --git a/src/engine/studio.h b/src/engine/studio.h index b0b9960..53479c3 100644 --- a/src/engine/studio.h +++ b/src/engine/studio.h @@ -1,362 +1,362 @@ -/*** -* -* 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. -* -****/ - - - - -#ifndef _STUDIO_H_ -#define _STUDIO_H_ - -/* -============================================================================== - -STUDIO MODELS - -Studio models are position independent, so the cache manager can move them. -============================================================================== -*/ - - -#define MAXSTUDIOTRIANGLES 20000 // TODO: tune this -#define MAXSTUDIOVERTS 2048 // TODO: tune this -#define MAXSTUDIOSEQUENCES 256 // total animation sequences -#define MAXSTUDIOSKINS 100 // total textures -#define MAXSTUDIOSRCBONES 512 // bones allowed at source movement -#define MAXSTUDIOBONES 128 // total bones actually used -#define MAXSTUDIOMODELS 32 // sub-models per model -#define MAXSTUDIOBODYPARTS 32 -#define MAXSTUDIOGROUPS 16 -#define MAXSTUDIOANIMATIONS 512 // per sequence -#define MAXSTUDIOMESHES 256 -#define MAXSTUDIOEVENTS 1024 -#define MAXSTUDIOPIVOTS 256 -#define MAXSTUDIOCONTROLLERS 8 - -typedef struct -{ - int id; - int version; - - char name[64]; - int length; - - vec3_t eyeposition; // ideal eye position - vec3_t min; // ideal movement hull size - vec3_t max; - - vec3_t bbmin; // clipping bounding box - vec3_t bbmax; - - int flags; - - int numbones; // bones - int boneindex; - - int numbonecontrollers; // bone controllers - int bonecontrollerindex; - - int numhitboxes; // complex bounding boxes - int hitboxindex; - - int numseq; // animation sequences - int seqindex; - - int numseqgroups; // demand loaded sequences - int seqgroupindex; - - int numtextures; // raw textures - int textureindex; - int texturedataindex; - - int numskinref; // replaceable textures - int numskinfamilies; - int skinindex; - - int numbodyparts; - int bodypartindex; - - int numattachments; // queryable attachable points - int attachmentindex; - - int soundtable; - int soundindex; - int soundgroups; - int soundgroupindex; - - int numtransitions; // animation node to animation node transition graph - int transitionindex; -} studiohdr_t; - -// header for demand loaded sequence group data -typedef struct -{ - int id; - int version; - - char name[64]; - int length; -} studioseqhdr_t; - -// bones -typedef struct -{ - char name[32]; // bone name for symbolic links - int parent; // parent bone - int flags; // ?? - int bonecontroller[6]; // bone controller index, -1 == none - float value[6]; // default DoF values - float scale[6]; // scale for delta DoF values -} mstudiobone_t; - - -// bone controllers -typedef struct -{ - int bone; // -1 == 0 - int type; // X, Y, Z, XR, YR, ZR, M - float start; - float end; - int rest; // byte index value at rest - int index; // 0-3 user set controller, 4 mouth -} mstudiobonecontroller_t; - -// intersection boxes -typedef struct -{ - int bone; - int group; // intersection group - vec3_t bbmin; // bounding box - vec3_t bbmax; -} mstudiobbox_t; - -#if !defined( CACHE_USER ) && !defined( QUAKEDEF_H ) -#define CACHE_USER -typedef struct cache_user_s -{ - void *data; -} cache_user_t; -#endif - -// demand loaded sequence groups -typedef struct -{ - char label[32]; // textual name - char name[64]; // file name - cache_user_t cache; // cache index pointer - int data; // hack for group 0 -} mstudioseqgroup_t; - -// sequence descriptions -typedef struct -{ - char label[32]; // sequence label - - float fps; // frames per second - int flags; // looping/non-looping flags - - int activity; - int actweight; - - int numevents; - int eventindex; - - int numframes; // number of frames per sequence - - int numpivots; // number of foot pivots - int pivotindex; - - int motiontype; - int motionbone; - vec3_t linearmovement; - int automoveposindex; - int automoveangleindex; - - vec3_t bbmin; // per sequence bounding box - vec3_t bbmax; - - int numblends; - int animindex; // mstudioanim_t pointer relative to start of sequence group data - // [blend][bone][X, Y, Z, XR, YR, ZR] - - int blendtype[2]; // X, Y, Z, XR, YR, ZR - float blendstart[2]; // starting value - float blendend[2]; // ending value - int blendparent; - - int seqgroup; // sequence group for demand loading - - int entrynode; // transition node at entry - int exitnode; // transition node at exit - int nodeflags; // transition rules - - int nextseq; // auto advancing sequences -} mstudioseqdesc_t; - -// events -#include "studio_event.h" -/* -typedef struct -{ - int frame; - int event; - int type; - char options[64]; -} mstudioevent_t; -*/ - -// pivots -typedef struct -{ - vec3_t org; // pivot point - int start; - int end; -} mstudiopivot_t; - -// attachment -typedef struct -{ - char name[32]; - int type; - int bone; - vec3_t org; // attachment point - vec3_t vectors[3]; -} mstudioattachment_t; - -typedef struct -{ - unsigned short offset[6]; -} mstudioanim_t; - -// animation frames -typedef union -{ - struct { - byte valid; - byte total; - } num; - short value; -} mstudioanimvalue_t; - - - -// body part index -typedef struct -{ - char name[64]; - int nummodels; - int base; - int modelindex; // index into models array -} mstudiobodyparts_t; - - - -// skin info -typedef struct -{ - char name[64]; - int flags; - int width; - int height; - int index; -} mstudiotexture_t; - - -// skin families -// short index[skinfamilies][skinref] - -// studio models -typedef struct -{ - char name[64]; - - int type; - - float boundingradius; - - int nummesh; - int meshindex; - - int numverts; // number of unique vertices - int vertinfoindex; // vertex bone info - int vertindex; // vertex vec3_t - int numnorms; // number of unique surface normals - int norminfoindex; // normal bone info - int normindex; // normal vec3_t - - int numgroups; // deformation groups - int groupindex; -} mstudiomodel_t; - - -// vec3_t boundingbox[model][bone][2]; // complex intersection info - - -// meshes -typedef struct -{ - int numtris; - int triindex; - int skinref; - int numnorms; // per mesh normals - int normindex; // normal vec3_t -} mstudiomesh_t; - -// triangles -#if 0 -typedef struct -{ - short vertindex; // index into vertex array - short normindex; // index into normal array - short s,t; // s,t position on skin -} mstudiotrivert_t; -#endif - -// lighting options -#define STUDIO_NF_FLATSHADE 0x0001 -#define STUDIO_NF_CHROME 0x0002 -#define STUDIO_NF_FULLBRIGHT 0x0004 - -// motion flags -#define STUDIO_X 0x0001 -#define STUDIO_Y 0x0002 -#define STUDIO_Z 0x0004 -#define STUDIO_XR 0x0008 -#define STUDIO_YR 0x0010 -#define STUDIO_ZR 0x0020 -#define STUDIO_LX 0x0040 -#define STUDIO_LY 0x0080 -#define STUDIO_LZ 0x0100 -#define STUDIO_AX 0x0200 -#define STUDIO_AY 0x0400 -#define STUDIO_AZ 0x0800 -#define STUDIO_AXR 0x1000 -#define STUDIO_AYR 0x2000 -#define STUDIO_AZR 0x4000 -#define STUDIO_TYPES 0x7FFF -#define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance - -// sequence flags -#define STUDIO_LOOPING 0x0001 - -// bone flags -#define STUDIO_HAS_NORMALS 0x0001 -#define STUDIO_HAS_VERTICES 0x0002 -#define STUDIO_HAS_BBOX 0x0004 -#define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them - -#define RAD_TO_STUDIO (32768.0/M_PI) -#define STUDIO_TO_RAD (M_PI/32768.0) - -#endif +/*** +* +* 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. +* +****/ + + + + +#ifndef _STUDIO_H_ +#define _STUDIO_H_ + +/* +============================================================================== + +STUDIO MODELS + +Studio models are position independent, so the cache manager can move them. +============================================================================== +*/ + + +#define MAXSTUDIOTRIANGLES 20000 // TODO: tune this +#define MAXSTUDIOVERTS 2048 // TODO: tune this +#define MAXSTUDIOSEQUENCES 256 // total animation sequences +#define MAXSTUDIOSKINS 100 // total textures +#define MAXSTUDIOSRCBONES 512 // bones allowed at source movement +#define MAXSTUDIOBONES 128 // total bones actually used +#define MAXSTUDIOMODELS 32 // sub-models per model +#define MAXSTUDIOBODYPARTS 32 +#define MAXSTUDIOGROUPS 16 +#define MAXSTUDIOANIMATIONS 512 // per sequence +#define MAXSTUDIOMESHES 256 +#define MAXSTUDIOEVENTS 1024 +#define MAXSTUDIOPIVOTS 256 +#define MAXSTUDIOCONTROLLERS 8 + +typedef struct +{ + int id; + int version; + + char name[64]; + int length; + + vec3_t eyeposition; // ideal eye position + vec3_t min; // ideal movement hull size + vec3_t max; + + vec3_t bbmin; // clipping bounding box + vec3_t bbmax; + + int flags; + + int numbones; // bones + int boneindex; + + int numbonecontrollers; // bone controllers + int bonecontrollerindex; + + int numhitboxes; // complex bounding boxes + int hitboxindex; + + int numseq; // animation sequences + int seqindex; + + int numseqgroups; // demand loaded sequences + int seqgroupindex; + + int numtextures; // raw textures + int textureindex; + int texturedataindex; + + int numskinref; // replaceable textures + int numskinfamilies; + int skinindex; + + int numbodyparts; + int bodypartindex; + + int numattachments; // queryable attachable points + int attachmentindex; + + int soundtable; + int soundindex; + int soundgroups; + int soundgroupindex; + + int numtransitions; // animation node to animation node transition graph + int transitionindex; +} studiohdr_t; + +// header for demand loaded sequence group data +typedef struct +{ + int id; + int version; + + char name[64]; + int length; +} studioseqhdr_t; + +// bones +typedef struct +{ + char name[32]; // bone name for symbolic links + int parent; // parent bone + int flags; // ?? + int bonecontroller[6]; // bone controller index, -1 == none + float value[6]; // default DoF values + float scale[6]; // scale for delta DoF values +} mstudiobone_t; + + +// bone controllers +typedef struct +{ + int bone; // -1 == 0 + int type; // X, Y, Z, XR, YR, ZR, M + float start; + float end; + int rest; // byte index value at rest + int index; // 0-3 user set controller, 4 mouth +} mstudiobonecontroller_t; + +// intersection boxes +typedef struct +{ + int bone; + int group; // intersection group + vec3_t bbmin; // bounding box + vec3_t bbmax; +} mstudiobbox_t; + +#if !defined( CACHE_USER ) && !defined( QUAKEDEF_H ) +#define CACHE_USER +typedef struct cache_user_s +{ + void *data; +} cache_user_t; +#endif + +// demand loaded sequence groups +typedef struct +{ + char label[32]; // textual name + char name[64]; // file name + cache_user_t cache; // cache index pointer + int data; // hack for group 0 +} mstudioseqgroup_t; + +// sequence descriptions +typedef struct +{ + char label[32]; // sequence label + + float fps; // frames per second + int flags; // looping/non-looping flags + + int activity; + int actweight; + + int numevents; + int eventindex; + + int numframes; // number of frames per sequence + + int numpivots; // number of foot pivots + int pivotindex; + + int motiontype; + int motionbone; + vec3_t linearmovement; + int automoveposindex; + int automoveangleindex; + + vec3_t bbmin; // per sequence bounding box + vec3_t bbmax; + + int numblends; + int animindex; // mstudioanim_t pointer relative to start of sequence group data + // [blend][bone][X, Y, Z, XR, YR, ZR] + + int blendtype[2]; // X, Y, Z, XR, YR, ZR + float blendstart[2]; // starting value + float blendend[2]; // ending value + int blendparent; + + int seqgroup; // sequence group for demand loading + + int entrynode; // transition node at entry + int exitnode; // transition node at exit + int nodeflags; // transition rules + + int nextseq; // auto advancing sequences +} mstudioseqdesc_t; + +// events +#include "studio_event.h" +/* +typedef struct +{ + int frame; + int event; + int type; + char options[64]; +} mstudioevent_t; +*/ + +// pivots +typedef struct +{ + vec3_t org; // pivot point + int start; + int end; +} mstudiopivot_t; + +// attachment +typedef struct +{ + char name[32]; + int type; + int bone; + vec3_t org; // attachment point + vec3_t vectors[3]; +} mstudioattachment_t; + +typedef struct +{ + unsigned short offset[6]; +} mstudioanim_t; + +// animation frames +typedef union +{ + struct { + byte valid; + byte total; + } num; + short value; +} mstudioanimvalue_t; + + + +// body part index +typedef struct +{ + char name[64]; + int nummodels; + int base; + int modelindex; // index into models array +} mstudiobodyparts_t; + + + +// skin info +typedef struct +{ + char name[64]; + int flags; + int width; + int height; + int index; +} mstudiotexture_t; + + +// skin families +// short index[skinfamilies][skinref] + +// studio models +typedef struct +{ + char name[64]; + + int type; + + float boundingradius; + + int nummesh; + int meshindex; + + int numverts; // number of unique vertices + int vertinfoindex; // vertex bone info + int vertindex; // vertex vec3_t + int numnorms; // number of unique surface normals + int norminfoindex; // normal bone info + int normindex; // normal vec3_t + + int numgroups; // deformation groups + int groupindex; +} mstudiomodel_t; + + +// vec3_t boundingbox[model][bone][2]; // complex intersection info + + +// meshes +typedef struct +{ + int numtris; + int triindex; + int skinref; + int numnorms; // per mesh normals + int normindex; // normal vec3_t +} mstudiomesh_t; + +// triangles +#if 0 +typedef struct +{ + short vertindex; // index into vertex array + short normindex; // index into normal array + short s,t; // s,t position on skin +} mstudiotrivert_t; +#endif + +// lighting options +#define STUDIO_NF_FLATSHADE 0x0001 +#define STUDIO_NF_CHROME 0x0002 +#define STUDIO_NF_FULLBRIGHT 0x0004 + +// motion flags +#define STUDIO_X 0x0001 +#define STUDIO_Y 0x0002 +#define STUDIO_Z 0x0004 +#define STUDIO_XR 0x0008 +#define STUDIO_YR 0x0010 +#define STUDIO_ZR 0x0020 +#define STUDIO_LX 0x0040 +#define STUDIO_LY 0x0080 +#define STUDIO_LZ 0x0100 +#define STUDIO_AX 0x0200 +#define STUDIO_AY 0x0400 +#define STUDIO_AZ 0x0800 +#define STUDIO_AXR 0x1000 +#define STUDIO_AYR 0x2000 +#define STUDIO_AZR 0x4000 +#define STUDIO_TYPES 0x7FFF +#define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance + +// sequence flags +#define STUDIO_LOOPING 0x0001 + +// bone flags +#define STUDIO_HAS_NORMALS 0x0001 +#define STUDIO_HAS_VERTICES 0x0002 +#define STUDIO_HAS_BBOX 0x0004 +#define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them + +#define RAD_TO_STUDIO (32768.0/M_PI) +#define STUDIO_TO_RAD (M_PI/32768.0) + +#endif diff --git a/src/metamod/api_hook.h b/src/metamod/api_hook.h index bdebf36..d2532f1 100644 --- a/src/metamod/api_hook.h +++ b/src/metamod/api_hook.h @@ -1,397 +1,397 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 API_HOOK_H -#define API_HOOK_H - -#include "ret_type.h" -#include "api_info.h" -#include "meta_api.h" -#include "osdep.h" //OPEN_ARGS - -// Compine 4 parts for single name -#define _COMBINE4(w,x,y,z) w##x##y##z -#define _COMBINE2(x,y) x##y - -// simplified 'void' version of main hook function -void DLLINTERNAL main_hook_function_void(unsigned int api_info_offset, enum_api_t api, unsigned int func_offset, const void * packed_args); - -// full return typed version of main hook function -void * DLLINTERNAL main_hook_function(const class_ret_t ret_init, unsigned int api_info_offset, enum_api_t api, unsigned int func_offset, const void * packed_args); - -// -// API function args structures/classes -// -#define API_PACK_ARGS(type, args) \ - _COMBINE2(pack_args_type_, type) packed_args args; - -#define PACK_ARGS_CLASS_HEADER(type, constructor_args) \ - class _COMBINE2(pack_args_type_, type) : public class_metamod_new { \ - public: inline _COMBINE2(pack_args_type_, type) constructor_args - -#define PACK_ARGS_END }; - -#define VOID_ARG 0 - -PACK_ARGS_CLASS_HEADER(void, (int)) {}; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(i, (int _i1)): i1(_i1) {}; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2i, (int _i1, int _i2)): i1(_i1), i2(_i2) {}; - int i1,i2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(3i, (int _i1, int _i2, int _i3)): i1(_i1), i2(_i2), i3(_i3) {}; - int i1,i2,i3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(ui, (unsigned int _ui1)): ui1(_ui1) {}; - unsigned int ui1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(ul, (unsigned long _ul1)): ul1(_ul1) {}; - unsigned long ul1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(f, (float _f1)): f1(_f1) {}; - float f1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2f, (float _f1, float _f2)): f1(_f1), f2(_f2) {}; - float f1,f2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p, (const void *_p1)): p1(_p1) {}; - const void *p1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2p, (const void *_p1, const void *_p2)): p1(_p1), p2(_p2) {}; - const void *p1,*p2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(3p, (const void *_p1, const void *_p2, const void *_p3)): p1(_p1), p2(_p2), p3(_p3) {}; - const void *p1,*p2,*p3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(4p, (const void *_p1, const void *_p2, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), p3(_p3), p4(_p4) {}; - const void *p1,*p2,*p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pV, (const void *_p1, const void *_p2, const void *_str)): p1(_p1), p2(_p2), str(_str) {}; - const void *p1,*p2,*str; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(ipV, (int _i1, const void *_p1, const void *_str)): i1(_i1), p1(_p1), str(_str) {}; - int i1; - const void *p1,*str; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2i2p, (int _i1, int _i2, const void *_p1, const void *_p2)): i1(_i1), i2(_i2), p1(_p1), p2(_p2) {}; - int i1,i2; - const void *p1,*p2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2p2f, (const void *_p1, const void *_p2, float _f1, float _f2)): p1(_p1), p2(_p2), f1(_f1), f2(_f2) {}; - const void *p1,*p2; - float f1,f2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2p2i2p, (const void *_p1, const void *_p2, int _i1, int _i2, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), i2(_i2), p3(_p3), p4(_p4) {}; - const void *p1,*p2; - int i1,i2; - const void *p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2p3fus2uc, (const void *_p1, const void *_p2, float _f1, float _f2, float _f3, unsigned short _us1, unsigned char _uc1, unsigned char _uc2)): p1(_p1), p2(_p2), f1(_f1), f2(_f2), f3(_f3), us1(_us1), uc1(_uc1), uc2(_uc2) {}; - const void *p1,*p2; - float f1,f2,f3; - unsigned int us1; - unsigned int uc1,uc2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pf, (const void *_p1, const void *_p2, float _f1)): p1(_p1), p2(_p2), f1(_f1) {}; - const void *p1,*p2; - float f1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pfi, (const void *_p1, const void *_p2, float _f1, int _i1)): p1(_p1), p2(_p2), f1(_f1), i1(_i1) {}; - const void *p1,*p2; - float f1; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pi, (const void *_p1, const void *_p2, int _i1)): p1(_p1), p2(_p2), i1(_i1) {}; - const void *p1,*p2; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pi2p, (const void *_p1, const void *_p2, int _i1, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), p3(_p3), p4(_p4) {}; - const void *p1,*p2; - int i1; - const void *p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pif2p, (const void *_p1, const void *_p2, int _i1, float _f1, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), f1(_f1), p3(_p3), p4(_p4) {}; - const void *p1,*p2; - int i1; - float f1; - const void *p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(3p2f2i, (const void *_p1, const void *_p2, const void *_p3, float _f1, float _f2, int _i1, int _i2)): p1(_p1), p2(_p2), p3(_p3), f1(_f1), f2(_f2), i1(_i1), i2(_i2) {}; - const void *p1,*p2,*p3; - float f1,f2; - int i1,i2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(3pi2p, (const void *_p1, const void *_p2, const void *_p3, int _i1, const void *_p4, const void *_p5)): p1(_p1), p2(_p2), p3(_p3), i1(_i1), p4(_p4), p5(_p5) {}; - const void *p1,*p2,*p3; - int i1; - const void *p4,*p5; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(i3p, (int _i1, const void *_p1, const void *_p2, const void *_p3)): i1(_i1), p1(_p1), p2(_p2), p3(_p3) {}; - int i1; - const void *p1,*p2,*p3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(ip, (int _i1, const void *_p1)): i1(_i1), p1(_p1) {}; - int i1; - const void *p1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(ipusf2p2f4i, (int _i1, const void *_p1, unsigned short _us1, float _f1, const void *_p2, const void *_p3, float _f2, float _f3, int _i2, int _i3, int _i4, int _i5)): i1(_i1), p1(_p1), us1(_us1), f1(_f1), p2(_p2), p3(_p3), f2(_f2), f3(_f3), i2(_i2), i3(_i3), i4(_i4), i5(_i5) {}; - int i1; - const void *p1; - unsigned int us1; - float f1; - const void *p2,*p3; - float f2,f3; - int i2,i3,i4,i5; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(3pi, (const void *_p1, const void *_p2, const void *_p3, int _i1)): p1(_p1), p2(_p2), p3(_p3), i1(_i1) {}; - const void *p1,*p2,*p3; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(4pi, (const void *_p1, const void *_p2, const void *_p3, const void *_p4, int _i1)): p1(_p1), p2(_p2), p3(_p3), p4(_p4), i1(_i1) {}; - const void *p1,*p2,*p3,*p4; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pf, (const void *_p1, float _f1)): p1(_p1), f1(_f1) {}; - const void *p1; - float f1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pfp, (const void *_p1, float _f1, const void *_p2)): p1(_p1), f1(_f1), p2(_p2) {}; - const void *p1; - float f1; - const void *p2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pi, (const void *_p1, int _i1)): p1(_p1), i1(_i1) {}; - const void *p1; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pi2p, (const void *_p1, int _i1, const void *_p2, const void *_p3)): p1(_p1), i1(_i1), p2(_p2), p3(_p3) {}; - const void *p1; - int i1; - const void *p2, *p3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pip, (const void *_p1, int _i1, const void *_p2)): p1(_p1), i1(_i1), p2(_p2) {}; - const void *p1; - int i1; - const void *p2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pip2f2i, (const void *_p1, int _i1, const void *_p2, float _f1, float _f2, int _i2, int _i3)): p1(_p1), i1(_i1), p2(_p2), f1(_f1), f2(_f2), i2(_i2), i3(_i3) {}; - const void *p1; - int i1; - const void *p2; - float f1,f2; - int i2,i3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pip2f4i2p, (const void *_p1, int _i1, const void *_p2, float _f1, float _f2, int _i2, int _i3, int _i4, int _i5, const void *_p3, const void *_p4)): p1(_p1), i1(_i1), p2(_p2), f1(_f1), f2(_f2), i2(_i2), i3(_i3), i4(_i4), i5(_i5), p3(_p3), p4(_p4) {}; - const void *p1; - int i1; - const void *p2; - float f1,f2; - int i2,i3,i4,i5; - const void *p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(puc, (const void *_p1, unsigned char _uc1)): p1(_p1), uc1(_uc1) {}; - const void *p1; - unsigned int uc1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2i2pi2p, (int _i1, int _i2, const void *_p1, const void *_p2, int _i3, const void *_p3, const void *_p4)): i1(_i1), i2(_i2), p1(_p1), p2(_p2), i3(_i3), p3(_p3), p4(_p4) {}; - int i1,i2; - const void *p1,*p2; - int i3; - const void *p3,*p4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(2pui, (const void *_p1, const void *_p2, unsigned int _ui1)): p1(_p1), p2(_p2), ui1(_ui1) {}; - const void *p1,*p2; - unsigned int ui1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(i2p, (int _i1, const void *_p1, const void *_p2)): i1(_i1), p1(_p1), p2(_p2) {}; - int i1; - const void *p1,*p2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p2f, (const void *_p1, float _f1, float _f2)): p1(_p1), f1(_f1), f2(_f2) {}; - const void *p1; - float f1,f2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p2fi, (const void *_p1, float _f1, float _f2, int _i1)): p1(_p1), f1(_f1), f2(_f2), i1(_i1) {}; - const void *p1; - float f1,f2; - int i1; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p2i, (const void *_p1, int _i1, int _i2)): p1(_p1), i1(_i1), i2(_i2) {}; - const void *p1; - int i1,i2; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p3i, (const void *_p1, int _i1, int _i2, int _i3)): p1(_p1), i1(_i1), i2(_i2), i3(_i3) {}; - const void *p1; - int i1,i2,i3; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(p4i, (const void *_p1, int _i1, int _i2, int _i3, int _i4)): p1(_p1), i1(_i1), i2(_i2), i3(_i3), i4(_i4) {}; - const void *p1; - int i1,i2,i3,i4; -PACK_ARGS_END - -PACK_ARGS_CLASS_HEADER(pi2p2ip, (const void *_p1, int _i1, const void *_p2, const void *_p3, int _i2, int _i3, const void *_p4)): p1(_p1), i1(_i1), p2(_p2), p3(_p3), i2(_i2), i3(_i3), p4(_p4) {}; - const void *p1; - int i1; - const void *p2,*p3; - int i2,i3; - const void *p4; -PACK_ARGS_END - -// -// API function callers. -// -#ifdef __METAMOD_BUILD__ - #define EXTERN_API_CALLER_FUNCTION(ret_type, args_code) \ - void * DLLINTERNAL _COMBINE4(api_caller_, ret_type, _args_, args_code)(const void * func, const void * packed_args) -#else - #define EXTERN_API_CALLER_FUNCTION(ret_type, args_code) \ - static const api_caller_func_t _COMBINE4(api_caller_, ret_type, _args_, args_code) DLLHIDDEN = (api_caller_func_t)0 -#endif - -EXTERN_API_CALLER_FUNCTION(void, ipV); -EXTERN_API_CALLER_FUNCTION(void, 2pV); -EXTERN_API_CALLER_FUNCTION(void, void); -EXTERN_API_CALLER_FUNCTION(ptr, void); -EXTERN_API_CALLER_FUNCTION(int, void); -EXTERN_API_CALLER_FUNCTION(float, void); -EXTERN_API_CALLER_FUNCTION(float, 2f); -EXTERN_API_CALLER_FUNCTION(void, 2i); -EXTERN_API_CALLER_FUNCTION(int, 2i); -EXTERN_API_CALLER_FUNCTION(void, 2i2p); -EXTERN_API_CALLER_FUNCTION(void, 2i2pi2p); -EXTERN_API_CALLER_FUNCTION(void, 2p); -EXTERN_API_CALLER_FUNCTION(ptr, 2p); -EXTERN_API_CALLER_FUNCTION(int, 2p); -EXTERN_API_CALLER_FUNCTION(void, 2p2f); -EXTERN_API_CALLER_FUNCTION(void, 2p2i2p); -EXTERN_API_CALLER_FUNCTION(void, 2p3fus2uc); -EXTERN_API_CALLER_FUNCTION(ptr, 2pf); -EXTERN_API_CALLER_FUNCTION(void, 2pfi); -EXTERN_API_CALLER_FUNCTION(void, 2pi); -EXTERN_API_CALLER_FUNCTION(int, 2pi); -EXTERN_API_CALLER_FUNCTION(void, 2pui); -EXTERN_API_CALLER_FUNCTION(void, 2pi2p); -EXTERN_API_CALLER_FUNCTION(void, 2pif2p); -EXTERN_API_CALLER_FUNCTION(int, 3i); -EXTERN_API_CALLER_FUNCTION(void, 3p); -EXTERN_API_CALLER_FUNCTION(ptr, 3p); -EXTERN_API_CALLER_FUNCTION(int, 3p); -EXTERN_API_CALLER_FUNCTION(void, 3p2f2i); -EXTERN_API_CALLER_FUNCTION(int, 3pi2p); -EXTERN_API_CALLER_FUNCTION(void, 4p); -EXTERN_API_CALLER_FUNCTION(int, 4p); -EXTERN_API_CALLER_FUNCTION(void, 4pi); -EXTERN_API_CALLER_FUNCTION(int, 4pi); -EXTERN_API_CALLER_FUNCTION(void, f); -EXTERN_API_CALLER_FUNCTION(void, i); -EXTERN_API_CALLER_FUNCTION(ptr, i); -EXTERN_API_CALLER_FUNCTION(int, i); -EXTERN_API_CALLER_FUNCTION(ptr, ui); -EXTERN_API_CALLER_FUNCTION(uint, ui); -EXTERN_API_CALLER_FUNCTION(ulong, ul); -EXTERN_API_CALLER_FUNCTION(void, i2p); -EXTERN_API_CALLER_FUNCTION(int, i2p); -EXTERN_API_CALLER_FUNCTION(void, i3p); -EXTERN_API_CALLER_FUNCTION(void, ip); -EXTERN_API_CALLER_FUNCTION(ushort, ip); -EXTERN_API_CALLER_FUNCTION(int, ip); -EXTERN_API_CALLER_FUNCTION(void, ipusf2p2f4i); -EXTERN_API_CALLER_FUNCTION(void, p); -EXTERN_API_CALLER_FUNCTION(ptr, p); -EXTERN_API_CALLER_FUNCTION(char, p); -EXTERN_API_CALLER_FUNCTION(int, p); -EXTERN_API_CALLER_FUNCTION(uint, p); -EXTERN_API_CALLER_FUNCTION(float, p); -EXTERN_API_CALLER_FUNCTION(void, p2f); -EXTERN_API_CALLER_FUNCTION(int, p2fi); -EXTERN_API_CALLER_FUNCTION(void, p2i); -EXTERN_API_CALLER_FUNCTION(void, p3i); -EXTERN_API_CALLER_FUNCTION(void, p4i); -EXTERN_API_CALLER_FUNCTION(void, puc); -EXTERN_API_CALLER_FUNCTION(void, pf); -EXTERN_API_CALLER_FUNCTION(void, pfp); -EXTERN_API_CALLER_FUNCTION(void, pi); -EXTERN_API_CALLER_FUNCTION(ptr, pi); -EXTERN_API_CALLER_FUNCTION(int, pi); -EXTERN_API_CALLER_FUNCTION(void, pi2p); -EXTERN_API_CALLER_FUNCTION(int, pi2p2ip); -EXTERN_API_CALLER_FUNCTION(void, pip); -EXTERN_API_CALLER_FUNCTION(ptr, pip); -EXTERN_API_CALLER_FUNCTION(void, pip2f2i); -EXTERN_API_CALLER_FUNCTION(void, pip2f4i2p); - -#endif /*API_HOOK_H*/ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 API_HOOK_H +#define API_HOOK_H + +#include "ret_type.h" +#include "api_info.h" +#include "meta_api.h" +#include "osdep.h" //OPEN_ARGS + +// Compine 4 parts for single name +#define _COMBINE4(w,x,y,z) w##x##y##z +#define _COMBINE2(x,y) x##y + +// simplified 'void' version of main hook function +void DLLINTERNAL main_hook_function_void(unsigned int api_info_offset, enum_api_t api, unsigned int func_offset, const void * packed_args); + +// full return typed version of main hook function +void * DLLINTERNAL main_hook_function(const class_ret_t ret_init, unsigned int api_info_offset, enum_api_t api, unsigned int func_offset, const void * packed_args); + +// +// API function args structures/classes +// +#define API_PACK_ARGS(type, args) \ + _COMBINE2(pack_args_type_, type) packed_args args; + +#define PACK_ARGS_CLASS_HEADER(type, constructor_args) \ + class _COMBINE2(pack_args_type_, type) : public class_metamod_new { \ + public: inline _COMBINE2(pack_args_type_, type) constructor_args + +#define PACK_ARGS_END }; + +#define VOID_ARG 0 + +PACK_ARGS_CLASS_HEADER(void, (int)) {}; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(i, (int _i1)): i1(_i1) {}; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2i, (int _i1, int _i2)): i1(_i1), i2(_i2) {}; + int i1,i2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(3i, (int _i1, int _i2, int _i3)): i1(_i1), i2(_i2), i3(_i3) {}; + int i1,i2,i3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(ui, (unsigned int _ui1)): ui1(_ui1) {}; + unsigned int ui1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(ul, (unsigned long _ul1)): ul1(_ul1) {}; + unsigned long ul1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(f, (float _f1)): f1(_f1) {}; + float f1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2f, (float _f1, float _f2)): f1(_f1), f2(_f2) {}; + float f1,f2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p, (const void *_p1)): p1(_p1) {}; + const void *p1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2p, (const void *_p1, const void *_p2)): p1(_p1), p2(_p2) {}; + const void *p1,*p2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(3p, (const void *_p1, const void *_p2, const void *_p3)): p1(_p1), p2(_p2), p3(_p3) {}; + const void *p1,*p2,*p3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(4p, (const void *_p1, const void *_p2, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), p3(_p3), p4(_p4) {}; + const void *p1,*p2,*p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pV, (const void *_p1, const void *_p2, const void *_str)): p1(_p1), p2(_p2), str(_str) {}; + const void *p1,*p2,*str; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(ipV, (int _i1, const void *_p1, const void *_str)): i1(_i1), p1(_p1), str(_str) {}; + int i1; + const void *p1,*str; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2i2p, (int _i1, int _i2, const void *_p1, const void *_p2)): i1(_i1), i2(_i2), p1(_p1), p2(_p2) {}; + int i1,i2; + const void *p1,*p2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2p2f, (const void *_p1, const void *_p2, float _f1, float _f2)): p1(_p1), p2(_p2), f1(_f1), f2(_f2) {}; + const void *p1,*p2; + float f1,f2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2p2i2p, (const void *_p1, const void *_p2, int _i1, int _i2, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), i2(_i2), p3(_p3), p4(_p4) {}; + const void *p1,*p2; + int i1,i2; + const void *p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2p3fus2uc, (const void *_p1, const void *_p2, float _f1, float _f2, float _f3, unsigned short _us1, unsigned char _uc1, unsigned char _uc2)): p1(_p1), p2(_p2), f1(_f1), f2(_f2), f3(_f3), us1(_us1), uc1(_uc1), uc2(_uc2) {}; + const void *p1,*p2; + float f1,f2,f3; + unsigned int us1; + unsigned int uc1,uc2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pf, (const void *_p1, const void *_p2, float _f1)): p1(_p1), p2(_p2), f1(_f1) {}; + const void *p1,*p2; + float f1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pfi, (const void *_p1, const void *_p2, float _f1, int _i1)): p1(_p1), p2(_p2), f1(_f1), i1(_i1) {}; + const void *p1,*p2; + float f1; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pi, (const void *_p1, const void *_p2, int _i1)): p1(_p1), p2(_p2), i1(_i1) {}; + const void *p1,*p2; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pi2p, (const void *_p1, const void *_p2, int _i1, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), p3(_p3), p4(_p4) {}; + const void *p1,*p2; + int i1; + const void *p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pif2p, (const void *_p1, const void *_p2, int _i1, float _f1, const void *_p3, const void *_p4)): p1(_p1), p2(_p2), i1(_i1), f1(_f1), p3(_p3), p4(_p4) {}; + const void *p1,*p2; + int i1; + float f1; + const void *p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(3p2f2i, (const void *_p1, const void *_p2, const void *_p3, float _f1, float _f2, int _i1, int _i2)): p1(_p1), p2(_p2), p3(_p3), f1(_f1), f2(_f2), i1(_i1), i2(_i2) {}; + const void *p1,*p2,*p3; + float f1,f2; + int i1,i2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(3pi2p, (const void *_p1, const void *_p2, const void *_p3, int _i1, const void *_p4, const void *_p5)): p1(_p1), p2(_p2), p3(_p3), i1(_i1), p4(_p4), p5(_p5) {}; + const void *p1,*p2,*p3; + int i1; + const void *p4,*p5; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(i3p, (int _i1, const void *_p1, const void *_p2, const void *_p3)): i1(_i1), p1(_p1), p2(_p2), p3(_p3) {}; + int i1; + const void *p1,*p2,*p3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(ip, (int _i1, const void *_p1)): i1(_i1), p1(_p1) {}; + int i1; + const void *p1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(ipusf2p2f4i, (int _i1, const void *_p1, unsigned short _us1, float _f1, const void *_p2, const void *_p3, float _f2, float _f3, int _i2, int _i3, int _i4, int _i5)): i1(_i1), p1(_p1), us1(_us1), f1(_f1), p2(_p2), p3(_p3), f2(_f2), f3(_f3), i2(_i2), i3(_i3), i4(_i4), i5(_i5) {}; + int i1; + const void *p1; + unsigned int us1; + float f1; + const void *p2,*p3; + float f2,f3; + int i2,i3,i4,i5; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(3pi, (const void *_p1, const void *_p2, const void *_p3, int _i1)): p1(_p1), p2(_p2), p3(_p3), i1(_i1) {}; + const void *p1,*p2,*p3; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(4pi, (const void *_p1, const void *_p2, const void *_p3, const void *_p4, int _i1)): p1(_p1), p2(_p2), p3(_p3), p4(_p4), i1(_i1) {}; + const void *p1,*p2,*p3,*p4; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pf, (const void *_p1, float _f1)): p1(_p1), f1(_f1) {}; + const void *p1; + float f1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pfp, (const void *_p1, float _f1, const void *_p2)): p1(_p1), f1(_f1), p2(_p2) {}; + const void *p1; + float f1; + const void *p2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pi, (const void *_p1, int _i1)): p1(_p1), i1(_i1) {}; + const void *p1; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pi2p, (const void *_p1, int _i1, const void *_p2, const void *_p3)): p1(_p1), i1(_i1), p2(_p2), p3(_p3) {}; + const void *p1; + int i1; + const void *p2, *p3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pip, (const void *_p1, int _i1, const void *_p2)): p1(_p1), i1(_i1), p2(_p2) {}; + const void *p1; + int i1; + const void *p2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pip2f2i, (const void *_p1, int _i1, const void *_p2, float _f1, float _f2, int _i2, int _i3)): p1(_p1), i1(_i1), p2(_p2), f1(_f1), f2(_f2), i2(_i2), i3(_i3) {}; + const void *p1; + int i1; + const void *p2; + float f1,f2; + int i2,i3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pip2f4i2p, (const void *_p1, int _i1, const void *_p2, float _f1, float _f2, int _i2, int _i3, int _i4, int _i5, const void *_p3, const void *_p4)): p1(_p1), i1(_i1), p2(_p2), f1(_f1), f2(_f2), i2(_i2), i3(_i3), i4(_i4), i5(_i5), p3(_p3), p4(_p4) {}; + const void *p1; + int i1; + const void *p2; + float f1,f2; + int i2,i3,i4,i5; + const void *p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(puc, (const void *_p1, unsigned char _uc1)): p1(_p1), uc1(_uc1) {}; + const void *p1; + unsigned int uc1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2i2pi2p, (int _i1, int _i2, const void *_p1, const void *_p2, int _i3, const void *_p3, const void *_p4)): i1(_i1), i2(_i2), p1(_p1), p2(_p2), i3(_i3), p3(_p3), p4(_p4) {}; + int i1,i2; + const void *p1,*p2; + int i3; + const void *p3,*p4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(2pui, (const void *_p1, const void *_p2, unsigned int _ui1)): p1(_p1), p2(_p2), ui1(_ui1) {}; + const void *p1,*p2; + unsigned int ui1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(i2p, (int _i1, const void *_p1, const void *_p2)): i1(_i1), p1(_p1), p2(_p2) {}; + int i1; + const void *p1,*p2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p2f, (const void *_p1, float _f1, float _f2)): p1(_p1), f1(_f1), f2(_f2) {}; + const void *p1; + float f1,f2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p2fi, (const void *_p1, float _f1, float _f2, int _i1)): p1(_p1), f1(_f1), f2(_f2), i1(_i1) {}; + const void *p1; + float f1,f2; + int i1; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p2i, (const void *_p1, int _i1, int _i2)): p1(_p1), i1(_i1), i2(_i2) {}; + const void *p1; + int i1,i2; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p3i, (const void *_p1, int _i1, int _i2, int _i3)): p1(_p1), i1(_i1), i2(_i2), i3(_i3) {}; + const void *p1; + int i1,i2,i3; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(p4i, (const void *_p1, int _i1, int _i2, int _i3, int _i4)): p1(_p1), i1(_i1), i2(_i2), i3(_i3), i4(_i4) {}; + const void *p1; + int i1,i2,i3,i4; +PACK_ARGS_END + +PACK_ARGS_CLASS_HEADER(pi2p2ip, (const void *_p1, int _i1, const void *_p2, const void *_p3, int _i2, int _i3, const void *_p4)): p1(_p1), i1(_i1), p2(_p2), p3(_p3), i2(_i2), i3(_i3), p4(_p4) {}; + const void *p1; + int i1; + const void *p2,*p3; + int i2,i3; + const void *p4; +PACK_ARGS_END + +// +// API function callers. +// +#ifdef __METAMOD_BUILD__ + #define EXTERN_API_CALLER_FUNCTION(ret_type, args_code) \ + void * DLLINTERNAL _COMBINE4(api_caller_, ret_type, _args_, args_code)(const void * func, const void * packed_args) +#else + #define EXTERN_API_CALLER_FUNCTION(ret_type, args_code) \ + static const api_caller_func_t _COMBINE4(api_caller_, ret_type, _args_, args_code) DLLHIDDEN = (api_caller_func_t)0 +#endif + +EXTERN_API_CALLER_FUNCTION(void, ipV); +EXTERN_API_CALLER_FUNCTION(void, 2pV); +EXTERN_API_CALLER_FUNCTION(void, void); +EXTERN_API_CALLER_FUNCTION(ptr, void); +EXTERN_API_CALLER_FUNCTION(int, void); +EXTERN_API_CALLER_FUNCTION(float, void); +EXTERN_API_CALLER_FUNCTION(float, 2f); +EXTERN_API_CALLER_FUNCTION(void, 2i); +EXTERN_API_CALLER_FUNCTION(int, 2i); +EXTERN_API_CALLER_FUNCTION(void, 2i2p); +EXTERN_API_CALLER_FUNCTION(void, 2i2pi2p); +EXTERN_API_CALLER_FUNCTION(void, 2p); +EXTERN_API_CALLER_FUNCTION(ptr, 2p); +EXTERN_API_CALLER_FUNCTION(int, 2p); +EXTERN_API_CALLER_FUNCTION(void, 2p2f); +EXTERN_API_CALLER_FUNCTION(void, 2p2i2p); +EXTERN_API_CALLER_FUNCTION(void, 2p3fus2uc); +EXTERN_API_CALLER_FUNCTION(ptr, 2pf); +EXTERN_API_CALLER_FUNCTION(void, 2pfi); +EXTERN_API_CALLER_FUNCTION(void, 2pi); +EXTERN_API_CALLER_FUNCTION(int, 2pi); +EXTERN_API_CALLER_FUNCTION(void, 2pui); +EXTERN_API_CALLER_FUNCTION(void, 2pi2p); +EXTERN_API_CALLER_FUNCTION(void, 2pif2p); +EXTERN_API_CALLER_FUNCTION(int, 3i); +EXTERN_API_CALLER_FUNCTION(void, 3p); +EXTERN_API_CALLER_FUNCTION(ptr, 3p); +EXTERN_API_CALLER_FUNCTION(int, 3p); +EXTERN_API_CALLER_FUNCTION(void, 3p2f2i); +EXTERN_API_CALLER_FUNCTION(int, 3pi2p); +EXTERN_API_CALLER_FUNCTION(void, 4p); +EXTERN_API_CALLER_FUNCTION(int, 4p); +EXTERN_API_CALLER_FUNCTION(void, 4pi); +EXTERN_API_CALLER_FUNCTION(int, 4pi); +EXTERN_API_CALLER_FUNCTION(void, f); +EXTERN_API_CALLER_FUNCTION(void, i); +EXTERN_API_CALLER_FUNCTION(ptr, i); +EXTERN_API_CALLER_FUNCTION(int, i); +EXTERN_API_CALLER_FUNCTION(ptr, ui); +EXTERN_API_CALLER_FUNCTION(uint, ui); +EXTERN_API_CALLER_FUNCTION(ulong, ul); +EXTERN_API_CALLER_FUNCTION(void, i2p); +EXTERN_API_CALLER_FUNCTION(int, i2p); +EXTERN_API_CALLER_FUNCTION(void, i3p); +EXTERN_API_CALLER_FUNCTION(void, ip); +EXTERN_API_CALLER_FUNCTION(ushort, ip); +EXTERN_API_CALLER_FUNCTION(int, ip); +EXTERN_API_CALLER_FUNCTION(void, ipusf2p2f4i); +EXTERN_API_CALLER_FUNCTION(void, p); +EXTERN_API_CALLER_FUNCTION(ptr, p); +EXTERN_API_CALLER_FUNCTION(char, p); +EXTERN_API_CALLER_FUNCTION(int, p); +EXTERN_API_CALLER_FUNCTION(uint, p); +EXTERN_API_CALLER_FUNCTION(float, p); +EXTERN_API_CALLER_FUNCTION(void, p2f); +EXTERN_API_CALLER_FUNCTION(int, p2fi); +EXTERN_API_CALLER_FUNCTION(void, p2i); +EXTERN_API_CALLER_FUNCTION(void, p3i); +EXTERN_API_CALLER_FUNCTION(void, p4i); +EXTERN_API_CALLER_FUNCTION(void, puc); +EXTERN_API_CALLER_FUNCTION(void, pf); +EXTERN_API_CALLER_FUNCTION(void, pfp); +EXTERN_API_CALLER_FUNCTION(void, pi); +EXTERN_API_CALLER_FUNCTION(ptr, pi); +EXTERN_API_CALLER_FUNCTION(int, pi); +EXTERN_API_CALLER_FUNCTION(void, pi2p); +EXTERN_API_CALLER_FUNCTION(int, pi2p2ip); +EXTERN_API_CALLER_FUNCTION(void, pip); +EXTERN_API_CALLER_FUNCTION(ptr, pip); +EXTERN_API_CALLER_FUNCTION(void, pip2f2i); +EXTERN_API_CALLER_FUNCTION(void, pip2f4i2p); + +#endif /*API_HOOK_H*/ diff --git a/src/metamod/api_info.h b/src/metamod/api_info.h index 9c1f590..c90d3c7 100644 --- a/src/metamod/api_info.h +++ b/src/metamod/api_info.h @@ -1,312 +1,312 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// api_info.h - structures to store info about api routines - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 API_INFO_H -#define API_INFO_H - -#include "comp_dep.h" -#include "types_meta.h" // mBOOL -#include "ret_type.h" - - -#define P_PRE 0 // plugin function called before gamedll -#define P_POST 1 // plugin function called after gamedll - - -// API selector -typedef enum enum_api_t { - e_api_engine = 0, - e_api_dllapi = 1, - e_api_newapi = 2, -} enum_api_t; - -// API caller function prototype -typedef void * (DLLINTERNAL_NOVIS * api_caller_func_t)(const void * func, const void * packed_args); - - -typedef struct api_info_s { - mBOOL trace; // if true, log info about this function - int loglevel; // level at which to log info about this function - api_caller_func_t api_caller; // argument format/type for single-main-hook-function optimization - const char *name; // string representation of function name -} api_info_t; - - -// DLL api functions -typedef struct dllapi_info_s { - api_info_t pfnGameInit; - api_info_t pfnSpawn; - api_info_t pfnThink; - api_info_t pfnUse; - api_info_t pfnTouch; - api_info_t pfnBlocked; - api_info_t pfnKeyValue; - api_info_t pfnSave; - api_info_t pfnRestore; - api_info_t pfnSetAbsBox; - api_info_t pfnSaveWriteFields; - api_info_t pfnSaveReadFields; - api_info_t pfnSaveGlobalState; - api_info_t pfnRestoreGlobalState; - api_info_t pfnResetGlobalState; - api_info_t pfnClientConnect; - api_info_t pfnClientDisconnect; - api_info_t pfnClientKill; - api_info_t pfnClientPutInServer; - api_info_t pfnClientCommand; - api_info_t pfnClientUserInfoChanged; - api_info_t pfnServerActivate; - api_info_t pfnServerDeactivate; - api_info_t pfnPlayerPreThink; - api_info_t pfnPlayerPostThink; - api_info_t pfnStartFrame; - api_info_t pfnParmsNewLevel; - api_info_t pfnParmsChangeLevel; - api_info_t pfnGetGameDescription; - api_info_t pfnPlayerCustomization; - api_info_t pfnSpectatorConnect; - api_info_t pfnSpectatorDisconnect; - api_info_t pfnSpectatorThink; - api_info_t pfnSys_Error; - api_info_t pfnPM_Move; - api_info_t pfnPM_Init; - api_info_t pfnPM_FindTextureType; - api_info_t pfnSetupVisibility; - api_info_t pfnUpdateClientData; - api_info_t pfnAddToFullPack; - api_info_t pfnCreateBaseline; - api_info_t pfnRegisterEncoders; - api_info_t pfnGetWeaponData; - api_info_t pfnCmdStart; - api_info_t pfnCmdEnd; - api_info_t pfnConnectionlessPacket; - api_info_t pfnGetHullBounds; - api_info_t pfnCreateInstancedBaselines; - api_info_t pfnInconsistentFile; - api_info_t pfnAllowLagCompensation; - api_info_t END; -} dllapi_info_t; - - -// "New" api functions -typedef struct newapi_info_s { - api_info_t pfnOnFreeEntPrivateData; - api_info_t pfnGameShutdown; - api_info_t pfnShouldCollide; - // Added 2005/08/11 (no SDK update): - api_info_t pfnCvarValue; - // Added 2005/11/21 (no SDK update): - api_info_t pfnCvarValue2; - api_info_t END; -} newapi_info_t; - - -// Engine functions -typedef struct engine_info_s { - api_info_t pfnPrecacheModel; - api_info_t pfnPrecacheSound; - api_info_t pfnSetModel; - api_info_t pfnModelIndex; - api_info_t pfnModelFrames; - api_info_t pfnSetSize; - api_info_t pfnChangeLevel; - api_info_t pfnGetSpawnParms; - api_info_t pfnSaveSpawnParms; - api_info_t pfnVecToYaw; - api_info_t pfnVecToAngles; - api_info_t pfnMoveToOrigin; - api_info_t pfnChangeYaw; - api_info_t pfnChangePitch; - api_info_t pfnFindEntityByString; - api_info_t pfnGetEntityIllum; - api_info_t pfnFindEntityInSphere; - api_info_t pfnFindClientInPVS; - api_info_t pfnEntitiesInPVS; - api_info_t pfnMakeVectors; - api_info_t pfnAngleVectors; - api_info_t pfnCreateEntity; - api_info_t pfnRemoveEntity; - api_info_t pfnCreateNamedEntity; - api_info_t pfnMakeStatic; - api_info_t pfnEntIsOnFloor; - api_info_t pfnDropToFloor; - api_info_t pfnWalkMove; - api_info_t pfnSetOrigin; - api_info_t pfnEmitSound; - api_info_t pfnEmitAmbientSound; - api_info_t pfnTraceLine; - api_info_t pfnTraceToss; - api_info_t pfnTraceMonsterHull; - api_info_t pfnTraceHull; - api_info_t pfnTraceModel; - api_info_t pfnTraceTexture; - api_info_t pfnTraceSphere; - api_info_t pfnGetAimVector; - api_info_t pfnServerCommand; - api_info_t pfnServerExecute; - api_info_t pfnClientCommand; - api_info_t pfnParticleEffect; - api_info_t pfnLightStyle; - api_info_t pfnDecalIndex; - api_info_t pfnPointContents; - api_info_t pfnMessageBegin; - api_info_t pfnMessageEnd; - api_info_t pfnWriteByte; - api_info_t pfnWriteChar; - api_info_t pfnWriteShort; - api_info_t pfnWriteLong; - api_info_t pfnWriteAngle; - api_info_t pfnWriteCoord; - api_info_t pfnWriteString; - api_info_t pfnWriteEntity; - api_info_t pfnCVarRegister; - api_info_t pfnCVarGetFloat; - api_info_t pfnCVarGetString; - api_info_t pfnCVarSetFloat; - api_info_t pfnCVarSetString; - api_info_t pfnAlertMessage; - api_info_t pfnEngineFprintf; - api_info_t pfnPvAllocEntPrivateData; - api_info_t pfnPvEntPrivateData; - api_info_t pfnFreeEntPrivateData; - api_info_t pfnSzFromIndex; - api_info_t pfnAllocString; - api_info_t pfnGetVarsOfEnt; - api_info_t pfnPEntityOfEntOffset; - api_info_t pfnEntOffsetOfPEntity; - api_info_t pfnIndexOfEdict; - api_info_t pfnPEntityOfEntIndex; - api_info_t pfnFindEntityByVars; - api_info_t pfnGetModelPtr; - api_info_t pfnRegUserMsg; - api_info_t pfnAnimationAutomove; - api_info_t pfnGetBonePosition; - api_info_t pfnFunctionFromName; - api_info_t pfnNameForFunction; - api_info_t pfnClientPrintf; - api_info_t pfnServerPrint; - api_info_t pfnCmd_Args; - api_info_t pfnCmd_Argv; - api_info_t pfnCmd_Argc; - api_info_t pfnGetAttachment; - api_info_t pfnCRC32_Init; - api_info_t pfnCRC32_ProcessBuffer; - api_info_t pfnCRC32_ProcessByte; - api_info_t pfnCRC32_Final; - api_info_t pfnRandomLong; - api_info_t pfnRandomFloat; - api_info_t pfnSetView; - api_info_t pfnTime; - api_info_t pfnCrosshairAngle; - api_info_t pfnLoadFileForMe; - api_info_t pfnFreeFile; - api_info_t pfnEndSection; - api_info_t pfnCompareFileTime; - api_info_t pfnGetGameDir; - api_info_t pfnCvar_RegisterVariable; - api_info_t pfnFadeClientVolume; - api_info_t pfnSetClientMaxspeed; - api_info_t pfnCreateFakeClient; - api_info_t pfnRunPlayerMove; - api_info_t pfnNumberOfEntities; - api_info_t pfnGetInfoKeyBuffer; - api_info_t pfnInfoKeyValue; - api_info_t pfnSetKeyValue; - api_info_t pfnSetClientKeyValue; - api_info_t pfnIsMapValid; - api_info_t pfnStaticDecal; - api_info_t pfnPrecacheGeneric; - api_info_t pfnGetPlayerUserId; - api_info_t pfnBuildSoundMsg; - api_info_t pfnIsDedicatedServer; - api_info_t pfnCVarGetPointer; - api_info_t pfnGetPlayerWONId; - api_info_t pfnInfo_RemoveKey; - api_info_t pfnGetPhysicsKeyValue; - api_info_t pfnSetPhysicsKeyValue; - api_info_t pfnGetPhysicsInfoString; - api_info_t pfnPrecacheEvent; - api_info_t pfnPlaybackEvent; - api_info_t pfnSetFatPVS; - api_info_t pfnSetFatPAS; - api_info_t pfnCheckVisibility; - api_info_t pfnDeltaSetField; - api_info_t pfnDeltaUnsetField; - api_info_t pfnDeltaAddEncoder; - api_info_t pfnGetCurrentPlayer; - api_info_t pfnCanSkipPlayer; - api_info_t pfnDeltaFindField; - api_info_t pfnDeltaSetFieldByIndex; - api_info_t pfnDeltaUnsetFieldByIndex; - api_info_t pfnSetGroupMask; - api_info_t pfnCreateInstancedBaseline; - api_info_t pfnCvar_DirectSet; - api_info_t pfnForceUnmodified; - api_info_t pfnGetPlayerStats; - api_info_t pfnAddServerCommand; - // Added in SDK 2.2: - api_info_t pfnVoice_GetClientListening; - api_info_t pfnVoice_SetClientListening; - // Added for HL 1109 (no SDK update): - api_info_t pfnGetPlayerAuthId; - // Added 2003/11/10 (no SDK update): - api_info_t pfnSequenceGet; - api_info_t pfnSequencePickSentence; - api_info_t pfnGetFileSize; - api_info_t pfnGetApproxWavePlayLen; - api_info_t pfnIsCareerMatch; - api_info_t pfnGetLocalizedStringLength; - api_info_t pfnRegisterTutorMessageShown; - api_info_t pfnGetTimesTutorMessageShown; - api_info_t pfnProcessTutorMessageDecayBuffer; - api_info_t pfnConstructTutorMessageDecayBuffer; - api_info_t pfnResetTutorMessageDecayData; - // Added 2005/08/11 (no SDK update): - api_info_t pfnQueryClientCvarValue; - // Added 2005/11/21 (no SDK update): - api_info_t pfnQueryClientCvarValue2; - // Added 2009/06/17 (no SDK update): - api_info_t pfnEngCheckParm; - // end - api_info_t END; -} engine_info_t; - - -extern const dllapi_info_t dllapi_info DLLHIDDEN; -extern const newapi_info_t newapi_info DLLHIDDEN; -extern const engine_info_t engine_info DLLHIDDEN; - -#endif /* API_INFO_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// api_info.h - structures to store info about api routines + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 API_INFO_H +#define API_INFO_H + +#include "comp_dep.h" +#include "types_meta.h" // mBOOL +#include "ret_type.h" + + +#define P_PRE 0 // plugin function called before gamedll +#define P_POST 1 // plugin function called after gamedll + + +// API selector +typedef enum enum_api_t { + e_api_engine = 0, + e_api_dllapi = 1, + e_api_newapi = 2, +} enum_api_t; + +// API caller function prototype +typedef void * (DLLINTERNAL_NOVIS * api_caller_func_t)(const void * func, const void * packed_args); + + +typedef struct api_info_s { + mBOOL trace; // if true, log info about this function + int loglevel; // level at which to log info about this function + api_caller_func_t api_caller; // argument format/type for single-main-hook-function optimization + const char *name; // string representation of function name +} api_info_t; + + +// DLL api functions +typedef struct dllapi_info_s { + api_info_t pfnGameInit; + api_info_t pfnSpawn; + api_info_t pfnThink; + api_info_t pfnUse; + api_info_t pfnTouch; + api_info_t pfnBlocked; + api_info_t pfnKeyValue; + api_info_t pfnSave; + api_info_t pfnRestore; + api_info_t pfnSetAbsBox; + api_info_t pfnSaveWriteFields; + api_info_t pfnSaveReadFields; + api_info_t pfnSaveGlobalState; + api_info_t pfnRestoreGlobalState; + api_info_t pfnResetGlobalState; + api_info_t pfnClientConnect; + api_info_t pfnClientDisconnect; + api_info_t pfnClientKill; + api_info_t pfnClientPutInServer; + api_info_t pfnClientCommand; + api_info_t pfnClientUserInfoChanged; + api_info_t pfnServerActivate; + api_info_t pfnServerDeactivate; + api_info_t pfnPlayerPreThink; + api_info_t pfnPlayerPostThink; + api_info_t pfnStartFrame; + api_info_t pfnParmsNewLevel; + api_info_t pfnParmsChangeLevel; + api_info_t pfnGetGameDescription; + api_info_t pfnPlayerCustomization; + api_info_t pfnSpectatorConnect; + api_info_t pfnSpectatorDisconnect; + api_info_t pfnSpectatorThink; + api_info_t pfnSys_Error; + api_info_t pfnPM_Move; + api_info_t pfnPM_Init; + api_info_t pfnPM_FindTextureType; + api_info_t pfnSetupVisibility; + api_info_t pfnUpdateClientData; + api_info_t pfnAddToFullPack; + api_info_t pfnCreateBaseline; + api_info_t pfnRegisterEncoders; + api_info_t pfnGetWeaponData; + api_info_t pfnCmdStart; + api_info_t pfnCmdEnd; + api_info_t pfnConnectionlessPacket; + api_info_t pfnGetHullBounds; + api_info_t pfnCreateInstancedBaselines; + api_info_t pfnInconsistentFile; + api_info_t pfnAllowLagCompensation; + api_info_t END; +} dllapi_info_t; + + +// "New" api functions +typedef struct newapi_info_s { + api_info_t pfnOnFreeEntPrivateData; + api_info_t pfnGameShutdown; + api_info_t pfnShouldCollide; + // Added 2005/08/11 (no SDK update): + api_info_t pfnCvarValue; + // Added 2005/11/21 (no SDK update): + api_info_t pfnCvarValue2; + api_info_t END; +} newapi_info_t; + + +// Engine functions +typedef struct engine_info_s { + api_info_t pfnPrecacheModel; + api_info_t pfnPrecacheSound; + api_info_t pfnSetModel; + api_info_t pfnModelIndex; + api_info_t pfnModelFrames; + api_info_t pfnSetSize; + api_info_t pfnChangeLevel; + api_info_t pfnGetSpawnParms; + api_info_t pfnSaveSpawnParms; + api_info_t pfnVecToYaw; + api_info_t pfnVecToAngles; + api_info_t pfnMoveToOrigin; + api_info_t pfnChangeYaw; + api_info_t pfnChangePitch; + api_info_t pfnFindEntityByString; + api_info_t pfnGetEntityIllum; + api_info_t pfnFindEntityInSphere; + api_info_t pfnFindClientInPVS; + api_info_t pfnEntitiesInPVS; + api_info_t pfnMakeVectors; + api_info_t pfnAngleVectors; + api_info_t pfnCreateEntity; + api_info_t pfnRemoveEntity; + api_info_t pfnCreateNamedEntity; + api_info_t pfnMakeStatic; + api_info_t pfnEntIsOnFloor; + api_info_t pfnDropToFloor; + api_info_t pfnWalkMove; + api_info_t pfnSetOrigin; + api_info_t pfnEmitSound; + api_info_t pfnEmitAmbientSound; + api_info_t pfnTraceLine; + api_info_t pfnTraceToss; + api_info_t pfnTraceMonsterHull; + api_info_t pfnTraceHull; + api_info_t pfnTraceModel; + api_info_t pfnTraceTexture; + api_info_t pfnTraceSphere; + api_info_t pfnGetAimVector; + api_info_t pfnServerCommand; + api_info_t pfnServerExecute; + api_info_t pfnClientCommand; + api_info_t pfnParticleEffect; + api_info_t pfnLightStyle; + api_info_t pfnDecalIndex; + api_info_t pfnPointContents; + api_info_t pfnMessageBegin; + api_info_t pfnMessageEnd; + api_info_t pfnWriteByte; + api_info_t pfnWriteChar; + api_info_t pfnWriteShort; + api_info_t pfnWriteLong; + api_info_t pfnWriteAngle; + api_info_t pfnWriteCoord; + api_info_t pfnWriteString; + api_info_t pfnWriteEntity; + api_info_t pfnCVarRegister; + api_info_t pfnCVarGetFloat; + api_info_t pfnCVarGetString; + api_info_t pfnCVarSetFloat; + api_info_t pfnCVarSetString; + api_info_t pfnAlertMessage; + api_info_t pfnEngineFprintf; + api_info_t pfnPvAllocEntPrivateData; + api_info_t pfnPvEntPrivateData; + api_info_t pfnFreeEntPrivateData; + api_info_t pfnSzFromIndex; + api_info_t pfnAllocString; + api_info_t pfnGetVarsOfEnt; + api_info_t pfnPEntityOfEntOffset; + api_info_t pfnEntOffsetOfPEntity; + api_info_t pfnIndexOfEdict; + api_info_t pfnPEntityOfEntIndex; + api_info_t pfnFindEntityByVars; + api_info_t pfnGetModelPtr; + api_info_t pfnRegUserMsg; + api_info_t pfnAnimationAutomove; + api_info_t pfnGetBonePosition; + api_info_t pfnFunctionFromName; + api_info_t pfnNameForFunction; + api_info_t pfnClientPrintf; + api_info_t pfnServerPrint; + api_info_t pfnCmd_Args; + api_info_t pfnCmd_Argv; + api_info_t pfnCmd_Argc; + api_info_t pfnGetAttachment; + api_info_t pfnCRC32_Init; + api_info_t pfnCRC32_ProcessBuffer; + api_info_t pfnCRC32_ProcessByte; + api_info_t pfnCRC32_Final; + api_info_t pfnRandomLong; + api_info_t pfnRandomFloat; + api_info_t pfnSetView; + api_info_t pfnTime; + api_info_t pfnCrosshairAngle; + api_info_t pfnLoadFileForMe; + api_info_t pfnFreeFile; + api_info_t pfnEndSection; + api_info_t pfnCompareFileTime; + api_info_t pfnGetGameDir; + api_info_t pfnCvar_RegisterVariable; + api_info_t pfnFadeClientVolume; + api_info_t pfnSetClientMaxspeed; + api_info_t pfnCreateFakeClient; + api_info_t pfnRunPlayerMove; + api_info_t pfnNumberOfEntities; + api_info_t pfnGetInfoKeyBuffer; + api_info_t pfnInfoKeyValue; + api_info_t pfnSetKeyValue; + api_info_t pfnSetClientKeyValue; + api_info_t pfnIsMapValid; + api_info_t pfnStaticDecal; + api_info_t pfnPrecacheGeneric; + api_info_t pfnGetPlayerUserId; + api_info_t pfnBuildSoundMsg; + api_info_t pfnIsDedicatedServer; + api_info_t pfnCVarGetPointer; + api_info_t pfnGetPlayerWONId; + api_info_t pfnInfo_RemoveKey; + api_info_t pfnGetPhysicsKeyValue; + api_info_t pfnSetPhysicsKeyValue; + api_info_t pfnGetPhysicsInfoString; + api_info_t pfnPrecacheEvent; + api_info_t pfnPlaybackEvent; + api_info_t pfnSetFatPVS; + api_info_t pfnSetFatPAS; + api_info_t pfnCheckVisibility; + api_info_t pfnDeltaSetField; + api_info_t pfnDeltaUnsetField; + api_info_t pfnDeltaAddEncoder; + api_info_t pfnGetCurrentPlayer; + api_info_t pfnCanSkipPlayer; + api_info_t pfnDeltaFindField; + api_info_t pfnDeltaSetFieldByIndex; + api_info_t pfnDeltaUnsetFieldByIndex; + api_info_t pfnSetGroupMask; + api_info_t pfnCreateInstancedBaseline; + api_info_t pfnCvar_DirectSet; + api_info_t pfnForceUnmodified; + api_info_t pfnGetPlayerStats; + api_info_t pfnAddServerCommand; + // Added in SDK 2.2: + api_info_t pfnVoice_GetClientListening; + api_info_t pfnVoice_SetClientListening; + // Added for HL 1109 (no SDK update): + api_info_t pfnGetPlayerAuthId; + // Added 2003/11/10 (no SDK update): + api_info_t pfnSequenceGet; + api_info_t pfnSequencePickSentence; + api_info_t pfnGetFileSize; + api_info_t pfnGetApproxWavePlayLen; + api_info_t pfnIsCareerMatch; + api_info_t pfnGetLocalizedStringLength; + api_info_t pfnRegisterTutorMessageShown; + api_info_t pfnGetTimesTutorMessageShown; + api_info_t pfnProcessTutorMessageDecayBuffer; + api_info_t pfnConstructTutorMessageDecayBuffer; + api_info_t pfnResetTutorMessageDecayData; + // Added 2005/08/11 (no SDK update): + api_info_t pfnQueryClientCvarValue; + // Added 2005/11/21 (no SDK update): + api_info_t pfnQueryClientCvarValue2; + // Added 2009/06/17 (no SDK update): + api_info_t pfnEngCheckParm; + // end + api_info_t END; +} engine_info_t; + + +extern const dllapi_info_t dllapi_info DLLHIDDEN; +extern const newapi_info_t newapi_info DLLHIDDEN; +extern const engine_info_t engine_info DLLHIDDEN; + +#endif /* API_INFO_H */ diff --git a/src/metamod/commands_meta.h b/src/metamod/commands_meta.h index 4001c8e..a88a95b 100644 --- a/src/metamod/commands_meta.h +++ b/src/metamod/commands_meta.h @@ -1,83 +1,83 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// commands_meta.h - prototypes for console commands - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 COMMANDS_META_H -#define COMMANDS_META_H - -#include "types_meta.h" // mBOOL -#include "comp_dep.h" - -// Flags to use for meta_cmd_doplug(), to operate on existing plugins; note -// "load" operates on a non-existing plugin thus isn't included here. -typedef enum { - PC_NULL = 0, - PC_PAUSE, // pause the plugin - PC_UNPAUSE, // unpause the plugin - PC_UNLOAD, // unload the plugin - PC_RELOAD, // unload the plugin and load it again - PC_RETRY, // retry a failed operation (usually load/attach) - PC_INFO, // show all info about the plugin - PC_CLEAR, // remove a failed plugin from the list - PC_FORCE_UNLOAD, // forcibly unload the plugin - PC_REQUIRE, // require that this plugin is loaded/running -} PLUG_CMD; - -void DLLINTERNAL meta_register_cmdcvar(); - -void DLLHIDDEN svr_meta(void); // only hidden because called from outside! - -void DLLINTERNAL cmd_meta_usage(void); -void DLLINTERNAL cmd_meta_version(void); -void DLLINTERNAL cmd_meta_gpl(void); - -void DLLINTERNAL cmd_meta_game(void); -void DLLINTERNAL cmd_meta_refresh(void); -void DLLINTERNAL cmd_meta_load(void); - -void DLLINTERNAL cmd_meta_pluginlist(void); -void DLLINTERNAL cmd_meta_cmdlist(void); -void DLLINTERNAL cmd_meta_cvarlist(void); -void DLLINTERNAL cmd_meta_config(void); - -void DLLINTERNAL cmd_doplug(PLUG_CMD pcmd); - -void DLLINTERNAL client_meta(edict_t *pEntity); -void DLLINTERNAL client_meta_usage(edict_t *pEntity); -void DLLINTERNAL client_meta_version(edict_t *pEntity); -void DLLINTERNAL client_meta_pluginlist(edict_t *pEntity); -void DLLINTERNAL client_meta_aybabtu(edict_t *pEntity); - -#endif /* COMMANDS_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// commands_meta.h - prototypes for console commands + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 COMMANDS_META_H +#define COMMANDS_META_H + +#include "types_meta.h" // mBOOL +#include "comp_dep.h" + +// Flags to use for meta_cmd_doplug(), to operate on existing plugins; note +// "load" operates on a non-existing plugin thus isn't included here. +typedef enum { + PC_NULL = 0, + PC_PAUSE, // pause the plugin + PC_UNPAUSE, // unpause the plugin + PC_UNLOAD, // unload the plugin + PC_RELOAD, // unload the plugin and load it again + PC_RETRY, // retry a failed operation (usually load/attach) + PC_INFO, // show all info about the plugin + PC_CLEAR, // remove a failed plugin from the list + PC_FORCE_UNLOAD, // forcibly unload the plugin + PC_REQUIRE, // require that this plugin is loaded/running +} PLUG_CMD; + +void DLLINTERNAL meta_register_cmdcvar(); + +void DLLHIDDEN svr_meta(void); // only hidden because called from outside! + +void DLLINTERNAL cmd_meta_usage(void); +void DLLINTERNAL cmd_meta_version(void); +void DLLINTERNAL cmd_meta_gpl(void); + +void DLLINTERNAL cmd_meta_game(void); +void DLLINTERNAL cmd_meta_refresh(void); +void DLLINTERNAL cmd_meta_load(void); + +void DLLINTERNAL cmd_meta_pluginlist(void); +void DLLINTERNAL cmd_meta_cmdlist(void); +void DLLINTERNAL cmd_meta_cvarlist(void); +void DLLINTERNAL cmd_meta_config(void); + +void DLLINTERNAL cmd_doplug(PLUG_CMD pcmd); + +void DLLINTERNAL client_meta(edict_t *pEntity); +void DLLINTERNAL client_meta_usage(edict_t *pEntity); +void DLLINTERNAL client_meta_version(edict_t *pEntity); +void DLLINTERNAL client_meta_pluginlist(edict_t *pEntity); +void DLLINTERNAL client_meta_aybabtu(edict_t *pEntity); + +#endif /* COMMANDS_META_H */ diff --git a/src/metamod/comp_dep.h b/src/metamod/comp_dep.h index 92c585e..3d49de0 100644 --- a/src/metamod/comp_dep.h +++ b/src/metamod/comp_dep.h @@ -1,97 +1,97 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 COMP_DEP_H -#define COMP_DEP_H - -#define DECLSPEC(kw) -#if defined (_WIN32) && defined (_MSC_VER) - #define ATTRIBUTE(kw) -#else - #define ATTRIBUTE(kw) __attribute__((kw)) -#endif -#define MM_CDECL - -// We use these macros to hide our internal globals from being exported -// on ELF .so -#if defined(__GNUC__) && !defined(_WIN32) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 3 - // Hidden data/function. - #define DLLHIDDEN __attribute__((visibility("hidden"))) - // Hidden internal function. - #if defined(__x86_64__) || defined(__amd64__) - #define DLLINTERNAL __attribute__((visibility("internal"))) - #define DLLINTERNAL_NOVIS - #else - #ifdef __INTERNALS_USE_REGPARAMS__ - #define DLLINTERNAL __attribute__((visibility("internal"), regparm(3))) - #define DLLINTERNAL_NOVIS __attribute__((regparm(3))) - #else - #define DLLINTERNAL __attribute__((visibility("internal"))) - #define DLLINTERNAL_NOVIS - #endif - #endif -#else - #define DLLHIDDEN - #if defined (_WIN32) && defined (_MSC_VER) - #define DLLINTERNAL_NOVIS - #define DLLINTERNAL - #else - #ifdef __INTERNALS_USE_REGPARAMS__ - #define DLLINTERNAL_NOVIS __attribute__((regparm(3))) - #define DLLINTERNAL DLLINTERNAL_NOVIS - #else - #define DLLINTERNAL_NOVIS - #define DLLINTERNAL - #endif - #endif //defined WIN32 -#endif - -#if defined (_WIN32) && defined (_MSC_VER) - // On x86 va_list is just a pointer. - #define va_copy(dst,src) ((dst)=(src)) -#else - // Some systems that do not supply va_copy have __va_copy instead, since - // that was the name used in the draft proposal. - #if !defined(__GNUC__) || __GNUC__ < 3 - #define va_copy __va_copy - #endif -#endif - -// Manual branch optimization for GCC 3.0.0 and newer -#if !defined(__GNUC__) || __GNUC__ < 3 - #define likely(x) (x) - #define unlikely(x) (x) -#else - #define likely(x) __builtin_expect((long int)(x), true) - #define unlikely(x) __builtin_expect((long int)(x), false) -#endif - -#endif /*COMP_DEP_H*/ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 COMP_DEP_H +#define COMP_DEP_H + +#define DECLSPEC(kw) +#if defined (_WIN32) && defined (_MSC_VER) + #define ATTRIBUTE(kw) +#else + #define ATTRIBUTE(kw) __attribute__((kw)) +#endif +#define MM_CDECL + +// We use these macros to hide our internal globals from being exported +// on ELF .so +#if defined(__GNUC__) && !defined(_WIN32) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 3 + // Hidden data/function. + #define DLLHIDDEN __attribute__((visibility("hidden"))) + // Hidden internal function. + #if defined(__x86_64__) || defined(__amd64__) + #define DLLINTERNAL __attribute__((visibility("internal"))) + #define DLLINTERNAL_NOVIS + #else + #ifdef __INTERNALS_USE_REGPARAMS__ + #define DLLINTERNAL __attribute__((visibility("internal"), regparm(3))) + #define DLLINTERNAL_NOVIS __attribute__((regparm(3))) + #else + #define DLLINTERNAL __attribute__((visibility("internal"))) + #define DLLINTERNAL_NOVIS + #endif + #endif +#else + #define DLLHIDDEN + #if defined (_WIN32) && defined (_MSC_VER) + #define DLLINTERNAL_NOVIS + #define DLLINTERNAL + #else + #ifdef __INTERNALS_USE_REGPARAMS__ + #define DLLINTERNAL_NOVIS __attribute__((regparm(3))) + #define DLLINTERNAL DLLINTERNAL_NOVIS + #else + #define DLLINTERNAL_NOVIS + #define DLLINTERNAL + #endif + #endif //defined WIN32 +#endif + +#if defined (_WIN32) && defined (_MSC_VER) + // On x86 va_list is just a pointer. + #define va_copy(dst,src) ((dst)=(src)) +#else + // Some systems that do not supply va_copy have __va_copy instead, since + // that was the name used in the draft proposal. + #if !defined(__GNUC__) || __GNUC__ < 3 + #define va_copy __va_copy + #endif +#endif + +// Manual branch optimization for GCC 3.0.0 and newer +#if !defined(__GNUC__) || __GNUC__ < 3 + #define likely(x) (x) + #define unlikely(x) (x) +#else + #define likely(x) __builtin_expect((long int)(x), true) + #define unlikely(x) __builtin_expect((long int)(x), false) +#endif + +#endif /*COMP_DEP_H*/ diff --git a/src/metamod/conf_meta.h b/src/metamod/conf_meta.h index bf751ab..1fdfa1f 100644 --- a/src/metamod/conf_meta.h +++ b/src/metamod/conf_meta.h @@ -1,100 +1,100 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// conf_meta.h - configfile reading - -// Modeled after mutt/init.[ch]. - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 CONF_META_H -#define CONF_META_H - -#include "types_meta.h" // mBOOL -#include "new_baseclass.h" -#include "comp_dep.h" - -// Max length of line in config file. -#define MAX_CONF_LEN 1024 - -// Supported config value-types. -typedef enum { - CF_NONE=0, - CF_INT, - CF_BOOL, - CF_STR, - CF_PATH, -#if 0 - CF_CVAR, - CF_CMD, -#endif -} cf_type_t; - -//typedef mBOOL (*SETOPT_FN) (char *key, char *value); - -typedef struct option_s { - char *name; // option name - cf_type_t type; // option type - void *dest; // addr of destination variable, or handler function - char *init; // initial value, as a string, just as config file would -} option_t; - -class MConfig : public class_metamod_new { - private: - // data - option_t *list; - char *filename; - // functions - option_t * DLLINTERNAL find(const char *lookup); - mBOOL DLLINTERNAL set(option_t *setp, const char *value); - // Private; to satisfy -Weffc++ "has pointer data members but does - // not override" copy/assignment constructor. - void operator=(const MConfig &src); - MConfig(const MConfig &src); - public: - // contructor - MConfig(void) DLLINTERNAL; - // data - int debuglevel; // to use for meta_debug - char *gamedll; // string if specified in config.ini - char *plugins_file; // ie metamod.ini, plugins.ini - char *exec_cfg; // ie metaexec.cfg, exec.cfg - int autodetect; // autodetection of gamedll (Metamod-All-Support patch) - int clientmeta; // control 'meta' client-command - // functions - void DLLINTERNAL init(option_t *global_options); - mBOOL DLLINTERNAL load(const char *filename); - mBOOL DLLINTERNAL set(const char *key, const char *value); - void DLLINTERNAL show(void); -}; - -#endif /* CONF_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// conf_meta.h - configfile reading + +// Modeled after mutt/init.[ch]. + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 CONF_META_H +#define CONF_META_H + +#include "types_meta.h" // mBOOL +#include "new_baseclass.h" +#include "comp_dep.h" + +// Max length of line in config file. +#define MAX_CONF_LEN 1024 + +// Supported config value-types. +typedef enum { + CF_NONE=0, + CF_INT, + CF_BOOL, + CF_STR, + CF_PATH, +#if 0 + CF_CVAR, + CF_CMD, +#endif +} cf_type_t; + +//typedef mBOOL (*SETOPT_FN) (char *key, char *value); + +typedef struct option_s { + char *name; // option name + cf_type_t type; // option type + void *dest; // addr of destination variable, or handler function + char *init; // initial value, as a string, just as config file would +} option_t; + +class MConfig : public class_metamod_new { + private: + // data + option_t *list; + char *filename; + // functions + option_t * DLLINTERNAL find(const char *lookup); + mBOOL DLLINTERNAL set(option_t *setp, const char *value); + // Private; to satisfy -Weffc++ "has pointer data members but does + // not override" copy/assignment constructor. + void operator=(const MConfig &src); + MConfig(const MConfig &src); + public: + // contructor + MConfig(void) DLLINTERNAL; + // data + int debuglevel; // to use for meta_debug + char *gamedll; // string if specified in config.ini + char *plugins_file; // ie metamod.ini, plugins.ini + char *exec_cfg; // ie metaexec.cfg, exec.cfg + int autodetect; // autodetection of gamedll (Metamod-All-Support patch) + int clientmeta; // control 'meta' client-command + // functions + void DLLINTERNAL init(option_t *global_options); + mBOOL DLLINTERNAL load(const char *filename); + mBOOL DLLINTERNAL set(const char *key, const char *value); + void DLLINTERNAL show(void); +}; + +#endif /* CONF_META_H */ diff --git a/src/metamod/dllapi.h b/src/metamod/dllapi.h index f04d542..186edac 100644 --- a/src/metamod/dllapi.h +++ b/src/metamod/dllapi.h @@ -1,121 +1,121 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// dllapi.h - prototypes and typedefs for Half-Life DLL API routines - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 DLLAPI_H -#define DLLAPI_H - -#include "sdk_util.h" // BOOL -#include "osdep.h" // DLLEXPORT, etc - -// Typedefs for these are provided in SDK engine/eiface.h, but I didn't -// like the names (APIFUNCTION, APIFUNCTION2, NEW_DLL_FUNCTIONS_FN). -typedef int (*GETENTITYAPI_FN) (DLL_FUNCTIONS *pFunctionTable, int interfaceVersion); -typedef int (*GETENTITYAPI2_FN) (DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); -typedef int (*GETNEWDLLFUNCTIONS_FN) (NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); - -// From SDK dlls/cbase.h: -C_DLLEXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); -C_DLLEXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); - -// No example in SDK.. -// From Adminmod dll.cpp: -C_DLLEXPORT int GetNewDLLFunctions( NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion ); - -// Typedefs for the above functions: - -typedef void (*FN_GAMEINIT) ( void ); -typedef int (*FN_DISPATCHSPAWN) ( edict_t *pent ); -typedef void (*FN_DISPATCHTHINK) ( edict_t *pent ); -typedef void (*FN_DISPATCHUSE) ( edict_t *pentUsed, edict_t *pentOther ); -typedef void (*FN_DISPATCHTOUCH) ( edict_t *pentTouched, edict_t *pentOther ); -typedef void (*FN_DISPATCHBLOCKED) ( edict_t *pentBlocked, edict_t *pentOther ); -typedef void (*FN_DISPATCHKEYVALUE) ( edict_t *pentKeyvalue, KeyValueData *pkvd ); -typedef void (*FN_DISPATCHSAVE) ( edict_t *pent, SAVERESTOREDATA *pSaveData ); -typedef int (*FN_DISPATCHRESTORE) ( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); -typedef void (*FN_DISPATCHOBJECTCOLLISIONBOX) ( edict_t *pent ); -typedef void (*FN_SAVEWRITEFIELDS) ( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); -typedef void (*FN_SAVEREADFIELDS) ( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); -typedef void (*FN_SAVEGLOBALSTATE) ( SAVERESTOREDATA *pSaveData ); -typedef void (*FN_RESTOREGLOBALSTATE) ( SAVERESTOREDATA *pSaveData ); -typedef void (*FN_RESETGLOBALSTATE) ( void ); - -typedef qboolean (*FN_CLIENTCONNECT) ( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); -typedef void (*FN_CLIENTDISCONNECT) ( edict_t *pEntity ); -typedef void (*FN_CLIENTKILL) ( edict_t *pEntity ); -typedef void (*FN_CLIENTPUTINSERVER) ( edict_t *pEntity ); -typedef void (*FN_CLIENTCOMMAND) ( edict_t *pEntity ); -typedef void (*FN_CLIENTUSERINFOCHANGED) ( edict_t *pEntity, char *infobuffer ); -typedef void (*FN_SERVERACTIVATE) ( edict_t *pEdictList, int edictCount, int clientMax ); -typedef void (*FN_SERVERDEACTIVATE) ( void ); -typedef void (*FN_PLAYERPRETHINK) ( edict_t *pEntity ); -typedef void (*FN_PLAYERPOSTTHINK) ( edict_t *pEntity ); -typedef void (*FN_STARTFRAME) ( void ); -typedef void (*FN_PARMSNEWLEVEL) ( void ); -typedef void (*FN_PARMSCHANGELEVEL) ( void ); -typedef const char *(*FN_GETGAMEDESCRIPTION) ( void ); -typedef void (*FN_PLAYERCUSTOMIZATION) ( edict_t *pEntity, customization_t *pCust ); -typedef void (*FN_SPECTATORCONNECT) ( edict_t *pEntity ); -typedef void (*FN_SPECTATORDISCONNECT) ( edict_t *pEntity ); -typedef void (*FN_SPECTATORTHINK) ( edict_t *pEntity ); -typedef void (*FN_SYS_ERROR) ( const char *error_string ); - -typedef void (*FN_PM_MOVE) ( struct playermove_s *ppmove, int server ); -typedef void (*FN_PM_INIT) ( struct playermove_s *ppmove ); -typedef char (*FN_PM_FINDTEXTURETYPE) ( char *name ); - -typedef void (*FN_SETUPVISIBILITY) ( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ); -typedef void (*FN_UPDATECLIENTDATA) ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); -typedef int (*FN_ADDTOFULLPACK) ( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); -typedef void (*FN_CREATEBASELINE) ( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); -typedef void (*FN_REGISTERENCODERS) ( void ); -typedef int (*FN_GETWEAPONDATA) ( struct edict_s *player, struct weapon_data_s *info ); -typedef void (*FN_CMDSTART) ( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); -typedef void (*FN_CMDEND) ( const edict_t *player ); -typedef int (*FN_CONNECTIONLESSPACKET) ( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); -typedef int (*FN_GETHULLBOUNDS) ( int hullnumber, float *mins, float *maxs ); -typedef void (*FN_CREATEINSTANCEDBASELINES) ( void ); -typedef int (*FN_INCONSISTENTFILE) ( const edict_t *player, const char *filename, char *disconnect_message ); -typedef int (*FN_ALLOWLAGCOMPENSATION) ( void ); - -typedef void (*FN_ONFREEENTPRIVATEDATA) (edict_t *pEnt); -typedef void (*FN_GAMESHUTDOWN) (void); -typedef int (*FN_SHOULDCOLLIDE) (edict_t *pentTouched, edict_t *pentOther); -// Added 2005/08/11 (no SDK update): -typedef void (*FN_CVARVALUE)(const edict_t *pEnt, const char *value); -// Added 2005/11/21 (no SDK update): -typedef void (*FN_CVARVALUE2)(const edict_t *pEnt, int requestID, const char *cvarName, const char *value); - -#endif /* DLLAPI_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// dllapi.h - prototypes and typedefs for Half-Life DLL API routines + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 DLLAPI_H +#define DLLAPI_H + +#include "sdk_util.h" // BOOL +#include "osdep.h" // DLLEXPORT, etc + +// Typedefs for these are provided in SDK engine/eiface.h, but I didn't +// like the names (APIFUNCTION, APIFUNCTION2, NEW_DLL_FUNCTIONS_FN). +typedef int (*GETENTITYAPI_FN) (DLL_FUNCTIONS *pFunctionTable, int interfaceVersion); +typedef int (*GETENTITYAPI2_FN) (DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); +typedef int (*GETNEWDLLFUNCTIONS_FN) (NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion); + +// From SDK dlls/cbase.h: +C_DLLEXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion ); +C_DLLEXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); + +// No example in SDK.. +// From Adminmod dll.cpp: +C_DLLEXPORT int GetNewDLLFunctions( NEW_DLL_FUNCTIONS *pNewFunctionTable, int *interfaceVersion ); + +// Typedefs for the above functions: + +typedef void (*FN_GAMEINIT) ( void ); +typedef int (*FN_DISPATCHSPAWN) ( edict_t *pent ); +typedef void (*FN_DISPATCHTHINK) ( edict_t *pent ); +typedef void (*FN_DISPATCHUSE) ( edict_t *pentUsed, edict_t *pentOther ); +typedef void (*FN_DISPATCHTOUCH) ( edict_t *pentTouched, edict_t *pentOther ); +typedef void (*FN_DISPATCHBLOCKED) ( edict_t *pentBlocked, edict_t *pentOther ); +typedef void (*FN_DISPATCHKEYVALUE) ( edict_t *pentKeyvalue, KeyValueData *pkvd ); +typedef void (*FN_DISPATCHSAVE) ( edict_t *pent, SAVERESTOREDATA *pSaveData ); +typedef int (*FN_DISPATCHRESTORE) ( edict_t *pent, SAVERESTOREDATA *pSaveData, int globalEntity ); +typedef void (*FN_DISPATCHOBJECTCOLLISIONBOX) ( edict_t *pent ); +typedef void (*FN_SAVEWRITEFIELDS) ( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); +typedef void (*FN_SAVEREADFIELDS) ( SAVERESTOREDATA *pSaveData, const char *pname, void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCount ); +typedef void (*FN_SAVEGLOBALSTATE) ( SAVERESTOREDATA *pSaveData ); +typedef void (*FN_RESTOREGLOBALSTATE) ( SAVERESTOREDATA *pSaveData ); +typedef void (*FN_RESETGLOBALSTATE) ( void ); + +typedef qboolean (*FN_CLIENTCONNECT) ( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128]); +typedef void (*FN_CLIENTDISCONNECT) ( edict_t *pEntity ); +typedef void (*FN_CLIENTKILL) ( edict_t *pEntity ); +typedef void (*FN_CLIENTPUTINSERVER) ( edict_t *pEntity ); +typedef void (*FN_CLIENTCOMMAND) ( edict_t *pEntity ); +typedef void (*FN_CLIENTUSERINFOCHANGED) ( edict_t *pEntity, char *infobuffer ); +typedef void (*FN_SERVERACTIVATE) ( edict_t *pEdictList, int edictCount, int clientMax ); +typedef void (*FN_SERVERDEACTIVATE) ( void ); +typedef void (*FN_PLAYERPRETHINK) ( edict_t *pEntity ); +typedef void (*FN_PLAYERPOSTTHINK) ( edict_t *pEntity ); +typedef void (*FN_STARTFRAME) ( void ); +typedef void (*FN_PARMSNEWLEVEL) ( void ); +typedef void (*FN_PARMSCHANGELEVEL) ( void ); +typedef const char *(*FN_GETGAMEDESCRIPTION) ( void ); +typedef void (*FN_PLAYERCUSTOMIZATION) ( edict_t *pEntity, customization_t *pCust ); +typedef void (*FN_SPECTATORCONNECT) ( edict_t *pEntity ); +typedef void (*FN_SPECTATORDISCONNECT) ( edict_t *pEntity ); +typedef void (*FN_SPECTATORTHINK) ( edict_t *pEntity ); +typedef void (*FN_SYS_ERROR) ( const char *error_string ); + +typedef void (*FN_PM_MOVE) ( struct playermove_s *ppmove, int server ); +typedef void (*FN_PM_INIT) ( struct playermove_s *ppmove ); +typedef char (*FN_PM_FINDTEXTURETYPE) ( char *name ); + +typedef void (*FN_SETUPVISIBILITY) ( edict_t *pViewEntity, edict_t *pClient, unsigned char **pvs, unsigned char **pas ); +typedef void (*FN_UPDATECLIENTDATA) ( const struct edict_s *ent, int sendweapons, struct clientdata_s *cd ); +typedef int (*FN_ADDTOFULLPACK) ( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet ); +typedef void (*FN_CREATEBASELINE) ( int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, vec3_t player_mins, vec3_t player_maxs ); +typedef void (*FN_REGISTERENCODERS) ( void ); +typedef int (*FN_GETWEAPONDATA) ( struct edict_s *player, struct weapon_data_s *info ); +typedef void (*FN_CMDSTART) ( const edict_t *player, const struct usercmd_s *cmd, unsigned int random_seed ); +typedef void (*FN_CMDEND) ( const edict_t *player ); +typedef int (*FN_CONNECTIONLESSPACKET) ( const struct netadr_s *net_from, const char *args, char *response_buffer, int *response_buffer_size ); +typedef int (*FN_GETHULLBOUNDS) ( int hullnumber, float *mins, float *maxs ); +typedef void (*FN_CREATEINSTANCEDBASELINES) ( void ); +typedef int (*FN_INCONSISTENTFILE) ( const edict_t *player, const char *filename, char *disconnect_message ); +typedef int (*FN_ALLOWLAGCOMPENSATION) ( void ); + +typedef void (*FN_ONFREEENTPRIVATEDATA) (edict_t *pEnt); +typedef void (*FN_GAMESHUTDOWN) (void); +typedef int (*FN_SHOULDCOLLIDE) (edict_t *pentTouched, edict_t *pentOther); +// Added 2005/08/11 (no SDK update): +typedef void (*FN_CVARVALUE)(const edict_t *pEnt, const char *value); +// Added 2005/11/21 (no SDK update): +typedef void (*FN_CVARVALUE2)(const edict_t *pEnt, int requestID, const char *cvarName, const char *value); + +#endif /* DLLAPI_H */ diff --git a/src/metamod/engine_api.h b/src/metamod/engine_api.h index 7b2f10f..2a3bce7 100644 --- a/src/metamod/engine_api.h +++ b/src/metamod/engine_api.h @@ -1,241 +1,241 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// engine_api.h - prototypes and typedefs for Half-Life engine functions - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 ENGINE_API_H -#define ENGINE_API_H - -#include // why? -#include "comp_dep.h" - -// Plugin's GetEngineFunctions, called by metamod. -typedef int (*GET_ENGINE_FUNCTIONS_FN) (enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion); - -// According to SDK engine/eiface.h: -//! enginefuncs_t -//! ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 -#define ENGINE_INTERFACE_VERSION 138 - -// Protect against other projects which use this include file but use the -// normal enginefuncs_t type for their meta_engfuncs. -#ifdef __METAMOD_BUILD__ -# include "meta_eiface.h" // meta_enginefuncs_t -extern meta_enginefuncs_t meta_engfuncs DLLHIDDEN; -#else -extern enginefuncs_t meta_engfuncs DLLHIDDEN; -#endif - -// Typedefs for the above functions: - -typedef int (*FN_PRECACHEMODEL) (char* s); -typedef int (*FN_PRECACHESOUND) (char* s); -typedef void (*FN_SETMODEL) (edict_t *e, const char *m); -typedef int (*FN_MODELINDEX) (const char *m); -typedef int (*FN_MODELFRAMES) (int modelIndex); -typedef void (*FN_SETSIZE) (edict_t *e, const float *rgflMin, const float *rgflMax); -typedef void (*FN_CHANGELEVEL) (char *s1, char *s2); -typedef void (*FN_GETSPAWNPARMS) (edict_t *ent); -typedef void (*FN_SAVESPAWNPARMS) (edict_t *ent); -typedef float (*FN_VECTOYAW) (const float *rgflVector); -typedef void (*FN_VECTOANGLES) (const float *rgflVectorIn, float *rgflVectorOut); -typedef void (*FN_MOVETOORIGIN) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); -typedef void (*FN_CHANGEYAW) (edict_t *ent); -typedef void (*FN_CHANGEPITCH) (edict_t *ent); -typedef edict_t * (*FN_FINDENTITYBYSTRING) (edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue); -typedef int (*FN_GETENTITYILLUM) (edict_t *pEnt); -typedef edict_t * (*FN_FINDENTITYINSPHERE) (edict_t *pEdictStartSearchAfter, const float *org, float rad); -typedef edict_t * (*FN_FINDCLIENTINPVS) (edict_t *pEdict); -typedef edict_t * (*FN_ENTITIESINPVS) (edict_t *pplayer); -typedef void (*FN_MAKEVECTORS) (const float *rgflVector); -typedef void (*FN_ANGLEVECTORS) (const float *rgflVector, float *forward, float *right, float *up); -typedef edict_t * (*FN_CREATEENTITY) (void); -typedef void (*FN_REMOVEENTITY) (edict_t *e); -typedef edict_t * (*FN_CREATENAMEDENTITY) (int className); -typedef void (*FN_MAKESTATIC) (edict_t *ent); -typedef int (*FN_ENTISONFLOOR) (edict_t *e); -typedef int (*FN_DROPTOFLOOR) (edict_t *e); -typedef int (*FN_WALKMOVE) (edict_t *ent, float yaw, float dist, int iMode); -typedef void (*FN_SETORIGIN) (edict_t *e, const float *rgflOrigin); -typedef void (*FN_EMITSOUND) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch); -typedef void (*FN_EMITAMBIENTSOUND) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); -typedef void (*FN_TRACELINE) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); -typedef void (*FN_TRACETOSS) (edict_t *pent, edict_t *pentToIgnore, TraceResult *ptr); -typedef int (*FN_TRACEMONSTERHULL) (edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); -typedef void (*FN_TRACEHULL) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); -typedef void (*FN_TRACEMODEL) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); -typedef const char * (*FN_TRACETEXTURE) (edict_t *pTextureEntity, const float *v1, const float *v2 ); -typedef void (*FN_TRACESPHERE) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); -typedef void (*FN_GETAIMVECTOR) (edict_t *ent, float speed, float *rgflReturn); -typedef void (*FN_SERVERCOMMAND) (char *str); -typedef void (*FN_SERVEREXECUTE) (void); -typedef void (*FN_CLIENTCOMMAND_ENG) (edict_t *pEdict, char *szFmt, ...); -typedef void (*FN_PARTICLEEFFECT) (const float *org, const float *dir, float color, float count); -typedef void (*FN_LIGHTSTYLE) (int style, char *val); -typedef int (*FN_DECALINDEX) (const char *name); -typedef int (*FN_POINTCONTENTS) (const float *rgflVector); -typedef void (*FN_MESSAGEBEGIN) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); -typedef void (*FN_MESSAGEEND) (void); -typedef void (*FN_WRITEBYTE) (int iValue); -typedef void (*FN_WRITECHAR) (int iValue); -typedef void (*FN_WRITESHORT) (int iValue); -typedef void (*FN_WRITELONG) (int iValue); -typedef void (*FN_WRITEANGLE) (float flValue); -typedef void (*FN_WRITECOORD) (float flValue); -typedef void (*FN_WRITESTRING) (const char *sz); -typedef void (*FN_WRITEENTITY) (int iValue); -typedef void (*FN_CVARREGISTER) (cvar_t *pCvar); -typedef float (*FN_CVARGETFLOAT) (const char *szVarName); -typedef const char * (*FN_CVARGETSTRING) (const char *szVarName); -typedef void (*FN_CVARSETFLOAT) (const char *szVarName, float flValue); -typedef void (*FN_CVARSETSTRING) (const char *szVarName, const char *szValue); -typedef void (*FN_ALERTMESSAGE) (ALERT_TYPE atype, char *szFmt, ...); -#ifdef HLSDK_3_2_OLD_EIFACE -typedef void (*FN_ENGINEFPRINTF) (FILE *pfile, char *szFmt, ...); -typedef void * (*FN_PVALLOCENTPRIVATEDATA) (edict_t *pEdict, long cb); -#else -typedef void (*FN_ENGINEFPRINTF) (void *pfile, char *szFmt, ...); -typedef void * (*FN_PVALLOCENTPRIVATEDATA) (edict_t *pEdict, int32_t cb); -#endif -typedef void * (*FN_PVENTPRIVATEDATA) (edict_t *pEdict); -typedef void (*FN_FREEENTPRIVATEDATA) (edict_t *pEdict); -typedef const char * (*FN_SZFROMINDEX) (int iString); -typedef int (*FN_ALLOCSTRING) (const char *szValue); -typedef struct entvars_s * (*FN_GETVARSOFENT) (edict_t *pEdict); -typedef edict_t * (*FN_PENTITYOFENTOFFSET) (int iEntOffset); -typedef int (*FN_ENTOFFSETOFPENTITY) (const edict_t *pEdict); -typedef int (*FN_INDEXOFEDICT) (const edict_t *pEdict); -typedef edict_t * (*FN_PENTITYOFENTINDEX) (int iEntIndex); -typedef edict_t * (*FN_FINDENTITYBYVARS) (struct entvars_s *pvars); -typedef void * (*FN_GETMODELPTR) (edict_t *pEdict); -typedef int (*FN_REGUSERMSG) (const char *pszName, int iSize); -typedef void (*FN_ANIMATIONAUTOMOVE) (const edict_t *pEdict, float flTime); -typedef void (*FN_GETBONEPOSITION) (const edict_t *pEdict, int iBone, float *rgflOrigin, float *rgflAngles ); -#ifdef HLSDK_3_2_OLD_EIFACE -typedef unsigned long (*FN_FUNCTIONFROMNAME) ( const char *pName ); -typedef const char * (*FN_NAMEFORFUNCTION) ( unsigned long function ); -#else -typedef uint32_t (*FN_FUNCTIONFROMNAME) ( const char *pName ); -typedef const char * (*FN_NAMEFORFUNCTION) ( uint32_t function ); -#endif -typedef void (*FN_CLIENTPRINTF) ( edict_t *pEdict, PRINT_TYPE ptype, const char *szMsg ); -typedef void (*FN_SERVERPRINT) ( const char *szMsg ); -typedef const char * (*FN_CMD_ARGS) ( void ); -typedef const char * (*FN_CMD_ARGV) ( int argc ); -typedef int (*FN_CMD_ARGC) ( void ); -typedef void (*FN_GETATTACHMENT) (const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles ); -typedef void (*FN_CRC32_INIT) (CRC32_t *pulCRC); -typedef void (*FN_CRC32_PROCESSBUFFER) (CRC32_t *pulCRC, void *p, int len); -typedef void (*FN_CRC32_PROCESSBYTE) (CRC32_t *pulCRC, unsigned char ch); -typedef CRC32_t (*FN_CRC32_FINAL) (CRC32_t pulCRC); -#ifdef HLSDK_3_2_OLD_EIFACE -typedef long (*FN_RANDOMLONG) (long lLow, long lHigh); -#else -typedef int32_t (*FN_RANDOMLONG) (int32_t lLow, int32_t lHigh); -#endif -typedef float (*FN_RANDOMFLOAT) (float flLow, float flHigh); -typedef void (*FN_SETVIEW) (const edict_t *pClient, const edict_t *pViewent ); -typedef float (*FN_TIME) ( void ); -typedef void (*FN_CROSSHAIRANGLE) (const edict_t *pClient, float pitch, float yaw); -typedef byte * (*FN_LOADFILEFORME) (char *filename, int *pLength); -typedef void (*FN_FREEFILE) (void *buffer); -typedef void (*FN_ENDSECTION) (const char *pszSectionName); -typedef int (*FN_COMPAREFILETIME) (char *filename1, char *filename2, int *iCompare); -typedef void (*FN_GETGAMEDIR) (char *szGetGameDir); -typedef void (*FN_CVAR_REGISTERVARIABLE) (cvar_t *variable); -typedef void (*FN_FADECLIENTVOLUME) (const edict_t *pEdict, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); -typedef void (*FN_SETCLIENTMAXSPEED) (const edict_t *pEdict, float fNewMaxspeed); -typedef edict_t * (*FN_CREATEFAKECLIENT) (const char *netname); -typedef void (*FN_RUNPLAYERMOVE) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, byte msec ); -typedef int (*FN_NUMBEROFENTITIES) (void); -typedef char * (*FN_GETINFOKEYBUFFER) (edict_t *e); -typedef char * (*FN_INFOKEYVALUE) (char *infobuffer, char *key); -typedef void (*FN_SETKEYVALUE) (char *infobuffer, char *key, char *value); -typedef void (*FN_SETCLIENTKEYVALUE) (int clientIndex, char *infobuffer, char *key, char *value); -typedef int (*FN_ISMAPVALID) (char *filename); -typedef void (*FN_STATICDECAL) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); -typedef int (*FN_PRECACHEGENERIC) (char *s); -typedef int (*FN_GETPLAYERUSERID) (edict_t *e ); -typedef void (*FN_BUILDSOUNDMSG) (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); -typedef int (*FN_ISDEDICATEDSERVER) (void); -typedef cvar_t * (*FN_CVARGETPOINTER) (const char *szVarName); -typedef unsigned int (*FN_GETPLAYERWONID) (edict_t *e); -typedef void (*FN_INFO_REMOVEKEY) ( char *s, const char *key ); -typedef const char * (*FN_GETPHYSICSKEYVALUE) ( const edict_t *pClient, const char *key ); -typedef void (*FN_SETPHYSICSKEYVALUE) ( const edict_t *pClient, const char *key, const char *value ); -typedef const char * (*FN_GETPHYSICSINFOSTRING) ( const edict_t *pClient ); -typedef unsigned short (*FN_PRECACHEEVENT) ( int type, const char *psz ); -typedef void (*FN_PLAYBACKEVENT) ( 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 ); -typedef unsigned char * (*FN_SETFATPVS) ( float *org ); -typedef unsigned char * (*FN_SETFATPAS) ( float *org ); -typedef int (*FN_CHECKVISIBILITY) ( const edict_t *entity, unsigned char *pset ); -typedef void (*FN_DELTASETFIELD) ( struct delta_s *pFields, const char *fieldname ); -typedef void (*FN_DELTAUNSETFIELD) ( struct delta_s *pFields, const char *fieldname ); -typedef void (*FN_DELTAADDENCODER) ( char *name, void (*conditionalencode)( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) ); -typedef int (*FN_GETCURRENTPLAYER) ( void ); -typedef int (*FN_CANSKIPPLAYER) ( const edict_t *player ); -typedef int (*FN_DELTAFINDFIELD) ( struct delta_s *pFields, const char *fieldname ); -typedef void (*FN_DELTASETFIELDBYINDEX) ( struct delta_s *pFields, int fieldNumber ); -typedef void (*FN_DELTAUNSETFIELDBYINDEX) ( struct delta_s *pFields, int fieldNumber ); -typedef void (*FN_SETGROUPMASK) ( int mask, int op ); -typedef int (*FN_CREATEINSTANCEDBASELINE) ( int classname, struct entity_state_s *baseline ); -typedef void (*FN_CVAR_DIRECTSET) ( struct cvar_s *var, char *value ); -typedef void (*FN_FORCEUNMODIFIED) ( FORCE_TYPE type, float *mins, float *maxs, const char *filename ); -typedef void (*FN_GETPLAYERSTATS) ( const edict_t *pClient, int *ping, int *packet_loss ); -typedef void (*FN_ADDSERVERCOMMAND) ( char *cmd_name, void (*function) (void) ); -// Added in SDK 2.2: -typedef qboolean (*FN_VOICE_GETCLIENTLISTENING) (int iReceiver, int iSender); -typedef qboolean (*FN_VOICE_SETCLIENTLISTENING) (int iReceiver, int iSender, qboolean bListen); -// Added for HL 1109 (no SDK update): -typedef const char * (*FN_GETPLAYERAUTHID) (edict_t *e); -// Added 2003/11/10 (no SDK update): -typedef sequenceEntry_s * (*FN_SEQUENCEGET) (const char* fileName, const char* entryName); -typedef sentenceEntry_s * (*FN_SEQUENCEPICKSENTENCE) (const char* groupName, int pickMethod, int *picked); -typedef int (*FN_GETFILESIZE) (char *filename); -typedef unsigned int (*FN_GETAPPROXWAVEPLAYLEN) (const char *filepath); -typedef int (*FN_ISCAREERMATCH) (void); -typedef int (*FN_GETLOCALIZEDSTRINGLENGTH) (const char *label); -typedef void (*FN_REGISTERTUTORMESSAGESHOWN) (int mid); -typedef int (*FN_GETTIMESTUTORMESSAGESHOWN) (int mid); -typedef void (*FN_PROCESSTUTORMESSAGEDECAYBUFFER) (int *buffer, int bufferLength); -typedef void (*FN_CONSTRUCTTUTORMESSAGEDECAYBUFFER) (int *buffer, int bufferLength); -typedef void (*FN_RESETTUTORMESSAGEDECAYDATA) (void); -// Added 2005/08/11 (no SDK update): -typedef void (*FN_QUERYCLIENTCVARVALUE) ( const edict_t *player, const char *cvarName ); -// Added 2005/11/21 (no SDK update): -typedef void (*FN_QUERYCLIENTCVARVALUE2) ( const edict_t *player, const char *cvarName, int requestID ); -// Added 2009/06/17 (no SDK update): -typedef void (*FN_ENGCHECKPARM) ( const char *pchCmdLineToken, char **pchNextVal ); - -#endif /* ENGINE_API_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// engine_api.h - prototypes and typedefs for Half-Life engine functions + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 ENGINE_API_H +#define ENGINE_API_H + +#include // why? +#include "comp_dep.h" + +// Plugin's GetEngineFunctions, called by metamod. +typedef int (*GET_ENGINE_FUNCTIONS_FN) (enginefuncs_t *pengfuncsFromEngine, int *interfaceVersion); + +// According to SDK engine/eiface.h: +//! enginefuncs_t +//! ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 +#define ENGINE_INTERFACE_VERSION 138 + +// Protect against other projects which use this include file but use the +// normal enginefuncs_t type for their meta_engfuncs. +#ifdef __METAMOD_BUILD__ +# include "meta_eiface.h" // meta_enginefuncs_t +extern meta_enginefuncs_t meta_engfuncs DLLHIDDEN; +#else +extern enginefuncs_t meta_engfuncs DLLHIDDEN; +#endif + +// Typedefs for the above functions: + +typedef int (*FN_PRECACHEMODEL) (char* s); +typedef int (*FN_PRECACHESOUND) (char* s); +typedef void (*FN_SETMODEL) (edict_t *e, const char *m); +typedef int (*FN_MODELINDEX) (const char *m); +typedef int (*FN_MODELFRAMES) (int modelIndex); +typedef void (*FN_SETSIZE) (edict_t *e, const float *rgflMin, const float *rgflMax); +typedef void (*FN_CHANGELEVEL) (char *s1, char *s2); +typedef void (*FN_GETSPAWNPARMS) (edict_t *ent); +typedef void (*FN_SAVESPAWNPARMS) (edict_t *ent); +typedef float (*FN_VECTOYAW) (const float *rgflVector); +typedef void (*FN_VECTOANGLES) (const float *rgflVectorIn, float *rgflVectorOut); +typedef void (*FN_MOVETOORIGIN) (edict_t *ent, const float *pflGoal, float dist, int iMoveType); +typedef void (*FN_CHANGEYAW) (edict_t *ent); +typedef void (*FN_CHANGEPITCH) (edict_t *ent); +typedef edict_t * (*FN_FINDENTITYBYSTRING) (edict_t *pEdictStartSearchAfter, const char *pszField, const char *pszValue); +typedef int (*FN_GETENTITYILLUM) (edict_t *pEnt); +typedef edict_t * (*FN_FINDENTITYINSPHERE) (edict_t *pEdictStartSearchAfter, const float *org, float rad); +typedef edict_t * (*FN_FINDCLIENTINPVS) (edict_t *pEdict); +typedef edict_t * (*FN_ENTITIESINPVS) (edict_t *pplayer); +typedef void (*FN_MAKEVECTORS) (const float *rgflVector); +typedef void (*FN_ANGLEVECTORS) (const float *rgflVector, float *forward, float *right, float *up); +typedef edict_t * (*FN_CREATEENTITY) (void); +typedef void (*FN_REMOVEENTITY) (edict_t *e); +typedef edict_t * (*FN_CREATENAMEDENTITY) (int className); +typedef void (*FN_MAKESTATIC) (edict_t *ent); +typedef int (*FN_ENTISONFLOOR) (edict_t *e); +typedef int (*FN_DROPTOFLOOR) (edict_t *e); +typedef int (*FN_WALKMOVE) (edict_t *ent, float yaw, float dist, int iMode); +typedef void (*FN_SETORIGIN) (edict_t *e, const float *rgflOrigin); +typedef void (*FN_EMITSOUND) (edict_t *entity, int channel, const char *sample, /*int*/float volume, float attenuation, int fFlags, int pitch); +typedef void (*FN_EMITAMBIENTSOUND) (edict_t *entity, float *pos, const char *samp, float vol, float attenuation, int fFlags, int pitch); +typedef void (*FN_TRACELINE) (const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); +typedef void (*FN_TRACETOSS) (edict_t *pent, edict_t *pentToIgnore, TraceResult *ptr); +typedef int (*FN_TRACEMONSTERHULL) (edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr); +typedef void (*FN_TRACEHULL) (const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr); +typedef void (*FN_TRACEMODEL) (const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr); +typedef const char * (*FN_TRACETEXTURE) (edict_t *pTextureEntity, const float *v1, const float *v2 ); +typedef void (*FN_TRACESPHERE) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr); +typedef void (*FN_GETAIMVECTOR) (edict_t *ent, float speed, float *rgflReturn); +typedef void (*FN_SERVERCOMMAND) (char *str); +typedef void (*FN_SERVEREXECUTE) (void); +typedef void (*FN_CLIENTCOMMAND_ENG) (edict_t *pEdict, char *szFmt, ...); +typedef void (*FN_PARTICLEEFFECT) (const float *org, const float *dir, float color, float count); +typedef void (*FN_LIGHTSTYLE) (int style, char *val); +typedef int (*FN_DECALINDEX) (const char *name); +typedef int (*FN_POINTCONTENTS) (const float *rgflVector); +typedef void (*FN_MESSAGEBEGIN) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); +typedef void (*FN_MESSAGEEND) (void); +typedef void (*FN_WRITEBYTE) (int iValue); +typedef void (*FN_WRITECHAR) (int iValue); +typedef void (*FN_WRITESHORT) (int iValue); +typedef void (*FN_WRITELONG) (int iValue); +typedef void (*FN_WRITEANGLE) (float flValue); +typedef void (*FN_WRITECOORD) (float flValue); +typedef void (*FN_WRITESTRING) (const char *sz); +typedef void (*FN_WRITEENTITY) (int iValue); +typedef void (*FN_CVARREGISTER) (cvar_t *pCvar); +typedef float (*FN_CVARGETFLOAT) (const char *szVarName); +typedef const char * (*FN_CVARGETSTRING) (const char *szVarName); +typedef void (*FN_CVARSETFLOAT) (const char *szVarName, float flValue); +typedef void (*FN_CVARSETSTRING) (const char *szVarName, const char *szValue); +typedef void (*FN_ALERTMESSAGE) (ALERT_TYPE atype, char *szFmt, ...); +#ifdef HLSDK_3_2_OLD_EIFACE +typedef void (*FN_ENGINEFPRINTF) (FILE *pfile, char *szFmt, ...); +typedef void * (*FN_PVALLOCENTPRIVATEDATA) (edict_t *pEdict, long cb); +#else +typedef void (*FN_ENGINEFPRINTF) (void *pfile, char *szFmt, ...); +typedef void * (*FN_PVALLOCENTPRIVATEDATA) (edict_t *pEdict, int32_t cb); +#endif +typedef void * (*FN_PVENTPRIVATEDATA) (edict_t *pEdict); +typedef void (*FN_FREEENTPRIVATEDATA) (edict_t *pEdict); +typedef const char * (*FN_SZFROMINDEX) (int iString); +typedef int (*FN_ALLOCSTRING) (const char *szValue); +typedef struct entvars_s * (*FN_GETVARSOFENT) (edict_t *pEdict); +typedef edict_t * (*FN_PENTITYOFENTOFFSET) (int iEntOffset); +typedef int (*FN_ENTOFFSETOFPENTITY) (const edict_t *pEdict); +typedef int (*FN_INDEXOFEDICT) (const edict_t *pEdict); +typedef edict_t * (*FN_PENTITYOFENTINDEX) (int iEntIndex); +typedef edict_t * (*FN_FINDENTITYBYVARS) (struct entvars_s *pvars); +typedef void * (*FN_GETMODELPTR) (edict_t *pEdict); +typedef int (*FN_REGUSERMSG) (const char *pszName, int iSize); +typedef void (*FN_ANIMATIONAUTOMOVE) (const edict_t *pEdict, float flTime); +typedef void (*FN_GETBONEPOSITION) (const edict_t *pEdict, int iBone, float *rgflOrigin, float *rgflAngles ); +#ifdef HLSDK_3_2_OLD_EIFACE +typedef unsigned long (*FN_FUNCTIONFROMNAME) ( const char *pName ); +typedef const char * (*FN_NAMEFORFUNCTION) ( unsigned long function ); +#else +typedef uint32_t (*FN_FUNCTIONFROMNAME) ( const char *pName ); +typedef const char * (*FN_NAMEFORFUNCTION) ( uint32_t function ); +#endif +typedef void (*FN_CLIENTPRINTF) ( edict_t *pEdict, PRINT_TYPE ptype, const char *szMsg ); +typedef void (*FN_SERVERPRINT) ( const char *szMsg ); +typedef const char * (*FN_CMD_ARGS) ( void ); +typedef const char * (*FN_CMD_ARGV) ( int argc ); +typedef int (*FN_CMD_ARGC) ( void ); +typedef void (*FN_GETATTACHMENT) (const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles ); +typedef void (*FN_CRC32_INIT) (CRC32_t *pulCRC); +typedef void (*FN_CRC32_PROCESSBUFFER) (CRC32_t *pulCRC, void *p, int len); +typedef void (*FN_CRC32_PROCESSBYTE) (CRC32_t *pulCRC, unsigned char ch); +typedef CRC32_t (*FN_CRC32_FINAL) (CRC32_t pulCRC); +#ifdef HLSDK_3_2_OLD_EIFACE +typedef long (*FN_RANDOMLONG) (long lLow, long lHigh); +#else +typedef int32_t (*FN_RANDOMLONG) (int32_t lLow, int32_t lHigh); +#endif +typedef float (*FN_RANDOMFLOAT) (float flLow, float flHigh); +typedef void (*FN_SETVIEW) (const edict_t *pClient, const edict_t *pViewent ); +typedef float (*FN_TIME) ( void ); +typedef void (*FN_CROSSHAIRANGLE) (const edict_t *pClient, float pitch, float yaw); +typedef byte * (*FN_LOADFILEFORME) (char *filename, int *pLength); +typedef void (*FN_FREEFILE) (void *buffer); +typedef void (*FN_ENDSECTION) (const char *pszSectionName); +typedef int (*FN_COMPAREFILETIME) (char *filename1, char *filename2, int *iCompare); +typedef void (*FN_GETGAMEDIR) (char *szGetGameDir); +typedef void (*FN_CVAR_REGISTERVARIABLE) (cvar_t *variable); +typedef void (*FN_FADECLIENTVOLUME) (const edict_t *pEdict, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds); +typedef void (*FN_SETCLIENTMAXSPEED) (const edict_t *pEdict, float fNewMaxspeed); +typedef edict_t * (*FN_CREATEFAKECLIENT) (const char *netname); +typedef void (*FN_RUNPLAYERMOVE) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, byte msec ); +typedef int (*FN_NUMBEROFENTITIES) (void); +typedef char * (*FN_GETINFOKEYBUFFER) (edict_t *e); +typedef char * (*FN_INFOKEYVALUE) (char *infobuffer, char *key); +typedef void (*FN_SETKEYVALUE) (char *infobuffer, char *key, char *value); +typedef void (*FN_SETCLIENTKEYVALUE) (int clientIndex, char *infobuffer, char *key, char *value); +typedef int (*FN_ISMAPVALID) (char *filename); +typedef void (*FN_STATICDECAL) ( const float *origin, int decalIndex, int entityIndex, int modelIndex ); +typedef int (*FN_PRECACHEGENERIC) (char *s); +typedef int (*FN_GETPLAYERUSERID) (edict_t *e ); +typedef void (*FN_BUILDSOUNDMSG) (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); +typedef int (*FN_ISDEDICATEDSERVER) (void); +typedef cvar_t * (*FN_CVARGETPOINTER) (const char *szVarName); +typedef unsigned int (*FN_GETPLAYERWONID) (edict_t *e); +typedef void (*FN_INFO_REMOVEKEY) ( char *s, const char *key ); +typedef const char * (*FN_GETPHYSICSKEYVALUE) ( const edict_t *pClient, const char *key ); +typedef void (*FN_SETPHYSICSKEYVALUE) ( const edict_t *pClient, const char *key, const char *value ); +typedef const char * (*FN_GETPHYSICSINFOSTRING) ( const edict_t *pClient ); +typedef unsigned short (*FN_PRECACHEEVENT) ( int type, const char *psz ); +typedef void (*FN_PLAYBACKEVENT) ( 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 ); +typedef unsigned char * (*FN_SETFATPVS) ( float *org ); +typedef unsigned char * (*FN_SETFATPAS) ( float *org ); +typedef int (*FN_CHECKVISIBILITY) ( const edict_t *entity, unsigned char *pset ); +typedef void (*FN_DELTASETFIELD) ( struct delta_s *pFields, const char *fieldname ); +typedef void (*FN_DELTAUNSETFIELD) ( struct delta_s *pFields, const char *fieldname ); +typedef void (*FN_DELTAADDENCODER) ( char *name, void (*conditionalencode)( struct delta_s *pFields, const unsigned char *from, const unsigned char *to ) ); +typedef int (*FN_GETCURRENTPLAYER) ( void ); +typedef int (*FN_CANSKIPPLAYER) ( const edict_t *player ); +typedef int (*FN_DELTAFINDFIELD) ( struct delta_s *pFields, const char *fieldname ); +typedef void (*FN_DELTASETFIELDBYINDEX) ( struct delta_s *pFields, int fieldNumber ); +typedef void (*FN_DELTAUNSETFIELDBYINDEX) ( struct delta_s *pFields, int fieldNumber ); +typedef void (*FN_SETGROUPMASK) ( int mask, int op ); +typedef int (*FN_CREATEINSTANCEDBASELINE) ( int classname, struct entity_state_s *baseline ); +typedef void (*FN_CVAR_DIRECTSET) ( struct cvar_s *var, char *value ); +typedef void (*FN_FORCEUNMODIFIED) ( FORCE_TYPE type, float *mins, float *maxs, const char *filename ); +typedef void (*FN_GETPLAYERSTATS) ( const edict_t *pClient, int *ping, int *packet_loss ); +typedef void (*FN_ADDSERVERCOMMAND) ( char *cmd_name, void (*function) (void) ); +// Added in SDK 2.2: +typedef qboolean (*FN_VOICE_GETCLIENTLISTENING) (int iReceiver, int iSender); +typedef qboolean (*FN_VOICE_SETCLIENTLISTENING) (int iReceiver, int iSender, qboolean bListen); +// Added for HL 1109 (no SDK update): +typedef const char * (*FN_GETPLAYERAUTHID) (edict_t *e); +// Added 2003/11/10 (no SDK update): +typedef sequenceEntry_s * (*FN_SEQUENCEGET) (const char* fileName, const char* entryName); +typedef sentenceEntry_s * (*FN_SEQUENCEPICKSENTENCE) (const char* groupName, int pickMethod, int *picked); +typedef int (*FN_GETFILESIZE) (char *filename); +typedef unsigned int (*FN_GETAPPROXWAVEPLAYLEN) (const char *filepath); +typedef int (*FN_ISCAREERMATCH) (void); +typedef int (*FN_GETLOCALIZEDSTRINGLENGTH) (const char *label); +typedef void (*FN_REGISTERTUTORMESSAGESHOWN) (int mid); +typedef int (*FN_GETTIMESTUTORMESSAGESHOWN) (int mid); +typedef void (*FN_PROCESSTUTORMESSAGEDECAYBUFFER) (int *buffer, int bufferLength); +typedef void (*FN_CONSTRUCTTUTORMESSAGEDECAYBUFFER) (int *buffer, int bufferLength); +typedef void (*FN_RESETTUTORMESSAGEDECAYDATA) (void); +// Added 2005/08/11 (no SDK update): +typedef void (*FN_QUERYCLIENTCVARVALUE) ( const edict_t *player, const char *cvarName ); +// Added 2005/11/21 (no SDK update): +typedef void (*FN_QUERYCLIENTCVARVALUE2) ( const edict_t *player, const char *cvarName, int requestID ); +// Added 2009/06/17 (no SDK update): +typedef void (*FN_ENGCHECKPARM) ( const char *pchCmdLineToken, char **pchNextVal ); + +#endif /* ENGINE_API_H */ diff --git a/src/metamod/engine_t.h b/src/metamod/engine_t.h index f3ff86b..7790b54 100644 --- a/src/metamod/engine_t.h +++ b/src/metamod/engine_t.h @@ -1,82 +1,82 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// engine_t.h - The engine_t type - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MM_ENGINE_T_H -#define MM_ENGINE_T_H - -#include "eiface.h" // engfuncs_t, globalvars_t -#include "engineinfo.h" // EngineInfo -#include "comp_dep.h" -#include "osdep.h" //unlikely, OPEN_ARGS - -// Our structure for storing engine references. -struct engine_t { - engine_t() DLLINTERNAL; - engine_t(const engine_t&) DLLINTERNAL; - engine_t& operator=(const engine_t&) DLLINTERNAL; - - enginefuncs_t *funcs; // engine funcs - globalvars_t *globals; // engine globals - enginefuncs_t *pl_funcs; // "modified" eng funcs we give to plugins - EngineInfo info; // some special info elements -}; - -inline engine_t::engine_t() - : funcs(NULL), globals(NULL), pl_funcs(NULL), info() -{ -} - - -inline engine_t::engine_t(const engine_t& _rhs) - : funcs(_rhs.funcs), globals(_rhs.globals), pl_funcs(_rhs.pl_funcs), info(_rhs.info) -{ -} - - -inline engine_t& engine_t::operator=(const engine_t& _rhs) -{ - funcs = _rhs.funcs; - globals = _rhs.globals; - pl_funcs = _rhs.pl_funcs; - info = _rhs.info; - return *this; -} - - -extern engine_t Engine DLLHIDDEN; - -#endif /* MM_ENGINE_T_H */ - +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// engine_t.h - The engine_t type + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MM_ENGINE_T_H +#define MM_ENGINE_T_H + +#include "eiface.h" // engfuncs_t, globalvars_t +#include "engineinfo.h" // EngineInfo +#include "comp_dep.h" +#include "osdep.h" //unlikely, OPEN_ARGS + +// Our structure for storing engine references. +struct engine_t { + engine_t() DLLINTERNAL; + engine_t(const engine_t&) DLLINTERNAL; + engine_t& operator=(const engine_t&) DLLINTERNAL; + + enginefuncs_t *funcs; // engine funcs + globalvars_t *globals; // engine globals + enginefuncs_t *pl_funcs; // "modified" eng funcs we give to plugins + EngineInfo info; // some special info elements +}; + +inline engine_t::engine_t() + : funcs(NULL), globals(NULL), pl_funcs(NULL), info() +{ +} + + +inline engine_t::engine_t(const engine_t& _rhs) + : funcs(_rhs.funcs), globals(_rhs.globals), pl_funcs(_rhs.pl_funcs), info(_rhs.info) +{ +} + + +inline engine_t& engine_t::operator=(const engine_t& _rhs) +{ + funcs = _rhs.funcs; + globals = _rhs.globals; + pl_funcs = _rhs.pl_funcs; + info = _rhs.info; + return *this; +} + + +extern engine_t Engine DLLHIDDEN; + +#endif /* MM_ENGINE_T_H */ + diff --git a/src/metamod/enginecallbacks.h b/src/metamod/enginecallbacks.h index fc1760f..25db78f 100644 --- a/src/metamod/enginecallbacks.h +++ b/src/metamod/enginecallbacks.h @@ -1,78 +1,78 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// enginecallbacks.h - wrapper for - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MM_ENGINECALLBACKS_H -#define MM_ENGINECALLBACKS_H - -// This file is a wrapper around the SDK's enginecallback.h file. We need -// this because we use a different type for the global object g_engfuncs, -// which is still compatible with the enginefuncs_t that the SDK -// uses. -// This is only done for files that belong to Metamod, not other projects -// like plugins that use this file, or others that include it, too. -// Since we don't have a clean seperation of include files right now we -// "hack" our way around that by using a flag METAMOD_CORE which is set -// when compiling Metamod proper. - -#ifdef __METAMOD_BUILD__ -# include "meta_eiface.h" // HL_enginefuncs_t - -// Use a #define to bend the enginefuncs_t type to our HL_enginefuncs_t -// type instead as we now use that for the global object g_engfuncs. -# define enginefuncs_t HL_enginefuncs_t -#endif /* METAMOD_CORE */ - -#include // ALERT, etc - -#ifdef __METAMOD_BUILD__ -# undef enginefuncs_t -#endif /* METAMOD_CORE */ - -// Also, create some additional macros for engine callback functions, which -// weren't in SDK dlls/enginecallbacks.h but probably should have been. - -#define GET_INFOKEYBUFFER (*g_engfuncs.pfnGetInfoKeyBuffer) -#define INFOKEY_VALUE (*g_engfuncs.pfnInfoKeyValue) -#define SET_CLIENT_KEYVALUE (*g_engfuncs.pfnSetClientKeyValue) -#define REG_SVR_COMMAND (*g_engfuncs.pfnAddServerCommand) -#define SERVER_PRINT (*g_engfuncs.pfnServerPrint) -#define SET_SERVER_KEYVALUE (*g_engfuncs.pfnSetKeyValue) -#define QUERY_CLIENT_CVAR_VALUE (*g_engfuncs.pfnQueryClientCvarValue) -#define QUERY_CLIENT_CVAR_VALUE2 (*g_engfuncs.pfnQueryClientCvarValue2) - - -#endif /* MM_ENGINECALLBACKS_H */ - +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// enginecallbacks.h - wrapper for + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MM_ENGINECALLBACKS_H +#define MM_ENGINECALLBACKS_H + +// This file is a wrapper around the SDK's enginecallback.h file. We need +// this because we use a different type for the global object g_engfuncs, +// which is still compatible with the enginefuncs_t that the SDK +// uses. +// This is only done for files that belong to Metamod, not other projects +// like plugins that use this file, or others that include it, too. +// Since we don't have a clean seperation of include files right now we +// "hack" our way around that by using a flag METAMOD_CORE which is set +// when compiling Metamod proper. + +#ifdef __METAMOD_BUILD__ +# include "meta_eiface.h" // HL_enginefuncs_t + +// Use a #define to bend the enginefuncs_t type to our HL_enginefuncs_t +// type instead as we now use that for the global object g_engfuncs. +# define enginefuncs_t HL_enginefuncs_t +#endif /* METAMOD_CORE */ + +#include // ALERT, etc + +#ifdef __METAMOD_BUILD__ +# undef enginefuncs_t +#endif /* METAMOD_CORE */ + +// Also, create some additional macros for engine callback functions, which +// weren't in SDK dlls/enginecallbacks.h but probably should have been. + +#define GET_INFOKEYBUFFER (*g_engfuncs.pfnGetInfoKeyBuffer) +#define INFOKEY_VALUE (*g_engfuncs.pfnInfoKeyValue) +#define SET_CLIENT_KEYVALUE (*g_engfuncs.pfnSetClientKeyValue) +#define REG_SVR_COMMAND (*g_engfuncs.pfnAddServerCommand) +#define SERVER_PRINT (*g_engfuncs.pfnServerPrint) +#define SET_SERVER_KEYVALUE (*g_engfuncs.pfnSetKeyValue) +#define QUERY_CLIENT_CVAR_VALUE (*g_engfuncs.pfnQueryClientCvarValue) +#define QUERY_CLIENT_CVAR_VALUE2 (*g_engfuncs.pfnQueryClientCvarValue2) + + +#endif /* MM_ENGINECALLBACKS_H */ + diff --git a/src/metamod/engineinfo.h b/src/metamod/engineinfo.h index af5eae9..ab0e40b 100644 --- a/src/metamod/engineinfo.h +++ b/src/metamod/engineinfo.h @@ -1,263 +1,263 @@ - -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// engineinfo.h - info about HL engine, like file used and -// text segment range -// - -#ifndef MM_ENGINEINFO_H -#define MM_ENGINEINFO_H - - - -#ifdef _WIN32 -typedef void* MemAddr; -#else -# include // ElfW(Addr/Phdr) macros -typedef void* MemAddr; -#endif /* _WIN32 */ - -#include "extdll.h" // eiface.h: enginefuncs_t - -#include "comp_dep.h" -#include "osdep.h" //unlikely, OPEN_ARGS -#include "new_baseclass.h" - - -// What we return in is_valid_code_pointer() when the EngineInfo object is -// in an INVALID state, i.e. no code address range could be determined. -static const bool c_DefaultReturnOnInvalidState = true; - -static const int c_EngineInfo__typeLen = 10; - - -class EngineInfo : public class_metamod_new -{ - private: - // data : - - MemAddr m_codeStart; - MemAddr m_codeEnd; - - // State is either NULL when not yet initialised, - // VALID if a code range could be determined - // or INVALID when no valid range for code addresses - // could be determined. - char m_state; - - // Type of engine dso/dll used. - // For Linux this specifies the architecture, i.e. 'i486', 'i686', - // 'amd', 'amd64' etc. - // For Windows this is either 'sw' or 'hw' or 'swds' depending on - // the server type. - char m_type[c_EngineInfo__typeLen]; - - // functions : - - - // Check if string is valid name of engine dso/dll. - bool DLLINTERNAL check_for_engine_module( const char* pName ); - -#ifdef _WIN32 - - // Set info using the PE header found by module name. - // Returns 0 on success, error code on failure. - int DLLINTERNAL nthdr_module_name( void ); - - int DLLINTERNAL vac_pe_approx( enginefuncs_t* pFuncs ); - - // Set code segment start and end from PEheader. The base - // address, that relative addresses are based on, is passed in - // pBase. - void DLLINTERNAL set_code_range( unsigned char* pBase, PIMAGE_NT_HEADERS pNThdr ); - -#else - - // Set info using the Programheader found via r_debug struct. - // Returns 0 on success, error code on failure. - int DLLINTERNAL phdr_r_debug( void ); - // Set info using the Programheader found with reference address - // via dladdr(). Returns 0 on success, error code on failure. - int DLLINTERNAL phdr_dladdr( void* pMem ); - // Set info using the Programheader found via ELF header passed as - // pElfHdr. Return 0 on success, error code on failure. - int DLLINTERNAL phdr_elfhdr( void* pElfHdr ); - // Set code segment start and end from Programheader. The base - // address, that relative addresses are based on, is passed in - // pBase. - void DLLINTERNAL set_code_range( void* pBase, ElfW(Phdr)* pPhdr ); - -#endif /* _WIN32 */ - - public: - // codes : - - enum { - STATE_NULL = 0, - STATE_VALID, - STATE_INVALID, - - MODULE_NAME_NOTFOUND = 5, - INVALID_DOS_SIGN, - INVALID_NT_SIGN, - INVALID_ARG, - HEADER_NOTFOUND, - NOTFOUND - }; - - - // functions : - - EngineInfo() DLLINTERNAL; - EngineInfo& operator=( const EngineInfo& ) DLLINTERNAL; - EngineInfo( const EngineInfo& ) DLLINTERNAL; - - const char* DLLINTERNAL type( void ); - - // Initilaise object, determining the bounds of the code segment of - // the HL engine shared object. - int DLLINTERNAL initialise( enginefuncs_t* pFuncs = NULL ); - - // Test if pMem is within bounds of the code segment. - bool DLLINTERNAL is_valid_code_pointer( void* pMem ); - - // Overloaded versions of above test to keep the ugly pointer - // conversion stuff in here. - bool DLLINTERNAL is_valid_code_pointer( const char* (*fp) (edict_t*) ); - bool DLLINTERNAL is_valid_code_pointer( sequenceEntry_s* (*fp) (const char*, const char*) ); - bool DLLINTERNAL is_valid_code_pointer( sentenceEntry_s* (*fp) (const char*, int, int*) ); - bool DLLINTERNAL is_valid_code_pointer( int (*fp) (char*) ); - bool DLLINTERNAL is_valid_code_pointer( unsigned int (*fp) (const char*) ); - bool DLLINTERNAL is_valid_code_pointer( int (*fp) (void) ); - bool DLLINTERNAL is_valid_code_pointer( int (*fp) (const char*) ); - bool DLLINTERNAL is_valid_code_pointer( void (*fp) (int) ); - bool DLLINTERNAL is_valid_code_pointer( int (*fp) (int) ); - bool DLLINTERNAL is_valid_code_pointer( void (*fp) (int*, int) ); - bool DLLINTERNAL is_valid_code_pointer( void (*fp) (void) ); - bool DLLINTERNAL is_valid_code_pointer( void (*fp) (const edict_t*, const char*) ); - bool DLLINTERNAL is_valid_code_pointer( void (*fp) (const edict_t*, const char*, int) ); - bool DLLINTERNAL is_valid_code_pointer( int (*fp) (const char *, char**) ); -}; - - - -// We probably should run an initialisation here without a reference -// pointer so that the object has valid info in any case. -inline EngineInfo::EngineInfo() : - m_codeStart(NULL), - m_codeEnd(NULL), - m_state(STATE_NULL) -{ - m_type[0] = '\0'; -} - - -inline EngineInfo::EngineInfo( const EngineInfo& _rhs) : - m_codeStart(_rhs.m_codeStart), - m_codeEnd(_rhs.m_codeEnd), - m_state(STATE_NULL) -{ - memcpy( m_type, _rhs.m_type, c_EngineInfo__typeLen ); -} - - -inline EngineInfo& EngineInfo::operator=( const EngineInfo& _rhs) -{ - m_state = _rhs.m_state; - m_codeStart = _rhs.m_codeStart; - m_codeEnd = _rhs.m_codeEnd; - memcpy( m_type, _rhs.m_type, c_EngineInfo__typeLen ); - return *this; -} - - -inline const char* EngineInfo::type( void ) -{ - return m_type; -} - -inline bool EngineInfo::is_valid_code_pointer( void* _pMem ) -{ - if ( STATE_INVALID == m_state ) { - return c_DefaultReturnOnInvalidState; - } - if ( NULL != _pMem && m_codeStart <= _pMem && _pMem <= m_codeEnd ) { - return true; - } - - return false; -} - -inline bool EngineInfo::is_valid_code_pointer( const char* (*_fp) (edict_t*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( sequenceEntry_s* (*_fp) (const char*, const char*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( sentenceEntry_s* (*_fp) (const char*, int, int*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (char*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( unsigned int (*_fp) (const char*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (void) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (const char*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (int) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (int) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (int*, int) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (void) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (const edict_t*, const char*) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (const edict_t*, const char*, int) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (const char*, char**) ) -{ - return is_valid_code_pointer( (void*)_fp ); -} - -#endif /* MM_ENGINEINFO_H */ - + +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// engineinfo.h - info about HL engine, like file used and +// text segment range +// + +#ifndef MM_ENGINEINFO_H +#define MM_ENGINEINFO_H + + + +#ifdef _WIN32 +typedef void* MemAddr; +#else +# include // ElfW(Addr/Phdr) macros +typedef void* MemAddr; +#endif /* _WIN32 */ + +#include "extdll.h" // eiface.h: enginefuncs_t + +#include "comp_dep.h" +#include "osdep.h" //unlikely, OPEN_ARGS +#include "new_baseclass.h" + + +// What we return in is_valid_code_pointer() when the EngineInfo object is +// in an INVALID state, i.e. no code address range could be determined. +static const bool c_DefaultReturnOnInvalidState = true; + +static const int c_EngineInfo__typeLen = 10; + + +class EngineInfo : public class_metamod_new +{ + private: + // data : + + MemAddr m_codeStart; + MemAddr m_codeEnd; + + // State is either NULL when not yet initialised, + // VALID if a code range could be determined + // or INVALID when no valid range for code addresses + // could be determined. + char m_state; + + // Type of engine dso/dll used. + // For Linux this specifies the architecture, i.e. 'i486', 'i686', + // 'amd', 'amd64' etc. + // For Windows this is either 'sw' or 'hw' or 'swds' depending on + // the server type. + char m_type[c_EngineInfo__typeLen]; + + // functions : + + + // Check if string is valid name of engine dso/dll. + bool DLLINTERNAL check_for_engine_module( const char* pName ); + +#ifdef _WIN32 + + // Set info using the PE header found by module name. + // Returns 0 on success, error code on failure. + int DLLINTERNAL nthdr_module_name( void ); + + int DLLINTERNAL vac_pe_approx( enginefuncs_t* pFuncs ); + + // Set code segment start and end from PEheader. The base + // address, that relative addresses are based on, is passed in + // pBase. + void DLLINTERNAL set_code_range( unsigned char* pBase, PIMAGE_NT_HEADERS pNThdr ); + +#else + + // Set info using the Programheader found via r_debug struct. + // Returns 0 on success, error code on failure. + int DLLINTERNAL phdr_r_debug( void ); + // Set info using the Programheader found with reference address + // via dladdr(). Returns 0 on success, error code on failure. + int DLLINTERNAL phdr_dladdr( void* pMem ); + // Set info using the Programheader found via ELF header passed as + // pElfHdr. Return 0 on success, error code on failure. + int DLLINTERNAL phdr_elfhdr( void* pElfHdr ); + // Set code segment start and end from Programheader. The base + // address, that relative addresses are based on, is passed in + // pBase. + void DLLINTERNAL set_code_range( void* pBase, ElfW(Phdr)* pPhdr ); + +#endif /* _WIN32 */ + + public: + // codes : + + enum { + STATE_NULL = 0, + STATE_VALID, + STATE_INVALID, + + MODULE_NAME_NOTFOUND = 5, + INVALID_DOS_SIGN, + INVALID_NT_SIGN, + INVALID_ARG, + HEADER_NOTFOUND, + NOTFOUND + }; + + + // functions : + + EngineInfo() DLLINTERNAL; + EngineInfo& operator=( const EngineInfo& ) DLLINTERNAL; + EngineInfo( const EngineInfo& ) DLLINTERNAL; + + const char* DLLINTERNAL type( void ); + + // Initilaise object, determining the bounds of the code segment of + // the HL engine shared object. + int DLLINTERNAL initialise( enginefuncs_t* pFuncs = NULL ); + + // Test if pMem is within bounds of the code segment. + bool DLLINTERNAL is_valid_code_pointer( void* pMem ); + + // Overloaded versions of above test to keep the ugly pointer + // conversion stuff in here. + bool DLLINTERNAL is_valid_code_pointer( const char* (*fp) (edict_t*) ); + bool DLLINTERNAL is_valid_code_pointer( sequenceEntry_s* (*fp) (const char*, const char*) ); + bool DLLINTERNAL is_valid_code_pointer( sentenceEntry_s* (*fp) (const char*, int, int*) ); + bool DLLINTERNAL is_valid_code_pointer( int (*fp) (char*) ); + bool DLLINTERNAL is_valid_code_pointer( unsigned int (*fp) (const char*) ); + bool DLLINTERNAL is_valid_code_pointer( int (*fp) (void) ); + bool DLLINTERNAL is_valid_code_pointer( int (*fp) (const char*) ); + bool DLLINTERNAL is_valid_code_pointer( void (*fp) (int) ); + bool DLLINTERNAL is_valid_code_pointer( int (*fp) (int) ); + bool DLLINTERNAL is_valid_code_pointer( void (*fp) (int*, int) ); + bool DLLINTERNAL is_valid_code_pointer( void (*fp) (void) ); + bool DLLINTERNAL is_valid_code_pointer( void (*fp) (const edict_t*, const char*) ); + bool DLLINTERNAL is_valid_code_pointer( void (*fp) (const edict_t*, const char*, int) ); + bool DLLINTERNAL is_valid_code_pointer( int (*fp) (const char *, char**) ); +}; + + + +// We probably should run an initialisation here without a reference +// pointer so that the object has valid info in any case. +inline EngineInfo::EngineInfo() : + m_codeStart(NULL), + m_codeEnd(NULL), + m_state(STATE_NULL) +{ + m_type[0] = '\0'; +} + + +inline EngineInfo::EngineInfo( const EngineInfo& _rhs) : + m_codeStart(_rhs.m_codeStart), + m_codeEnd(_rhs.m_codeEnd), + m_state(STATE_NULL) +{ + memcpy( m_type, _rhs.m_type, c_EngineInfo__typeLen ); +} + + +inline EngineInfo& EngineInfo::operator=( const EngineInfo& _rhs) +{ + m_state = _rhs.m_state; + m_codeStart = _rhs.m_codeStart; + m_codeEnd = _rhs.m_codeEnd; + memcpy( m_type, _rhs.m_type, c_EngineInfo__typeLen ); + return *this; +} + + +inline const char* EngineInfo::type( void ) +{ + return m_type; +} + +inline bool EngineInfo::is_valid_code_pointer( void* _pMem ) +{ + if ( STATE_INVALID == m_state ) { + return c_DefaultReturnOnInvalidState; + } + if ( NULL != _pMem && m_codeStart <= _pMem && _pMem <= m_codeEnd ) { + return true; + } + + return false; +} + +inline bool EngineInfo::is_valid_code_pointer( const char* (*_fp) (edict_t*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( sequenceEntry_s* (*_fp) (const char*, const char*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( sentenceEntry_s* (*_fp) (const char*, int, int*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (char*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( unsigned int (*_fp) (const char*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (void) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (const char*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (int) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (int) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (int*, int) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (void) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (const edict_t*, const char*) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( void (*_fp) (const edict_t*, const char*, int) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +inline bool EngineInfo::is_valid_code_pointer( int (*_fp) (const char*, char**) ) +{ + return is_valid_code_pointer( (void*)_fp ); +} + +#endif /* MM_ENGINEINFO_H */ + diff --git a/src/metamod/game_autodetect.h b/src/metamod/game_autodetect.h index a16b762..d68ba4f 100644 --- a/src/metamod/game_autodetect.h +++ b/src/metamod/game_autodetect.h @@ -1,43 +1,43 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// autodetect.h - GameDLL search and autodetection. - -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 GAME_AUTODETECT_H -#define GAME_AUTODETECT_H - -#include "metamod.h" -const char * DLLINTERNAL autodetect_gamedll(const gamedll_t *gamedll, const char *knownfn); - -#endif /*GAME_AUTODETECT_H*/ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// autodetect.h - GameDLL search and autodetection. + +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 GAME_AUTODETECT_H +#define GAME_AUTODETECT_H + +#include "metamod.h" +const char * DLLINTERNAL autodetect_gamedll(const gamedll_t *gamedll, const char *knownfn); + +#endif /*GAME_AUTODETECT_H*/ diff --git a/src/metamod/game_support.h b/src/metamod/game_support.h index 61583cc..53f769c 100644 --- a/src/metamod/game_support.h +++ b/src/metamod/game_support.h @@ -1,56 +1,56 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// game_support.h - structures for supporting different HL mod "games" - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 GAME_SUPPORT_H -#define GAME_SUPPORT_H - -#include "types_meta.h" // mBOOL -#include "metamod.h" // gamedll_t - -// Information we have about each game/mod DLL. -typedef struct game_modinfo_s { - const char *name; // name (the game dir) - const char *linux_so; // filename of linux shared lib - const char *win_dll; // filename of win32 dll - const char *desc; // our long-name description -} game_modinfo_t; - -typedef game_modinfo_t game_modlist_t[]; - -const DLLINTERNAL game_modinfo_t *lookup_game(const char *name); -mBOOL DLLINTERNAL setup_gamedll(gamedll_t *gamedll); - -#endif /* GAME_SUPPORT_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// game_support.h - structures for supporting different HL mod "games" + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 GAME_SUPPORT_H +#define GAME_SUPPORT_H + +#include "types_meta.h" // mBOOL +#include "metamod.h" // gamedll_t + +// Information we have about each game/mod DLL. +typedef struct game_modinfo_s { + const char *name; // name (the game dir) + const char *linux_so; // filename of linux shared lib + const char *win_dll; // filename of win32 dll + const char *desc; // our long-name description +} game_modinfo_t; + +typedef game_modinfo_t game_modlist_t[]; + +const DLLINTERNAL game_modinfo_t *lookup_game(const char *name); +mBOOL DLLINTERNAL setup_gamedll(gamedll_t *gamedll); + +#endif /* GAME_SUPPORT_H */ diff --git a/src/metamod/games.h b/src/metamod/games.h index d4c5e61..0a8a93e 100644 --- a/src/metamod/games.h +++ b/src/metamod/games.h @@ -1,140 +1,140 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// games.h - list of supported game mods and their data - -/* - * Copyright (c) 2001-2013 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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. - * - */ - -// This list is now kept in a separate file to facilitate generating the -// list from game data stored in a convenient db. - -#if defined(__x86_64__) || defined(__amd64__) -# define MODARCH "_amd64" -#else -# define MODARCH "_i386" -#endif - - {"action", "ahl"MODARCH".so", "ahl.dll", "Action Half-Life"}, - {"ag", "ag"MODARCH".so", "ag.dll", "Adrenaline Gamer Steam"}, - {"ag3", "hl"MODARCH".so", "hl.dll", "Adrenalinegamer 3.x"}, - {"aghl", "ag"MODARCH".so", "ag.dll", "Adrenalinegamer 4.x"}, - {"arg", "arg"MODARCH".so", "hl.dll", "Arg!"}, - {"asheep", "hl"MODARCH".so", "hl.dll", "Azure Sheep"}, - {"hcfrenzy", "hcfrenzy.so", "hcfrenzy.dll", "Headcrab Frenzy" }, - {"bdef", "../cl_dlls/server.so", "../cl_dlls/server.dll", "Base Defense [Modification]" }, - {"bdef", "server.so", "server.dll", "Base Defense [Steam Version]" }, - {"bg", "bg"MODARCH".so", "bg.dll", "The Battle Grounds"}, - {"bhl", "none", "bhl.dll", "Brutal Half-Life" }, - {"bot", "bot"MODARCH".so", "bot.dll", "Bot"}, - {"brainbread", "bb"MODARCH".so", "bb.dll", "BrainBread"}, - {"bumpercars", "hl"MODARCH".so", "hl.dll", "Bumper Cars"}, - {"buzzybots", "bb"MODARCH".so", "bb.dll", "BuzzyBots"}, - {"ckf3", "none", "mp.dll", "Chicken Fortress 3" }, - {"cs13", "cs"MODARCH".so", "mp.dll", "Counter-Strike 1.3"}, - {"cstrike", "cs"MODARCH".so", "mp.dll", "Counter-Strike"}, - {"csv15", "cs"MODARCH".so", "mp.dll", "CS 1.5 for Steam"}, - {"czero", "cs"MODARCH".so", "mp.dll", "Counter-Strike:Condition Zero"}, - {"dcrisis", "dc"MODARCH".so", "dc.dll", "Desert Crisis"}, - {"dmc", "dmc"MODARCH".so", "dmc.dll", "Deathmatch Classic"}, - {"dod", "dod"MODARCH".so", "dod.dll", "Day of Defeat"}, - {"dpb", "pb.i386.so", "pb.dll", "Digital Paintball"}, - {"dragonmodz", "hl"MODARCH".so", "mp.dll", "Dragon Mod Z"}, - {"esf", "hl"MODARCH".so", "hl.dll", "Earth's Special Forces"}, - {"existence", "ex"MODARCH".so", "existence.dll", "Existence"}, - {"firearms", "fa"MODARCH".so", "firearms.dll", "Firearms"}, - {"firearms25", "fa"MODARCH".so", "firearms.dll", "Retro Firearms"}, - {"freeze", "mp"MODARCH".so", "mp.dll", "Freeze"}, - {"frontline", "front"MODARCH".so", "frontline.dll", "Frontline Force"}, - {"gangstawars", "gangsta"MODARCH".so", "gwars27.dll", "Gangsta Wars"}, - {"gangwars", "mp"MODARCH".so", "mp.dll", "Gangwars"}, - {"gearbox", "opfor"MODARCH".so", "opfor.dll", "Opposing Force"}, - {"globalwarfare", "gw"MODARCH".so", "mp.dll", "Global Warfare"}, - {"goldeneye", "golden"MODARCH".so", "mp.dll", "Goldeneye"}, - {"hl15we", "hl"MODARCH".so", "hl.dll", "Half-Life 1.5: Weapon Edition"}, - {"HLAinGOLDSrc", "none", "hl.dll", "Half-Life Alpha in GOLDSrc"}, - {"hlrally", "hlr"MODARCH".so", "hlrally.dll", "HL-Rally"}, - {"holywars", "hl"MODARCH".so", "holywars.dll", "Holy Wars"}, - {"hostileintent", "hl"MODARCH".so", "hl.dll", "Hostile Intent"}, - {"ios", "ios"MODARCH".so", "ios.dll", "International Online Soccer"}, - {"judgedm", "judge"MODARCH".so", "mp.dll", "Judgement"}, - {"kanonball", "hl"MODARCH".so", "kanonball.dll", "Kanonball"}, - {"monkeystrike", "ms"MODARCH".so", "monkey.dll", "Monkeystrike"}, - {"MorbidPR", "morbid"MODARCH".so", "morbid.dll", "Morbid Inclination"}, - {"movein", "hl"MODARCH".so", "hl.dll", "Move In!"}, - {"msc", "none", "ms.dll", "Master Sword Continued" }, - {"ns", "ns"MODARCH".so", "ns.dll", "Natural Selection"}, - {"nsp", "ns"MODARCH".so", "ns.dll", "Natural Selection Beta"}, - {"oel", "hl"MODARCH".so", "hl.dll", "OeL Half-Life"}, - {"og", "og"MODARCH".so", "og.dll", "Over Ground"}, - {"ol", "ol"MODARCH".so", "hl.dll", "Outlawsmod"}, - {"ops1942", "spirit"MODARCH".so", "spirit.dll", "Operations 1942"}, - {"osjb", "osjb"MODARCH".so", "jail.dll", "Open-Source Jailbreak"}, - {"outbreak", "none", "hl.dll", "Out Break"}, - {"oz", "mp"MODARCH".so", "mp.dll", "Oz Deathmatch"}, - {"paintball", "pb"MODARCH".so", "mp.dll", "Paintball"}, - {"penemy", "pe"MODARCH".so", "pe.dll", "Public Enemy"}, - {"phineas", "phineas"MODARCH".so", "phineas.dll", "Phineas Bot"}, - {"ponreturn", "ponr"MODARCH".so", "mp.dll", "Point of No Return"}, - {"pvk", "hl"MODARCH".so", "hl.dll", "Pirates, Vikings and Knights"}, - {"rc2", "rc2"MODARCH".so", "rc2.dll", "Rocket Crowbar 2"}, - {"recbb2", "recb"MODARCH".so", "recb.dll", "Resident Evil : Cold Blood" }, - {"retrocs", "rcs"MODARCH".so", "rcs.dll", "Retro Counter-Strike"}, - {"rewolf", "hl"MODARCH".so", "gunman.dll", "Gunman Chronicles"}, - {"ricochet", "ricochet"MODARCH".so", "mp.dll", "Ricochet"}, - {"rockcrowbar", "rc"MODARCH".so", "rc.dll", "Rocket Crowbar"}, - {"rspecies", "hl"MODARCH".so", "hl.dll", "Rival Species"}, - {"scihunt", "shunt.so", "shunt.dll", "Scientist Hunt"}, - {"sdm", "sdmmod"MODARCH".so", "sdmmod.dll", "Special Death Match"}, - {"Ship", "ship"MODARCH".so", "ship.dll", "The Ship"}, - {"si", "si"MODARCH".so", "si.dll", "Science & Industry"}, - {"snow", "snow"MODARCH".so", "snow.dll", "Snow-War"}, - {"stargatetc", "hl"MODARCH".so", "hl.dll", "StargateTC"}, - {"svencoop", "hl"MODARCH".so", "hl.dll", "Sven Coop [Modification]" }, - {"svencoop", "server.so", "server.dll", "Sven Coop [Steam Version]" }, - {"swarm", "swarm"MODARCH".so", "swarm.dll", "Swarm"}, - {"tfc", "tfc"MODARCH".so", "tfc.dll", "Team Fortress Classic"}, - {"thewastes", "thewastes"MODARCH".so", "thewastes.dll", "The Wastes"}, - {"timeless", "pt"MODARCH".so", "timeless.dll", "Project Timeless"}, - {"tod", "hl"MODARCH".so", "hl.dll", "Tour of Duty"}, - {"trainhunters", "th"MODARCH".so", "th.dll", "Train Hunters"}, - {"trevenge", "trevenge.so", "trevenge.dll", "The Terrorist Revenge"}, - {"TS", "ts"MODARCH".so", "mp.dll", "The Specialists"}, - {"tt", "tt"MODARCH".so", "tt.dll", "The Trenches"}, - {"underworld", "uw"MODARCH".so", "uw.dll", "Underworld Bloodline"}, - {"valve", "hl"MODARCH".so", "hl.dll", "Half-Life Deathmatch"}, - {"vs", "vs"MODARCH".so", "mp.dll", "VampireSlayer"}, - {"wantedhl", "hl"MODARCH".so", "wanted.dll", "Wanted!"}, - {"wasteland", "whl_linux.so", "mp.dll", "Wasteland"}, - {"weapon_wars", "ww"MODARCH".so", "hl.dll", "Weapon Wars"}, - {"wizwars", "mp"MODARCH".so", "hl.dll", "Wizard Wars"}, - {"wormshl", "wormshl_i586.so", "wormshl.dll", "WormsHL"}, - {"zp", "none", "mp.dll", "Zombie Panic"}, +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// games.h - list of supported game mods and their data + +/* + * Copyright (c) 2001-2013 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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. + * + */ + +// This list is now kept in a separate file to facilitate generating the +// list from game data stored in a convenient db. + +#if defined(__x86_64__) || defined(__amd64__) +# define MODARCH "_amd64" +#else +# define MODARCH "_i386" +#endif + + {"action", "ahl"MODARCH".so", "ahl.dll", "Action Half-Life"}, + {"ag", "ag"MODARCH".so", "ag.dll", "Adrenaline Gamer Steam"}, + {"ag3", "hl"MODARCH".so", "hl.dll", "Adrenalinegamer 3.x"}, + {"aghl", "ag"MODARCH".so", "ag.dll", "Adrenalinegamer 4.x"}, + {"arg", "arg"MODARCH".so", "hl.dll", "Arg!"}, + {"asheep", "hl"MODARCH".so", "hl.dll", "Azure Sheep"}, + {"hcfrenzy", "hcfrenzy.so", "hcfrenzy.dll", "Headcrab Frenzy" }, + {"bdef", "../cl_dlls/server.so", "../cl_dlls/server.dll", "Base Defense [Modification]" }, + {"bdef", "server.so", "server.dll", "Base Defense [Steam Version]" }, + {"bg", "bg"MODARCH".so", "bg.dll", "The Battle Grounds"}, + {"bhl", "none", "bhl.dll", "Brutal Half-Life" }, + {"bot", "bot"MODARCH".so", "bot.dll", "Bot"}, + {"brainbread", "bb"MODARCH".so", "bb.dll", "BrainBread"}, + {"bumpercars", "hl"MODARCH".so", "hl.dll", "Bumper Cars"}, + {"buzzybots", "bb"MODARCH".so", "bb.dll", "BuzzyBots"}, + {"ckf3", "none", "mp.dll", "Chicken Fortress 3" }, + {"cs13", "cs"MODARCH".so", "mp.dll", "Counter-Strike 1.3"}, + {"cstrike", "cs"MODARCH".so", "mp.dll", "Counter-Strike"}, + {"csv15", "cs"MODARCH".so", "mp.dll", "CS 1.5 for Steam"}, + {"czero", "cs"MODARCH".so", "mp.dll", "Counter-Strike:Condition Zero"}, + {"dcrisis", "dc"MODARCH".so", "dc.dll", "Desert Crisis"}, + {"dmc", "dmc"MODARCH".so", "dmc.dll", "Deathmatch Classic"}, + {"dod", "dod"MODARCH".so", "dod.dll", "Day of Defeat"}, + {"dpb", "pb.i386.so", "pb.dll", "Digital Paintball"}, + {"dragonmodz", "hl"MODARCH".so", "mp.dll", "Dragon Mod Z"}, + {"esf", "hl"MODARCH".so", "hl.dll", "Earth's Special Forces"}, + {"existence", "ex"MODARCH".so", "existence.dll", "Existence"}, + {"firearms", "fa"MODARCH".so", "firearms.dll", "Firearms"}, + {"firearms25", "fa"MODARCH".so", "firearms.dll", "Retro Firearms"}, + {"freeze", "mp"MODARCH".so", "mp.dll", "Freeze"}, + {"frontline", "front"MODARCH".so", "frontline.dll", "Frontline Force"}, + {"gangstawars", "gangsta"MODARCH".so", "gwars27.dll", "Gangsta Wars"}, + {"gangwars", "mp"MODARCH".so", "mp.dll", "Gangwars"}, + {"gearbox", "opfor"MODARCH".so", "opfor.dll", "Opposing Force"}, + {"globalwarfare", "gw"MODARCH".so", "mp.dll", "Global Warfare"}, + {"goldeneye", "golden"MODARCH".so", "mp.dll", "Goldeneye"}, + {"hl15we", "hl"MODARCH".so", "hl.dll", "Half-Life 1.5: Weapon Edition"}, + {"HLAinGOLDSrc", "none", "hl.dll", "Half-Life Alpha in GOLDSrc"}, + {"hlrally", "hlr"MODARCH".so", "hlrally.dll", "HL-Rally"}, + {"holywars", "hl"MODARCH".so", "holywars.dll", "Holy Wars"}, + {"hostileintent", "hl"MODARCH".so", "hl.dll", "Hostile Intent"}, + {"ios", "ios"MODARCH".so", "ios.dll", "International Online Soccer"}, + {"judgedm", "judge"MODARCH".so", "mp.dll", "Judgement"}, + {"kanonball", "hl"MODARCH".so", "kanonball.dll", "Kanonball"}, + {"monkeystrike", "ms"MODARCH".so", "monkey.dll", "Monkeystrike"}, + {"MorbidPR", "morbid"MODARCH".so", "morbid.dll", "Morbid Inclination"}, + {"movein", "hl"MODARCH".so", "hl.dll", "Move In!"}, + {"msc", "none", "ms.dll", "Master Sword Continued" }, + {"ns", "ns"MODARCH".so", "ns.dll", "Natural Selection"}, + {"nsp", "ns"MODARCH".so", "ns.dll", "Natural Selection Beta"}, + {"oel", "hl"MODARCH".so", "hl.dll", "OeL Half-Life"}, + {"og", "og"MODARCH".so", "og.dll", "Over Ground"}, + {"ol", "ol"MODARCH".so", "hl.dll", "Outlawsmod"}, + {"ops1942", "spirit"MODARCH".so", "spirit.dll", "Operations 1942"}, + {"osjb", "osjb"MODARCH".so", "jail.dll", "Open-Source Jailbreak"}, + {"outbreak", "none", "hl.dll", "Out Break"}, + {"oz", "mp"MODARCH".so", "mp.dll", "Oz Deathmatch"}, + {"paintball", "pb"MODARCH".so", "mp.dll", "Paintball"}, + {"penemy", "pe"MODARCH".so", "pe.dll", "Public Enemy"}, + {"phineas", "phineas"MODARCH".so", "phineas.dll", "Phineas Bot"}, + {"ponreturn", "ponr"MODARCH".so", "mp.dll", "Point of No Return"}, + {"pvk", "hl"MODARCH".so", "hl.dll", "Pirates, Vikings and Knights"}, + {"rc2", "rc2"MODARCH".so", "rc2.dll", "Rocket Crowbar 2"}, + {"recbb2", "recb"MODARCH".so", "recb.dll", "Resident Evil : Cold Blood" }, + {"retrocs", "rcs"MODARCH".so", "rcs.dll", "Retro Counter-Strike"}, + {"rewolf", "hl"MODARCH".so", "gunman.dll", "Gunman Chronicles"}, + {"ricochet", "ricochet"MODARCH".so", "mp.dll", "Ricochet"}, + {"rockcrowbar", "rc"MODARCH".so", "rc.dll", "Rocket Crowbar"}, + {"rspecies", "hl"MODARCH".so", "hl.dll", "Rival Species"}, + {"scihunt", "shunt.so", "shunt.dll", "Scientist Hunt"}, + {"sdm", "sdmmod"MODARCH".so", "sdmmod.dll", "Special Death Match"}, + {"Ship", "ship"MODARCH".so", "ship.dll", "The Ship"}, + {"si", "si"MODARCH".so", "si.dll", "Science & Industry"}, + {"snow", "snow"MODARCH".so", "snow.dll", "Snow-War"}, + {"stargatetc", "hl"MODARCH".so", "hl.dll", "StargateTC"}, + {"svencoop", "hl"MODARCH".so", "hl.dll", "Sven Coop [Modification]" }, + {"svencoop", "server.so", "server.dll", "Sven Coop [Steam Version]" }, + {"swarm", "swarm"MODARCH".so", "swarm.dll", "Swarm"}, + {"tfc", "tfc"MODARCH".so", "tfc.dll", "Team Fortress Classic"}, + {"thewastes", "thewastes"MODARCH".so", "thewastes.dll", "The Wastes"}, + {"timeless", "pt"MODARCH".so", "timeless.dll", "Project Timeless"}, + {"tod", "hl"MODARCH".so", "hl.dll", "Tour of Duty"}, + {"trainhunters", "th"MODARCH".so", "th.dll", "Train Hunters"}, + {"trevenge", "trevenge.so", "trevenge.dll", "The Terrorist Revenge"}, + {"TS", "ts"MODARCH".so", "mp.dll", "The Specialists"}, + {"tt", "tt"MODARCH".so", "tt.dll", "The Trenches"}, + {"underworld", "uw"MODARCH".so", "uw.dll", "Underworld Bloodline"}, + {"valve", "hl"MODARCH".so", "hl.dll", "Half-Life Deathmatch"}, + {"vs", "vs"MODARCH".so", "mp.dll", "VampireSlayer"}, + {"wantedhl", "hl"MODARCH".so", "wanted.dll", "Wanted!"}, + {"wasteland", "whl_linux.so", "mp.dll", "Wasteland"}, + {"weapon_wars", "ww"MODARCH".so", "hl.dll", "Weapon Wars"}, + {"wizwars", "mp"MODARCH".so", "hl.dll", "Wizard Wars"}, + {"wormshl", "wormshl_i586.so", "wormshl.dll", "WormsHL"}, + {"zp", "none", "mp.dll", "Zombie Panic"}, diff --git a/src/metamod/h_export.h b/src/metamod/h_export.h index 494d8e0..e8cc1d0 100644 --- a/src/metamod/h_export.h +++ b/src/metamod/h_export.h @@ -1,49 +1,49 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// h_export.h - prototypes for h_export.cpp - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 H_EXPORT_H -#define H_EXPORT_H - -#include "osdep.h" // DLLEXPORT, WINAPI, etc - -// Our GiveFnptrsToDll, called by engine. -typedef void (WINAPI *GIVE_ENGINE_FUNCTIONS_FN) (enginefuncs_t - *pengfuncsFromEngine, globalvars_t *pGlobals); - -C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, - globalvars_t *pGlobals); - -#endif /* H_EXPORT_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// h_export.h - prototypes for h_export.cpp + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 H_EXPORT_H +#define H_EXPORT_H + +#include "osdep.h" // DLLEXPORT, WINAPI, etc + +// Our GiveFnptrsToDll, called by engine. +typedef void (WINAPI *GIVE_ENGINE_FUNCTIONS_FN) (enginefuncs_t + *pengfuncsFromEngine, globalvars_t *pGlobals); + +C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, + globalvars_t *pGlobals); + +#endif /* H_EXPORT_H */ diff --git a/src/metamod/info_name.h b/src/metamod/info_name.h index 9df3ae1..4859f1a 100644 --- a/src/metamod/info_name.h +++ b/src/metamod/info_name.h @@ -1,56 +1,56 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// info_name.h - name, desc, author, etc - -/* - * Copyright (c) 2001-2013 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 INFO_NAME_H -#define INFO_NAME_H - -#include "vers_meta.h" // VDATE, VVERSION, etc - -#define VNAME "Metamod" -#define VAUTHOR "Will Day" -#define VURL "http://www.metamod.org/" - -#define COPYRIGHT_YEAR "2013" - -// Various strings for the Windows DLL Resources in res_meta.rc -#define RC_COMMENTS "Metamod-P is enhanced version of Metamod. Metamod allows running multiple mod-like plugin DLLs, to add functionality or change the behavior of the running HLDS game mod. See " VURL -#define RC_DESC "Metamod-P Half-Life MOD DLL" -#define RC_FILENAME "METAMOD.DLL" -#define RC_INTERNAL "METAMOD-P" -#define RC_COPYRIGHT "Copyright© 2001-" COPYRIGHT_YEAR " Will Day; 2004-" VPATCH_COPYRIGHT_YEAR " Jussi Kivilinna; GPL licensed" -#define RC_LICENSE "Licensed under the GNU General Public License" - -#endif /* INFO_NAME_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// info_name.h - name, desc, author, etc + +/* + * Copyright (c) 2001-2013 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 INFO_NAME_H +#define INFO_NAME_H + +#include "vers_meta.h" // VDATE, VVERSION, etc + +#define VNAME "Metamod" +#define VAUTHOR "Will Day" +#define VURL "http://www.metamod.org/" + +#define COPYRIGHT_YEAR "2013" + +// Various strings for the Windows DLL Resources in res_meta.rc +#define RC_COMMENTS "Metamod-P is enhanced version of Metamod. Metamod allows running multiple mod-like plugin DLLs, to add functionality or change the behavior of the running HLDS game mod. See " VURL +#define RC_DESC "Metamod-P Half-Life MOD DLL" +#define RC_FILENAME "METAMOD.DLL" +#define RC_INTERNAL "METAMOD-P" +#define RC_COPYRIGHT "Copyright© 2001-" COPYRIGHT_YEAR " Will Day; 2004-" VPATCH_COPYRIGHT_YEAR " Jussi Kivilinna; GPL licensed" +#define RC_LICENSE "Licensed under the GNU General Public License" + +#endif /* INFO_NAME_H */ diff --git a/src/metamod/linkent.h b/src/metamod/linkent.h index f61eeaa..1b8b1b5 100644 --- a/src/metamod/linkent.h +++ b/src/metamod/linkent.h @@ -1,109 +1,109 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// linkent.h - export entities from mod "games" back to the HL engine - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 LINK_ENT_H -#define LINK_ENT_H - -#include // always - -#include "osdep.h" // DLLEXPORT, etc -#include "metamod.h" // GameDLL, etc -#include "mlist.h" // MPluginList::find_match, etc -#include "mplugin.h" // MPlugin::info, etc -#include "log_meta.h" // META_DEBUG, etc - - -//Initializes replacement code -int DLLINTERNAL init_linkent_replacement(DLHANDLE moduleMetamod, DLHANDLE moduleGame); - - -// Comments from SDK dlls/util.h: -//! This is the glue that hooks .MAP entity class names to our CPP classes. -//! The _declspec forces them to be exported by name so we can do a lookup with GetProcAddress(). -//! The function is used to intialize / allocate the object for the entity. - -// Adapted from LINK_ENTITY_TO_FUNC in adminmod linkfunc.cpp. - -typedef void (*ENTITY_FN) (entvars_t *); - - -// For now, we have to explicitly export functions for plugin entities, -// just as for gamedll entities. Ideally, this could be generalized in -// some manner, so that plugins can declare and use their own entities -// without having them explicitly supported by metamod, but I don't know -// yet if that is actually possible. -// -// LINK_ENTITY_TO_PLUGIN -// - if plugin not loaded & running, return -// - plugin has to be set loadable=startup only, else log error, return -// - (plugin loaded) if func missing, return -// - (plugin loaded) if func not found, dlsym -// - (plugin loaded) if func still not found, set missing, return -// - (plugin loaded, func found) call func -#define LINK_ENTITY_TO_PLUGIN(entityName, pluginName) \ - C_DLLEXPORT void entityName(entvars_t *pev); \ - void entityName(entvars_t *pev) { \ - static ENTITY_FN pfnEntity = NULL; \ - static int missing=0; \ - char *entStr; \ - MPlugin *findp; \ - entStr = STRINGIZE(entityName, 0); \ - if(missing) \ - return; \ - if(!pfnEntity) { \ - if(!(findp=Plugins->find_match(pluginName))) { \ - META_WARNING("Couldn't find loaded plugin '%s' for plugin entity '%s'", pluginName, entStr); \ - missing=1; \ - return; \ - } \ - if(findp->info && findp->info->loadable != PT_STARTUP) { \ - META_WARNING("Can't link entity '%s' for plugin '%s'; loadable != startup: %s", entStr, pluginName, findp->str_loadable()); \ - missing=1; \ - return; \ - } \ - META_DEBUG(9, ("Looking up plugin entity '%s'", entStr)); \ - pfnEntity = (ENTITY_FN) DLSYM(findp->handle, entStr); \ - } \ - if(!pfnEntity) { \ - META_WARNING("Couldn't find plugin entity '%s' in plugin DLL '%s'", entStr, findp->file); \ - missing=1; \ - return; \ - } \ - META_DEBUG(8, ("Linking plugin entity '%s'", entStr)); \ - (*pfnEntity)(pev); \ - } - -#endif /* LINK_ENT_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// linkent.h - export entities from mod "games" back to the HL engine + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 LINK_ENT_H +#define LINK_ENT_H + +#include // always + +#include "osdep.h" // DLLEXPORT, etc +#include "metamod.h" // GameDLL, etc +#include "mlist.h" // MPluginList::find_match, etc +#include "mplugin.h" // MPlugin::info, etc +#include "log_meta.h" // META_DEBUG, etc + + +//Initializes replacement code +int DLLINTERNAL init_linkent_replacement(DLHANDLE moduleMetamod, DLHANDLE moduleGame); + + +// Comments from SDK dlls/util.h: +//! This is the glue that hooks .MAP entity class names to our CPP classes. +//! The _declspec forces them to be exported by name so we can do a lookup with GetProcAddress(). +//! The function is used to intialize / allocate the object for the entity. + +// Adapted from LINK_ENTITY_TO_FUNC in adminmod linkfunc.cpp. + +typedef void (*ENTITY_FN) (entvars_t *); + + +// For now, we have to explicitly export functions for plugin entities, +// just as for gamedll entities. Ideally, this could be generalized in +// some manner, so that plugins can declare and use their own entities +// without having them explicitly supported by metamod, but I don't know +// yet if that is actually possible. +// +// LINK_ENTITY_TO_PLUGIN +// - if plugin not loaded & running, return +// - plugin has to be set loadable=startup only, else log error, return +// - (plugin loaded) if func missing, return +// - (plugin loaded) if func not found, dlsym +// - (plugin loaded) if func still not found, set missing, return +// - (plugin loaded, func found) call func +#define LINK_ENTITY_TO_PLUGIN(entityName, pluginName) \ + C_DLLEXPORT void entityName(entvars_t *pev); \ + void entityName(entvars_t *pev) { \ + static ENTITY_FN pfnEntity = NULL; \ + static int missing=0; \ + char *entStr; \ + MPlugin *findp; \ + entStr = STRINGIZE(entityName, 0); \ + if(missing) \ + return; \ + if(!pfnEntity) { \ + if(!(findp=Plugins->find_match(pluginName))) { \ + META_WARNING("Couldn't find loaded plugin '%s' for plugin entity '%s'", pluginName, entStr); \ + missing=1; \ + return; \ + } \ + if(findp->info && findp->info->loadable != PT_STARTUP) { \ + META_WARNING("Can't link entity '%s' for plugin '%s'; loadable != startup: %s", entStr, pluginName, findp->str_loadable()); \ + missing=1; \ + return; \ + } \ + META_DEBUG(9, ("Looking up plugin entity '%s'", entStr)); \ + pfnEntity = (ENTITY_FN) DLSYM(findp->handle, entStr); \ + } \ + if(!pfnEntity) { \ + META_WARNING("Couldn't find plugin entity '%s' in plugin DLL '%s'", entStr, findp->file); \ + missing=1; \ + return; \ + } \ + META_DEBUG(8, ("Linking plugin entity '%s'", entStr)); \ + (*pfnEntity)(pev); \ + } + +#endif /* LINK_ENT_H */ diff --git a/src/metamod/log_meta.h b/src/metamod/log_meta.h index 3b0225f..b9ab97f 100644 --- a/src/metamod/log_meta.h +++ b/src/metamod/log_meta.h @@ -1,116 +1,116 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// log_meta.h - functions & macros for logging - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 LOG_META_H -#define LOG_META_H - -#include "comp_dep.h" -#include "osdep.h" //unlikely, OPEN_ARGS - -// Debug logging. -// -// This is done as a macro, rather than a function. This way, you can add -// DEBUG statements all over, without worrying about performance -// implications. If the debugging level is set low, all those statements -// will only generate a simple float/int compare each; if we were to use a -// function instead of a macro, it would end up wasting a lot of cpu cycles -// calling/returning from the function every time. With a fair number of -// DEBUG statements, or if they're placed in frequently excuted code, the -// overhead of the wasted function calls could significantly impact server -// performance. -// -// For this reason, we also compare directly to the float value of the -// cvar, rather than calling CVAR_GET_FLOAT() and thus generating a string -// compare for each DEBUG statement. -// -// Called as: -// META_DEBUG(3, ("return code: %d", ret)); -// -// Note the double parens, and the missing parens around "args" in the -// macro itself. Note also the "do..while(0)" loop wrapping the -// statements, so they become a single statement when expanded, necessary -// for times when it might be called as a single-statement result of an -// else (or other flow control). -// -// Yes, it's all a bit of a hack. -// -// Using meta_debug_value instead of meta_debug.value. -// meta_debug_value is preconverted int-value of meta_debug.value. -// Reason for this optimization: Integer compare is much faster than float compare. -// i686 has fast float compare, but since we want to have i386 binary, we use this. - -#ifdef __BUILD_FAST_METAMOD__ - #define META_DEBUG(level, args) do { break; } while(0) -#else - #define META_DEBUG(level, args) \ - do { \ - if(unlikely(meta_debug_value >= level)) { \ - META_DEBUG_SET_LEVEL(level); \ - META_DO_DEBUG args; \ - } \ - } while(0) -#endif - -// max buffer size for printed messages -#define MAX_LOGMSG_LEN 1024 - -// max buffer size for client messages -#define MAX_CLIENTMSG_LEN 128 - -extern cvar_t meta_debug DLLHIDDEN; -extern int meta_debug_value DLLHIDDEN; - -// META_DEV provides debug logging via the cvar "developer" (when set to 1) -// and uses a function call rather than a macro as it's really intended to -// be used only during startup, before meta_debug has been set from reading -// server.cfg. -// NOTE: META_DEV has now been mostly obsoleted in the code. - -void DLLINTERNAL META_CONS(const char *fmt, ...); -void DLLINTERNAL META_DEV(const char *fmt, ...); -void DLLINTERNAL META_INFO(const char *fmt, ...); -void DLLINTERNAL META_WARNING(const char *fmt, ...); -void DLLINTERNAL META_ERROR(const char *fmt, ...); -void DLLINTERNAL META_LOG(const char *fmt, ...); -void DLLINTERNAL META_CLIENT(edict_t *pEntity, const char *fmt, ...); -#ifndef __BUILD_FAST_METAMOD__ - void DLLINTERNAL META_DEBUG_SET_LEVEL(int level); - void DLLINTERNAL META_DO_DEBUG(const char *fmt, ...); -#endif - -void DLLINTERNAL flush_ALERT_buffer(void); - -#endif /* LOG_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// log_meta.h - functions & macros for logging + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 LOG_META_H +#define LOG_META_H + +#include "comp_dep.h" +#include "osdep.h" //unlikely, OPEN_ARGS + +// Debug logging. +// +// This is done as a macro, rather than a function. This way, you can add +// DEBUG statements all over, without worrying about performance +// implications. If the debugging level is set low, all those statements +// will only generate a simple float/int compare each; if we were to use a +// function instead of a macro, it would end up wasting a lot of cpu cycles +// calling/returning from the function every time. With a fair number of +// DEBUG statements, or if they're placed in frequently excuted code, the +// overhead of the wasted function calls could significantly impact server +// performance. +// +// For this reason, we also compare directly to the float value of the +// cvar, rather than calling CVAR_GET_FLOAT() and thus generating a string +// compare for each DEBUG statement. +// +// Called as: +// META_DEBUG(3, ("return code: %d", ret)); +// +// Note the double parens, and the missing parens around "args" in the +// macro itself. Note also the "do..while(0)" loop wrapping the +// statements, so they become a single statement when expanded, necessary +// for times when it might be called as a single-statement result of an +// else (or other flow control). +// +// Yes, it's all a bit of a hack. +// +// Using meta_debug_value instead of meta_debug.value. +// meta_debug_value is preconverted int-value of meta_debug.value. +// Reason for this optimization: Integer compare is much faster than float compare. +// i686 has fast float compare, but since we want to have i386 binary, we use this. + +#ifdef __BUILD_FAST_METAMOD__ + #define META_DEBUG(level, args) do { break; } while(0) +#else + #define META_DEBUG(level, args) \ + do { \ + if(unlikely(meta_debug_value >= level)) { \ + META_DEBUG_SET_LEVEL(level); \ + META_DO_DEBUG args; \ + } \ + } while(0) +#endif + +// max buffer size for printed messages +#define MAX_LOGMSG_LEN 1024 + +// max buffer size for client messages +#define MAX_CLIENTMSG_LEN 128 + +extern cvar_t meta_debug DLLHIDDEN; +extern int meta_debug_value DLLHIDDEN; + +// META_DEV provides debug logging via the cvar "developer" (when set to 1) +// and uses a function call rather than a macro as it's really intended to +// be used only during startup, before meta_debug has been set from reading +// server.cfg. +// NOTE: META_DEV has now been mostly obsoleted in the code. + +void DLLINTERNAL META_CONS(const char *fmt, ...); +void DLLINTERNAL META_DEV(const char *fmt, ...); +void DLLINTERNAL META_INFO(const char *fmt, ...); +void DLLINTERNAL META_WARNING(const char *fmt, ...); +void DLLINTERNAL META_ERROR(const char *fmt, ...); +void DLLINTERNAL META_LOG(const char *fmt, ...); +void DLLINTERNAL META_CLIENT(edict_t *pEntity, const char *fmt, ...); +#ifndef __BUILD_FAST_METAMOD__ + void DLLINTERNAL META_DEBUG_SET_LEVEL(int level); + void DLLINTERNAL META_DO_DEBUG(const char *fmt, ...); +#endif + +void DLLINTERNAL flush_ALERT_buffer(void); + +#endif /* LOG_META_H */ diff --git a/src/metamod/meta_api.h b/src/metamod/meta_api.h index 4480b06..b32cd74 100644 --- a/src/metamod/meta_api.h +++ b/src/metamod/meta_api.h @@ -1,232 +1,232 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// meta_api.h - description of metamod's DLL interface - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 META_API_H -#define META_API_H - -#include "comp_dep.h" -#include "dllapi.h" // GETENTITYAPI_FN, etc -#include "engine_api.h" // GET_ENGINE_FUNCTIONS_FN, etc -#include "plinfo.h" // plugin_info_t, etc -#include "osdep.h" // DLLEXPORT, etc -#include "mutil.h" - -// Version consists of "major:minor", two separate integer numbers. -// Version 1 original -// Version 2 added plugin_info_t **pinfo -// Version 3 init split into query and attach, added detach -// Version 4 added (PLUG_LOADTIME now) to attach -// Version 5 changed mm ifvers int* to string, moved pl ifvers to info -// Version 5:1 added link support for entity "adminmod_timer" (adminmod) -// Version 5:2 added gamedll_funcs to meta_attach() [v1.0-rc2] -// Version 5:3 added mutil_funcs to meta_query() [v1.05] -// Version 5:4 added centersay utility functions [v1.06] -// Version 5:5 added Meta_Init to plugin API [v1.08] -// Version 5:6 added CallGameEntity utility function [v1.09] -// Version 5:7 added GetUserMsgID, GetUserMsgName util funcs [v1.11] -// Version 5:8 added GetPluginPath [v1.11] -// Version 5:9 added GetGameInfo [v1.14] -// Version 5:10 added GINFO_REALDLL_FULLPATH for GetGameInfo [v1.17] -// Version 5:11 added plugin loading and unloading API [v1.18] -// Version 5:12 added IS_QUERYING_CLIENT_CVAR to mutils [v1.18] -// Version 5:13 added MAKE_REQUESTID and GET_HOOK_TABLES to mutils [v1.19] -#define META_INTERFACE_VERSION "5:13" - -// Flags returned by a plugin's api function. -// NOTE: order is crucial, as greater/less comparisons are made. -typedef enum { - MRES_UNSET = 0, - MRES_IGNORED, // plugin didn't take any action - MRES_HANDLED, // plugin did something, but real function should still be called - MRES_OVERRIDE, // call real function, but use my return value - MRES_SUPERCEDE, // skip real function; use my return value -} META_RES; - -// Variables provided to plugins. -typedef struct meta_globals_s { - META_RES mres; // writable; plugin's return flag - META_RES prev_mres; // readable; return flag of the previous plugin called - META_RES status; // readable; "highest" return flag so far - void *orig_ret; // readable; return value from "real" function - void *override_ret; // readable; return value from overriding/superceding plugin -} meta_globals_t; - -extern meta_globals_t *gpMetaGlobals DLLHIDDEN; -#define SET_META_RESULT(result) gpMetaGlobals->mres=result -#define RETURN_META(result) \ - do { gpMetaGlobals->mres=result; return; } while(0) -#define RETURN_META_VALUE(result, value) \ - do { gpMetaGlobals->mres=result; return(value); } while(0) -#define META_RESULT_STATUS gpMetaGlobals->status -#define META_RESULT_PREVIOUS gpMetaGlobals->prev_mres -#define META_RESULT_ORIG_RET(type) *(type *)gpMetaGlobals->orig_ret -#define META_RESULT_OVERRIDE_RET(type) *(type *)gpMetaGlobals->override_ret - -// Table of getapi functions, retrieved from each plugin. -typedef struct { - GETENTITYAPI_FN pfnGetEntityAPI; - GETENTITYAPI_FN pfnGetEntityAPI_Post; - GETENTITYAPI2_FN pfnGetEntityAPI2; - GETENTITYAPI2_FN pfnGetEntityAPI2_Post; - GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions; - GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions_Post; - GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions; - GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions_Post; -} META_FUNCTIONS; - -// Pair of function tables provided by game DLL. -typedef struct { - DLL_FUNCTIONS *dllapi_table; - NEW_DLL_FUNCTIONS *newapi_table; -} gamedll_funcs_t; - -// Declared in plugin; referenced in macros. -extern gamedll_funcs_t *gpGamedllFuncs DLLHIDDEN; -extern mutil_funcs_t *gpMetaUtilFuncs DLLHIDDEN; - -// Tell the dll that we'll be loading it as a metamod plugin, in case it -// needs to do something special prior to the standard query/attach -// procedure. In particular, this will allow for DLL's that can be used as -// both standalone DLL's and metamod plugins. (optional; not required in -// plugin) -C_DLLEXPORT void Meta_Init(void); -typedef void (*META_INIT_FN) (void); - -// Get info about plugin, compare meta_interface versions, provide meta -// utility callback functions. -C_DLLEXPORT int Meta_Query(char *interfaceVersion, - plugin_info_t **plinfo, - mutil_funcs_t *pMetaUtilFuncs); -typedef int (*META_QUERY_FN) (char *interfaceVersion, - plugin_info_t **plinfo, - mutil_funcs_t *pMetaUtilFuncs); - -// Attach the plugin to the API; get the table of getapi functions; give -// meta_globals and gamedll_funcs. -C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, - META_FUNCTIONS *pFunctionTable, - meta_globals_t *pMGlobals, - gamedll_funcs_t *pGamedllFuncs); -typedef int (*META_ATTACH_FN) (PLUG_LOADTIME now, - META_FUNCTIONS *pFunctionTable, - meta_globals_t *pMGlobals, - gamedll_funcs_t *pGamedllFuncs); - -// Detach the plugin; tell why and when. -C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); -typedef int (*META_DETACH_FN) (PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - -// Standard HL SDK interface function prototypes. -C_DLLEXPORT int GetEntityAPI_Post(DLL_FUNCTIONS *pFunctionTable, - int interfaceVersion ); -C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, - int *interfaceVersion ); - -// Additional SDK-like interface function prototypes. -C_DLLEXPORT int GetNewDLLFunctions_Post(NEW_DLL_FUNCTIONS *pNewFunctionTable, - int *interfaceVersion ); -C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, - int *interfaceVersion); -C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, - int *interfaceVersion); - -// Convenience macros for accessing GameDLL functions. Note: these talk -// _directly_ to the gamedll, and are not multiplexed through Metamod to -// the other plugins. - -// DLL API functions: -#define MDLL_FUNC gpGamedllFuncs->dllapi_table - -#define MDLL_GameDLLInit MDLL_FUNC->pfnGameInit -#define MDLL_Spawn MDLL_FUNC->pfnSpawn -#define MDLL_Think MDLL_FUNC->pfnThink -#define MDLL_Use MDLL_FUNC->pfnUse -#define MDLL_Touch MDLL_FUNC->pfnTouch -#define MDLL_Blocked MDLL_FUNC->pfnBlocked -#define MDLL_KeyValue MDLL_FUNC->pfnKeyValue -#define MDLL_Save MDLL_FUNC->pfnSave -#define MDLL_Restore MDLL_FUNC->pfnRestore -#define MDLL_ObjectCollsionBox MDLL_FUNC->pfnAbsBox -#define MDLL_SaveWriteFields MDLL_FUNC->pfnSaveWriteFields -#define MDLL_SaveReadFields MDLL_FUNC->pfnSaveReadFields -#define MDLL_SaveGlobalState MDLL_FUNC->pfnSaveGlobalState -#define MDLL_RestoreGlobalState MDLL_FUNC->pfnRestoreGlobalState -#define MDLL_ResetGlobalState MDLL_FUNC->pfnResetGlobalState -#define MDLL_ClientConnect MDLL_FUNC->pfnClientConnect -#define MDLL_ClientDisconnect MDLL_FUNC->pfnClientDisconnect -#define MDLL_ClientKill MDLL_FUNC->pfnClientKill -#define MDLL_ClientPutInServer MDLL_FUNC->pfnClientPutInServer -#define MDLL_ClientCommand MDLL_FUNC->pfnClientCommand -#define MDLL_ClientUserInfoChanged MDLL_FUNC->pfnClientUserInfoChanged -#define MDLL_ServerActivate MDLL_FUNC->pfnServerActivate -#define MDLL_ServerDeactivate MDLL_FUNC->pfnServerDeactivate -#define MDLL_PlayerPreThink MDLL_FUNC->pfnPlayerPreThink -#define MDLL_PlayerPostThink MDLL_FUNC->pfnPlayerPostThink -#define MDLL_StartFrame MDLL_FUNC->pfnStartFrame -#define MDLL_ParmsNewLevel MDLL_FUNC->pfnParmsNewLevel -#define MDLL_ParmsChangeLevel MDLL_FUNC->pfnParmsChangeLevel -#define MDLL_GetGameDescription MDLL_FUNC->pfnGetGameDescription -#define MDLL_PlayerCustomization MDLL_FUNC->pfnPlayerCustomization -#define MDLL_SpectatorConnect MDLL_FUNC->pfnSpectatorConnect -#define MDLL_SpectatorDisconnect MDLL_FUNC->pfnSpectatorDisconnect -#define MDLL_SpectatorThink MDLL_FUNC->pfnSpectatorThink -#define MDLL_Sys_Error MDLL_FUNC->pfnSys_Error -#define MDLL_PM_Move MDLL_FUNC->pfnPM_Move -#define MDLL_PM_Init MDLL_FUNC->pfnPM_Init -#define MDLL_PM_FindTextureType MDLL_FUNC->pfnPM_FindTextureType -#define MDLL_SetupVisibility MDLL_FUNC->pfnSetupVisibility -#define MDLL_UpdateClientData MDLL_FUNC->pfnUpdateClientData -#define MDLL_AddToFullPack MDLL_FUNC->pfnAddToFullPack -#define MDLL_CreateBaseline MDLL_FUNC->pfnCreateBaseline -#define MDLL_RegisterEncoders MDLL_FUNC->pfnRegisterEncoders -#define MDLL_GetWeaponData MDLL_FUNC->pfnGetWeaponData -#define MDLL_CmdStart MDLL_FUNC->pfnCmdStart -#define MDLL_CmdEnd MDLL_FUNC->pfnCmdEnd -#define MDLL_ConnectionlessPacket MDLL_FUNC->pfnConnectionlessPacket -#define MDLL_GetHullBounds MDLL_FUNC->pfnGetHullBounds -#define MDLL_CreateInstancedBaselines MDLL_FUNC->pfnCreateInstancedBaselines -#define MDLL_InconsistentFile MDLL_FUNC->pfnInconsistentFile -#define MDLL_AllowLagCompensation MDLL_FUNC->pfnAllowLagCompensation - -// NEW API functions: -#define MNEW_FUNC gpGamedllFuncs->newapi_table - -#define MNEW_OnFreeEntPrivateData MNEW_FUNC->pfnOnFreeEntPrivateData -#define MNEW_GameShutdown MNEW_FUNC->pfnGameShutdown -#define MNEW_ShouldCollide MNEW_FUNC->pfnShouldCollide -#define MNEW_CvarValue MNEW_FUNC->pfnCvarValue - -#endif /* META_API_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// meta_api.h - description of metamod's DLL interface + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 META_API_H +#define META_API_H + +#include "comp_dep.h" +#include "dllapi.h" // GETENTITYAPI_FN, etc +#include "engine_api.h" // GET_ENGINE_FUNCTIONS_FN, etc +#include "plinfo.h" // plugin_info_t, etc +#include "osdep.h" // DLLEXPORT, etc +#include "mutil.h" + +// Version consists of "major:minor", two separate integer numbers. +// Version 1 original +// Version 2 added plugin_info_t **pinfo +// Version 3 init split into query and attach, added detach +// Version 4 added (PLUG_LOADTIME now) to attach +// Version 5 changed mm ifvers int* to string, moved pl ifvers to info +// Version 5:1 added link support for entity "adminmod_timer" (adminmod) +// Version 5:2 added gamedll_funcs to meta_attach() [v1.0-rc2] +// Version 5:3 added mutil_funcs to meta_query() [v1.05] +// Version 5:4 added centersay utility functions [v1.06] +// Version 5:5 added Meta_Init to plugin API [v1.08] +// Version 5:6 added CallGameEntity utility function [v1.09] +// Version 5:7 added GetUserMsgID, GetUserMsgName util funcs [v1.11] +// Version 5:8 added GetPluginPath [v1.11] +// Version 5:9 added GetGameInfo [v1.14] +// Version 5:10 added GINFO_REALDLL_FULLPATH for GetGameInfo [v1.17] +// Version 5:11 added plugin loading and unloading API [v1.18] +// Version 5:12 added IS_QUERYING_CLIENT_CVAR to mutils [v1.18] +// Version 5:13 added MAKE_REQUESTID and GET_HOOK_TABLES to mutils [v1.19] +#define META_INTERFACE_VERSION "5:13" + +// Flags returned by a plugin's api function. +// NOTE: order is crucial, as greater/less comparisons are made. +typedef enum { + MRES_UNSET = 0, + MRES_IGNORED, // plugin didn't take any action + MRES_HANDLED, // plugin did something, but real function should still be called + MRES_OVERRIDE, // call real function, but use my return value + MRES_SUPERCEDE, // skip real function; use my return value +} META_RES; + +// Variables provided to plugins. +typedef struct meta_globals_s { + META_RES mres; // writable; plugin's return flag + META_RES prev_mres; // readable; return flag of the previous plugin called + META_RES status; // readable; "highest" return flag so far + void *orig_ret; // readable; return value from "real" function + void *override_ret; // readable; return value from overriding/superceding plugin +} meta_globals_t; + +extern meta_globals_t *gpMetaGlobals DLLHIDDEN; +#define SET_META_RESULT(result) gpMetaGlobals->mres=result +#define RETURN_META(result) \ + do { gpMetaGlobals->mres=result; return; } while(0) +#define RETURN_META_VALUE(result, value) \ + do { gpMetaGlobals->mres=result; return(value); } while(0) +#define META_RESULT_STATUS gpMetaGlobals->status +#define META_RESULT_PREVIOUS gpMetaGlobals->prev_mres +#define META_RESULT_ORIG_RET(type) *(type *)gpMetaGlobals->orig_ret +#define META_RESULT_OVERRIDE_RET(type) *(type *)gpMetaGlobals->override_ret + +// Table of getapi functions, retrieved from each plugin. +typedef struct { + GETENTITYAPI_FN pfnGetEntityAPI; + GETENTITYAPI_FN pfnGetEntityAPI_Post; + GETENTITYAPI2_FN pfnGetEntityAPI2; + GETENTITYAPI2_FN pfnGetEntityAPI2_Post; + GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions; + GETNEWDLLFUNCTIONS_FN pfnGetNewDLLFunctions_Post; + GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions; + GET_ENGINE_FUNCTIONS_FN pfnGetEngineFunctions_Post; +} META_FUNCTIONS; + +// Pair of function tables provided by game DLL. +typedef struct { + DLL_FUNCTIONS *dllapi_table; + NEW_DLL_FUNCTIONS *newapi_table; +} gamedll_funcs_t; + +// Declared in plugin; referenced in macros. +extern gamedll_funcs_t *gpGamedllFuncs DLLHIDDEN; +extern mutil_funcs_t *gpMetaUtilFuncs DLLHIDDEN; + +// Tell the dll that we'll be loading it as a metamod plugin, in case it +// needs to do something special prior to the standard query/attach +// procedure. In particular, this will allow for DLL's that can be used as +// both standalone DLL's and metamod plugins. (optional; not required in +// plugin) +C_DLLEXPORT void Meta_Init(void); +typedef void (*META_INIT_FN) (void); + +// Get info about plugin, compare meta_interface versions, provide meta +// utility callback functions. +C_DLLEXPORT int Meta_Query(char *interfaceVersion, + plugin_info_t **plinfo, + mutil_funcs_t *pMetaUtilFuncs); +typedef int (*META_QUERY_FN) (char *interfaceVersion, + plugin_info_t **plinfo, + mutil_funcs_t *pMetaUtilFuncs); + +// Attach the plugin to the API; get the table of getapi functions; give +// meta_globals and gamedll_funcs. +C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, + META_FUNCTIONS *pFunctionTable, + meta_globals_t *pMGlobals, + gamedll_funcs_t *pGamedllFuncs); +typedef int (*META_ATTACH_FN) (PLUG_LOADTIME now, + META_FUNCTIONS *pFunctionTable, + meta_globals_t *pMGlobals, + gamedll_funcs_t *pGamedllFuncs); + +// Detach the plugin; tell why and when. +C_DLLEXPORT int Meta_Detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); +typedef int (*META_DETACH_FN) (PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + +// Standard HL SDK interface function prototypes. +C_DLLEXPORT int GetEntityAPI_Post(DLL_FUNCTIONS *pFunctionTable, + int interfaceVersion ); +C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, + int *interfaceVersion ); + +// Additional SDK-like interface function prototypes. +C_DLLEXPORT int GetNewDLLFunctions_Post(NEW_DLL_FUNCTIONS *pNewFunctionTable, + int *interfaceVersion ); +C_DLLEXPORT int GetEngineFunctions(enginefuncs_t *pengfuncsFromEngine, + int *interfaceVersion); +C_DLLEXPORT int GetEngineFunctions_Post(enginefuncs_t *pengfuncsFromEngine, + int *interfaceVersion); + +// Convenience macros for accessing GameDLL functions. Note: these talk +// _directly_ to the gamedll, and are not multiplexed through Metamod to +// the other plugins. + +// DLL API functions: +#define MDLL_FUNC gpGamedllFuncs->dllapi_table + +#define MDLL_GameDLLInit MDLL_FUNC->pfnGameInit +#define MDLL_Spawn MDLL_FUNC->pfnSpawn +#define MDLL_Think MDLL_FUNC->pfnThink +#define MDLL_Use MDLL_FUNC->pfnUse +#define MDLL_Touch MDLL_FUNC->pfnTouch +#define MDLL_Blocked MDLL_FUNC->pfnBlocked +#define MDLL_KeyValue MDLL_FUNC->pfnKeyValue +#define MDLL_Save MDLL_FUNC->pfnSave +#define MDLL_Restore MDLL_FUNC->pfnRestore +#define MDLL_ObjectCollsionBox MDLL_FUNC->pfnAbsBox +#define MDLL_SaveWriteFields MDLL_FUNC->pfnSaveWriteFields +#define MDLL_SaveReadFields MDLL_FUNC->pfnSaveReadFields +#define MDLL_SaveGlobalState MDLL_FUNC->pfnSaveGlobalState +#define MDLL_RestoreGlobalState MDLL_FUNC->pfnRestoreGlobalState +#define MDLL_ResetGlobalState MDLL_FUNC->pfnResetGlobalState +#define MDLL_ClientConnect MDLL_FUNC->pfnClientConnect +#define MDLL_ClientDisconnect MDLL_FUNC->pfnClientDisconnect +#define MDLL_ClientKill MDLL_FUNC->pfnClientKill +#define MDLL_ClientPutInServer MDLL_FUNC->pfnClientPutInServer +#define MDLL_ClientCommand MDLL_FUNC->pfnClientCommand +#define MDLL_ClientUserInfoChanged MDLL_FUNC->pfnClientUserInfoChanged +#define MDLL_ServerActivate MDLL_FUNC->pfnServerActivate +#define MDLL_ServerDeactivate MDLL_FUNC->pfnServerDeactivate +#define MDLL_PlayerPreThink MDLL_FUNC->pfnPlayerPreThink +#define MDLL_PlayerPostThink MDLL_FUNC->pfnPlayerPostThink +#define MDLL_StartFrame MDLL_FUNC->pfnStartFrame +#define MDLL_ParmsNewLevel MDLL_FUNC->pfnParmsNewLevel +#define MDLL_ParmsChangeLevel MDLL_FUNC->pfnParmsChangeLevel +#define MDLL_GetGameDescription MDLL_FUNC->pfnGetGameDescription +#define MDLL_PlayerCustomization MDLL_FUNC->pfnPlayerCustomization +#define MDLL_SpectatorConnect MDLL_FUNC->pfnSpectatorConnect +#define MDLL_SpectatorDisconnect MDLL_FUNC->pfnSpectatorDisconnect +#define MDLL_SpectatorThink MDLL_FUNC->pfnSpectatorThink +#define MDLL_Sys_Error MDLL_FUNC->pfnSys_Error +#define MDLL_PM_Move MDLL_FUNC->pfnPM_Move +#define MDLL_PM_Init MDLL_FUNC->pfnPM_Init +#define MDLL_PM_FindTextureType MDLL_FUNC->pfnPM_FindTextureType +#define MDLL_SetupVisibility MDLL_FUNC->pfnSetupVisibility +#define MDLL_UpdateClientData MDLL_FUNC->pfnUpdateClientData +#define MDLL_AddToFullPack MDLL_FUNC->pfnAddToFullPack +#define MDLL_CreateBaseline MDLL_FUNC->pfnCreateBaseline +#define MDLL_RegisterEncoders MDLL_FUNC->pfnRegisterEncoders +#define MDLL_GetWeaponData MDLL_FUNC->pfnGetWeaponData +#define MDLL_CmdStart MDLL_FUNC->pfnCmdStart +#define MDLL_CmdEnd MDLL_FUNC->pfnCmdEnd +#define MDLL_ConnectionlessPacket MDLL_FUNC->pfnConnectionlessPacket +#define MDLL_GetHullBounds MDLL_FUNC->pfnGetHullBounds +#define MDLL_CreateInstancedBaselines MDLL_FUNC->pfnCreateInstancedBaselines +#define MDLL_InconsistentFile MDLL_FUNC->pfnInconsistentFile +#define MDLL_AllowLagCompensation MDLL_FUNC->pfnAllowLagCompensation + +// NEW API functions: +#define MNEW_FUNC gpGamedllFuncs->newapi_table + +#define MNEW_OnFreeEntPrivateData MNEW_FUNC->pfnOnFreeEntPrivateData +#define MNEW_GameShutdown MNEW_FUNC->pfnGameShutdown +#define MNEW_ShouldCollide MNEW_FUNC->pfnShouldCollide +#define MNEW_CvarValue MNEW_FUNC->pfnCvarValue + +#endif /* META_API_H */ diff --git a/src/metamod/meta_eiface.h b/src/metamod/meta_eiface.h index 08a6a7a..e947d85 100644 --- a/src/metamod/meta_eiface.h +++ b/src/metamod/meta_eiface.h @@ -1,480 +1,480 @@ - -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// meta_eiface.h - wrapper for engine/dll interface - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MM_META_EIFACE_H -#define MM_META_EIFACE_H - -#include // NEW_DLL_FUNCTIONS, enginefuncs_t -#include // memset() - -#include "comp_dep.h" -#include "osdep.h" //unlikely, OPEN_ARGS - - -// We use our own versions of the engine/dll interface structs. We add a -// few dummy entries to the end and set them to 0. That way we are -// protected from updates to the HL SDK adding new functions which would -// cause a) the game dll copying arbitrary values from us and b) the game -// dll overwriting our memory when using an old Metamod with a new game -// dll. - -const int c_NumDummies = 5; -typedef void (*pdummyfunc)(void); - - -// -------------------------------------------------------------------- -// meta_new_dll_functions_t -// -------------------------------------------------------------------- - -struct meta_new_dll_functions_t : public NEW_DLL_FUNCTIONS { - // Array of five dummy function pointers. Must be filled with NULL. - pdummyfunc dummies[c_NumDummies]; - - // functions : - meta_new_dll_functions_t() DLLINTERNAL; - - meta_new_dll_functions_t( - void (*pfnOnFreeEntPrivateData) (edict_t*), - void (*pfnGameShutdown) (void), - int (*pfnShouldCollide) (edict_t*, edict_t*), - void (*pfnCvarValue) (const edict_t*, const char*), - void (*pfnCvarValue2) (const edict_t*, int, const char*, const char*) - ) DLLINTERNAL; - - meta_new_dll_functions_t( const meta_new_dll_functions_t& ) DLLINTERNAL; - meta_new_dll_functions_t& operator=( const meta_new_dll_functions_t& ) DLLINTERNAL; - - // Fill this object with pointers copied from a NEW_DLL_FUNCTIONS struct. - void DLLINTERNAL set_from( NEW_DLL_FUNCTIONS* pFuncs ); - - // Copy the pointers from this object to a NEW_DLL_FUNCTIONS struct. - void DLLINTERNAL copy_to( NEW_DLL_FUNCTIONS* pFuncs ); - - // return the engine's version of NEW_DLL_FUNCTIONS - int DLLINTERNAL version( void ); - - private: - - // data : - - // The NEW_DLL_FUNCTIONS struct also changed, but the version - // number did not change. That begs the question why to have - // it versioned in the first place, but whaddaya know. - // While the official version is left at 1, we internally - // calculate a different version of the engine's NEW_DLL_FUNCTIONS - // struct since we know that the engine lies to us about the - // version that it uses. - // - // The default version is 1. - // - // With the enginefuncs interface version 156 the function - // pfnCvarValue() was added, which we call version 2. - // - // With the enginefuncs interface version 157 the function - // pfnCvarValue2() was added, which we call version 3. - // - // If Valve ever decides to change the version of the - // NEW_DLL_FUNCTIONS interface in the future (haha), - // we are in trouble and will need to change our - // internal versions. - - static int sm_version; - - // functions : - - // Calculates our idea of the engine's version of the - // NEW_DLL_FUNCTIONS interface. Stores this version for future - // reference in m_version and returns it. - int DLLINTERNAL determine_interface_version( void ); - - // Comfort function to determine the size of the NEW_DLL_FUNCTIONS - // struct for the different versions. - // If passed a version number other than 0, the size for that - // specific version is returned. - // If passed 0 as version number (default) the size for the version - // that was determined to be the version of the currently connected - // engine's interface. Should that version have not yet been - // determined (via the enginefuncs_t interface), 0 is returned to - // indicated this error state. - size_t DLLINTERNAL get_size( int version = 0 ); -}; - - -// Inline functions - -inline meta_new_dll_functions_t::meta_new_dll_functions_t() -{ - memset( this, 0, sizeof(meta_new_dll_functions_t) ); -} - - -inline meta_new_dll_functions_t::meta_new_dll_functions_t( const meta_new_dll_functions_t& _rhs ) -{ - memcpy( this, &_rhs, sizeof(NEW_DLL_FUNCTIONS) ); - memset( dummies, 0, sizeof(pdummyfunc) * c_NumDummies ); -} - - -inline meta_new_dll_functions_t& meta_new_dll_functions_t::operator=( const meta_new_dll_functions_t& _rhs) -{ - memcpy( this, &_rhs, sizeof(NEW_DLL_FUNCTIONS) ); - return *this; -} - - -inline void meta_new_dll_functions_t::set_from( NEW_DLL_FUNCTIONS* _pFuncs ) -{ - memcpy( this, _pFuncs, sizeof(NEW_DLL_FUNCTIONS) ); -} - - -inline int meta_new_dll_functions_t::version( void ) -{ - return sm_version ? sm_version : determine_interface_version(); -} - - - -// No meta version of DLL_FUNCTIONS because that won't be changing anymore. - - - -// -------------------------------------------------------------------- -// meta_enginefuncs_t -// -------------------------------------------------------------------- - - -struct meta_enginefuncs_t : public enginefuncs_t { - // data : - - // Array of five dummy function pointers. Must be filled with NULL. - pdummyfunc dummies[c_NumDummies]; - - // functions : - meta_enginefuncs_t() DLLINTERNAL; - - // Spawn of the devil - meta_enginefuncs_t( - int (*_pfnPrecacheModel) (char*), - int (*_pfnPrecacheSound) (char*), - void (*_pfnSetModel) (edict_t*, const char*), - int (*_pfnModelIndex) (const char*), - int (*_pfnModelFrames) (int), - void (*_pfnSetSize) (edict_t*, const float*, const float*), - void (*_pfnChangeLevel) (char*, char*), - void (*_pfnGetSpawnParms) (edict_t*), - void (*_pfnSaveSpawnParms) (edict_t*), - float (*_pfnVecToYaw) (const float*), - void (*_pfnVecToAngles) (const float*, float*), - void (*_pfnMoveToOrigin) (edict_t*, const float*, float, int), - void (*_pfnChangeYaw) (edict_t*), - void (*_pfnChangePitch) (edict_t*), - edict_t* (*_pfnFindEntityByString) (edict_t*, const char*, const char*), - int (*_pfnGetEntityIllum) (edict_t*), - edict_t* (*_pfnFindEntityInSphere) (edict_t*, const float*, float), - edict_t* (*_pfnFindClientInPVS) (edict_t*), - edict_t* (*_pfnEntitiesInPVS) (edict_t*), - void (*_pfnMakeVectors) (const float*), - void (*_pfnAngleVectors) (const float*, float*, float*, float*), - edict_t* (*_pfnCreateEntity) (void), - void (*_pfnRemoveEntity) (edict_t*), - edict_t* (*_pfnCreateNamedEntity) (int), - void (*_pfnMakeStatic) (edict_t*), - int (*_pfnEntIsOnFloor) (edict_t*), - int (*_pfnDropToFloor) (edict_t*), - int (*_pfnWalkMove) (edict_t*, float, float, int), - void (*_pfnSetOrigin) (edict_t*, const float*), - void (*_pfnEmitSound) (edict_t*, int, const char*, float, float, int, int), - void (*_pfnEmitAmbientSound) (edict_t*, float*, const char*, float, float, int, int), - void (*_pfnTraceLine) (const float*, const float*, int, edict_t*, TraceResult*), - void (*_pfnTraceToss) (edict_t*, edict_t*, TraceResult*), - int (*_pfnTraceMonsterHull) (edict_t*, const float*, const float*, int, edict_t*, TraceResult*), - void (*_pfnTraceHull) (const float*, const float*, int, int, edict_t*, TraceResult*), - void (*_pfnTraceModel) (const float*, const float*, int, edict_t*, TraceResult*), - const char* (*_pfnTraceTexture) (edict_t*, const float*, const float*), - void (*_pfnTraceSphere) (const float*, const float*, int, float, edict_t*, TraceResult*), - void (*_pfnGetAimVector) (edict_t*, float, float*), - void (*_pfnServerCommand) (char*), - void (*_pfnServerExecute) (void), - void (*_pfnClientCommand) (edict_t*, char*, ...), - void (*_pfnParticleEffect) (const float*, const float*, float, float), - void (*_pfnLightStyle) (int, char*), - int (*_pfnDecalIndex) (const char*), - int (*_pfnPointContents) (const float*), - void (*_pfnMessageBegin) (int, int, const float*, edict_t*), - void (*_pfnMessageEnd) (void), - void (*_pfnWriteByte) (int), - void (*_pfnWriteChar) (int), - void (*_pfnWriteShort) (int), - void (*_pfnWriteLong) (int), - void (*_pfnWriteAngle) (float), - void (*_pfnWriteCoord) (float), - void (*_pfnWriteString) (const char*), - void (*_pfnWriteEntity) (int), - void (*_pfnCVarRegister) (cvar_t*), - float (*_pfnCVarGetFloat) (const char*), - const char* (*_pfnCVarGetString) (const char*), - void (*_pfnCVarSetFloat) (const char*, float), - void (*_pfnCVarSetString) (const char*, const char*), - void (*_pfnAlertMessage) (ALERT_TYPE, char*, ...), - void (*_pfnEngineFprintf) (void*, char*, ...), - void* (*_pfnPvAllocEntPrivateData) (edict_t*, int32), - void* (*_pfnPvEntPrivateData) (edict_t*), - void (*_pfnFreeEntPrivateData) (edict_t*), - const char* (*_pfnSzFromIndex) (int), - int (*_pfnAllocString) (const char*), - struct entvars_s*(*_pfnGetVarsOfEnt) (edict_t*), - edict_t* (*_pfnPEntityOfEntOffset) (int), - int (*_pfnEntOffsetOfPEntity) (const edict_t*), - int (*_pfnIndexOfEdict) (const edict_t*), - edict_t* (*_pfnPEntityOfEntIndex) (int), - edict_t* (*_pfnFindEntityByVars) (struct entvars_s*), - void* (*_pfnGetModelPtr) (edict_t*), - int (*_pfnRegUserMsg) (const char*, int), - void (*_pfnAnimationAutomove) (const edict_t*, float), - void (*_pfnGetBonePosition) (const edict_t*, int, float*, float* ), - uint32 (*_pfnFunctionFromName) (const char*), - const char* (*_pfnNameForFunction) (uint32), - void (*_pfnClientPrintf) (edict_t*, PRINT_TYPE, const char*), - void (*_pfnServerPrint) (const char*), - const char* (*_pfnCmd_Args) (void), - const char* (*_pfnCmd_Argv) (int argc), - int (*_pfnCmd_Argc) (void), - void (*_pfnGetAttachment) (const edict_t*, int, float*, float*), - void (*_pfnCRC32_Init) (CRC32_t*), - void (*_pfnCRC32_ProcessBuffer) (CRC32_t*, void*, int), - void (*_pfnCRC32_ProcessByte) (CRC32_t*, unsigned char), - CRC32_t (*_pfnCRC32_Final) (CRC32_t), - int32 (*_pfnRandomLong) (int32, int32), - float (*_pfnRandomFloat) (float, float), - void (*_pfnSetView) (const edict_t*, const edict_t*), - float (*_pfnTime) (void), - void (*_pfnCrosshairAngle) (const edict_t*, float, float), - byte* (*_pfnLoadFileForMe) (char*, int*), - void (*_pfnFreeFile) (void*), - void (*_pfnEndSection) (const char*), - int (*_pfnCompareFileTime) (char*, char*, int*), - void (*_pfnGetGameDir) (char*), - void (*_pfnCvar_RegisterVariable) (cvar_t*), - void (*_pfnFadeClientVolume) (const edict_t*, int, int, int, int), - void (*_pfnSetClientMaxspeed) (const edict_t*, float), - edict_t* (*_pfnCreateFakeClient) (const char*), - void (*_pfnRunPlayerMove) (edict_t*, const float*, float, float, float, unsigned short, byte, byte), - int (*_pfnNumberOfEntities) (void), - char* (*_pfnGetInfoKeyBuffer) (edict_t*), - char* (*_pfnInfoKeyValue) (char*, char*), - void (*_pfnSetKeyValue) (char*, char*, char*), - void (*_pfnSetClientKeyValue) (int, char*, char*, char*), - int (*_pfnIsMapValid) (char*), - void (*_pfnStaticDecal) (const float*, int, int, int), - int (*_pfnPrecacheGeneric) (char*), - int (*_pfnGetPlayerUserId) (edict_t*), - void (*_pfnBuildSoundMsg) (edict_t*, int, const char*, float, float, int, int, int, int, const float*, edict_t*), - int (*_pfnIsDedicatedServer) (void), - cvar_t* (*_pfnCVarGetPointer) (const char*), - unsigned int (*_pfnGetPlayerWONId) (edict_t*), - void (*_pfnInfo_RemoveKey) (char*, const char*), - const char* (*_pfnGetPhysicsKeyValue) (const edict_t*, const char*), - void (*_pfnSetPhysicsKeyValue) (const edict_t*, const char*, const char*), - const char* (*_pfnGetPhysicsInfoString) (const edict_t*), - unsigned short (*_pfnPrecacheEvent) (int, const char*), - void (*_pfnPlaybackEvent) (int, const edict_t*, unsigned short, float, float*, float*, float, float, int, int, int, int), - unsigned char* (*_pfnSetFatPVS) (float*), - unsigned char* (*_pfnSetFatPAS) (float*), - int (*_pfnCheckVisibility) (const edict_t*, unsigned char*), - void (*_pfnDeltaSetField) (struct delta_s*, const char*), - void (*_pfnDeltaUnsetField) (struct delta_s*, const char*), - void (*_pfnDeltaAddEncoder) (char*, void (*)(struct delta_s*, const unsigned char*, const unsigned char*)), - int (*_pfnGetCurrentPlayer) (void), - int (*_pfnCanSkipPlayer) (const edict_t*), - int (*_pfnDeltaFindField) (struct delta_s*, const char*), - void (*_pfnDeltaSetFieldByIndex) (struct delta_s*, int), - void (*_pfnDeltaUnsetFieldByIndex) (struct delta_s*, int), - void (*_pfnSetGroupMask) (int, int), - int (*_pfnCreateInstancedBaseline) (int, struct entity_state_s*), - void (*_pfnCvar_DirectSet) (struct cvar_s*, char*), - void (*_pfnForceUnmodified) (FORCE_TYPE, float*, float*, const char*), - void (*_pfnGetPlayerStats) (const edict_t*, int*, int*), - void (*_pfnAddServerCommand) (char*, void (*) (void)), - qboolean (*_pfnVoice_GetClientListening) (int, int), - qboolean (*_pfnVoice_SetClientListening) (int, int, qboolean), - const char* (*_pfnGetPlayerAuthId) (edict_t*), - sequenceEntry_s* (*_pfnSequenceGet) (const char*, const char*), - sentenceEntry_s* (*_pfnSequencePickSentence) (const char*, int, int*), - int (*_pfnGetFileSize) (char*), - unsigned int (*_pfnGetApproxWavePlayLen) (const char*), - int (*_pfnIsCareerMatch) (void), - int (*_pfnGetLocalizedStringLength) (const char*), - void (*_pfnRegisterTutorMessageShown) (int), - int (*_pfnGetTimesTutorMessageShown) (int), - void (*_pfnProcessTutorMessageDecayBuffer) (int*, int), - void (*_pfnConstructTutorMessageDecayBuffer)(int*, int), - void (*_pfnResetTutorMessageDecayData) (void), - void (*_pfnQueryClientCvarValue) (const edict_t*, const char*), - void (*_pfnQueryClientCvarValue2) (const edict_t*, const char*, int), - int (*_pfnEngCheckParm) (const char *, char**) - ) DLLINTERNAL; - - meta_enginefuncs_t( const meta_enginefuncs_t& ) DLLINTERNAL; - meta_enginefuncs_t& operator=( const meta_enginefuncs_t& ) DLLINTERNAL; - - // Fill this object with pointers copied from an enginefuncs_t struct. - void DLLINTERNAL set_from( enginefuncs_t *pFuncs ); - - // Copy the pointers from this object to an enginefuncs_t struct. - void DLLINTERNAL copy_to( enginefuncs_t *pFuncs ); - - // return the engine interface version - static int DLLINTERNAL version( void ); - - protected: - - // data : - - // The version of the engine functions interface. It is frozen at 138. But no one knows - // when that was and what it looked like then. So we simply interprete it as the - // number of functions that the enginefuncs struct contains. - // - // That means we get gaps inbetween versions and right now we can detect only - // about five different versions anyway, but that suffices for the current itches - // to get scratched. - // - // The default is hence 138. - // A value of 0 means "not yet determined". - // Other possible versions currently detectable: - // 144: engine versions after 1.1.0.9 build 1996 - // 147: engine versions after build 2384 with pfnGetFileSize() - // 155: all versions between build 2384 and the one - // including pfnQueryClientCvarValue() - // 156: includes pfnQueryClientCvarValue() - // 157: includes pfnQueryClientCvarValue2() - // 158: includes pfnEngCheckParm() - static int sm_version DLLHIDDEN; - -}; - -// -// Inline functions -// - -inline meta_enginefuncs_t::meta_enginefuncs_t() -{ - memset( this, 0, sizeof(meta_enginefuncs_t) ); -} - - -inline meta_enginefuncs_t::meta_enginefuncs_t( const meta_enginefuncs_t& _rhs ) -{ - memcpy( this, &_rhs, sizeof(enginefuncs_t) ); - memset( dummies, 0, sizeof(pdummyfunc) * c_NumDummies ); -} - - -inline meta_enginefuncs_t& meta_enginefuncs_t::operator=( const meta_enginefuncs_t& _rhs) -{ - memcpy( this, &_rhs, sizeof(enginefuncs_t) ); - return *this; -} - - -inline void meta_enginefuncs_t::set_from( enginefuncs_t* _pFuncs ) -{ - memcpy( this, _pFuncs, sizeof(enginefuncs_t) ); -} - - -inline void meta_enginefuncs_t::copy_to( enginefuncs_t* _pFuncs ) -{ - memcpy( _pFuncs, this, sizeof(enginefuncs_t) ); -} - - -inline int meta_enginefuncs_t::version( void ) -{ - return sm_version; -} - - - -// -------------------------------------------------------------------- -// HL_enginefuncs_t -// -------------------------------------------------------------------- - -// -// This is a specialisation of the meta_enginefuncs_t struct which is only -// used for the initial copy of the engine functions, i.e. those we get -// passed from the HL engine right at the beginning. -// This specialisation does some extra initialisation when getting set up -// like calculating the engine interface version and fixing up any invalid -// pointers. -// Since there is only one master copy of engine functions this could be -// implemented as a singleton. This is left as an option for later. -// -struct HL_enginefuncs_t : public meta_enginefuncs_t { - - // functions : - HL_enginefuncs_t() DLLINTERNAL; - - // Fill this object with pointers copied from an enginefuncs_t struct - // and fixup the interface. - // For this class this happens in the GiveFptrsToDll() function - // with the pointers passed from the HL engine. - void initialise_interface( enginefuncs_t *pFuncs ) DLLINTERNAL; - - private: - - // functions : - - // Moving copy_to() and set_from() to the private space. - void DLLINTERNAL set_from( enginefuncs_t *pFuncs ) { meta_enginefuncs_t::set_from( pFuncs ); }; - void DLLINTERNAL copy_to( enginefuncs_t *pFuncs ) { meta_enginefuncs_t::copy_to( pFuncs ); }; - - // Determine the version of the engine interface from the - // enginefuncs signature. - void DLLINTERNAL determine_engine_interface_version( void ); - - // Fixup the enginefuncs pointers according to the determined - // version as some pointers may be invalid. - void DLLINTERNAL fixup_engine_interface( void ); -}; - - -inline HL_enginefuncs_t :: HL_enginefuncs_t() : meta_enginefuncs_t() { }; - - -#endif /* META_EIFACE_H */ - + +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// meta_eiface.h - wrapper for engine/dll interface + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MM_META_EIFACE_H +#define MM_META_EIFACE_H + +#include // NEW_DLL_FUNCTIONS, enginefuncs_t +#include // memset() + +#include "comp_dep.h" +#include "osdep.h" //unlikely, OPEN_ARGS + + +// We use our own versions of the engine/dll interface structs. We add a +// few dummy entries to the end and set them to 0. That way we are +// protected from updates to the HL SDK adding new functions which would +// cause a) the game dll copying arbitrary values from us and b) the game +// dll overwriting our memory when using an old Metamod with a new game +// dll. + +const int c_NumDummies = 5; +typedef void (*pdummyfunc)(void); + + +// -------------------------------------------------------------------- +// meta_new_dll_functions_t +// -------------------------------------------------------------------- + +struct meta_new_dll_functions_t : public NEW_DLL_FUNCTIONS { + // Array of five dummy function pointers. Must be filled with NULL. + pdummyfunc dummies[c_NumDummies]; + + // functions : + meta_new_dll_functions_t() DLLINTERNAL; + + meta_new_dll_functions_t( + void (*pfnOnFreeEntPrivateData) (edict_t*), + void (*pfnGameShutdown) (void), + int (*pfnShouldCollide) (edict_t*, edict_t*), + void (*pfnCvarValue) (const edict_t*, const char*), + void (*pfnCvarValue2) (const edict_t*, int, const char*, const char*) + ) DLLINTERNAL; + + meta_new_dll_functions_t( const meta_new_dll_functions_t& ) DLLINTERNAL; + meta_new_dll_functions_t& operator=( const meta_new_dll_functions_t& ) DLLINTERNAL; + + // Fill this object with pointers copied from a NEW_DLL_FUNCTIONS struct. + void DLLINTERNAL set_from( NEW_DLL_FUNCTIONS* pFuncs ); + + // Copy the pointers from this object to a NEW_DLL_FUNCTIONS struct. + void DLLINTERNAL copy_to( NEW_DLL_FUNCTIONS* pFuncs ); + + // return the engine's version of NEW_DLL_FUNCTIONS + int DLLINTERNAL version( void ); + + private: + + // data : + + // The NEW_DLL_FUNCTIONS struct also changed, but the version + // number did not change. That begs the question why to have + // it versioned in the first place, but whaddaya know. + // While the official version is left at 1, we internally + // calculate a different version of the engine's NEW_DLL_FUNCTIONS + // struct since we know that the engine lies to us about the + // version that it uses. + // + // The default version is 1. + // + // With the enginefuncs interface version 156 the function + // pfnCvarValue() was added, which we call version 2. + // + // With the enginefuncs interface version 157 the function + // pfnCvarValue2() was added, which we call version 3. + // + // If Valve ever decides to change the version of the + // NEW_DLL_FUNCTIONS interface in the future (haha), + // we are in trouble and will need to change our + // internal versions. + + static int sm_version; + + // functions : + + // Calculates our idea of the engine's version of the + // NEW_DLL_FUNCTIONS interface. Stores this version for future + // reference in m_version and returns it. + int DLLINTERNAL determine_interface_version( void ); + + // Comfort function to determine the size of the NEW_DLL_FUNCTIONS + // struct for the different versions. + // If passed a version number other than 0, the size for that + // specific version is returned. + // If passed 0 as version number (default) the size for the version + // that was determined to be the version of the currently connected + // engine's interface. Should that version have not yet been + // determined (via the enginefuncs_t interface), 0 is returned to + // indicated this error state. + size_t DLLINTERNAL get_size( int version = 0 ); +}; + + +// Inline functions + +inline meta_new_dll_functions_t::meta_new_dll_functions_t() +{ + memset( this, 0, sizeof(meta_new_dll_functions_t) ); +} + + +inline meta_new_dll_functions_t::meta_new_dll_functions_t( const meta_new_dll_functions_t& _rhs ) +{ + memcpy( this, &_rhs, sizeof(NEW_DLL_FUNCTIONS) ); + memset( dummies, 0, sizeof(pdummyfunc) * c_NumDummies ); +} + + +inline meta_new_dll_functions_t& meta_new_dll_functions_t::operator=( const meta_new_dll_functions_t& _rhs) +{ + memcpy( this, &_rhs, sizeof(NEW_DLL_FUNCTIONS) ); + return *this; +} + + +inline void meta_new_dll_functions_t::set_from( NEW_DLL_FUNCTIONS* _pFuncs ) +{ + memcpy( this, _pFuncs, sizeof(NEW_DLL_FUNCTIONS) ); +} + + +inline int meta_new_dll_functions_t::version( void ) +{ + return sm_version ? sm_version : determine_interface_version(); +} + + + +// No meta version of DLL_FUNCTIONS because that won't be changing anymore. + + + +// -------------------------------------------------------------------- +// meta_enginefuncs_t +// -------------------------------------------------------------------- + + +struct meta_enginefuncs_t : public enginefuncs_t { + // data : + + // Array of five dummy function pointers. Must be filled with NULL. + pdummyfunc dummies[c_NumDummies]; + + // functions : + meta_enginefuncs_t() DLLINTERNAL; + + // Spawn of the devil + meta_enginefuncs_t( + int (*_pfnPrecacheModel) (char*), + int (*_pfnPrecacheSound) (char*), + void (*_pfnSetModel) (edict_t*, const char*), + int (*_pfnModelIndex) (const char*), + int (*_pfnModelFrames) (int), + void (*_pfnSetSize) (edict_t*, const float*, const float*), + void (*_pfnChangeLevel) (char*, char*), + void (*_pfnGetSpawnParms) (edict_t*), + void (*_pfnSaveSpawnParms) (edict_t*), + float (*_pfnVecToYaw) (const float*), + void (*_pfnVecToAngles) (const float*, float*), + void (*_pfnMoveToOrigin) (edict_t*, const float*, float, int), + void (*_pfnChangeYaw) (edict_t*), + void (*_pfnChangePitch) (edict_t*), + edict_t* (*_pfnFindEntityByString) (edict_t*, const char*, const char*), + int (*_pfnGetEntityIllum) (edict_t*), + edict_t* (*_pfnFindEntityInSphere) (edict_t*, const float*, float), + edict_t* (*_pfnFindClientInPVS) (edict_t*), + edict_t* (*_pfnEntitiesInPVS) (edict_t*), + void (*_pfnMakeVectors) (const float*), + void (*_pfnAngleVectors) (const float*, float*, float*, float*), + edict_t* (*_pfnCreateEntity) (void), + void (*_pfnRemoveEntity) (edict_t*), + edict_t* (*_pfnCreateNamedEntity) (int), + void (*_pfnMakeStatic) (edict_t*), + int (*_pfnEntIsOnFloor) (edict_t*), + int (*_pfnDropToFloor) (edict_t*), + int (*_pfnWalkMove) (edict_t*, float, float, int), + void (*_pfnSetOrigin) (edict_t*, const float*), + void (*_pfnEmitSound) (edict_t*, int, const char*, float, float, int, int), + void (*_pfnEmitAmbientSound) (edict_t*, float*, const char*, float, float, int, int), + void (*_pfnTraceLine) (const float*, const float*, int, edict_t*, TraceResult*), + void (*_pfnTraceToss) (edict_t*, edict_t*, TraceResult*), + int (*_pfnTraceMonsterHull) (edict_t*, const float*, const float*, int, edict_t*, TraceResult*), + void (*_pfnTraceHull) (const float*, const float*, int, int, edict_t*, TraceResult*), + void (*_pfnTraceModel) (const float*, const float*, int, edict_t*, TraceResult*), + const char* (*_pfnTraceTexture) (edict_t*, const float*, const float*), + void (*_pfnTraceSphere) (const float*, const float*, int, float, edict_t*, TraceResult*), + void (*_pfnGetAimVector) (edict_t*, float, float*), + void (*_pfnServerCommand) (char*), + void (*_pfnServerExecute) (void), + void (*_pfnClientCommand) (edict_t*, char*, ...), + void (*_pfnParticleEffect) (const float*, const float*, float, float), + void (*_pfnLightStyle) (int, char*), + int (*_pfnDecalIndex) (const char*), + int (*_pfnPointContents) (const float*), + void (*_pfnMessageBegin) (int, int, const float*, edict_t*), + void (*_pfnMessageEnd) (void), + void (*_pfnWriteByte) (int), + void (*_pfnWriteChar) (int), + void (*_pfnWriteShort) (int), + void (*_pfnWriteLong) (int), + void (*_pfnWriteAngle) (float), + void (*_pfnWriteCoord) (float), + void (*_pfnWriteString) (const char*), + void (*_pfnWriteEntity) (int), + void (*_pfnCVarRegister) (cvar_t*), + float (*_pfnCVarGetFloat) (const char*), + const char* (*_pfnCVarGetString) (const char*), + void (*_pfnCVarSetFloat) (const char*, float), + void (*_pfnCVarSetString) (const char*, const char*), + void (*_pfnAlertMessage) (ALERT_TYPE, char*, ...), + void (*_pfnEngineFprintf) (void*, char*, ...), + void* (*_pfnPvAllocEntPrivateData) (edict_t*, int32), + void* (*_pfnPvEntPrivateData) (edict_t*), + void (*_pfnFreeEntPrivateData) (edict_t*), + const char* (*_pfnSzFromIndex) (int), + int (*_pfnAllocString) (const char*), + struct entvars_s*(*_pfnGetVarsOfEnt) (edict_t*), + edict_t* (*_pfnPEntityOfEntOffset) (int), + int (*_pfnEntOffsetOfPEntity) (const edict_t*), + int (*_pfnIndexOfEdict) (const edict_t*), + edict_t* (*_pfnPEntityOfEntIndex) (int), + edict_t* (*_pfnFindEntityByVars) (struct entvars_s*), + void* (*_pfnGetModelPtr) (edict_t*), + int (*_pfnRegUserMsg) (const char*, int), + void (*_pfnAnimationAutomove) (const edict_t*, float), + void (*_pfnGetBonePosition) (const edict_t*, int, float*, float* ), + uint32 (*_pfnFunctionFromName) (const char*), + const char* (*_pfnNameForFunction) (uint32), + void (*_pfnClientPrintf) (edict_t*, PRINT_TYPE, const char*), + void (*_pfnServerPrint) (const char*), + const char* (*_pfnCmd_Args) (void), + const char* (*_pfnCmd_Argv) (int argc), + int (*_pfnCmd_Argc) (void), + void (*_pfnGetAttachment) (const edict_t*, int, float*, float*), + void (*_pfnCRC32_Init) (CRC32_t*), + void (*_pfnCRC32_ProcessBuffer) (CRC32_t*, void*, int), + void (*_pfnCRC32_ProcessByte) (CRC32_t*, unsigned char), + CRC32_t (*_pfnCRC32_Final) (CRC32_t), + int32 (*_pfnRandomLong) (int32, int32), + float (*_pfnRandomFloat) (float, float), + void (*_pfnSetView) (const edict_t*, const edict_t*), + float (*_pfnTime) (void), + void (*_pfnCrosshairAngle) (const edict_t*, float, float), + byte* (*_pfnLoadFileForMe) (char*, int*), + void (*_pfnFreeFile) (void*), + void (*_pfnEndSection) (const char*), + int (*_pfnCompareFileTime) (char*, char*, int*), + void (*_pfnGetGameDir) (char*), + void (*_pfnCvar_RegisterVariable) (cvar_t*), + void (*_pfnFadeClientVolume) (const edict_t*, int, int, int, int), + void (*_pfnSetClientMaxspeed) (const edict_t*, float), + edict_t* (*_pfnCreateFakeClient) (const char*), + void (*_pfnRunPlayerMove) (edict_t*, const float*, float, float, float, unsigned short, byte, byte), + int (*_pfnNumberOfEntities) (void), + char* (*_pfnGetInfoKeyBuffer) (edict_t*), + char* (*_pfnInfoKeyValue) (char*, char*), + void (*_pfnSetKeyValue) (char*, char*, char*), + void (*_pfnSetClientKeyValue) (int, char*, char*, char*), + int (*_pfnIsMapValid) (char*), + void (*_pfnStaticDecal) (const float*, int, int, int), + int (*_pfnPrecacheGeneric) (char*), + int (*_pfnGetPlayerUserId) (edict_t*), + void (*_pfnBuildSoundMsg) (edict_t*, int, const char*, float, float, int, int, int, int, const float*, edict_t*), + int (*_pfnIsDedicatedServer) (void), + cvar_t* (*_pfnCVarGetPointer) (const char*), + unsigned int (*_pfnGetPlayerWONId) (edict_t*), + void (*_pfnInfo_RemoveKey) (char*, const char*), + const char* (*_pfnGetPhysicsKeyValue) (const edict_t*, const char*), + void (*_pfnSetPhysicsKeyValue) (const edict_t*, const char*, const char*), + const char* (*_pfnGetPhysicsInfoString) (const edict_t*), + unsigned short (*_pfnPrecacheEvent) (int, const char*), + void (*_pfnPlaybackEvent) (int, const edict_t*, unsigned short, float, float*, float*, float, float, int, int, int, int), + unsigned char* (*_pfnSetFatPVS) (float*), + unsigned char* (*_pfnSetFatPAS) (float*), + int (*_pfnCheckVisibility) (const edict_t*, unsigned char*), + void (*_pfnDeltaSetField) (struct delta_s*, const char*), + void (*_pfnDeltaUnsetField) (struct delta_s*, const char*), + void (*_pfnDeltaAddEncoder) (char*, void (*)(struct delta_s*, const unsigned char*, const unsigned char*)), + int (*_pfnGetCurrentPlayer) (void), + int (*_pfnCanSkipPlayer) (const edict_t*), + int (*_pfnDeltaFindField) (struct delta_s*, const char*), + void (*_pfnDeltaSetFieldByIndex) (struct delta_s*, int), + void (*_pfnDeltaUnsetFieldByIndex) (struct delta_s*, int), + void (*_pfnSetGroupMask) (int, int), + int (*_pfnCreateInstancedBaseline) (int, struct entity_state_s*), + void (*_pfnCvar_DirectSet) (struct cvar_s*, char*), + void (*_pfnForceUnmodified) (FORCE_TYPE, float*, float*, const char*), + void (*_pfnGetPlayerStats) (const edict_t*, int*, int*), + void (*_pfnAddServerCommand) (char*, void (*) (void)), + qboolean (*_pfnVoice_GetClientListening) (int, int), + qboolean (*_pfnVoice_SetClientListening) (int, int, qboolean), + const char* (*_pfnGetPlayerAuthId) (edict_t*), + sequenceEntry_s* (*_pfnSequenceGet) (const char*, const char*), + sentenceEntry_s* (*_pfnSequencePickSentence) (const char*, int, int*), + int (*_pfnGetFileSize) (char*), + unsigned int (*_pfnGetApproxWavePlayLen) (const char*), + int (*_pfnIsCareerMatch) (void), + int (*_pfnGetLocalizedStringLength) (const char*), + void (*_pfnRegisterTutorMessageShown) (int), + int (*_pfnGetTimesTutorMessageShown) (int), + void (*_pfnProcessTutorMessageDecayBuffer) (int*, int), + void (*_pfnConstructTutorMessageDecayBuffer)(int*, int), + void (*_pfnResetTutorMessageDecayData) (void), + void (*_pfnQueryClientCvarValue) (const edict_t*, const char*), + void (*_pfnQueryClientCvarValue2) (const edict_t*, const char*, int), + int (*_pfnEngCheckParm) (const char *, char**) + ) DLLINTERNAL; + + meta_enginefuncs_t( const meta_enginefuncs_t& ) DLLINTERNAL; + meta_enginefuncs_t& operator=( const meta_enginefuncs_t& ) DLLINTERNAL; + + // Fill this object with pointers copied from an enginefuncs_t struct. + void DLLINTERNAL set_from( enginefuncs_t *pFuncs ); + + // Copy the pointers from this object to an enginefuncs_t struct. + void DLLINTERNAL copy_to( enginefuncs_t *pFuncs ); + + // return the engine interface version + static int DLLINTERNAL version( void ); + + protected: + + // data : + + // The version of the engine functions interface. It is frozen at 138. But no one knows + // when that was and what it looked like then. So we simply interprete it as the + // number of functions that the enginefuncs struct contains. + // + // That means we get gaps inbetween versions and right now we can detect only + // about five different versions anyway, but that suffices for the current itches + // to get scratched. + // + // The default is hence 138. + // A value of 0 means "not yet determined". + // Other possible versions currently detectable: + // 144: engine versions after 1.1.0.9 build 1996 + // 147: engine versions after build 2384 with pfnGetFileSize() + // 155: all versions between build 2384 and the one + // including pfnQueryClientCvarValue() + // 156: includes pfnQueryClientCvarValue() + // 157: includes pfnQueryClientCvarValue2() + // 158: includes pfnEngCheckParm() + static int sm_version DLLHIDDEN; + +}; + +// +// Inline functions +// + +inline meta_enginefuncs_t::meta_enginefuncs_t() +{ + memset( this, 0, sizeof(meta_enginefuncs_t) ); +} + + +inline meta_enginefuncs_t::meta_enginefuncs_t( const meta_enginefuncs_t& _rhs ) +{ + memcpy( this, &_rhs, sizeof(enginefuncs_t) ); + memset( dummies, 0, sizeof(pdummyfunc) * c_NumDummies ); +} + + +inline meta_enginefuncs_t& meta_enginefuncs_t::operator=( const meta_enginefuncs_t& _rhs) +{ + memcpy( this, &_rhs, sizeof(enginefuncs_t) ); + return *this; +} + + +inline void meta_enginefuncs_t::set_from( enginefuncs_t* _pFuncs ) +{ + memcpy( this, _pFuncs, sizeof(enginefuncs_t) ); +} + + +inline void meta_enginefuncs_t::copy_to( enginefuncs_t* _pFuncs ) +{ + memcpy( _pFuncs, this, sizeof(enginefuncs_t) ); +} + + +inline int meta_enginefuncs_t::version( void ) +{ + return sm_version; +} + + + +// -------------------------------------------------------------------- +// HL_enginefuncs_t +// -------------------------------------------------------------------- + +// +// This is a specialisation of the meta_enginefuncs_t struct which is only +// used for the initial copy of the engine functions, i.e. those we get +// passed from the HL engine right at the beginning. +// This specialisation does some extra initialisation when getting set up +// like calculating the engine interface version and fixing up any invalid +// pointers. +// Since there is only one master copy of engine functions this could be +// implemented as a singleton. This is left as an option for later. +// +struct HL_enginefuncs_t : public meta_enginefuncs_t { + + // functions : + HL_enginefuncs_t() DLLINTERNAL; + + // Fill this object with pointers copied from an enginefuncs_t struct + // and fixup the interface. + // For this class this happens in the GiveFptrsToDll() function + // with the pointers passed from the HL engine. + void initialise_interface( enginefuncs_t *pFuncs ) DLLINTERNAL; + + private: + + // functions : + + // Moving copy_to() and set_from() to the private space. + void DLLINTERNAL set_from( enginefuncs_t *pFuncs ) { meta_enginefuncs_t::set_from( pFuncs ); }; + void DLLINTERNAL copy_to( enginefuncs_t *pFuncs ) { meta_enginefuncs_t::copy_to( pFuncs ); }; + + // Determine the version of the engine interface from the + // enginefuncs signature. + void DLLINTERNAL determine_engine_interface_version( void ); + + // Fixup the enginefuncs pointers according to the determined + // version as some pointers may be invalid. + void DLLINTERNAL fixup_engine_interface( void ); +}; + + +inline HL_enginefuncs_t :: HL_enginefuncs_t() : meta_enginefuncs_t() { }; + + +#endif /* META_EIFACE_H */ + diff --git a/src/metamod/metamod.h b/src/metamod/metamod.h index 1b699c9..3d86100 100644 --- a/src/metamod/metamod.h +++ b/src/metamod/metamod.h @@ -1,256 +1,256 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// metamod.h - (main) description of metamod operations - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 METAMOD_H -#define METAMOD_H - -#include "comp_dep.h" -#include "meta_api.h" // META_RES, etc -#include "mlist.h" // MPluginList, etc -#include "mreg.h" // MRegCmdList, etc -#include "conf_meta.h" // MConfig -#include "osdep.h" // NAME_MAX, etc -#include "types_meta.h" // mBOOL -#include "mplayer.h" // MPlayerList -#include "meta_eiface.h" // HL_enginefuncs_t, meta_enginefuncs_t -#include "engine_t.h" // engine_t, Engine - -// file that lists plugins to load at startup -#define PLUGINS_INI "addons/metamod/plugins.ini" -#define OLD_PLUGINS_INI "metamod.ini" - -// file that contains commands to metamod plugins at startup -#define EXEC_CFG "addons/metamod/exec.cfg" -#define OLD_EXEC_CFG "metaexec.cfg" - -// previously, file that contained path for an override-gamedll -#define OLD_GAMEDLL_TXT "metagame.ini" - -// generic config file -#define CONFIG_INI "addons/metamod/config.ini" - -// metamod module handle -extern DLHANDLE metamod_handle DLLHIDDEN; - -// cvar to contain version -extern cvar_t meta_version DLLHIDDEN; - -// Info about the game dll/mod. -typedef struct gamedll_s { - char name[NAME_MAX]; // ie "cstrike" (from gamedir) - const char *desc; // ie "Counter-Strike" - char gamedir[PATH_MAX]; // ie "/home/willday/half-life/cstrike" - char pathname[PATH_MAX]; // ie "/home/willday/half-life/cstrike/dlls/cs_i386.so" - char const *file; // ie "cs_i386.so" - char real_pathname[PATH_MAX]; // in case pathname overridden by bot, etc - DLHANDLE handle; - gamedll_funcs_t funcs; // dllapi_table, newapi_table -} gamedll_t; -extern gamedll_t GameDLL DLLHIDDEN; - -// SDK variables for storing engine funcs and globals. -extern HL_enginefuncs_t g_engfuncs DLLHIDDEN; -extern globalvars_t *gpGlobals DLLHIDDEN; - -// Our modified version of the engine funcs, to give to plugins. -extern meta_enginefuncs_t g_plugin_engfuncs DLLHIDDEN; - -// Config structure. -extern MConfig *Config DLLHIDDEN; - -// List of plugins loaded/opened/running. -extern MPluginList *Plugins DLLHIDDEN; - -// List of command functions registered by plugins. -extern MRegCmdList *RegCmds DLLHIDDEN; - -// List of cvar structures registered by plugins. -extern MRegCvarList *RegCvars DLLHIDDEN; - -// List of user messages registered by gamedll. -extern MRegMsgList *RegMsgs DLLHIDDEN; - -// Data provided to plugins. -// Separate copies to prevent plugins from modifying "readable" parts. -// See meta_api.h for meta_globals_t structure. -extern meta_globals_t PublicMetaGlobals DLLHIDDEN; -extern meta_globals_t PrivateMetaGlobals DLLHIDDEN; - -// hook function tables -extern DLL_FUNCTIONS *g_pHookedDllFunctions DLLHIDDEN; -extern NEW_DLL_FUNCTIONS *g_pHookedNewDllFunctions DLLHIDDEN; - -extern int metamod_not_loaded DLLHIDDEN; - -// Holds cached player info, right now only things for querying cvars -// Max players is always 32, small enough that we can use a static array -extern MPlayerList g_Players DLLHIDDEN; - -extern int requestid_counter DLLHIDDEN; - -int DLLINTERNAL metamod_startup(void); - -mBOOL DLLINTERNAL meta_init_gamedll(void); -mBOOL DLLINTERNAL meta_load_gamedll(void); - -// ===== lotsa macros... ====================================================== - -// These are the meat of the metamod processing, and are as ugly as (or -// uglier) than they look. This is done via macros, because of the varying -// parameter types (int, void, edict_t*, etc) as well as varying -// function-pointer types and different api tables (dllapi, newapi, -// engine), which just can't be passed to a function. And, since the -// operation is similar for each api call, I didn't want to keep -// duplicating code all over the place. Thus the ugly macros. -// -// The basic operation is, for each api call: -// - iterate through list of plugins -// - for each plugin, if it provides this api call, then call the -// function in the plugin -// - call the "real" function (in the game dll, or from the engine) -// - for each plugin, check for a "post" version of the function, and call -// if present -// -// -// Also, for any api call, each plugin has the opportunity to replace the -// real routine, in two ways: -// - prevent the real routine from being called ("supercede") -// - allow the real routine to be called, but change the value that's -// returned ("override") -// -// Thus after each plugin is called, its META_RETURN flag is checked, and -// action taken appropriately. Note that supercede/override only affects -// the _real_ routine; other plugins will still be called. -// -// In addition to the SUPERCEDE and OVERRIDE flags, there are two -// additional flags a plugin can return: -// - HANDLED ("I did something here") -// - IGNORED ("I didn't really do anything") -// -// These aren't used by metamod itself, but could be used by plugins to -// get an idea if a previous plugin did anything. -// -// -// The 5 basic macros are: -// SETUP -// CALL_PLUGIN -// CALL_GAME and CALL_ENGINE -// RETURN -// -// These 5 are actually used to build second level macros for each api type -// (dllapi, newapi, engine), with the CALL_PLUGIN macro being used twice -// (before and after). Altogether, they end up expanding to approx 150 -// lines of code for _each_ api call. Ack, ugly indeed. -// -// However, due to some functions returning 'void', and others returning an -// actual value, I had to have separate macros for the two, since I -// couldn't seem to generalize the two into a form that the compiler would -// accept. Thus there are "_void" versions of the 5 macros; these are -// listed first. - -// ===== macros for void-returning functions ================================== - -// return (void) -#define RETURN_API_void() \ - return; - -// ===== macros for type-returning functions ================================== - -// return a value -#define RETURN_API(ret_t) \ - {return(GET_RET_CLASS(ret_val, ret_t));} - -// ===== end macros =========================================================== - -#ifdef META_PERFMON - -// ============================================================================ -// Api-hook performance monitoring -// ============================================================================ - -extern long double total_tsc DLLHIDDEN; -extern unsigned long long count_tsc DLLHIDDEN; -extern unsigned long long active_tsc DLLHIDDEN; -extern unsigned long long min_tsc DLLHIDDEN; - -inline unsigned long long DLLINTERNAL GET_TSC(void) { - union { struct { unsigned int eax, edx; } split; unsigned long long full; } tsc; -#ifdef __GNUC__ - __asm__ __volatile__("rdtsc":"=a"(tsc.split.eax), "=d"(tsc.split.edx)); -#else - __asm - { - rdtsc - mov tsc.split.eax, eax - mov tsc.split.edx, edx - } -#endif - return(tsc.full); -} - -#define API_START_TSC_TRACKING() \ - active_tsc = GET_TSC() - -#define API_PAUSE_TSC_TRACKING() \ - total_tsc += GET_TSC() - active_tsc - -#define API_UNPAUSE_TSC_TRACKING() \ - active_tsc = GET_TSC() - -#define API_END_TSC_TRACKING() { \ - unsigned long long run_tsc = GET_TSC() - active_tsc; \ - total_tsc += run_tsc; \ - count_tsc++; \ - if(min_tsc == 0 || run_tsc < min_tsc) \ - min_tsc = run_tsc; \ - } - -// ===== end ================================================================== - -#else - -// ===== performance monitor disabled ========================================= - -#define API_START_TSC_TRACKING() -#define API_PAUSE_TSC_TRACKING() -#define API_UNPAUSE_TSC_TRACKING() -#define API_END_TSC_TRACKING() - -// ===== end ================================================================== - -#endif /*META_PERFMON*/ - -#endif /* METAMOD_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// metamod.h - (main) description of metamod operations + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 METAMOD_H +#define METAMOD_H + +#include "comp_dep.h" +#include "meta_api.h" // META_RES, etc +#include "mlist.h" // MPluginList, etc +#include "mreg.h" // MRegCmdList, etc +#include "conf_meta.h" // MConfig +#include "osdep.h" // NAME_MAX, etc +#include "types_meta.h" // mBOOL +#include "mplayer.h" // MPlayerList +#include "meta_eiface.h" // HL_enginefuncs_t, meta_enginefuncs_t +#include "engine_t.h" // engine_t, Engine + +// file that lists plugins to load at startup +#define PLUGINS_INI "addons/metamod/plugins.ini" +#define OLD_PLUGINS_INI "metamod.ini" + +// file that contains commands to metamod plugins at startup +#define EXEC_CFG "addons/metamod/exec.cfg" +#define OLD_EXEC_CFG "metaexec.cfg" + +// previously, file that contained path for an override-gamedll +#define OLD_GAMEDLL_TXT "metagame.ini" + +// generic config file +#define CONFIG_INI "addons/metamod/config.ini" + +// metamod module handle +extern DLHANDLE metamod_handle DLLHIDDEN; + +// cvar to contain version +extern cvar_t meta_version DLLHIDDEN; + +// Info about the game dll/mod. +typedef struct gamedll_s { + char name[NAME_MAX]; // ie "cstrike" (from gamedir) + const char *desc; // ie "Counter-Strike" + char gamedir[PATH_MAX]; // ie "/home/willday/half-life/cstrike" + char pathname[PATH_MAX]; // ie "/home/willday/half-life/cstrike/dlls/cs_i386.so" + char const *file; // ie "cs_i386.so" + char real_pathname[PATH_MAX]; // in case pathname overridden by bot, etc + DLHANDLE handle; + gamedll_funcs_t funcs; // dllapi_table, newapi_table +} gamedll_t; +extern gamedll_t GameDLL DLLHIDDEN; + +// SDK variables for storing engine funcs and globals. +extern HL_enginefuncs_t g_engfuncs DLLHIDDEN; +extern globalvars_t *gpGlobals DLLHIDDEN; + +// Our modified version of the engine funcs, to give to plugins. +extern meta_enginefuncs_t g_plugin_engfuncs DLLHIDDEN; + +// Config structure. +extern MConfig *Config DLLHIDDEN; + +// List of plugins loaded/opened/running. +extern MPluginList *Plugins DLLHIDDEN; + +// List of command functions registered by plugins. +extern MRegCmdList *RegCmds DLLHIDDEN; + +// List of cvar structures registered by plugins. +extern MRegCvarList *RegCvars DLLHIDDEN; + +// List of user messages registered by gamedll. +extern MRegMsgList *RegMsgs DLLHIDDEN; + +// Data provided to plugins. +// Separate copies to prevent plugins from modifying "readable" parts. +// See meta_api.h for meta_globals_t structure. +extern meta_globals_t PublicMetaGlobals DLLHIDDEN; +extern meta_globals_t PrivateMetaGlobals DLLHIDDEN; + +// hook function tables +extern DLL_FUNCTIONS *g_pHookedDllFunctions DLLHIDDEN; +extern NEW_DLL_FUNCTIONS *g_pHookedNewDllFunctions DLLHIDDEN; + +extern int metamod_not_loaded DLLHIDDEN; + +// Holds cached player info, right now only things for querying cvars +// Max players is always 32, small enough that we can use a static array +extern MPlayerList g_Players DLLHIDDEN; + +extern int requestid_counter DLLHIDDEN; + +int DLLINTERNAL metamod_startup(void); + +mBOOL DLLINTERNAL meta_init_gamedll(void); +mBOOL DLLINTERNAL meta_load_gamedll(void); + +// ===== lotsa macros... ====================================================== + +// These are the meat of the metamod processing, and are as ugly as (or +// uglier) than they look. This is done via macros, because of the varying +// parameter types (int, void, edict_t*, etc) as well as varying +// function-pointer types and different api tables (dllapi, newapi, +// engine), which just can't be passed to a function. And, since the +// operation is similar for each api call, I didn't want to keep +// duplicating code all over the place. Thus the ugly macros. +// +// The basic operation is, for each api call: +// - iterate through list of plugins +// - for each plugin, if it provides this api call, then call the +// function in the plugin +// - call the "real" function (in the game dll, or from the engine) +// - for each plugin, check for a "post" version of the function, and call +// if present +// +// +// Also, for any api call, each plugin has the opportunity to replace the +// real routine, in two ways: +// - prevent the real routine from being called ("supercede") +// - allow the real routine to be called, but change the value that's +// returned ("override") +// +// Thus after each plugin is called, its META_RETURN flag is checked, and +// action taken appropriately. Note that supercede/override only affects +// the _real_ routine; other plugins will still be called. +// +// In addition to the SUPERCEDE and OVERRIDE flags, there are two +// additional flags a plugin can return: +// - HANDLED ("I did something here") +// - IGNORED ("I didn't really do anything") +// +// These aren't used by metamod itself, but could be used by plugins to +// get an idea if a previous plugin did anything. +// +// +// The 5 basic macros are: +// SETUP +// CALL_PLUGIN +// CALL_GAME and CALL_ENGINE +// RETURN +// +// These 5 are actually used to build second level macros for each api type +// (dllapi, newapi, engine), with the CALL_PLUGIN macro being used twice +// (before and after). Altogether, they end up expanding to approx 150 +// lines of code for _each_ api call. Ack, ugly indeed. +// +// However, due to some functions returning 'void', and others returning an +// actual value, I had to have separate macros for the two, since I +// couldn't seem to generalize the two into a form that the compiler would +// accept. Thus there are "_void" versions of the 5 macros; these are +// listed first. + +// ===== macros for void-returning functions ================================== + +// return (void) +#define RETURN_API_void() \ + return; + +// ===== macros for type-returning functions ================================== + +// return a value +#define RETURN_API(ret_t) \ + {return(GET_RET_CLASS(ret_val, ret_t));} + +// ===== end macros =========================================================== + +#ifdef META_PERFMON + +// ============================================================================ +// Api-hook performance monitoring +// ============================================================================ + +extern long double total_tsc DLLHIDDEN; +extern unsigned long long count_tsc DLLHIDDEN; +extern unsigned long long active_tsc DLLHIDDEN; +extern unsigned long long min_tsc DLLHIDDEN; + +inline unsigned long long DLLINTERNAL GET_TSC(void) { + union { struct { unsigned int eax, edx; } split; unsigned long long full; } tsc; +#ifdef __GNUC__ + __asm__ __volatile__("rdtsc":"=a"(tsc.split.eax), "=d"(tsc.split.edx)); +#else + __asm + { + rdtsc + mov tsc.split.eax, eax + mov tsc.split.edx, edx + } +#endif + return(tsc.full); +} + +#define API_START_TSC_TRACKING() \ + active_tsc = GET_TSC() + +#define API_PAUSE_TSC_TRACKING() \ + total_tsc += GET_TSC() - active_tsc + +#define API_UNPAUSE_TSC_TRACKING() \ + active_tsc = GET_TSC() + +#define API_END_TSC_TRACKING() { \ + unsigned long long run_tsc = GET_TSC() - active_tsc; \ + total_tsc += run_tsc; \ + count_tsc++; \ + if(min_tsc == 0 || run_tsc < min_tsc) \ + min_tsc = run_tsc; \ + } + +// ===== end ================================================================== + +#else + +// ===== performance monitor disabled ========================================= + +#define API_START_TSC_TRACKING() +#define API_PAUSE_TSC_TRACKING() +#define API_UNPAUSE_TSC_TRACKING() +#define API_END_TSC_TRACKING() + +// ===== end ================================================================== + +#endif /*META_PERFMON*/ + +#endif /* METAMOD_H */ diff --git a/src/metamod/mlist.h b/src/metamod/mlist.h index c1597c2..7b0f7a8 100644 --- a/src/metamod/mlist.h +++ b/src/metamod/mlist.h @@ -1,94 +1,94 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// mlist.h - class and constants to describe a list of plugins - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MLIST_H -#define MLIST_H - -#include "types_meta.h" // mBOOL -#include "mplugin.h" // class MPlugin -#include "plinfo.h" // plid_t, etc -#include "new_baseclass.h" - -// Max number of plugins we can manage. This is an arbitrary, fixed number, -// for convenience. It would probably be better to dynamically grow the -// list as needed, but we do this for now. -#define MAX_PLUGINS 50 -// Width required to printf above MAX, for show() functions. -#define WIDTH_MAX_PLUGINS 2 - - -// A list of plugins. -class MPluginList : public class_metamod_new { - public: - // data: - MPlugin plist[MAX_PLUGINS]; // array of plugins - int size; // size of list, ie MAX_PLUGINS - int endlist; // index of last used entry - char inifile[PATH_MAX]; // full pathname - - // constructor: - MPluginList(const char *ifile) DLLINTERNAL; - - // functions: - void DLLINTERNAL reset_plugin(MPlugin *pl_find); - MPlugin * DLLINTERNAL find(int pindex); // find by index - MPlugin * DLLINTERNAL find(const char *findpath); // find by pathname - MPlugin * DLLINTERNAL find(plid_t id); // find by plid_t - MPlugin * DLLINTERNAL find(DLHANDLE handle); // find by handle - MPlugin * DLLINTERNAL find_memloc(void *memptr); // find by memory location - MPlugin * DLLINTERNAL find_match(const char *prefix); // find by partial prefix match - MPlugin * DLLINTERNAL find_match(MPlugin *pmatch); // find by platform_match() - MPlugin * DLLINTERNAL add(MPlugin *padd); - - mBOOL DLLINTERNAL found_child_plugins(int source_index); - void DLLINTERNAL clear_source_plugin_index(int source_index); - void DLLINTERNAL trim_list(void); - - mBOOL DLLINTERNAL ini_startup(void); // read inifile at startup - mBOOL DLLINTERNAL ini_refresh(void); // re-read inifile - mBOOL DLLINTERNAL cmd_addload(const char *args); // load from console command - MPlugin * DLLINTERNAL plugin_addload(plid_t plid, const char *fname, PLUG_LOADTIME now); //load from plugin - - mBOOL DLLINTERNAL load(void); // load the list, at startup - mBOOL DLLINTERNAL refresh(PLUG_LOADTIME now); // update from re-read inifile - void DLLINTERNAL unpause_all(void); // unpause any paused plugins - void DLLINTERNAL retry_all(PLUG_LOADTIME now); // retry any pending plugin actions - void DLLINTERNAL show(int source_index); // list plugins to console - void DLLINTERNAL show(void) { show(-1); }; // list plugins to console - void DLLINTERNAL show_client(edict_t *pEntity); // list plugins to player client -}; - -#endif /* MLIST_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// mlist.h - class and constants to describe a list of plugins + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MLIST_H +#define MLIST_H + +#include "types_meta.h" // mBOOL +#include "mplugin.h" // class MPlugin +#include "plinfo.h" // plid_t, etc +#include "new_baseclass.h" + +// Max number of plugins we can manage. This is an arbitrary, fixed number, +// for convenience. It would probably be better to dynamically grow the +// list as needed, but we do this for now. +#define MAX_PLUGINS 50 +// Width required to printf above MAX, for show() functions. +#define WIDTH_MAX_PLUGINS 2 + + +// A list of plugins. +class MPluginList : public class_metamod_new { + public: + // data: + MPlugin plist[MAX_PLUGINS]; // array of plugins + int size; // size of list, ie MAX_PLUGINS + int endlist; // index of last used entry + char inifile[PATH_MAX]; // full pathname + + // constructor: + MPluginList(const char *ifile) DLLINTERNAL; + + // functions: + void DLLINTERNAL reset_plugin(MPlugin *pl_find); + MPlugin * DLLINTERNAL find(int pindex); // find by index + MPlugin * DLLINTERNAL find(const char *findpath); // find by pathname + MPlugin * DLLINTERNAL find(plid_t id); // find by plid_t + MPlugin * DLLINTERNAL find(DLHANDLE handle); // find by handle + MPlugin * DLLINTERNAL find_memloc(void *memptr); // find by memory location + MPlugin * DLLINTERNAL find_match(const char *prefix); // find by partial prefix match + MPlugin * DLLINTERNAL find_match(MPlugin *pmatch); // find by platform_match() + MPlugin * DLLINTERNAL add(MPlugin *padd); + + mBOOL DLLINTERNAL found_child_plugins(int source_index); + void DLLINTERNAL clear_source_plugin_index(int source_index); + void DLLINTERNAL trim_list(void); + + mBOOL DLLINTERNAL ini_startup(void); // read inifile at startup + mBOOL DLLINTERNAL ini_refresh(void); // re-read inifile + mBOOL DLLINTERNAL cmd_addload(const char *args); // load from console command + MPlugin * DLLINTERNAL plugin_addload(plid_t plid, const char *fname, PLUG_LOADTIME now); //load from plugin + + mBOOL DLLINTERNAL load(void); // load the list, at startup + mBOOL DLLINTERNAL refresh(PLUG_LOADTIME now); // update from re-read inifile + void DLLINTERNAL unpause_all(void); // unpause any paused plugins + void DLLINTERNAL retry_all(PLUG_LOADTIME now); // retry any pending plugin actions + void DLLINTERNAL show(int source_index); // list plugins to console + void DLLINTERNAL show(void) { show(-1); }; // list plugins to console + void DLLINTERNAL show_client(edict_t *pEntity); // list plugins to player client +}; + +#endif /* MLIST_H */ diff --git a/src/metamod/mm_pextensions.h b/src/metamod/mm_pextensions.h index 2a0329e..27ced94 100644 --- a/src/metamod/mm_pextensions.h +++ b/src/metamod/mm_pextensions.h @@ -1,116 +1,116 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MM_PEXTENSIONS_H -#define MM_PEXTENSIONS_H - -#include "plinfo.h" // plid_t -#include "meta_api.h" // PLUG_LOADTIME -/* - - How to use: - 1. Add new export function 'Meta_PExtGiveFnptrs' to your plugin file. - 'Meta_PExtGiveFnptrs' will be called right after 'Meta_Query' call. - 2. Meta_PExtGiveFnptrs is called with interface version 'META_PEXT_VERSION' - and pointer to extension function table. - 3. Meta_PExtGiveFnptrs should return plugin's interface version. - 4. !NOTE! Metamod will not stop loading plugin even if plugin returns - interface version greater than current. Plugin should disable itself in - this kind of situation. - - Example: - #include "mm_pextensions.h" - - pextension_funcs_t *gpMetaPExtFuncs; - - int Meta_PExtGiveFnptrs(int interfaceVersion, pextension_funcs_t *pMetaPExtFuncs) { - if(interfaceVersion < META_PEXT_VERSION) { - LOG_DEVELOPER(PLID, "Error! Metamod is too old, please update!"); - gpMetaPExtFuncs = NULL; - - return(META_PEXT_VERSION); - } - - gpMetaPExtFuncs = pMetaPExtFuncs; - - return(META_PEXT_VERSION); - } - - Callback functions: - - int PEXT_LOAD_PLUGIN_BY_NAME(PLID, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); - Parses 'cmdline' as metamod would parse 'meta load ' and loads found - plugin. If 'plugin_handle' is set, metamod writes module handle of loaded - plugin at it. - Returns zero on success. - For error codes see 'META_ERRNO' in 'types_meta.h'. - - - int PEXT_UNLOAD_PLUGIN_BY_NAME(PLID, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - Parses 'cmdline' as metamod would parse 'meta unload ' and - unloads found plugin. - Returns zero on success. - For error codes see 'META_ERRNO' in 'types_meta.h'. - - - int PEXT_UNLOAD_PLUGIN_BY_HANDLE(PLID, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - Unloads plugin with 'plugin_handle'. - Returns zero on success. - For error codes see 'META_ERRNO' in 'types_meta.h'. - - !NOTE! Plugin cannot unload itself! -*/ - -// Interface version -// 1: first version. Used in p13 -// 2: Complete remake (p14): -// pfnLoadMetaPluginByName -// pfnUnloadMetaPluginByName -// pfnUnloadMetaPluginByHandle -// v2 is locked now. Don't modify old functions. If you add new functions, increase META_PEXT_VERSION. -#define META_PEXT_VERSION 2 - -// Meta PExtension Function table type. -typedef struct pextension_funcs_s { - int (*pfnLoadMetaPluginByName)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); - int (*pfnUnloadMetaPluginByName)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - int (*pfnUnloadMetaPluginByHandle)(plid_t plid, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); -} pextension_funcs_t; - -// Convenience macros for MetaPExtension functions. -#define PEXT_LOAD_PLUGIN_BY_NAME (*gpMetaPExtFuncs->pfnLoadMetaPluginByName) -#define PEXT_UNLOAD_PLUGIN_BY_NAME (*gpMetaPExtFuncs->pfnUnloadMetaPluginByName) -#define PEXT_UNLOAD_PLUGIN_BY_HANDLE (*gpMetaPExtFuncs->pfnUnloadMetaPluginByHandle) - -// Give plugin extension function table. -C_DLLEXPORT int Meta_PExtGiveFnptrs(int interfaceVersion, - pextension_funcs_t *pMetaPExtFuncs); -typedef int (*META_GIVE_PEXT_FUNCTIONS_FN) (int interfaceVersion, - pextension_funcs_t *pMetaPExtFuncs); - -#endif /* MM_PEXTENSIONS_H */ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MM_PEXTENSIONS_H +#define MM_PEXTENSIONS_H + +#include "plinfo.h" // plid_t +#include "meta_api.h" // PLUG_LOADTIME +/* + + How to use: + 1. Add new export function 'Meta_PExtGiveFnptrs' to your plugin file. + 'Meta_PExtGiveFnptrs' will be called right after 'Meta_Query' call. + 2. Meta_PExtGiveFnptrs is called with interface version 'META_PEXT_VERSION' + and pointer to extension function table. + 3. Meta_PExtGiveFnptrs should return plugin's interface version. + 4. !NOTE! Metamod will not stop loading plugin even if plugin returns + interface version greater than current. Plugin should disable itself in + this kind of situation. + + Example: + #include "mm_pextensions.h" + + pextension_funcs_t *gpMetaPExtFuncs; + + int Meta_PExtGiveFnptrs(int interfaceVersion, pextension_funcs_t *pMetaPExtFuncs) { + if(interfaceVersion < META_PEXT_VERSION) { + LOG_DEVELOPER(PLID, "Error! Metamod is too old, please update!"); + gpMetaPExtFuncs = NULL; + + return(META_PEXT_VERSION); + } + + gpMetaPExtFuncs = pMetaPExtFuncs; + + return(META_PEXT_VERSION); + } + + Callback functions: + - int PEXT_LOAD_PLUGIN_BY_NAME(PLID, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); + Parses 'cmdline' as metamod would parse 'meta load ' and loads found + plugin. If 'plugin_handle' is set, metamod writes module handle of loaded + plugin at it. + Returns zero on success. + For error codes see 'META_ERRNO' in 'types_meta.h'. + + - int PEXT_UNLOAD_PLUGIN_BY_NAME(PLID, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + Parses 'cmdline' as metamod would parse 'meta unload ' and + unloads found plugin. + Returns zero on success. + For error codes see 'META_ERRNO' in 'types_meta.h'. + + - int PEXT_UNLOAD_PLUGIN_BY_HANDLE(PLID, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + Unloads plugin with 'plugin_handle'. + Returns zero on success. + For error codes see 'META_ERRNO' in 'types_meta.h'. + + !NOTE! Plugin cannot unload itself! +*/ + +// Interface version +// 1: first version. Used in p13 +// 2: Complete remake (p14): +// pfnLoadMetaPluginByName +// pfnUnloadMetaPluginByName +// pfnUnloadMetaPluginByHandle +// v2 is locked now. Don't modify old functions. If you add new functions, increase META_PEXT_VERSION. +#define META_PEXT_VERSION 2 + +// Meta PExtension Function table type. +typedef struct pextension_funcs_s { + int (*pfnLoadMetaPluginByName)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); + int (*pfnUnloadMetaPluginByName)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + int (*pfnUnloadMetaPluginByHandle)(plid_t plid, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); +} pextension_funcs_t; + +// Convenience macros for MetaPExtension functions. +#define PEXT_LOAD_PLUGIN_BY_NAME (*gpMetaPExtFuncs->pfnLoadMetaPluginByName) +#define PEXT_UNLOAD_PLUGIN_BY_NAME (*gpMetaPExtFuncs->pfnUnloadMetaPluginByName) +#define PEXT_UNLOAD_PLUGIN_BY_HANDLE (*gpMetaPExtFuncs->pfnUnloadMetaPluginByHandle) + +// Give plugin extension function table. +C_DLLEXPORT int Meta_PExtGiveFnptrs(int interfaceVersion, + pextension_funcs_t *pMetaPExtFuncs); +typedef int (*META_GIVE_PEXT_FUNCTIONS_FN) (int interfaceVersion, + pextension_funcs_t *pMetaPExtFuncs); + +#endif /* MM_PEXTENSIONS_H */ diff --git a/src/metamod/mplayer.h b/src/metamod/mplayer.h index 5e6352f..72f51ea 100644 --- a/src/metamod/mplayer.h +++ b/src/metamod/mplayer.h @@ -1,91 +1,91 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// mplayer.h - class to keep info about a player and a class listing all -// players - -/* - * Copyright (c) 2005-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 INCLUDE_METAMOD_PLAYER_H -#define INCLUDE_METAMOD_PLAYER_H - -#include "plinfo.h" // plugin_info_t, etc -#include "mutil.h" // query_callback_t -#include "types_meta.h" // mBOOL -#include "new_baseclass.h" // class_metamod_new - - -// Numbers of players limit set by the engine -#define MAX_PLAYERS 32 - - -// Info on an individual player -class MPlayer : public class_metamod_new -{ -private: - mBOOL isQueried; // is this player currently queried for a cvar value - char *cvarName; // name of the cvar if getting queried - - MPlayer (const MPlayer&) DLLINTERNAL; - MPlayer& operator=(const MPlayer&) DLLINTERNAL; - - -public: - MPlayer() DLLINTERNAL; - ~MPlayer() DLLINTERNAL; - void DLLINTERNAL set_cvar_query(const char *cvar); // mark this player as querying a client cvar - void DLLINTERNAL clear_cvar_query(const char *cvar=NULL); // unmark this player as querying a client cvar - const char *DLLINTERNAL is_querying_cvar(void); // check if a player is querying a cvar. returns - // NULL if not or the name of the cvar -}; - - - -// A list of players. The number of max players is fixed and small enough -// to use an array. -class MPlayerList -{ -private: - enum { NUM_SLOTS = MAX_PLAYERS + 1 }; - - MPlayer players[NUM_SLOTS]; // array of players - - -public: - void DLLINTERNAL set_player_cvar_query(const edict_t *pEntity, const char *cvar); - void DLLINTERNAL clear_player_cvar_query(const edict_t *pEntity, const char *cvar=NULL); - void DLLINTERNAL clear_all_cvar_queries(void); - const char *DLLINTERNAL is_querying_cvar(const edict_t *pEntity); -}; - - -#endif /* INCLUDE_METAMOD_PLAYER_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// mplayer.h - class to keep info about a player and a class listing all +// players + +/* + * Copyright (c) 2005-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 INCLUDE_METAMOD_PLAYER_H +#define INCLUDE_METAMOD_PLAYER_H + +#include "plinfo.h" // plugin_info_t, etc +#include "mutil.h" // query_callback_t +#include "types_meta.h" // mBOOL +#include "new_baseclass.h" // class_metamod_new + + +// Numbers of players limit set by the engine +#define MAX_PLAYERS 32 + + +// Info on an individual player +class MPlayer : public class_metamod_new +{ +private: + mBOOL isQueried; // is this player currently queried for a cvar value + char *cvarName; // name of the cvar if getting queried + + MPlayer (const MPlayer&) DLLINTERNAL; + MPlayer& operator=(const MPlayer&) DLLINTERNAL; + + +public: + MPlayer() DLLINTERNAL; + ~MPlayer() DLLINTERNAL; + void DLLINTERNAL set_cvar_query(const char *cvar); // mark this player as querying a client cvar + void DLLINTERNAL clear_cvar_query(const char *cvar=NULL); // unmark this player as querying a client cvar + const char *DLLINTERNAL is_querying_cvar(void); // check if a player is querying a cvar. returns + // NULL if not or the name of the cvar +}; + + + +// A list of players. The number of max players is fixed and small enough +// to use an array. +class MPlayerList +{ +private: + enum { NUM_SLOTS = MAX_PLAYERS + 1 }; + + MPlayer players[NUM_SLOTS]; // array of players + + +public: + void DLLINTERNAL set_player_cvar_query(const edict_t *pEntity, const char *cvar); + void DLLINTERNAL clear_player_cvar_query(const edict_t *pEntity, const char *cvar=NULL); + void DLLINTERNAL clear_all_cvar_queries(void); + const char *DLLINTERNAL is_querying_cvar(const edict_t *pEntity); +}; + + +#endif /* INCLUDE_METAMOD_PLAYER_H */ diff --git a/src/metamod/mplugin.h b/src/metamod/mplugin.h index 087058f..effd629 100644 --- a/src/metamod/mplugin.h +++ b/src/metamod/mplugin.h @@ -1,234 +1,234 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// mplugin.h - class and types to describe an individual plugin - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MPLUGIN_H -#define MPLUGIN_H - -#include // time_t, etc -#include // malloc, etc - -#include // DLL_FUNCTIONS, etc - -#include "types_meta.h" // mBOOL -#include "meta_api.h" // GETENTITYAPI_FN, etc -#include "api_info.h" // dllapi_info, etc -#include "support_meta.h" // MAX_DESC_LEN -#include "osdep.h" -#include "new_baseclass.h" - - -// Flags to indicate current "load" state of plugin. -// NOTE: order is important, as greater/less comparisons are made. -typedef enum { - PL_EMPTY = 0, // empty slot - PL_VALID, // has valid info in it - PL_BADFILE, // nonexistent file (open failed), - // or not a valid plugin file (query failed) - PL_OPENED, // dlopened and queried - PL_FAILED, // opened, but failed to attach or unattach - PL_RUNNING, // attached and running - PL_PAUSED, // attached but paused -} PLUG_STATUS; - -// Action to take for plugin at next opportunity. -typedef enum { - PA_NULL = 0, - PA_NONE, // no action needed right now - PA_KEEP, // keep, after ini refresh - PA_LOAD, // load (dlopen, query) and try to attach - PA_ATTACH, // attach - PA_UNLOAD, // unload (detach, dlclose) - PA_RELOAD, // unload and load again -} PLUG_ACTION; - -// Flags to indicate from where the plugin was loaded. -typedef enum { - PS_INI = 0, // was loaded from the plugins.ini - PS_CMD, // was loaded via a server command - PS_PLUGIN, // was loaded by other plugin -} PLOAD_SOURCE; - -// Flags for how to word description of plugin loadtime. -typedef enum { - SL_SIMPLE = 0, // single word - SL_SHOW, // for "show" output, 5 chars - SL_ALLOWED, // when plugin is allowed to load/unload - SL_NOW, // current situation -} STR_LOADTIME; - -// Flags for how to format description of status. -typedef enum { - ST_SIMPLE = 0, // single word - ST_SHOW, // for "show" output, 4 chars -} STR_STATUS; - -// Flags for how to format description of action. -typedef enum { - SA_SIMPLE = 0, // single word - SA_SHOW, // for "show" output, 4 chars -} STR_ACTION; - -// Flags for how to format description of source. -typedef enum { - SO_SIMPLE = 0, // two words - SO_SHOW, // for "list" output, 3 chars -} STR_SOURCE; - -// api table list -typedef struct { - enginefuncs_t *engine; - DLL_FUNCTIONS *dllapi; - NEW_DLL_FUNCTIONS *newapi; -} api_tables_t; - -// An individual plugin. -class MPlugin : public class_metamod_new { - public: - // data: - // reordered for faster api_hook.cpp functions - PLUG_STATUS status; // current status of plugin (loaded, etc) - api_tables_t tables; - api_tables_t post_tables; - - inline DLLINTERNAL void * get_api_table(enum_api_t api) { - return(((void**)&tables)[api]); - } - inline DLLINTERNAL void * get_api_post_table(enum_api_t api) { - return(((void**)&post_tables)[api]); - } - - int index; // 1-based - int pfspecific; // level of specific platform affinity, used during load time - PLUG_ACTION action; // what to do with plugin (load, unload, etc) - PLOAD_SOURCE source; // source of the request to load the plugin - int source_plugin_index; // index of plugin that loaded this plugin. -1 means source plugin has been unloaded. - int unloader_index; - mBOOL is_unloader; // fix to prevent other plugins unload active unloader. - - DLHANDLE handle; // handle for dlopen, dlsym, etc - plugin_info_t *info; // information plugin provides about itself - time_t time_loaded; // when plugin was loaded - - char filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile - char *file; // ie "mm_test_i386.so", ptr from filename - char desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile - char pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir - - // functions: - mBOOL DLLINTERNAL ini_parseline(const char *line); // parse line from inifile - mBOOL DLLINTERNAL cmd_parseline(const char *line); // parse from console command - mBOOL DLLINTERNAL plugin_parseline(const char *fname, int loader_index); // parse from plugin - mBOOL DLLINTERNAL check_input(void); - - mBOOL DLLINTERNAL resolve(void); // find a matching file on disk - char * DLLINTERNAL resolve_dirs(const char *path); - char * DLLINTERNAL resolve_prefix(const char *path); - char * DLLINTERNAL resolve_suffix(const char *path); - static mBOOL DLLINTERNAL is_platform_postfix(const char *pf); - - mBOOL DLLINTERNAL platform_match(MPlugin* plugin); - - mBOOL DLLINTERNAL load(PLUG_LOADTIME now); - mBOOL DLLINTERNAL unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASON real_reason); - mBOOL DLLINTERNAL reload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - mBOOL DLLINTERNAL pause(void); - mBOOL DLLINTERNAL unpause(void); - mBOOL DLLINTERNAL retry(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); // if previously failed - void DLLINTERNAL free_api_pointers(void); - mBOOL DLLINTERNAL clear(void); - mBOOL DLLINTERNAL plugin_unload(plid_t plid, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); // other plugin unloading - void DLLINTERNAL show(void); // print info about plugin to console - - mBOOL DLLINTERNAL newer_file(void); // check for newer file on disk - - // output string functions - const char * DLLINTERNAL str_status(STR_STATUS fmt); - const char * DLLINTERNAL str_action(STR_ACTION fmt); - const char * DLLINTERNAL str_source(STR_SOURCE fmt); - - const char * DLLINTERNAL str_reason(PL_UNLOAD_REASON preason, PL_UNLOAD_REASON preal_reason); - const char * DLLINTERNAL str_loadtime(PLUG_LOADTIME pallow, STR_LOADTIME fmt); - - inline const char * DLLINTERNAL str_status(void) { return(str_status(ST_SIMPLE)); }; - inline const char * DLLINTERNAL str_action(void) { return(str_action(SA_SIMPLE)); }; - inline const char * DLLINTERNAL str_source(void) { return(str_source(SO_SIMPLE)); }; - - inline const char * DLLINTERNAL str_loadable(void) { - return(info?str_loadtime(info->loadable, SL_SIMPLE):" -"); - }; - inline const char * DLLINTERNAL str_unloadable(void) { - return(info?str_loadtime(info->unloadable, SL_SIMPLE):" -"); - }; - inline const char * DLLINTERNAL str_loadable(STR_LOADTIME fmt) { - return(info?str_loadtime(info->loadable, fmt):" -"); - }; - inline const char * DLLINTERNAL str_unloadable(STR_LOADTIME fmt) { - return(info?str_loadtime(info->unloadable, fmt):" -"); - }; - private: - mBOOL DLLINTERNAL query(void); - mBOOL DLLINTERNAL attach(PLUG_LOADTIME now); - mBOOL DLLINTERNAL detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - - gamedll_funcs_t gamedll_funcs; - mutil_funcs_t mutil_funcs; -}; - -// Macros used by MPlugin::show(), to list the functions that the plugin -// catches. -#define SHOW_DEF_API(api_info, api_table, pre_str, post_str) \ - n=0; \ - { \ - const api_info_t * ainfo = (const api_info_t *)&api_info; \ - const void ** table = (const void **)api_table; \ - for(int i = 0; &ainfo[i] < &api_info.END; i++) { \ - if(table[i]) { \ - META_CONS("%s%s%s", pre_str, ainfo[i].name, post_str); \ - n++; \ - } \ - } \ - } - -#define SHOW_DEF_DLLAPI(api_table, pre_str, post_str) \ - SHOW_DEF_API(dllapi_info, api_table, pre_str, post_str) - -#define SHOW_DEF_NEWAPI(api_table, pre_str, post_str) \ - SHOW_DEF_API(newapi_info, api_table, pre_str, post_str) - -#define SHOW_DEF_ENGINE(api_table, pre_str, post_str) \ - SHOW_DEF_API(engine_info, api_table, pre_str, post_str) - -#endif /* MPLUGIN_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// mplugin.h - class and types to describe an individual plugin + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MPLUGIN_H +#define MPLUGIN_H + +#include // time_t, etc +#include // malloc, etc + +#include // DLL_FUNCTIONS, etc + +#include "types_meta.h" // mBOOL +#include "meta_api.h" // GETENTITYAPI_FN, etc +#include "api_info.h" // dllapi_info, etc +#include "support_meta.h" // MAX_DESC_LEN +#include "osdep.h" +#include "new_baseclass.h" + + +// Flags to indicate current "load" state of plugin. +// NOTE: order is important, as greater/less comparisons are made. +typedef enum { + PL_EMPTY = 0, // empty slot + PL_VALID, // has valid info in it + PL_BADFILE, // nonexistent file (open failed), + // or not a valid plugin file (query failed) + PL_OPENED, // dlopened and queried + PL_FAILED, // opened, but failed to attach or unattach + PL_RUNNING, // attached and running + PL_PAUSED, // attached but paused +} PLUG_STATUS; + +// Action to take for plugin at next opportunity. +typedef enum { + PA_NULL = 0, + PA_NONE, // no action needed right now + PA_KEEP, // keep, after ini refresh + PA_LOAD, // load (dlopen, query) and try to attach + PA_ATTACH, // attach + PA_UNLOAD, // unload (detach, dlclose) + PA_RELOAD, // unload and load again +} PLUG_ACTION; + +// Flags to indicate from where the plugin was loaded. +typedef enum { + PS_INI = 0, // was loaded from the plugins.ini + PS_CMD, // was loaded via a server command + PS_PLUGIN, // was loaded by other plugin +} PLOAD_SOURCE; + +// Flags for how to word description of plugin loadtime. +typedef enum { + SL_SIMPLE = 0, // single word + SL_SHOW, // for "show" output, 5 chars + SL_ALLOWED, // when plugin is allowed to load/unload + SL_NOW, // current situation +} STR_LOADTIME; + +// Flags for how to format description of status. +typedef enum { + ST_SIMPLE = 0, // single word + ST_SHOW, // for "show" output, 4 chars +} STR_STATUS; + +// Flags for how to format description of action. +typedef enum { + SA_SIMPLE = 0, // single word + SA_SHOW, // for "show" output, 4 chars +} STR_ACTION; + +// Flags for how to format description of source. +typedef enum { + SO_SIMPLE = 0, // two words + SO_SHOW, // for "list" output, 3 chars +} STR_SOURCE; + +// api table list +typedef struct { + enginefuncs_t *engine; + DLL_FUNCTIONS *dllapi; + NEW_DLL_FUNCTIONS *newapi; +} api_tables_t; + +// An individual plugin. +class MPlugin : public class_metamod_new { + public: + // data: + // reordered for faster api_hook.cpp functions + PLUG_STATUS status; // current status of plugin (loaded, etc) + api_tables_t tables; + api_tables_t post_tables; + + inline DLLINTERNAL void * get_api_table(enum_api_t api) { + return(((void**)&tables)[api]); + } + inline DLLINTERNAL void * get_api_post_table(enum_api_t api) { + return(((void**)&post_tables)[api]); + } + + int index; // 1-based + int pfspecific; // level of specific platform affinity, used during load time + PLUG_ACTION action; // what to do with plugin (load, unload, etc) + PLOAD_SOURCE source; // source of the request to load the plugin + int source_plugin_index; // index of plugin that loaded this plugin. -1 means source plugin has been unloaded. + int unloader_index; + mBOOL is_unloader; // fix to prevent other plugins unload active unloader. + + DLHANDLE handle; // handle for dlopen, dlsym, etc + plugin_info_t *info; // information plugin provides about itself + time_t time_loaded; // when plugin was loaded + + char filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile + char *file; // ie "mm_test_i386.so", ptr from filename + char desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile + char pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir + + // functions: + mBOOL DLLINTERNAL ini_parseline(const char *line); // parse line from inifile + mBOOL DLLINTERNAL cmd_parseline(const char *line); // parse from console command + mBOOL DLLINTERNAL plugin_parseline(const char *fname, int loader_index); // parse from plugin + mBOOL DLLINTERNAL check_input(void); + + mBOOL DLLINTERNAL resolve(void); // find a matching file on disk + char * DLLINTERNAL resolve_dirs(const char *path); + char * DLLINTERNAL resolve_prefix(const char *path); + char * DLLINTERNAL resolve_suffix(const char *path); + static mBOOL DLLINTERNAL is_platform_postfix(const char *pf); + + mBOOL DLLINTERNAL platform_match(MPlugin* plugin); + + mBOOL DLLINTERNAL load(PLUG_LOADTIME now); + mBOOL DLLINTERNAL unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASON real_reason); + mBOOL DLLINTERNAL reload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + mBOOL DLLINTERNAL pause(void); + mBOOL DLLINTERNAL unpause(void); + mBOOL DLLINTERNAL retry(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); // if previously failed + void DLLINTERNAL free_api_pointers(void); + mBOOL DLLINTERNAL clear(void); + mBOOL DLLINTERNAL plugin_unload(plid_t plid, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); // other plugin unloading + void DLLINTERNAL show(void); // print info about plugin to console + + mBOOL DLLINTERNAL newer_file(void); // check for newer file on disk + + // output string functions + const char * DLLINTERNAL str_status(STR_STATUS fmt); + const char * DLLINTERNAL str_action(STR_ACTION fmt); + const char * DLLINTERNAL str_source(STR_SOURCE fmt); + + const char * DLLINTERNAL str_reason(PL_UNLOAD_REASON preason, PL_UNLOAD_REASON preal_reason); + const char * DLLINTERNAL str_loadtime(PLUG_LOADTIME pallow, STR_LOADTIME fmt); + + inline const char * DLLINTERNAL str_status(void) { return(str_status(ST_SIMPLE)); }; + inline const char * DLLINTERNAL str_action(void) { return(str_action(SA_SIMPLE)); }; + inline const char * DLLINTERNAL str_source(void) { return(str_source(SO_SIMPLE)); }; + + inline const char * DLLINTERNAL str_loadable(void) { + return(info?str_loadtime(info->loadable, SL_SIMPLE):" -"); + }; + inline const char * DLLINTERNAL str_unloadable(void) { + return(info?str_loadtime(info->unloadable, SL_SIMPLE):" -"); + }; + inline const char * DLLINTERNAL str_loadable(STR_LOADTIME fmt) { + return(info?str_loadtime(info->loadable, fmt):" -"); + }; + inline const char * DLLINTERNAL str_unloadable(STR_LOADTIME fmt) { + return(info?str_loadtime(info->unloadable, fmt):" -"); + }; + private: + mBOOL DLLINTERNAL query(void); + mBOOL DLLINTERNAL attach(PLUG_LOADTIME now); + mBOOL DLLINTERNAL detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + + gamedll_funcs_t gamedll_funcs; + mutil_funcs_t mutil_funcs; +}; + +// Macros used by MPlugin::show(), to list the functions that the plugin +// catches. +#define SHOW_DEF_API(api_info, api_table, pre_str, post_str) \ + n=0; \ + { \ + const api_info_t * ainfo = (const api_info_t *)&api_info; \ + const void ** table = (const void **)api_table; \ + for(int i = 0; &ainfo[i] < &api_info.END; i++) { \ + if(table[i]) { \ + META_CONS("%s%s%s", pre_str, ainfo[i].name, post_str); \ + n++; \ + } \ + } \ + } + +#define SHOW_DEF_DLLAPI(api_table, pre_str, post_str) \ + SHOW_DEF_API(dllapi_info, api_table, pre_str, post_str) + +#define SHOW_DEF_NEWAPI(api_table, pre_str, post_str) \ + SHOW_DEF_API(newapi_info, api_table, pre_str, post_str) + +#define SHOW_DEF_ENGINE(api_table, pre_str, post_str) \ + SHOW_DEF_API(engine_info, api_table, pre_str, post_str) + +#endif /* MPLUGIN_H */ diff --git a/src/metamod/mreg.h b/src/metamod/mreg.h index 704dbfd..606c34e 100644 --- a/src/metamod/mreg.h +++ b/src/metamod/mreg.h @@ -1,191 +1,191 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// mreg.h - description of registered items (classes MRegCmd, MRegCmdList) - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MREG_H -#define MREG_H - -#include "types_meta.h" // mBOOL -#include "comp_dep.h" // -#include "new_baseclass.h" - -// Number of entries to add to reglists when they need to grow. Typically -// more cvars than commands, so we grow them at different increments. -#define REG_CMD_GROWSIZE 32 -#define REG_CVAR_GROWSIZE 64 - -// Width required to printf a Reg*List index number, for show() functions. -// This used to correspond to the number of digits in MAX_REG, which was a -// fixed, compile-time limit. However, now that the reg lists are grown -// dynamically, this has less strong correspondance to list sizes. So for -// the moment, it reflects what one might normally expect to be the max -// width needed to print an index number; 4 allows 9999 (which is a damn -// lot, if you ask me). -#define WIDTH_MAX_REG 4 - -// Max number of registered user msgs we can manage. -#define MAX_REG_MSGS 256 - -// Max number of clients on server -#define MAX_CLIENTS_CONNECTED 32 - -// Flags to indicate if given cvar or func is part of a loaded plugin. -typedef enum { - RG_INVALID, - RG_VALID, -} REG_STATUS; - -// Pointer to function registered by AddServerCommand. -typedef void (*REG_CMD_FN) (void); - - -// An individual registered function/command. -class MRegCmd : public class_metamod_new { - friend class MRegCmdList; - private: - // data: - int index; // 1-based - public: - char *name; // space is malloc'd - REG_CMD_FN pfnCmd; // pointer to the function - int plugid; // index id of corresponding plugin - REG_STATUS status; // whether corresponding plugin is loaded - // functions: - void DLLINTERNAL init(int idx); // init values, as not using constructors - mBOOL DLLINTERNAL call(void); // try to call the function -}; - - -// A list of registered commands. -class MRegCmdList : public class_metamod_new { - private: - // data: - MRegCmd *mlist; // malloc'd array of registered commands - int size; // current size of list - int endlist; // index of last used entry - // Private; to satisfy -Weffc++ "has pointer data members but does - // not override" copy/assignment constructor. - void operator=(const MRegCmdList &src); - MRegCmdList(const MRegCmdList &src); - - public: - // constructor: - MRegCmdList(void) DLLINTERNAL; - - // functions: - MRegCmd * DLLINTERNAL find(const char *findname); // find by MRegCmd->name - MRegCmd * DLLINTERNAL add(const char *addname); - void DLLINTERNAL disable(int plugin_id); // change status to Invalid - void DLLINTERNAL show(void); // list all funcs to console - void DLLINTERNAL show(int plugin_id); // list given plugin's funcs to console -}; - - - -// An individual registered cvar. -class MRegCvar : public class_metamod_new { - friend class MRegCvarList; - private: - // data: - int index; // 1-based - public: - cvar_t *data; // actual cvar structure, malloc'd - int plugid; // index id of corresponding plugin - REG_STATUS status; // whether corresponding plugin is loaded - // functions: - void DLLINTERNAL init(int idx); // init values, as not using constructors - mBOOL DLLINTERNAL set(cvar_t *src); -}; - - -// A list of registered cvars. -class MRegCvarList : public class_metamod_new { - private: - // data: - MRegCvar *vlist; // malloc'd array of registered cvars - int size; // size of list, ie MAX_REG_CVARS - int endlist; // index of last used entry - // Private; to satisfy -Weffc++ "has pointer data members but does - // not override" copy/assignment constructor. - void operator=(const MRegCvarList &src); - MRegCvarList(const MRegCvarList &src); - - public: - // constructor: - MRegCvarList(void) DLLINTERNAL; - - // functions: - MRegCvar * DLLINTERNAL add(const char *addname); - MRegCvar * DLLINTERNAL find(const char *findname); // find by MRegCvar->data.name - void DLLINTERNAL disable(int plugin_id); // change status to Invalid - void DLLINTERNAL show(void); // list all cvars to console - void DLLINTERNAL show(int plugin_id); // list given plugin's cvars to console -}; - - - -// An individual registered user msg, from gamedll. -class MRegMsg : public class_metamod_new { - friend class MRegMsgList; - private: - // data: - int index; // 1-based - public: - const char *name; // name, assumed constant string in gamedll - int msgid; // msgid, assigned by engine - int size; // size, if given by gamedll -}; - - -// A list of registered user msgs. -class MRegMsgList : public class_metamod_new { - private: - // data: - MRegMsg mlist[MAX_REG_MSGS]; // array of registered msgs - int size; // size of list, ie MAX_REG_MSGS - int endlist; // index of last used entry - - public: - // constructor: - MRegMsgList(void) DLLINTERNAL; - - // functions: - MRegMsg * DLLINTERNAL add(const char *addname, int addmsgid, int addsize); - MRegMsg * DLLINTERNAL find(const char *findname); - MRegMsg * DLLINTERNAL find(int findmsgid); - void DLLINTERNAL show(void); // list all msgs to console -}; - -#endif /* MREG_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// mreg.h - description of registered items (classes MRegCmd, MRegCmdList) + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MREG_H +#define MREG_H + +#include "types_meta.h" // mBOOL +#include "comp_dep.h" // +#include "new_baseclass.h" + +// Number of entries to add to reglists when they need to grow. Typically +// more cvars than commands, so we grow them at different increments. +#define REG_CMD_GROWSIZE 32 +#define REG_CVAR_GROWSIZE 64 + +// Width required to printf a Reg*List index number, for show() functions. +// This used to correspond to the number of digits in MAX_REG, which was a +// fixed, compile-time limit. However, now that the reg lists are grown +// dynamically, this has less strong correspondance to list sizes. So for +// the moment, it reflects what one might normally expect to be the max +// width needed to print an index number; 4 allows 9999 (which is a damn +// lot, if you ask me). +#define WIDTH_MAX_REG 4 + +// Max number of registered user msgs we can manage. +#define MAX_REG_MSGS 256 + +// Max number of clients on server +#define MAX_CLIENTS_CONNECTED 32 + +// Flags to indicate if given cvar or func is part of a loaded plugin. +typedef enum { + RG_INVALID, + RG_VALID, +} REG_STATUS; + +// Pointer to function registered by AddServerCommand. +typedef void (*REG_CMD_FN) (void); + + +// An individual registered function/command. +class MRegCmd : public class_metamod_new { + friend class MRegCmdList; + private: + // data: + int index; // 1-based + public: + char *name; // space is malloc'd + REG_CMD_FN pfnCmd; // pointer to the function + int plugid; // index id of corresponding plugin + REG_STATUS status; // whether corresponding plugin is loaded + // functions: + void DLLINTERNAL init(int idx); // init values, as not using constructors + mBOOL DLLINTERNAL call(void); // try to call the function +}; + + +// A list of registered commands. +class MRegCmdList : public class_metamod_new { + private: + // data: + MRegCmd *mlist; // malloc'd array of registered commands + int size; // current size of list + int endlist; // index of last used entry + // Private; to satisfy -Weffc++ "has pointer data members but does + // not override" copy/assignment constructor. + void operator=(const MRegCmdList &src); + MRegCmdList(const MRegCmdList &src); + + public: + // constructor: + MRegCmdList(void) DLLINTERNAL; + + // functions: + MRegCmd * DLLINTERNAL find(const char *findname); // find by MRegCmd->name + MRegCmd * DLLINTERNAL add(const char *addname); + void DLLINTERNAL disable(int plugin_id); // change status to Invalid + void DLLINTERNAL show(void); // list all funcs to console + void DLLINTERNAL show(int plugin_id); // list given plugin's funcs to console +}; + + + +// An individual registered cvar. +class MRegCvar : public class_metamod_new { + friend class MRegCvarList; + private: + // data: + int index; // 1-based + public: + cvar_t *data; // actual cvar structure, malloc'd + int plugid; // index id of corresponding plugin + REG_STATUS status; // whether corresponding plugin is loaded + // functions: + void DLLINTERNAL init(int idx); // init values, as not using constructors + mBOOL DLLINTERNAL set(cvar_t *src); +}; + + +// A list of registered cvars. +class MRegCvarList : public class_metamod_new { + private: + // data: + MRegCvar *vlist; // malloc'd array of registered cvars + int size; // size of list, ie MAX_REG_CVARS + int endlist; // index of last used entry + // Private; to satisfy -Weffc++ "has pointer data members but does + // not override" copy/assignment constructor. + void operator=(const MRegCvarList &src); + MRegCvarList(const MRegCvarList &src); + + public: + // constructor: + MRegCvarList(void) DLLINTERNAL; + + // functions: + MRegCvar * DLLINTERNAL add(const char *addname); + MRegCvar * DLLINTERNAL find(const char *findname); // find by MRegCvar->data.name + void DLLINTERNAL disable(int plugin_id); // change status to Invalid + void DLLINTERNAL show(void); // list all cvars to console + void DLLINTERNAL show(int plugin_id); // list given plugin's cvars to console +}; + + + +// An individual registered user msg, from gamedll. +class MRegMsg : public class_metamod_new { + friend class MRegMsgList; + private: + // data: + int index; // 1-based + public: + const char *name; // name, assumed constant string in gamedll + int msgid; // msgid, assigned by engine + int size; // size, if given by gamedll +}; + + +// A list of registered user msgs. +class MRegMsgList : public class_metamod_new { + private: + // data: + MRegMsg mlist[MAX_REG_MSGS]; // array of registered msgs + int size; // size of list, ie MAX_REG_MSGS + int endlist; // index of last used entry + + public: + // constructor: + MRegMsgList(void) DLLINTERNAL; + + // functions: + MRegMsg * DLLINTERNAL add(const char *addname, int addmsgid, int addsize); + MRegMsg * DLLINTERNAL find(const char *findname); + MRegMsg * DLLINTERNAL find(int findmsgid); + void DLLINTERNAL show(void); // list all msgs to console +}; + +#endif /* MREG_H */ diff --git a/src/metamod/mutil.h b/src/metamod/mutil.h index eeeaab6..17b4e8b 100644 --- a/src/metamod/mutil.h +++ b/src/metamod/mutil.h @@ -1,108 +1,108 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// mutil.h - prototypes for utility functions to provide to plugins - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 MUTIL_H -#define MUTIL_H - -#include "comp_dep.h" -#include "plinfo.h" // plugin_info_t, etc -#include "mhook.h" // game_event_t, etc -#include "sdk_util.h" // hudtextparms_t, etc - -// max buffer size for printed messages -#define MAX_LOGMSG_LEN 1024 - -// For GetGameInfo: -typedef enum { - GINFO_NAME = 0, - GINFO_DESC, - GINFO_GAMEDIR, - GINFO_DLL_FULLPATH, - GINFO_DLL_FILENAME, - GINFO_REALDLL_FULLPATH, -} ginfo_t; - -// Meta Utility Function table type. -typedef struct meta_util_funcs_s { - void (*pfnLogConsole) (plid_t plid, const char *fmt, ...); - void (*pfnLogMessage) (plid_t plid, const char *fmt, ...); - void (*pfnLogError) (plid_t plid, const char *fmt, ...); - void (*pfnLogDeveloper) (plid_t plid, const char *fmt, ...); - void (*pfnCenterSay) (plid_t plid, const char *fmt, ...); - void (*pfnCenterSayParms) (plid_t plid, hudtextparms_t tparms, - const char *fmt, ...); - void (*pfnCenterSayVarargs) (plid_t plid, hudtextparms_t tparms, - const char *fmt, va_list ap); - qboolean (*pfnCallGameEntity) (plid_t plid, const char *entStr, - entvars_t *pev); - int (*pfnGetUserMsgID) (plid_t plid, const char *msgname, int *size); - const char *(*pfnGetUserMsgName) (plid_t plid, int msgid, int *size); - const char *(*pfnGetPluginPath) (plid_t plid); - const char *(*pfnGetGameInfo) (plid_t plid, ginfo_t tag); - - int (*pfnLoadPlugin)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); - int (*pfnUnloadPlugin)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - int (*pfnUnloadPluginByHandle)(plid_t plid, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); - - const char *(*pfnIsQueryingClientCvar) (plid_t plid, const edict_t *player); - - int (*pfnMakeRequestID) (plid_t plid); - - void (*pfnGetHookTables) (plid_t plid, enginefuncs_t **peng, DLL_FUNCTIONS **pdll, NEW_DLL_FUNCTIONS **pnewdll); -} mutil_funcs_t; -extern mutil_funcs_t MetaUtilFunctions DLLHIDDEN; - -// Convenience macros for MetaUtil functions -#define LOG_CONSOLE (*gpMetaUtilFuncs->pfnLogConsole) -#define LOG_MESSAGE (*gpMetaUtilFuncs->pfnLogMessage) -#define LOG_ERROR (*gpMetaUtilFuncs->pfnLogError) -#define LOG_DEVELOPER (*gpMetaUtilFuncs->pfnLogDeveloper) -#define CENTER_SAY (*gpMetaUtilFuncs->pfnCenterSay) -#define CENTER_SAY_PARMS (*gpMetaUtilFuncs->pfnCenterSayParms) -#define CENTER_SAY_VARARGS (*gpMetaUtilFuncs->pfnCenterSayVarargs) -#define CALL_GAME_ENTITY (*gpMetaUtilFuncs->pfnCallGameEntity) -#define GET_USER_MSG_ID (*gpMetaUtilFuncs->pfnGetUserMsgID) -#define GET_USER_MSG_NAME (*gpMetaUtilFuncs->pfnGetUserMsgName) -#define GET_PLUGIN_PATH (*gpMetaUtilFuncs->pfnGetPluginPath) -#define GET_GAME_INFO (*gpMetaUtilFuncs->pfnGetGameInfo) -#define LOAD_PLUGIN (*gpMetaUtilFuncs->pfnLoadPlugin) -#define UNLOAD_PLUGIN (*gpMetaUtilFuncs->pfnUnloadPlugin) -#define UNLOAD_PLUGIN_BY_HANDLE (*gpMetaUtilFuncs->pfnUnloadPluginByHandle) -#define IS_QUERYING_CLIENT_CVAR (*gpMetaUtilFuncs->pfnIsQueryingClientCvar) -#define MAKE_REQUESTID (*gpMetaUtilFuncs->pfnMakeRequestID) -#define GET_HOOK_TABLES (*gpMetaUtilFuncs->pfnGetHookTables) - -#endif /* MUTIL_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// mutil.h - prototypes for utility functions to provide to plugins + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 MUTIL_H +#define MUTIL_H + +#include "comp_dep.h" +#include "plinfo.h" // plugin_info_t, etc +#include "mhook.h" // game_event_t, etc +#include "sdk_util.h" // hudtextparms_t, etc + +// max buffer size for printed messages +#define MAX_LOGMSG_LEN 1024 + +// For GetGameInfo: +typedef enum { + GINFO_NAME = 0, + GINFO_DESC, + GINFO_GAMEDIR, + GINFO_DLL_FULLPATH, + GINFO_DLL_FILENAME, + GINFO_REALDLL_FULLPATH, +} ginfo_t; + +// Meta Utility Function table type. +typedef struct meta_util_funcs_s { + void (*pfnLogConsole) (plid_t plid, const char *fmt, ...); + void (*pfnLogMessage) (plid_t plid, const char *fmt, ...); + void (*pfnLogError) (plid_t plid, const char *fmt, ...); + void (*pfnLogDeveloper) (plid_t plid, const char *fmt, ...); + void (*pfnCenterSay) (plid_t plid, const char *fmt, ...); + void (*pfnCenterSayParms) (plid_t plid, hudtextparms_t tparms, + const char *fmt, ...); + void (*pfnCenterSayVarargs) (plid_t plid, hudtextparms_t tparms, + const char *fmt, va_list ap); + qboolean (*pfnCallGameEntity) (plid_t plid, const char *entStr, + entvars_t *pev); + int (*pfnGetUserMsgID) (plid_t plid, const char *msgname, int *size); + const char *(*pfnGetUserMsgName) (plid_t plid, int msgid, int *size); + const char *(*pfnGetPluginPath) (plid_t plid); + const char *(*pfnGetGameInfo) (plid_t plid, ginfo_t tag); + + int (*pfnLoadPlugin)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, void **plugin_handle); + int (*pfnUnloadPlugin)(plid_t plid, const char *cmdline, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + int (*pfnUnloadPluginByHandle)(plid_t plid, void *plugin_handle, PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + + const char *(*pfnIsQueryingClientCvar) (plid_t plid, const edict_t *player); + + int (*pfnMakeRequestID) (plid_t plid); + + void (*pfnGetHookTables) (plid_t plid, enginefuncs_t **peng, DLL_FUNCTIONS **pdll, NEW_DLL_FUNCTIONS **pnewdll); +} mutil_funcs_t; +extern mutil_funcs_t MetaUtilFunctions DLLHIDDEN; + +// Convenience macros for MetaUtil functions +#define LOG_CONSOLE (*gpMetaUtilFuncs->pfnLogConsole) +#define LOG_MESSAGE (*gpMetaUtilFuncs->pfnLogMessage) +#define LOG_ERROR (*gpMetaUtilFuncs->pfnLogError) +#define LOG_DEVELOPER (*gpMetaUtilFuncs->pfnLogDeveloper) +#define CENTER_SAY (*gpMetaUtilFuncs->pfnCenterSay) +#define CENTER_SAY_PARMS (*gpMetaUtilFuncs->pfnCenterSayParms) +#define CENTER_SAY_VARARGS (*gpMetaUtilFuncs->pfnCenterSayVarargs) +#define CALL_GAME_ENTITY (*gpMetaUtilFuncs->pfnCallGameEntity) +#define GET_USER_MSG_ID (*gpMetaUtilFuncs->pfnGetUserMsgID) +#define GET_USER_MSG_NAME (*gpMetaUtilFuncs->pfnGetUserMsgName) +#define GET_PLUGIN_PATH (*gpMetaUtilFuncs->pfnGetPluginPath) +#define GET_GAME_INFO (*gpMetaUtilFuncs->pfnGetGameInfo) +#define LOAD_PLUGIN (*gpMetaUtilFuncs->pfnLoadPlugin) +#define UNLOAD_PLUGIN (*gpMetaUtilFuncs->pfnUnloadPlugin) +#define UNLOAD_PLUGIN_BY_HANDLE (*gpMetaUtilFuncs->pfnUnloadPluginByHandle) +#define IS_QUERYING_CLIENT_CVAR (*gpMetaUtilFuncs->pfnIsQueryingClientCvar) +#define MAKE_REQUESTID (*gpMetaUtilFuncs->pfnMakeRequestID) +#define GET_HOOK_TABLES (*gpMetaUtilFuncs->pfnGetHookTables) + +#endif /* MUTIL_H */ diff --git a/src/metamod/new_baseclass.h b/src/metamod/new_baseclass.h index 28707fc..534540b 100644 --- a/src/metamod/new_baseclass.h +++ b/src/metamod/new_baseclass.h @@ -1,69 +1,69 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 METAMOD_NEW_BASECLASS_H -#define METAMOD_NEW_BASECLASS_H - -#include - -#include "comp_dep.h" - -//new/delete operators with malloc/free to remove need for libstdc++ - -class class_metamod_new { -public: - // Construction - class_metamod_new(void) { }; - - // Operators - inline void * operator new(size_t size) { - if(size==0) - return(calloc(1, 1)); - return(calloc(1, size)); - } - - inline void * operator new[](size_t size) { - if(size==0) - return(calloc(1, 1)); - return(calloc(1, size)); - } - - inline void operator delete(void *ptr) { - if(ptr) - free(ptr); - } - - inline void operator delete[](void *ptr) { - if(ptr) - free(ptr); - } -}; - -#endif /*METAMOD_NEW_BASECLASS_H*/ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 METAMOD_NEW_BASECLASS_H +#define METAMOD_NEW_BASECLASS_H + +#include + +#include "comp_dep.h" + +//new/delete operators with malloc/free to remove need for libstdc++ + +class class_metamod_new { +public: + // Construction + class_metamod_new(void) { }; + + // Operators + inline void * operator new(size_t size) { + if(size==0) + return(calloc(1, 1)); + return(calloc(1, size)); + } + + inline void * operator new[](size_t size) { + if(size==0) + return(calloc(1, 1)); + return(calloc(1, size)); + } + + inline void operator delete(void *ptr) { + if(ptr) + free(ptr); + } + + inline void operator delete[](void *ptr) { + if(ptr) + free(ptr); + } +}; + +#endif /*METAMOD_NEW_BASECLASS_H*/ diff --git a/src/metamod/osdep.h b/src/metamod/osdep.h index ac7c5b7..49019fd 100644 --- a/src/metamod/osdep.h +++ b/src/metamod/osdep.h @@ -1,307 +1,307 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// osdep.h - operating system dependencies - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 OSDEP_H -#define OSDEP_H - -#include // strerror() -#include // isupper, tolower -#include // errno - -// Various differences between WIN32 and Linux. - -#include "comp_dep.h" -#include "types_meta.h" // mBOOL -#include "mreg.h" // REG_CMD_FN, etc -#include "log_meta.h" // LOG_ERROR, etc - -// String describing platform/DLL-type, for matching lines in plugins.ini. -#ifdef linux - #define PLATFORM "linux" -# if defined(__x86_64__) || defined(__amd64__) - #define PLATFORM_SPC "lin64" -# else - #define PLATFORM_SPC "lin32" -# endif - #define PLATFORM_DLEXT ".so" -#elif defined(_WIN32) - #define PLATFORM "mswin" - #define PLATFORM_SPC "win32" - #define PLATFORM_DLEXT ".dll" -#else /* unknown */ - #error "OS unrecognized" -#endif /* unknown */ - -// Macro for function-exporting from DLL.. -// from SDK dlls/cbase.h: -//! C functions for external declarations that call the appropriate C++ methods - -// Windows uses "__declspec(dllexport)" to mark functions in the DLL that -// should be visible/callable externally. -// -// It also apparently requires WINAPI for GiveFnptrsToDll(). -// -// See doc/notes_windows_coding for more information.. - -// Attributes to specify an "exported" function, visible from outside the -// DLL. -#undef DLLEXPORT -#ifdef _WIN32 - #define DLLEXPORT __declspec(dllexport) __attribute__ ((externally_visible)) - // WINAPI should be provided in the windows compiler headers. - // It's usually defined to something like "__stdcall". -#elif defined(linux) - #define DLLEXPORT __attribute__ ((visibility ("default"), externally_visible)) - #define WINAPI /* */ -#endif /* linux */ - -// Simplified macro for declaring/defining exported DLL functions. They -// need to be 'extern "C"' so that the C++ compiler enforces parameter -// type-matching, rather than considering routines with mis-matched -// arguments/types to be overloaded functions... -// -// AFAIK, this is os-independent, but it's included here in osdep.h where -// DLLEXPORT is defined, for convenience. -#define C_DLLEXPORT extern "C" DLLEXPORT - -// Special version that fixes vsnprintf bugs. -#ifndef DO_NOT_FIX_VARARG_ENGINE_API_WARPERS -int DLLINTERNAL safe_vsnprintf(char* s, size_t n, const char *format, va_list ap); -int DLLINTERNAL safe_snprintf(char* s, size_t n, const char* format, ...); -#endif -void DLLINTERNAL safevoid_vsnprintf(char* s, size_t n, const char *format, va_list ap); -void DLLINTERNAL safevoid_snprintf(char* s, size_t n, const char* format, ...); - -// Functions & types for DLL open/close/etc operations. -extern mBOOL dlclose_handle_invalid DLLHIDDEN; -#ifdef linux - #include - typedef void* DLHANDLE; - typedef void* DLFUNC; - inline DLHANDLE DLLINTERNAL DLOPEN(const char *filename) { - return(dlopen(filename, RTLD_NOW)); - } - inline DLFUNC DLLINTERNAL DLSYM(DLHANDLE handle, const char *string) { - return(dlsym(handle, string)); - } - //dlclose crashes if handle is null. - inline int DLLINTERNAL DLCLOSE(DLHANDLE handle) { - if(!handle) { - dlclose_handle_invalid = mTRUE; - return(1); - } - - dlclose_handle_invalid = mFALSE; - return(dlclose(handle)); - } - inline const char * DLLINTERNAL DLERROR(void) { - if(dlclose_handle_invalid) - return("Invalid handle."); - return(dlerror()); - } -#elif defined(_WIN32) - typedef HINSTANCE DLHANDLE; - typedef FARPROC DLFUNC; - inline DLHANDLE DLLINTERNAL DLOPEN(const char *filename) { - return(LoadLibraryA(filename)); - } - inline DLFUNC DLLINTERNAL DLSYM(DLHANDLE handle, const char *string) { - return(GetProcAddress(handle, string)); - } - inline int DLLINTERNAL DLCLOSE(DLHANDLE handle) { - if(!handle) { - dlclose_handle_invalid = mTRUE; - return(1); - } - - dlclose_handle_invalid = mFALSE; - - // NOTE: Windows FreeLibrary returns success=nonzero, fail=zero, - // which is the opposite of the unix convention, thus the '!'. - return(!FreeLibrary(handle)); - } - // Windows doesn't provide a function corresponding to dlerror(), so - // we make our own. - char * DLLINTERNAL str_GetLastError(void); - inline const char * DLLINTERNAL DLERROR(void) { - if(dlclose_handle_invalid) - return("Invalid handle."); - return(str_GetLastError()); - } -#endif /* _WIN32 */ -const char * DLLINTERNAL DLFNAME(void *memptr); -mBOOL DLLINTERNAL IS_VALID_PTR(void *memptr); - - -// Attempt to call the given function pointer, without segfaulting. -mBOOL DLLINTERNAL os_safe_call(REG_CMD_FN pfn); - - -// Windows doesn't have an strtok_r() routine, so we write our own. -#ifdef _WIN32 - #define strtok_r(s, delim, ptrptr) my_strtok_r(s, delim, ptrptr) - char * DLLINTERNAL my_strtok_r(char *s, const char *delim, char **ptrptr); -#endif /* _WIN32 */ - - -// Linux doesn't have an strlwr() routine, so we write our own. -#ifdef linux - #define strlwr(s) my_strlwr(s) - char * DLLINTERNAL my_strlwr(char *s); -#endif /* _WIN32 */ - - -// Set filename and pathname maximum lengths. Note some windows compilers -// provide a which is incomplete and/or causes problems; see -// doc/windows_notes.txt for more information. -// -// Note that both OS's include room for null-termination: -// linux: "# chars in a path name including nul" -// win32: "note that the sizes include space for 0-terminator" -#ifdef linux - #include -#elif defined(_WIN32) - #include - #define NAME_MAX _MAX_FNAME - #ifndef PATH_MAX - #define PATH_MAX _MAX_PATH - #endif -#endif /* _WIN32 */ - -// Various other windows routine differences. -#ifdef linux - #include // sleep - #ifndef O_BINARY - #define O_BINARY 0 - #endif -#elif defined(_WIN32) - #include - #include - - #define sleep(x) Sleep(x*1000) - - // Fixed MSVC compiling, by Nikolay "The Storm" Baklicharov. - #if defined(__GNUC__) || defined (_MSC_VER) && _MSC_VER >= 1400 - #define snprintf _snprintf - #define vsnprintf _vsnprintf - #define unlink _unlink - #define strlwr _strlwr - #define strdup _strdup - #define strcasecmp _stricmp - #define strncasecmp _strnicmp - #define getcwd _getcwd - #define open _open - #define read _read - #define write _write - #define close _close - #endif /* GCC or MSVC 8.0+ */ -#endif /* _WIN32 */ - -#if !defined WIN32 && !defined _MSC_VER -#include // getcwd -#endif - -#include -#ifndef S_ISREG - // Linux gcc defines this; earlier mingw didn't, later mingw does; - // MSVC doesn't seem to. - #define S_ISREG(m) ((m) & S_IFREG) -#endif /* not S_ISREG */ -#ifdef _WIN32 - // The following two are defined in mingw but not in MSVC - #ifndef S_IRUSR - #define S_IRUSR _S_IREAD - #endif - #ifndef S_IWUSR - #define S_IWUSR _S_IWRITE - #endif - - // The following two are defined neither in mingw nor in MSVC - #ifndef S_IRGRP - #define S_IRGRP S_IRUSR - #endif - #ifndef S_IWGRP - #define S_IWGRP S_IWUSR - #endif -#endif /* _WIN32 */ - -// Normalize/standardize a pathname. -// - For win32, this involves: -// - Turning backslashes (\) into slashes (/), so that config files and -// Metamod internal code can be simpler and just use slashes (/). -// - Turning upper/mixed case into lowercase, since windows is -// non-case-sensitive. -// - For linux, this requires no work, as paths uses slashes (/) natively, -// and pathnames are case-sensitive. -#ifdef linux -#define normalize_pathname(a) -#elif defined(_WIN32) -void DLLINTERNAL normalize_pathname(char *path); -#endif /* _WIN32 */ - -// Indicate if pathname appears to be an absolute-path. Under linux this -// is a leading slash (/). Under win32, this can be: -// - a drive-letter path (ie "D:blah" or "C:\blah") -// - a toplevel path (ie "\blah") -// - a UNC network address (ie "\\srv1\blah"). -// Also, handle both native and normalized pathnames. -inline mBOOL DLLINTERNAL is_absolute_path(const char *path) { - if(path[0]=='/') return(mTRUE); -#ifdef _WIN32 - if(path[1]==':') return(mTRUE); - if(path[0]=='\\') return(mTRUE); -#endif /* _WIN32 */ - return(mFALSE); -} - -#ifdef _WIN32 -// Buffer pointed to by resolved_name is assumed to be able to store a -// string of PATH_MAX length. -char * DLLINTERNAL realpath(const char *file_name, char *resolved_name); -#endif /* _WIN32 */ - -// Generic "error string" from a recent OS call. For linux, this is based -// on errno. For win32, it's based on GetLastError. -inline const char * DLLINTERNAL str_os_error(void) { -#ifdef linux - return(strerror(errno)); -#elif defined(_WIN32) - return(str_GetLastError()); -#endif /* _WIN32 */ -} - - -#endif /* OSDEP_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// osdep.h - operating system dependencies + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 OSDEP_H +#define OSDEP_H + +#include // strerror() +#include // isupper, tolower +#include // errno + +// Various differences between WIN32 and Linux. + +#include "comp_dep.h" +#include "types_meta.h" // mBOOL +#include "mreg.h" // REG_CMD_FN, etc +#include "log_meta.h" // LOG_ERROR, etc + +// String describing platform/DLL-type, for matching lines in plugins.ini. +#ifdef linux + #define PLATFORM "linux" +# if defined(__x86_64__) || defined(__amd64__) + #define PLATFORM_SPC "lin64" +# else + #define PLATFORM_SPC "lin32" +# endif + #define PLATFORM_DLEXT ".so" +#elif defined(_WIN32) + #define PLATFORM "mswin" + #define PLATFORM_SPC "win32" + #define PLATFORM_DLEXT ".dll" +#else /* unknown */ + #error "OS unrecognized" +#endif /* unknown */ + +// Macro for function-exporting from DLL.. +// from SDK dlls/cbase.h: +//! C functions for external declarations that call the appropriate C++ methods + +// Windows uses "__declspec(dllexport)" to mark functions in the DLL that +// should be visible/callable externally. +// +// It also apparently requires WINAPI for GiveFnptrsToDll(). +// +// See doc/notes_windows_coding for more information.. + +// Attributes to specify an "exported" function, visible from outside the +// DLL. +#undef DLLEXPORT +#ifdef _WIN32 + #define DLLEXPORT __declspec(dllexport) __attribute__ ((externally_visible)) + // WINAPI should be provided in the windows compiler headers. + // It's usually defined to something like "__stdcall". +#elif defined(linux) + #define DLLEXPORT __attribute__ ((visibility ("default"), externally_visible)) + #define WINAPI /* */ +#endif /* linux */ + +// Simplified macro for declaring/defining exported DLL functions. They +// need to be 'extern "C"' so that the C++ compiler enforces parameter +// type-matching, rather than considering routines with mis-matched +// arguments/types to be overloaded functions... +// +// AFAIK, this is os-independent, but it's included here in osdep.h where +// DLLEXPORT is defined, for convenience. +#define C_DLLEXPORT extern "C" DLLEXPORT + +// Special version that fixes vsnprintf bugs. +#ifndef DO_NOT_FIX_VARARG_ENGINE_API_WARPERS +int DLLINTERNAL safe_vsnprintf(char* s, size_t n, const char *format, va_list ap); +int DLLINTERNAL safe_snprintf(char* s, size_t n, const char* format, ...); +#endif +void DLLINTERNAL safevoid_vsnprintf(char* s, size_t n, const char *format, va_list ap); +void DLLINTERNAL safevoid_snprintf(char* s, size_t n, const char* format, ...); + +// Functions & types for DLL open/close/etc operations. +extern mBOOL dlclose_handle_invalid DLLHIDDEN; +#ifdef linux + #include + typedef void* DLHANDLE; + typedef void* DLFUNC; + inline DLHANDLE DLLINTERNAL DLOPEN(const char *filename) { + return(dlopen(filename, RTLD_NOW)); + } + inline DLFUNC DLLINTERNAL DLSYM(DLHANDLE handle, const char *string) { + return(dlsym(handle, string)); + } + //dlclose crashes if handle is null. + inline int DLLINTERNAL DLCLOSE(DLHANDLE handle) { + if(!handle) { + dlclose_handle_invalid = mTRUE; + return(1); + } + + dlclose_handle_invalid = mFALSE; + return(dlclose(handle)); + } + inline const char * DLLINTERNAL DLERROR(void) { + if(dlclose_handle_invalid) + return("Invalid handle."); + return(dlerror()); + } +#elif defined(_WIN32) + typedef HINSTANCE DLHANDLE; + typedef FARPROC DLFUNC; + inline DLHANDLE DLLINTERNAL DLOPEN(const char *filename) { + return(LoadLibraryA(filename)); + } + inline DLFUNC DLLINTERNAL DLSYM(DLHANDLE handle, const char *string) { + return(GetProcAddress(handle, string)); + } + inline int DLLINTERNAL DLCLOSE(DLHANDLE handle) { + if(!handle) { + dlclose_handle_invalid = mTRUE; + return(1); + } + + dlclose_handle_invalid = mFALSE; + + // NOTE: Windows FreeLibrary returns success=nonzero, fail=zero, + // which is the opposite of the unix convention, thus the '!'. + return(!FreeLibrary(handle)); + } + // Windows doesn't provide a function corresponding to dlerror(), so + // we make our own. + char * DLLINTERNAL str_GetLastError(void); + inline const char * DLLINTERNAL DLERROR(void) { + if(dlclose_handle_invalid) + return("Invalid handle."); + return(str_GetLastError()); + } +#endif /* _WIN32 */ +const char * DLLINTERNAL DLFNAME(void *memptr); +mBOOL DLLINTERNAL IS_VALID_PTR(void *memptr); + + +// Attempt to call the given function pointer, without segfaulting. +mBOOL DLLINTERNAL os_safe_call(REG_CMD_FN pfn); + + +// Windows doesn't have an strtok_r() routine, so we write our own. +#ifdef _WIN32 + #define strtok_r(s, delim, ptrptr) my_strtok_r(s, delim, ptrptr) + char * DLLINTERNAL my_strtok_r(char *s, const char *delim, char **ptrptr); +#endif /* _WIN32 */ + + +// Linux doesn't have an strlwr() routine, so we write our own. +#ifdef linux + #define strlwr(s) my_strlwr(s) + char * DLLINTERNAL my_strlwr(char *s); +#endif /* _WIN32 */ + + +// Set filename and pathname maximum lengths. Note some windows compilers +// provide a which is incomplete and/or causes problems; see +// doc/windows_notes.txt for more information. +// +// Note that both OS's include room for null-termination: +// linux: "# chars in a path name including nul" +// win32: "note that the sizes include space for 0-terminator" +#ifdef linux + #include +#elif defined(_WIN32) + #include + #define NAME_MAX _MAX_FNAME + #ifndef PATH_MAX + #define PATH_MAX _MAX_PATH + #endif +#endif /* _WIN32 */ + +// Various other windows routine differences. +#ifdef linux + #include // sleep + #ifndef O_BINARY + #define O_BINARY 0 + #endif +#elif defined(_WIN32) + #include + #include + + #define sleep(x) Sleep(x*1000) + + // Fixed MSVC compiling, by Nikolay "The Storm" Baklicharov. + #if defined(__GNUC__) || defined (_MSC_VER) && _MSC_VER >= 1400 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define unlink _unlink + #define strlwr _strlwr + #define strdup _strdup + #define strcasecmp _stricmp + #define strncasecmp _strnicmp + #define getcwd _getcwd + #define open _open + #define read _read + #define write _write + #define close _close + #endif /* GCC or MSVC 8.0+ */ +#endif /* _WIN32 */ + +#if !defined WIN32 && !defined _MSC_VER +#include // getcwd +#endif + +#include +#ifndef S_ISREG + // Linux gcc defines this; earlier mingw didn't, later mingw does; + // MSVC doesn't seem to. + #define S_ISREG(m) ((m) & S_IFREG) +#endif /* not S_ISREG */ +#ifdef _WIN32 + // The following two are defined in mingw but not in MSVC + #ifndef S_IRUSR + #define S_IRUSR _S_IREAD + #endif + #ifndef S_IWUSR + #define S_IWUSR _S_IWRITE + #endif + + // The following two are defined neither in mingw nor in MSVC + #ifndef S_IRGRP + #define S_IRGRP S_IRUSR + #endif + #ifndef S_IWGRP + #define S_IWGRP S_IWUSR + #endif +#endif /* _WIN32 */ + +// Normalize/standardize a pathname. +// - For win32, this involves: +// - Turning backslashes (\) into slashes (/), so that config files and +// Metamod internal code can be simpler and just use slashes (/). +// - Turning upper/mixed case into lowercase, since windows is +// non-case-sensitive. +// - For linux, this requires no work, as paths uses slashes (/) natively, +// and pathnames are case-sensitive. +#ifdef linux +#define normalize_pathname(a) +#elif defined(_WIN32) +void DLLINTERNAL normalize_pathname(char *path); +#endif /* _WIN32 */ + +// Indicate if pathname appears to be an absolute-path. Under linux this +// is a leading slash (/). Under win32, this can be: +// - a drive-letter path (ie "D:blah" or "C:\blah") +// - a toplevel path (ie "\blah") +// - a UNC network address (ie "\\srv1\blah"). +// Also, handle both native and normalized pathnames. +inline mBOOL DLLINTERNAL is_absolute_path(const char *path) { + if(path[0]=='/') return(mTRUE); +#ifdef _WIN32 + if(path[1]==':') return(mTRUE); + if(path[0]=='\\') return(mTRUE); +#endif /* _WIN32 */ + return(mFALSE); +} + +#ifdef _WIN32 +// Buffer pointed to by resolved_name is assumed to be able to store a +// string of PATH_MAX length. +char * DLLINTERNAL realpath(const char *file_name, char *resolved_name); +#endif /* _WIN32 */ + +// Generic "error string" from a recent OS call. For linux, this is based +// on errno. For win32, it's based on GetLastError. +inline const char * DLLINTERNAL str_os_error(void) { +#ifdef linux + return(strerror(errno)); +#elif defined(_WIN32) + return(str_GetLastError()); +#endif /* _WIN32 */ +} + + +#endif /* OSDEP_H */ diff --git a/src/metamod/osdep_p.h b/src/metamod/osdep_p.h index e265d36..d1e79c1 100644 --- a/src/metamod/osdep_p.h +++ b/src/metamod/osdep_p.h @@ -1,76 +1,76 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 OSDEP_P_H -#define OSDEP_P_H - -#include "types_meta.h" // mBOOL -#include "osdep.h" // PATH_MAX - -// Checks if file is hlsdk api game dll -// (osdep_detect_gamedll_linux.cpp and osdep_detect_gamedll_win32.cpp) -// --Jussi Kivilinna -mBOOL DLLINTERNAL is_gamedll(const char *filename); - -// MSVC doesn't provide opendir/readdir/closedir, so we write our own. -// --Jussi Kivilinna -#ifdef _WIN32 - struct my_dirent { - char d_name[PATH_MAX]; - }; - typedef struct { - HANDLE handle; - WIN32_FIND_DATAA find_data; - struct my_dirent ent; - int not_found; - } my_DIR; - - #define dirent my_dirent - #define DIR my_DIR - - DIR * DLLINTERNAL my_opendir(const char *); - struct dirent * DLLINTERNAL my_readdir(DIR *); - void DLLINTERNAL my_closedir(DIR *); - - #define opendir(x) my_opendir(x) - #define readdir(x) my_readdir(x) - #define closedir(x) my_closedir(x) -#else - #include -#endif /* _WIN32 */ - -DLHANDLE DLLINTERNAL get_module_handle_of_memptr(void * memptr); - -#ifdef linux - void * DLLINTERNAL get_dlsym_pointer(void); -#endif - -#endif /* OSDEP_P_H */ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 OSDEP_P_H +#define OSDEP_P_H + +#include "types_meta.h" // mBOOL +#include "osdep.h" // PATH_MAX + +// Checks if file is hlsdk api game dll +// (osdep_detect_gamedll_linux.cpp and osdep_detect_gamedll_win32.cpp) +// --Jussi Kivilinna +mBOOL DLLINTERNAL is_gamedll(const char *filename); + +// MSVC doesn't provide opendir/readdir/closedir, so we write our own. +// --Jussi Kivilinna +#ifdef _WIN32 + struct my_dirent { + char d_name[PATH_MAX]; + }; + typedef struct { + HANDLE handle; + WIN32_FIND_DATAA find_data; + struct my_dirent ent; + int not_found; + } my_DIR; + + #define dirent my_dirent + #define DIR my_DIR + + DIR * DLLINTERNAL my_opendir(const char *); + struct dirent * DLLINTERNAL my_readdir(DIR *); + void DLLINTERNAL my_closedir(DIR *); + + #define opendir(x) my_opendir(x) + #define readdir(x) my_readdir(x) + #define closedir(x) my_closedir(x) +#else + #include +#endif /* _WIN32 */ + +DLHANDLE DLLINTERNAL get_module_handle_of_memptr(void * memptr); + +#ifdef linux + void * DLLINTERNAL get_dlsym_pointer(void); +#endif + +#endif /* OSDEP_P_H */ diff --git a/src/metamod/plinfo.h b/src/metamod/plinfo.h index 1036db8..67ab109 100644 --- a/src/metamod/plinfo.h +++ b/src/metamod/plinfo.h @@ -1,85 +1,85 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// plinfo.h - typedefs for plugin info structure - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 PLINFO_H -#define PLINFO_H - -#include "comp_dep.h" - -// Flags for plugin to indicate when it can be be loaded/unloaded. -// NOTE: order is crucial, as greater/less comparisons are made. -typedef enum { - PT_NEVER = 0, - PT_STARTUP, // should only be loaded/unloaded at initial hlds execution - PT_CHANGELEVEL, // can be loaded/unloaded between maps - PT_ANYTIME, // can be loaded/unloaded at any time - PT_ANYPAUSE, // can be loaded/unloaded at any time, and can be "paused" during a map -} PLUG_LOADTIME; - -// Flags to indicate why the plugin is being unloaded. -typedef enum { - PNL_NULL = 0, - PNL_INI_DELETED, // was deleted from plugins.ini - PNL_FILE_NEWER, // file on disk is newer than last load - PNL_COMMAND, // requested by server/console command - PNL_CMD_FORCED, // forced by server/console command - PNL_DELAYED, // delayed from previous request; can't tell origin -//only used for 'real_reason' on MPlugin::unload() - PNL_PLUGIN, // requested by plugin function call - PNL_PLG_FORCED, // forced by plugin function call -//only used internally for 'meta reload' - PNL_RELOAD, // forced unload by reload() -} PL_UNLOAD_REASON; - -// Information plugin provides about itself. -typedef struct { - char *ifvers; // meta_interface version - char *name; // full name of plugin - char *version; // version - char *date; // date - char *author; // author name/email - char *url; // URL - char *logtag; // log message prefix (unused right now) - PLUG_LOADTIME loadable; // when loadable - PLUG_LOADTIME unloadable; // when unloadable -} plugin_info_t; -extern plugin_info_t Plugin_info DLLHIDDEN; - -// Plugin identifier, passed to all Meta Utility Functions. -typedef plugin_info_t* plid_t; -#define PLID &Plugin_info - -#endif /* PLINFO_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// plinfo.h - typedefs for plugin info structure + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 PLINFO_H +#define PLINFO_H + +#include "comp_dep.h" + +// Flags for plugin to indicate when it can be be loaded/unloaded. +// NOTE: order is crucial, as greater/less comparisons are made. +typedef enum { + PT_NEVER = 0, + PT_STARTUP, // should only be loaded/unloaded at initial hlds execution + PT_CHANGELEVEL, // can be loaded/unloaded between maps + PT_ANYTIME, // can be loaded/unloaded at any time + PT_ANYPAUSE, // can be loaded/unloaded at any time, and can be "paused" during a map +} PLUG_LOADTIME; + +// Flags to indicate why the plugin is being unloaded. +typedef enum { + PNL_NULL = 0, + PNL_INI_DELETED, // was deleted from plugins.ini + PNL_FILE_NEWER, // file on disk is newer than last load + PNL_COMMAND, // requested by server/console command + PNL_CMD_FORCED, // forced by server/console command + PNL_DELAYED, // delayed from previous request; can't tell origin +//only used for 'real_reason' on MPlugin::unload() + PNL_PLUGIN, // requested by plugin function call + PNL_PLG_FORCED, // forced by plugin function call +//only used internally for 'meta reload' + PNL_RELOAD, // forced unload by reload() +} PL_UNLOAD_REASON; + +// Information plugin provides about itself. +typedef struct { + char *ifvers; // meta_interface version + char *name; // full name of plugin + char *version; // version + char *date; // date + char *author; // author name/email + char *url; // URL + char *logtag; // log message prefix (unused right now) + PLUG_LOADTIME loadable; // when loadable + PLUG_LOADTIME unloadable; // when unloadable +} plugin_info_t; +extern plugin_info_t Plugin_info DLLHIDDEN; + +// Plugin identifier, passed to all Meta Utility Functions. +typedef plugin_info_t* plid_t; +#define PLID &Plugin_info + +#endif /* PLINFO_H */ diff --git a/src/metamod/reg_support.h b/src/metamod/reg_support.h index a4ec38a..4a1b5a4 100644 --- a/src/metamod/reg_support.h +++ b/src/metamod/reg_support.h @@ -1,49 +1,49 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// reg_support.h - functions for "registered" cmd/cvar/msg support - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 REG_SUPPORT_H -#define REG_SUPPORT_H - -#include "mreg.h" // REG_CMD_FN, etc - -// these are only 'hidden' because called from outside (plugins and engine) -void DLLHIDDEN meta_command_handler(void); -void DLLHIDDEN meta_AddServerCommand(char *cmd_name, REG_CMD_FN function); -void DLLHIDDEN meta_CVarRegister(cvar_t *pCvar); -int DLLHIDDEN meta_RegUserMsg(const char *pszName, int iSize); -void DLLHIDDEN meta_QueryClientCvarValue(const edict_t *player, const char *cvarName); - -#endif /* REG_SUPPORT_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// reg_support.h - functions for "registered" cmd/cvar/msg support + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 REG_SUPPORT_H +#define REG_SUPPORT_H + +#include "mreg.h" // REG_CMD_FN, etc + +// these are only 'hidden' because called from outside (plugins and engine) +void DLLHIDDEN meta_command_handler(void); +void DLLHIDDEN meta_AddServerCommand(char *cmd_name, REG_CMD_FN function); +void DLLHIDDEN meta_CVarRegister(cvar_t *pCvar); +int DLLHIDDEN meta_RegUserMsg(const char *pszName, int iSize); +void DLLHIDDEN meta_QueryClientCvarValue(const edict_t *player, const char *cvarName); + +#endif /* REG_SUPPORT_H */ diff --git a/src/metamod/ret_type.h b/src/metamod/ret_type.h index e246752..3429539 100644 --- a/src/metamod/ret_type.h +++ b/src/metamod/ret_type.h @@ -1,69 +1,69 @@ -/* - * Copyright (c) 2004-2006 Jussi Kivilinna - * - * This file is part of "Metamod All-Mod-Support"-patch for Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 RET_TYPE_H -#define RET_TYPE_H - -#include "new_baseclass.h" - -class class_ret_t : public class_metamod_new { -public: - // Construction - inline class_ret_t(void) { }; - inline class_ret_t(float f) { data.f = f; }; - inline class_ret_t(void * p) { data.p = p; }; - inline class_ret_t(const char * pc) { data.pc = pc; }; - inline class_ret_t(int i) { data.i = i; }; - inline class_ret_t(short s) { data.i = s; }; - inline class_ret_t(char c) { data.i = c; }; - inline class_ret_t(unsigned int ui) { data.ui = ui; }; - inline class_ret_t(unsigned long ui) { data.ui = ui; }; - inline class_ret_t(unsigned short us) { data.ui = us; }; - inline class_ret_t(unsigned char uc) { data.ui = uc; }; - - // Reading/Writing - inline void * getptr(void) { return(&data); }; - - #define SET_RET_CLASS(ret,type,x) \ - *(type*)((ret).getptr()) = (type)(x) - #define GET_RET_CLASS(ret,type) \ - (*(type*)((ret).getptr())) -private: - //Data (select data size of largest type) (x86: 32bit, x86_64: 64bit) - union { - void * p; - const char * pc; - float f; - long i; - unsigned long ui; - } data; -}; - -#endif /*RET_TYPE_H*/ +/* + * Copyright (c) 2004-2006 Jussi Kivilinna + * + * This file is part of "Metamod All-Mod-Support"-patch for Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 RET_TYPE_H +#define RET_TYPE_H + +#include "new_baseclass.h" + +class class_ret_t : public class_metamod_new { +public: + // Construction + inline class_ret_t(void) { }; + inline class_ret_t(float f) { data.f = f; }; + inline class_ret_t(void * p) { data.p = p; }; + inline class_ret_t(const char * pc) { data.pc = pc; }; + inline class_ret_t(int i) { data.i = i; }; + inline class_ret_t(short s) { data.i = s; }; + inline class_ret_t(char c) { data.i = c; }; + inline class_ret_t(unsigned int ui) { data.ui = ui; }; + inline class_ret_t(unsigned long ui) { data.ui = ui; }; + inline class_ret_t(unsigned short us) { data.ui = us; }; + inline class_ret_t(unsigned char uc) { data.ui = uc; }; + + // Reading/Writing + inline void * getptr(void) { return(&data); }; + + #define SET_RET_CLASS(ret,type,x) \ + *(type*)((ret).getptr()) = (type)(x) + #define GET_RET_CLASS(ret,type) \ + (*(type*)((ret).getptr())) +private: + //Data (select data size of largest type) (x86: 32bit, x86_64: 64bit) + union { + void * p; + const char * pc; + float f; + long i; + unsigned long ui; + } data; +}; + +#endif /*RET_TYPE_H*/ diff --git a/src/metamod/sdk_util.h b/src/metamod/sdk_util.h index 48a5025..1574661 100644 --- a/src/metamod/sdk_util.h +++ b/src/metamod/sdk_util.h @@ -1,121 +1,121 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// sdk_util.h - wrapper & extension of util.h from HL SDK - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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. - * - */ - -// Wrap util.h from SDK with ifndef/endif, to avoid problems from multiple -// inclusions. Dunno why Valve didn't do that in util.h themselves.. - -#ifndef SDK_UTIL_H -#define SDK_UTIL_H - -// We're not including the DBG_EntOfVars and DBG_AssertFunction routines -// mentioned in the SDK util.h, so we're going to unset DEBUG here so that -// we don't get "unresolved symbol" errors. -#ifdef DEBUG -#undef DEBUG -#endif /* DEBUG */ - -#include "enginecallbacks.h" -#include "comp_dep.h" -#include - - -// Also, create some additional macros for engine callback functions, which -// weren't in SDK dlls/enginecallbacks.h but probably should have been. - -#define GET_INFOKEYBUFFER (*g_engfuncs.pfnGetInfoKeyBuffer) -#define INFOKEY_VALUE (*g_engfuncs.pfnInfoKeyValue) -#define SET_CLIENT_KEYVALUE (*g_engfuncs.pfnSetClientKeyValue) -#define REG_SVR_COMMAND (*g_engfuncs.pfnAddServerCommand) -#define SERVER_PRINT (*g_engfuncs.pfnServerPrint) -#define SET_SERVER_KEYVALUE (*g_engfuncs.pfnSetKeyValue) -#define QUERY_CLIENT_CVAR_VALUE (*g_engfuncs.pfnQueryClientCvarValue) -#define QUERY_CLIENT_CVAR_VALUE2 (*g_engfuncs.pfnQueryClientCvarValue2) - -// Add overloaded ENTINDEX() version for const edict_t pointer. -// The pfnIndexOfEdict() function takes a const edict_t pointer -// as parameter anyway, so there is no reason why ENTINDEX() -// shouldn't. -inline int ENTINDEX(const edict_t *pEdict) { - return((*g_engfuncs.pfnIndexOfEdict)(pEdict)); -} - - -// Also, create some nice inlines for engine callback combos. - -// Get a setinfo value from a player entity. -inline char * DLLINTERNAL ENTITY_KEYVALUE(edict_t *entity, char *key) { - return(INFOKEY_VALUE(GET_INFOKEYBUFFER(entity), key)); -} - -// Set a setinfo value for a player entity. -inline void DLLINTERNAL ENTITY_SET_KEYVALUE(edict_t *entity, char *key, char *value) { - SET_CLIENT_KEYVALUE(ENTINDEX(entity), GET_INFOKEYBUFFER(entity), key, value); -} - -// Get a "serverinfo" value. -inline char * DLLINTERNAL SERVERINFO(char *key) { - return(ENTITY_KEYVALUE(INDEXENT(0), key)); -} - -// Set a "serverinfo" value. -inline void DLLINTERNAL SET_SERVERINFO(char *key, char *value) { - SET_SERVER_KEYVALUE(GET_INFOKEYBUFFER(INDEXENT(0)), key, value); -} - -// Get a "localinfo" value. -inline char * DLLINTERNAL LOCALINFO(char *key) { - return(ENTITY_KEYVALUE(NULL, key)); -} - -// Set a "localinfo" value. -inline void DLLINTERNAL SET_LOCALINFO(char *key, char *value) { - SET_SERVER_KEYVALUE(GET_INFOKEYBUFFER(NULL), key, value); -} - -inline int DLLINTERNAL fast_FNullEnt(const edict_t* pent) { - return(!pent || !(*g_engfuncs.pfnEntOffsetOfPEntity)(pent)); -} - -// Our slightly modified version, using an edict_t pointer instead of a -// CBaseEntity pointer. (was in 1.17p1, included in 1.17.1) -void DLLINTERNAL META_UTIL_HudMessage(edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage); - -const char * DLLINTERNAL META_UTIL_VarArgs(const char *format, ...); - -short DLLINTERNAL FixedSigned16(float value, float scale); -unsigned short DLLINTERNAL FixedUnsigned16(float value, float scale); - -#endif /* SDK_UTIL_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// sdk_util.h - wrapper & extension of util.h from HL SDK + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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. + * + */ + +// Wrap util.h from SDK with ifndef/endif, to avoid problems from multiple +// inclusions. Dunno why Valve didn't do that in util.h themselves.. + +#ifndef SDK_UTIL_H +#define SDK_UTIL_H + +// We're not including the DBG_EntOfVars and DBG_AssertFunction routines +// mentioned in the SDK util.h, so we're going to unset DEBUG here so that +// we don't get "unresolved symbol" errors. +#ifdef DEBUG +#undef DEBUG +#endif /* DEBUG */ + +#include "enginecallbacks.h" +#include "comp_dep.h" +#include + + +// Also, create some additional macros for engine callback functions, which +// weren't in SDK dlls/enginecallbacks.h but probably should have been. + +#define GET_INFOKEYBUFFER (*g_engfuncs.pfnGetInfoKeyBuffer) +#define INFOKEY_VALUE (*g_engfuncs.pfnInfoKeyValue) +#define SET_CLIENT_KEYVALUE (*g_engfuncs.pfnSetClientKeyValue) +#define REG_SVR_COMMAND (*g_engfuncs.pfnAddServerCommand) +#define SERVER_PRINT (*g_engfuncs.pfnServerPrint) +#define SET_SERVER_KEYVALUE (*g_engfuncs.pfnSetKeyValue) +#define QUERY_CLIENT_CVAR_VALUE (*g_engfuncs.pfnQueryClientCvarValue) +#define QUERY_CLIENT_CVAR_VALUE2 (*g_engfuncs.pfnQueryClientCvarValue2) + +// Add overloaded ENTINDEX() version for const edict_t pointer. +// The pfnIndexOfEdict() function takes a const edict_t pointer +// as parameter anyway, so there is no reason why ENTINDEX() +// shouldn't. +inline int ENTINDEX(const edict_t *pEdict) { + return((*g_engfuncs.pfnIndexOfEdict)(pEdict)); +} + + +// Also, create some nice inlines for engine callback combos. + +// Get a setinfo value from a player entity. +inline char * DLLINTERNAL ENTITY_KEYVALUE(edict_t *entity, char *key) { + return(INFOKEY_VALUE(GET_INFOKEYBUFFER(entity), key)); +} + +// Set a setinfo value for a player entity. +inline void DLLINTERNAL ENTITY_SET_KEYVALUE(edict_t *entity, char *key, char *value) { + SET_CLIENT_KEYVALUE(ENTINDEX(entity), GET_INFOKEYBUFFER(entity), key, value); +} + +// Get a "serverinfo" value. +inline char * DLLINTERNAL SERVERINFO(char *key) { + return(ENTITY_KEYVALUE(INDEXENT(0), key)); +} + +// Set a "serverinfo" value. +inline void DLLINTERNAL SET_SERVERINFO(char *key, char *value) { + SET_SERVER_KEYVALUE(GET_INFOKEYBUFFER(INDEXENT(0)), key, value); +} + +// Get a "localinfo" value. +inline char * DLLINTERNAL LOCALINFO(char *key) { + return(ENTITY_KEYVALUE(NULL, key)); +} + +// Set a "localinfo" value. +inline void DLLINTERNAL SET_LOCALINFO(char *key, char *value) { + SET_SERVER_KEYVALUE(GET_INFOKEYBUFFER(NULL), key, value); +} + +inline int DLLINTERNAL fast_FNullEnt(const edict_t* pent) { + return(!pent || !(*g_engfuncs.pfnEntOffsetOfPEntity)(pent)); +} + +// Our slightly modified version, using an edict_t pointer instead of a +// CBaseEntity pointer. (was in 1.17p1, included in 1.17.1) +void DLLINTERNAL META_UTIL_HudMessage(edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage); + +const char * DLLINTERNAL META_UTIL_VarArgs(const char *format, ...); + +short DLLINTERNAL FixedSigned16(float value, float scale); +unsigned short DLLINTERNAL FixedUnsigned16(float value, float scale); + +#endif /* SDK_UTIL_H */ diff --git a/src/metamod/stdint_c99.h b/src/metamod/stdint_c99.h new file mode 100644 index 0000000..a2907ae --- /dev/null +++ b/src/metamod/stdint_c99.h @@ -0,0 +1,229 @@ +/* ISO C9x 7.18 Integer types +* Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794) +* +* THIS SOFTWARE IS NOT COPYRIGHTED +* +* Contributor: Danny Smith +* +* This source code is offered for use in the public domain. You may +* use, modify or distribute it freely. +* +* This code is distributed in the hope that it will be useful but +* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY +* DISCLAIMED. This includes but is not limited to warranties of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* Date: 2000-12-02 +* +* mwb: This was modified in the following ways: +* +* - make it compatible with Visual C++ 6 (which uses +* non-standard keywords and suffixes for 64-bit types) +* - some environments need stddef.h included (for wchar stuff?) +* - handle the fact that Microsoft's limits.h header defines +* SIZE_MAX +* - make corrections for SIZE_MAX, INTPTR_MIN, INTPTR_MAX, UINTPTR_MAX, +* PTRDIFF_MIN, PTRDIFF_MAX, SIG_ATOMIC_MIN, and SIG_ATOMIC_MAX +* to be 64-bit aware. +*/ + +#ifndef _STDINT_H +#define _STDINT_H +#define __need_wint_t +#define __need_wchar_t +#include +#include + +#if _MSC_VER && (_MSC_VER < 1300) +/* using MSVC 6 or earlier - no "long long" type, but might have _int64 type */ +#define __STDINT_LONGLONG __int64 +#define __STDINT_LONGLONG_SUFFIX i64 +#else +#define __STDINT_LONGLONG long long +#define __STDINT_LONGLONG_SUFFIX LL +#endif + +#if !defined( PASTE) +#define PASTE2( x, y) x##y +#define PASTE( x, y) PASTE2( x, y) +#endif /* PASTE */ + +/* 7.18.1.1 Exact-width integer types */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned uint32_t; +typedef __STDINT_LONGLONG int64_t; +typedef unsigned __STDINT_LONGLONG uint64_t; + +/* 7.18.1.2 Minimum-width integer types */ +typedef signed char int_least8_t; +typedef unsigned char uint_least8_t; +typedef short int_least16_t; +typedef unsigned short uint_least16_t; +typedef int int_least32_t; +typedef unsigned uint_least32_t; +typedef __STDINT_LONGLONG int_least64_t; +typedef unsigned __STDINT_LONGLONG uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types +* Not actually guaranteed to be fastest for all purposes +* Here we use the exact-width types for 8 and 16-bit ints. +*/ +typedef char int_fast8_t; +typedef unsigned char uint_fast8_t; +typedef short int_fast16_t; +typedef unsigned short uint_fast16_t; +typedef int int_fast32_t; +typedef unsigned int uint_fast32_t; +typedef __STDINT_LONGLONG int_fast64_t; +typedef unsigned __STDINT_LONGLONG uint_fast64_t; + +/* 7.18.1.4 Integer types capable of holding object pointers */ +#ifndef _INTPTR_T_DEFINED +#define _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef __STDINT_LONGLONG intptr_t +#else +typedef int intptr_t; +#endif /* _WIN64 */ +#endif /* _INTPTR_T_DEFINED */ + +#ifndef _UINTPTR_T_DEFINED +#define _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __STDINT_LONGLONG uintptr_t +#else +typedef unsigned int uintptr_t; +#endif /* _WIN64 */ +#endif /* _UINTPTR_T_DEFINED */ + +/* 7.18.1.5 Greatest-width integer types */ +typedef __STDINT_LONGLONG intmax_t; +typedef unsigned __STDINT_LONGLONG uintmax_t; + +/* 7.18.2 Limits of specified-width integer types */ +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + +/* 7.18.2.1 Limits of exact-width integer types */ +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483647 - 1) +#define INT64_MIN (PASTE( -9223372036854775807, __STDINT_LONGLONG_SUFFIX) - 1) + +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 +#define INT64_MAX (PASTE( 9223372036854775807, __STDINT_LONGLONG_SUFFIX)) + +#define UINT8_MAX 0xff /* 255U */ +#define UINT16_MAX 0xffff /* 65535U */ +#define UINT32_MAX 0xffffffff /* 4294967295U */ +#define UINT64_MAX (PASTE( 0xffffffffffffffffU, __STDINT_LONGLONG_SUFFIX)) /* 18446744073709551615ULL */ + +/* 7.18.2.2 Limits of minimum-width integer types */ +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN + +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +/* 7.18.2.4 Limits of integer types capable of holding object pointers */ +#ifdef _WIN64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif /* _WIN64 */ + +/* 7.18.2.5 Limits of greatest-width integer types */ +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +/* 7.18.3 Limits of other integer types */ +#define PTRDIFF_MIN INTPTR_MIN +#define PTRDIFF_MAX INTPTR_MAX + +#define SIG_ATOMIC_MIN INTPTR_MIN +#define SIG_ATOMIC_MAX INTPTR_MAX + +/* we need to check for SIZE_MAX already defined because MS defines it in limits.h */ +#ifndef SIZE_MAX +#define SIZE_MAX UINTPTR_MAX +#endif + +#ifndef WCHAR_MIN /* also in wchar.h */ +#define WCHAR_MIN 0 +#define WCHAR_MAX ((wchar_t)-1) /* UINT16_MAX */ +#endif + +/* wint_t is unsigned short for compatibility with MS runtime */ +#define WINT_MIN 0 +#define WINT_MAX ((wint_t)-1) /* UINT16_MAX */ + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + +/* 7.18.4 Macros for integer constants */ +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + +/* 7.18.4.1 Macros for minimum-width integer constants + + Accoding to Douglas Gwyn : + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + 9899:1999 as initially published, the expansion was required + to be an integer constant of precisely matching type, which + is impossible to accomplish for the shorter types on most + platforms, because C99 provides no standard way to designate + an integer constant with width less than that of type int. + TC1 changed this to require just an integer constant + *expression* with *promoted* type." +*/ + +#define INT8_C(val) ((int8_t) + (val)) +#define UINT8_C(val) ((uint8_t) + (val##U)) +#define INT16_C(val) ((int16_t) + (val)) +#define UINT16_C(val) ((uint16_t) + (val##U)) + +#define INT32_C(val) val##L +#define UINT32_C(val) val##UL +#define INT64_C(val) (PASTE( val, __STDINT_LONGLONG_SUFFIX)) +#define UINT64_C(val)(PASTE( PASTE( val, U), __STDINT_LONGLONG_SUFFIX)) + +/* 7.18.4.2 Macros for greatest-width integer constants */ +#define INTMAX_C(val) INT64_C(val) +#define UINTMAX_C(val) UINT64_C(val) + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + +#endif diff --git a/src/metamod/studioapi.h b/src/metamod/studioapi.h index 6c2ee4a..aa3bdda 100644 --- a/src/metamod/studioapi.h +++ b/src/metamod/studioapi.h @@ -1,38 +1,38 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// studio.cpp - player model blending interfaces - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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. - * - */ - -//Jussi Kivilinna: This forwarder function to gamedll is not needed anymore, dynamic -// linkent code handles this too! +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// studio.cpp - player model blending interfaces + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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. + * + */ + +//Jussi Kivilinna: This forwarder function to gamedll is not needed anymore, dynamic +// linkent code handles this too! diff --git a/src/metamod/support_meta.h b/src/metamod/support_meta.h index 3b9028d..40e6dc9 100644 --- a/src/metamod/support_meta.h +++ b/src/metamod/support_meta.h @@ -1,177 +1,177 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// support_meta.h - generic support macros - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 SUPPORT_META_H -#define SUPPORT_META_H - -#include // strcpy(), strncat() -#include // stat -#include // stat - -#include "osdep.h" // strcasecmp, S_ISREG, -#include "enginecallbacks.h" // LOAD_FILE_FOR_ME, etc - -void DLLINTERNAL do_exit(int exitval); - -//use pointer to avoid inlining of strcmp -inline int DLLINTERNAL mm_strcmp(const char *s1, const char *s2) { -#if 0 - int (*__strcmp)(const char*, const char*) = &strcmp; - return((*__strcmp)(s1, s2)); -#else - return(strcmp(s1,s2)); -#endif -} - -//use pointer to avoid inlining of strncmp -inline int DLLINTERNAL mm_strncmp(const char *s1, const char *s2, size_t n) { -#if 0 - int (*__strncmp)(const char*, const char*, size_t) = &strncmp; - return((*__strncmp)(s1, s2, n)); -#else - return(strncmp(s1,s2,n)); -#endif -} - -// Unlike snprintf(), strncpy() doesn't necessarily null-terminate the -// target. It appears the former function reasonably considers the given -// size to be "max size of target string" (including the null-terminator), -// whereas strncpy() strangely considers the given size to be "total number -// of bytes to copy". Note strncpy() _always_ writes n bytes, whereas -// snprintf() writes a _max_ of n bytes (incl the NULL). If strncpy() -// needs to write extra bytes to reach n, it uses NULLs, so the target -// _can_ be null-terminated, but only if the source string would have fit -// anyway - in which case why not just use strcpy() instead? -// -// Thus, it appears strncpy() is not only unsafe, it's also inefficient, -// and seemingly no better than plain strcpy() anyway. -// -// With this logic, strncpy() doesn't appear to be much of a "str" function -// at all, IMHO. -// -// Strncat works better, although it considers the given size to be "number -// of bytes to append", and doesn't include the null-terminator in that -// count. Thus, we can use it for what we want to do, by setting the -// target to zero-length (NULL in first byte), and copying n-1 bytes -// (leaving room for the null-termiator). -// -// Why does it have to be soo haaard... - -// Also note, some kind of wrapper is necessary to group the two -// statements into one, for use in situations like non-braced else -// statements. - -// Technique 1: use "do..while": -#if 0 -#define STRNCPY(dst, src, size) \ - do { strcpy(dst, "\0"); strncat(dst, src, size-1); } while(0) -#endif - -// Technique 2: use parens and commas: -#if 0 -#define STRNCPY(dst, src, size) \ - (strcpy(dst, "\0"), strncat(dst, src, size-1)) -#endif - -// Technique 3: use inline -inline char * DLLINTERNAL STRNCPY(char *dst, const char *src, int size) { - return(strncat(&(*dst = 0), src, size-1)); -} - -// Renamed string functions to be clearer. -inline int DLLINTERNAL strmatch(const char *s1, const char *s2) { - if(likely(s1) && likely(s2)) - return(!mm_strcmp(s1, s2)); - else - return(0); -} -inline int DLLINTERNAL strnmatch(const char *s1, const char *s2, size_t n) { - if(likely(s1) && likely(s2)) - return(!mm_strncmp(s1, s2, n)); - else - return(0); -} -inline int DLLINTERNAL strcasematch(const char *s1, const char *s2) { - if(likely(s1) && likely(s2)) - return(!strcasecmp(s1, s2)); - else - return(0); -} -inline int DLLINTERNAL strncasematch(const char *s1, const char *s2, size_t n) { - if(likely(s1) && likely(s2)) - return(!strncasecmp(s1, s2, n)); - else - return(0); -} - -inline int DLLINTERNAL old_valid_file(char *path) { - char *cp; - int len, ret; - cp = (char *)LOAD_FILE_FOR_ME(path, &len); - if(cp && len) - ret=1; - else - ret=0; - FREE_FILE(cp); - return(ret); -} -int DLLINTERNAL valid_gamedir_file(const char *path); -char * DLLINTERNAL full_gamedir_path(const char *path, char *fullpath); - -// Turn a variable/function name into the corresponding string, optionally -// stripping off the leading "len" characters. Useful for things like -// turning 'pfnClientCommand' into "ClientCommand" so we don't have to -// specify strings used for all the debugging/log messages. -#define STRINGIZE(name, len) #name+len - - -// Max description length for plugins.ini and other places. -#define MAX_DESC_LEN 256 - - -// For various character string buffers. -#define MAX_STRBUF_LEN 1024 - - -// Smallest of two -#define MIN(x, y) (((x)<(y))?(x):(y)) - - -// Greatest of two -#define MAX(x, y) (((x)>(y))?(x):(y)) - - -#endif /* SUPPORT_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// support_meta.h - generic support macros + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 SUPPORT_META_H +#define SUPPORT_META_H + +#include // strcpy(), strncat() +#include // stat +#include // stat + +#include "osdep.h" // strcasecmp, S_ISREG, +#include "enginecallbacks.h" // LOAD_FILE_FOR_ME, etc + +void DLLINTERNAL do_exit(int exitval); + +//use pointer to avoid inlining of strcmp +inline int DLLINTERNAL mm_strcmp(const char *s1, const char *s2) { +#if 0 + int (*__strcmp)(const char*, const char*) = &strcmp; + return((*__strcmp)(s1, s2)); +#else + return(strcmp(s1,s2)); +#endif +} + +//use pointer to avoid inlining of strncmp +inline int DLLINTERNAL mm_strncmp(const char *s1, const char *s2, size_t n) { +#if 0 + int (*__strncmp)(const char*, const char*, size_t) = &strncmp; + return((*__strncmp)(s1, s2, n)); +#else + return(strncmp(s1,s2,n)); +#endif +} + +// Unlike snprintf(), strncpy() doesn't necessarily null-terminate the +// target. It appears the former function reasonably considers the given +// size to be "max size of target string" (including the null-terminator), +// whereas strncpy() strangely considers the given size to be "total number +// of bytes to copy". Note strncpy() _always_ writes n bytes, whereas +// snprintf() writes a _max_ of n bytes (incl the NULL). If strncpy() +// needs to write extra bytes to reach n, it uses NULLs, so the target +// _can_ be null-terminated, but only if the source string would have fit +// anyway - in which case why not just use strcpy() instead? +// +// Thus, it appears strncpy() is not only unsafe, it's also inefficient, +// and seemingly no better than plain strcpy() anyway. +// +// With this logic, strncpy() doesn't appear to be much of a "str" function +// at all, IMHO. +// +// Strncat works better, although it considers the given size to be "number +// of bytes to append", and doesn't include the null-terminator in that +// count. Thus, we can use it for what we want to do, by setting the +// target to zero-length (NULL in first byte), and copying n-1 bytes +// (leaving room for the null-termiator). +// +// Why does it have to be soo haaard... + +// Also note, some kind of wrapper is necessary to group the two +// statements into one, for use in situations like non-braced else +// statements. + +// Technique 1: use "do..while": +#if 0 +#define STRNCPY(dst, src, size) \ + do { strcpy(dst, "\0"); strncat(dst, src, size-1); } while(0) +#endif + +// Technique 2: use parens and commas: +#if 0 +#define STRNCPY(dst, src, size) \ + (strcpy(dst, "\0"), strncat(dst, src, size-1)) +#endif + +// Technique 3: use inline +inline char * DLLINTERNAL STRNCPY(char *dst, const char *src, int size) { + return(strncat(&(*dst = 0), src, size-1)); +} + +// Renamed string functions to be clearer. +inline int DLLINTERNAL strmatch(const char *s1, const char *s2) { + if(likely(s1) && likely(s2)) + return(!mm_strcmp(s1, s2)); + else + return(0); +} +inline int DLLINTERNAL strnmatch(const char *s1, const char *s2, size_t n) { + if(likely(s1) && likely(s2)) + return(!mm_strncmp(s1, s2, n)); + else + return(0); +} +inline int DLLINTERNAL strcasematch(const char *s1, const char *s2) { + if(likely(s1) && likely(s2)) + return(!strcasecmp(s1, s2)); + else + return(0); +} +inline int DLLINTERNAL strncasematch(const char *s1, const char *s2, size_t n) { + if(likely(s1) && likely(s2)) + return(!strncasecmp(s1, s2, n)); + else + return(0); +} + +inline int DLLINTERNAL old_valid_file(char *path) { + char *cp; + int len, ret; + cp = (char *)LOAD_FILE_FOR_ME(path, &len); + if(cp && len) + ret=1; + else + ret=0; + FREE_FILE(cp); + return(ret); +} +int DLLINTERNAL valid_gamedir_file(const char *path); +char * DLLINTERNAL full_gamedir_path(const char *path, char *fullpath); + +// Turn a variable/function name into the corresponding string, optionally +// stripping off the leading "len" characters. Useful for things like +// turning 'pfnClientCommand' into "ClientCommand" so we don't have to +// specify strings used for all the debugging/log messages. +#define STRINGIZE(name, len) #name+len + + +// Max description length for plugins.ini and other places. +#define MAX_DESC_LEN 256 + + +// For various character string buffers. +#define MAX_STRBUF_LEN 1024 + + +// Smallest of two +#define MIN(x, y) (((x)<(y))?(x):(y)) + + +// Greatest of two +#define MAX(x, y) (((x)>(y))?(x):(y)) + + +#endif /* SUPPORT_META_H */ diff --git a/src/metamod/tqueue.h b/src/metamod/tqueue.h index cc9c721..a3d5481 100644 --- a/src/metamod/tqueue.h +++ b/src/metamod/tqueue.h @@ -1,114 +1,114 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// tqueue.h - template classes for Queue and QItem - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 TQUEUE_H -#define TQUEUE_H - -#include "new_baseclass.h" - -// Forward declarations. -template class Queue; - -// Template for Queue. -template class Queue : public class_metamod_new { - private: - // private copy/assign constructors: - Queue(const Queue &src); - void operator=(const Queue &src); - protected: - // structs: - class QItem : public class_metamod_new { - private: - // private copy/assign constructors: - QItem(const QItem &src); - void operator=(const QItem &src); - public: - qdata_t *data; - QItem *next; - QItem(void) :data(NULL), next(NULL) { }; - QItem(qdata_t *dnew) :data(dnew), next(NULL) { }; - }; - // data: - int size; - QItem *front; - QItem *end; - public: - // constructor: - Queue(void) :size(0), front(NULL), end(NULL) {}; - // functions: - void push(qdata_t *qadd); - qdata_t * pop(void); -}; - - -///// Template Queue: - -// Push onto the queue (at end). -template inline void Queue::push(qdata_t *qadd) { - QItem *qnew = new QItem(qadd); - - if(size==0) - front=qnew; - else - end->next=qnew; - - end=qnew; - - size++; -} - -// Pop from queue (from front). Wait for an item to actually be available -// on the queue (block until there's something there). -template inline qdata_t* Queue::pop(void) { - QItem *qtmp; - qdata_t *ret; - - if(size==0) - return(NULL); - - qtmp=front; - - ret=front->data; - front=front->next; - - delete qtmp; - - size--; - - return(ret); -} - -#endif*/ /* TQUEUE_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// tqueue.h - template classes for Queue and QItem + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 TQUEUE_H +#define TQUEUE_H + +#include "new_baseclass.h" + +// Forward declarations. +template class Queue; + +// Template for Queue. +template class Queue : public class_metamod_new { + private: + // private copy/assign constructors: + Queue(const Queue &src); + void operator=(const Queue &src); + protected: + // structs: + class QItem : public class_metamod_new { + private: + // private copy/assign constructors: + QItem(const QItem &src); + void operator=(const QItem &src); + public: + qdata_t *data; + QItem *next; + QItem(void) :data(NULL), next(NULL) { }; + QItem(qdata_t *dnew) :data(dnew), next(NULL) { }; + }; + // data: + int size; + QItem *front; + QItem *end; + public: + // constructor: + Queue(void) :size(0), front(NULL), end(NULL) {}; + // functions: + void push(qdata_t *qadd); + qdata_t * pop(void); +}; + + +///// Template Queue: + +// Push onto the queue (at end). +template inline void Queue::push(qdata_t *qadd) { + QItem *qnew = new QItem(qadd); + + if(size==0) + front=qnew; + else + end->next=qnew; + + end=qnew; + + size++; +} + +// Pop from queue (from front). Wait for an item to actually be available +// on the queue (block until there's something there). +template inline qdata_t* Queue::pop(void) { + QItem *qtmp; + qdata_t *ret; + + if(size==0) + return(NULL); + + qtmp=front; + + ret=front->data; + front=front->next; + + delete qtmp; + + size--; + + return(ret); +} + +#endif*/ /* TQUEUE_H */ diff --git a/src/metamod/types_meta.h b/src/metamod/types_meta.h index 4e8ff11..a880472 100644 --- a/src/metamod/types_meta.h +++ b/src/metamod/types_meta.h @@ -1,85 +1,85 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// types_meta.h - common internal type, etc definitions - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 TYPES_META_H -#define TYPES_META_H - -#include "comp_dep.h" - - -// Our own boolean type, for stricter type matching. -typedef enum mBOOL { - mFALSE = 0, - mTRUE, -} mBOOL; - -// Like C's errno, for our various functions; describes causes of failure -// or mFALSE returns. -typedef enum { - ME_NOERROR = 0, - ME_FORMAT, // invalid format - ME_COMMENT, // ignored comment - ME_BLANK, // ignored blank (empty) line - ME_ALREADY, // request had already been done - ME_DELAYED, // request is delayed - ME_NOTALLOWED, // request not allowed - ME_SKIPPED, // request is being skipped for whatever reason - ME_BADREQ, // invalid request for this - ME_ARGUMENT, // invalid arguments - ME_NULLRESULT, // resulting data was empty or null - ME_MAXREACHED, // reached max/limit - ME_NOTUNIQ, // not unique (ambigious match) - ME_NOTFOUND, // in find operation, match not found - ME_NOFILE, // file empty or missing - ME_NOMEM, // malloc failed - ME_BADMEMPTR, // invalid memory address - ME_OSNOTSUP, // OS doesn't support this operation - ME_DLOPEN, // failed to open shared lib/dll - ME_DLMISSING, // symbol missing in lib/dll - ME_DLERROR, // some other error encountered calling functions from dll - ME_IFVERSION, // incompatible interface version - ME_UNLOAD_UNLOADER, // tried to unload unloader - ME_UNLOAD_SELF, // tried to unload self -} META_ERRNO; -extern META_ERRNO meta_errno DLLHIDDEN; - -#define RETURN_ERRNO(retval, errval) \ - do { meta_errno=errval; return(retval); } while(0) - -#define RETURN_LOGERR_ERRNO(errargs, retval, errval) \ - do { META_ERROR errargs ; meta_errno=errval; return(retval); } while(0) - -#endif /* TYPES_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// types_meta.h - common internal type, etc definitions + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 TYPES_META_H +#define TYPES_META_H + +#include "comp_dep.h" + + +// Our own boolean type, for stricter type matching. +typedef enum mBOOL { + mFALSE = 0, + mTRUE, +} mBOOL; + +// Like C's errno, for our various functions; describes causes of failure +// or mFALSE returns. +typedef enum { + ME_NOERROR = 0, + ME_FORMAT, // invalid format + ME_COMMENT, // ignored comment + ME_BLANK, // ignored blank (empty) line + ME_ALREADY, // request had already been done + ME_DELAYED, // request is delayed + ME_NOTALLOWED, // request not allowed + ME_SKIPPED, // request is being skipped for whatever reason + ME_BADREQ, // invalid request for this + ME_ARGUMENT, // invalid arguments + ME_NULLRESULT, // resulting data was empty or null + ME_MAXREACHED, // reached max/limit + ME_NOTUNIQ, // not unique (ambigious match) + ME_NOTFOUND, // in find operation, match not found + ME_NOFILE, // file empty or missing + ME_NOMEM, // malloc failed + ME_BADMEMPTR, // invalid memory address + ME_OSNOTSUP, // OS doesn't support this operation + ME_DLOPEN, // failed to open shared lib/dll + ME_DLMISSING, // symbol missing in lib/dll + ME_DLERROR, // some other error encountered calling functions from dll + ME_IFVERSION, // incompatible interface version + ME_UNLOAD_UNLOADER, // tried to unload unloader + ME_UNLOAD_SELF, // tried to unload self +} META_ERRNO; +extern META_ERRNO meta_errno DLLHIDDEN; + +#define RETURN_ERRNO(retval, errval) \ + do { meta_errno=errval; return(retval); } while(0) + +#define RETURN_LOGERR_ERRNO(errargs, retval, errval) \ + do { META_ERROR errargs ; meta_errno=errval; return(retval); } while(0) + +#endif /* TYPES_META_H */ diff --git a/src/metamod/vdate.h b/src/metamod/vdate.h index 6783c63..e6e23fa 100644 --- a/src/metamod/vdate.h +++ b/src/metamod/vdate.h @@ -1,45 +1,45 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// vdate.h - compile-time version date - -/* - * Copyright (c) 2001-2006 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 VDATE_H -#define VDATE_H - -#include "comp_dep.h" - -extern char const *COMPILE_TIME DLLHIDDEN; -extern char const *COMPILE_TZONE DLLHIDDEN; - -#endif /* VDATE_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// vdate.h - compile-time version date + +/* + * Copyright (c) 2001-2006 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 VDATE_H +#define VDATE_H + +#include "comp_dep.h" + +extern char const *COMPILE_TIME DLLHIDDEN; +extern char const *COMPILE_TZONE DLLHIDDEN; + +#endif /* VDATE_H */ diff --git a/src/metamod/vers_meta.h b/src/metamod/vers_meta.h index 96beea8..e45d019 100644 --- a/src/metamod/vers_meta.h +++ b/src/metamod/vers_meta.h @@ -1,61 +1,61 @@ -// vi: set ts=4 sw=4 : -// vim: set tw=75 : - -// vers_meta.h - version info, intended to be common among DLLs distributed -// with metamod. - -/* - * Copyright (c) 2001-2013 Will Day - * - * This file is part of Metamod. - * - * Metamod 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. - * - * Metamod 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 Metamod; 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 VERS_META_H -#define VERS_META_H - -#ifndef OPT_TYPE - #define OPT_TYPE "default" -#endif /* not OPT_TYPE */ - - -#define VDATE "2018/02/11" -#define VPATCH_COPYRIGHT_YEAR "2018" -#define VMETA_VERSION "1.21" - -#define VPATCH_NAME "Metamod-P (mm-p)" -#define VPATCH_IVERSION 38 -#define VPATCH_VERSION "38" -#define VPATCH_AUTHOR "Jussi Kivilinna" -#define VPATCH_WEBSITE "http://metamod-p.sourceforge.net/" - -#define VVERSION VMETA_VERSION "p" VPATCH_VERSION -#define RC_VERS_DWORD 1,21,0,VPATCH_IVERSION // Version Windows DLL Resources in res_meta.rc - - - -#endif /* VERS_META_H */ +// vi: set ts=4 sw=4 : +// vim: set tw=75 : + +// vers_meta.h - version info, intended to be common among DLLs distributed +// with metamod. + +/* + * Copyright (c) 2001-2013 Will Day + * + * This file is part of Metamod. + * + * Metamod 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. + * + * Metamod 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 Metamod; 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 VERS_META_H +#define VERS_META_H + +#ifndef OPT_TYPE + #define OPT_TYPE "default" +#endif /* not OPT_TYPE */ + + +#define VDATE "2018/02/11" +#define VPATCH_COPYRIGHT_YEAR "2018" +#define VMETA_VERSION "1.21" + +#define VPATCH_NAME "Metamod-P (mm-p)" +#define VPATCH_IVERSION 38 +#define VPATCH_VERSION "38" +#define VPATCH_AUTHOR "Jussi Kivilinna" +#define VPATCH_WEBSITE "http://metamod-p.sourceforge.net/" + +#define VVERSION VMETA_VERSION "p" VPATCH_VERSION +#define RC_VERS_DWORD 1,21,0,VPATCH_IVERSION // Version Windows DLL Resources in res_meta.rc + + + +#endif /* VERS_META_H */ diff --git a/src/pm_shared/pm_materials.h b/src/pm_shared/pm_materials.h index bc49c4d..88bba3b 100644 --- a/src/pm_shared/pm_materials.h +++ b/src/pm_shared/pm_materials.h @@ -1,33 +1,33 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ -#if !defined( PM_MATERIALSH ) -#define PM_MATERIALSH -#pragma once - -#define CBTEXTURENAMEMAX 13 // only load first n chars of name - -#define CHAR_TEX_CONCRETE 'C' // texture types -#define CHAR_TEX_METAL 'M' -#define CHAR_TEX_DIRT 'D' -#define CHAR_TEX_VENT 'V' -#define CHAR_TEX_GRATE 'G' -#define CHAR_TEX_TILE 'T' -#define CHAR_TEX_SLOSH 'S' -#define CHAR_TEX_WOOD 'W' -#define CHAR_TEX_COMPUTER 'P' -#define CHAR_TEX_GLASS 'Y' -#define CHAR_TEX_FLESH 'F' - -#endif // !PM_MATERIALSH +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ +#if !defined( PM_MATERIALSH ) +#define PM_MATERIALSH +#pragma once + +#define CBTEXTURENAMEMAX 13 // only load first n chars of name + +#define CHAR_TEX_CONCRETE 'C' // texture types +#define CHAR_TEX_METAL 'M' +#define CHAR_TEX_DIRT 'D' +#define CHAR_TEX_VENT 'V' +#define CHAR_TEX_GRATE 'G' +#define CHAR_TEX_TILE 'T' +#define CHAR_TEX_SLOSH 'S' +#define CHAR_TEX_WOOD 'W' +#define CHAR_TEX_COMPUTER 'P' +#define CHAR_TEX_GLASS 'Y' +#define CHAR_TEX_FLESH 'F' + +#endif // !PM_MATERIALSH diff --git a/src/utils/common/mathlib.h b/src/utils/common/mathlib.h index 209f9ef..4d13cf0 100644 --- a/src/utils/common/mathlib.h +++ b/src/utils/common/mathlib.h @@ -1,89 +1,89 @@ -/*** -* -* Copyright (c) 1996-2001, 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. -* -****/ - -#ifndef __MATHLIB__ -#define __MATHLIB__ - -// mathlib.h - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef DOUBLEVEC_T -typedef double vec_t; -#else -typedef float vec_t; -#endif -typedef vec_t vec3_t[3]; // x,y,z -typedef vec_t vec4_t[4]; // x,y,z,w - -#define SIDE_FRONT 0 -#define SIDE_ON 2 -#define SIDE_BACK 1 -#define SIDE_CROSS -2 - -#define Q_PI 3.14159265358979323846 - -extern vec3_t vec3_origin; - -// Use this definition globally -#define ON_EPSILON 0.01 -#define EQUAL_EPSILON 0.001 - -int VectorCompare (vec3_t v1, vec3_t v2); - -#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) -#define VectorFill(a,b) { (a)[0]=(b); (a)[1]=(b); (a)[2]=(b);} -#define VectorAvg(a) ( ( (a)[0] + (a)[1] + (a)[2] ) / 3 ) -#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} -#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];} -#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];} -#define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];} - -vec_t Q_rint (vec_t in); -vec_t _DotProduct (vec3_t v1, vec3_t v2); -void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); -void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); -void _VectorCopy (vec3_t in, vec3_t out); -void _VectorScale (vec3_t v, vec_t scale, vec3_t out); - -double VectorLength(vec3_t v); - -void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc); - -void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); -vec_t VectorNormalize (vec3_t v); -void VectorInverse (vec3_t v); - -void ClearBounds (vec3_t mins, vec3_t maxs); -void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); - -void AngleMatrix (const vec3_t angles, float matrix[3][4] ); -void AngleIMatrix (const vec3_t angles, float matrix[3][4] ); -void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]); - -void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out); -void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out); - -void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out); - -void AngleQuaternion( const vec3_t angles, vec4_t quaternion ); -void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ); -void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ); - - -#ifdef __cplusplus -} -#endif - -#endif +/*** +* +* Copyright (c) 1996-2001, 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. +* +****/ + +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DOUBLEVEC_T +typedef double vec_t; +#else +typedef float vec_t; +#endif +typedef vec_t vec3_t[3]; // x,y,z +typedef vec_t vec4_t[4]; // x,y,z,w + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +#define Q_PI 3.14159265358979323846 + +extern vec3_t vec3_origin; + +// Use this definition globally +#define ON_EPSILON 0.01 +#define EQUAL_EPSILON 0.001 + +int VectorCompare (vec3_t v1, vec3_t v2); + +#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) +#define VectorFill(a,b) { (a)[0]=(b); (a)[1]=(b); (a)[2]=(b);} +#define VectorAvg(a) ( ( (a)[0] + (a)[1] + (a)[2] ) / 3 ) +#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} +#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];} +#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];} +#define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];} + +vec_t Q_rint (vec_t in); +vec_t _DotProduct (vec3_t v1, vec3_t v2); +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out); +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out); +void _VectorCopy (vec3_t in, vec3_t out); +void _VectorScale (vec3_t v, vec_t scale, vec3_t out); + +double VectorLength(vec3_t v); + +void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc); + +void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); +vec_t VectorNormalize (vec3_t v); +void VectorInverse (vec3_t v); + +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); + +void AngleMatrix (const vec3_t angles, float matrix[3][4] ); +void AngleIMatrix (const vec3_t angles, float matrix[3][4] ); +void R_ConcatTransforms (const float in1[3][4], const float in2[3][4], float out[3][4]); + +void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out); +void VectorRotate (const vec3_t in1, const float in2[3][4], vec3_t out); + +void VectorTransform (const vec3_t in1, const float in2[3][4], vec3_t out); + +void AngleQuaternion( const vec3_t angles, vec4_t quaternion ); +void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ); +void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ); + + +#ifdef __cplusplus +} +#endif + +#endif