Initial commit.
This commit is contained in:
946
codemp/game/AnimalNPC.c
Normal file
946
codemp/game/AnimalNPC.c
Normal file
@@ -0,0 +1,946 @@
|
||||
// leave this line at the top for all g_xxxx.cpp files...
|
||||
#include "g_headers.h"
|
||||
|
||||
//seems to be a compiler bug, it doesn't clean out the #ifdefs between dif-compiles
|
||||
//or something, so the headers spew errors on these defs from the previous compile.
|
||||
//this fixes that. -rww
|
||||
#ifdef _JK2MP
|
||||
//get rid of all the crazy defs we added for this file
|
||||
#undef currentAngles
|
||||
#undef currentOrigin
|
||||
#undef mins
|
||||
#undef maxs
|
||||
#undef legsAnimTimer
|
||||
#undef torsoAnimTimer
|
||||
#undef bool
|
||||
#undef false
|
||||
#undef true
|
||||
|
||||
#undef sqrtf
|
||||
#undef Q_flrand
|
||||
|
||||
#undef MOD_EXPLOSIVE
|
||||
#endif
|
||||
|
||||
#ifdef _JK2 //SP does not have this preprocessor for game like MP does
|
||||
#ifndef _JK2MP
|
||||
#define _JK2MP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP //if single player
|
||||
#ifndef QAGAME //I don't think we have a QAGAME define
|
||||
#define QAGAME //but define it cause in sp we're always in the game
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^
|
||||
#include "g_local.h"
|
||||
#elif defined _JK2MP
|
||||
#include "bg_public.h"
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP
|
||||
#include "g_functions.h"
|
||||
#include "g_vehicles.h"
|
||||
#else
|
||||
#include "bg_vehicles.h"
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
//this is really horrible, but it works! just be sure not to use any locals or anything
|
||||
//with these names (exluding bool, false, true). -rww
|
||||
#define currentAngles r.currentAngles
|
||||
#define currentOrigin r.currentOrigin
|
||||
#define mins r.mins
|
||||
#define maxs r.maxs
|
||||
#define legsAnimTimer legsTimer
|
||||
#define torsoAnimTimer torsoTimer
|
||||
#define bool qboolean
|
||||
#define false qfalse
|
||||
#define true qtrue
|
||||
|
||||
#define sqrtf sqrt
|
||||
#define Q_flrand flrand
|
||||
|
||||
#define MOD_EXPLOSIVE MOD_SUICIDE
|
||||
#else
|
||||
#define bgEntity_t gentity_t
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME //we only want a few of these functions for BG
|
||||
|
||||
extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
|
||||
extern vmCvar_t cg_thirdPersonAlpha;
|
||||
extern vec3_t playerMins;
|
||||
extern vec3_t playerMaxs;
|
||||
extern cvar_t *g_speederControlScheme;
|
||||
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_begin.h"
|
||||
#endif
|
||||
extern void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
|
||||
extern int PM_AnimLength( int index, animNumber_t anim );
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_end.h"
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP
|
||||
extern void CG_ChangeWeapon( int num );
|
||||
#endif
|
||||
|
||||
extern void Vehicle_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
|
||||
extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
|
||||
extern void G_VehicleTrace( trace_t *results, const vec3_t start, const vec3_t tMins, const vec3_t tMaxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
|
||||
// Update death sequence.
|
||||
static void DeathUpdate( Vehicle_t *pVeh )
|
||||
{
|
||||
if ( level.time >= pVeh->m_iDieTime )
|
||||
{
|
||||
// If the vehicle is not empty.
|
||||
if ( pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
|
||||
{
|
||||
pVeh->m_pVehicleInfo->EjectAll( pVeh );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Waste this sucker.
|
||||
}
|
||||
|
||||
// Die now...
|
||||
/* else
|
||||
{
|
||||
vec3_t mins, maxs, bottom;
|
||||
trace_t trace;
|
||||
|
||||
if ( pVeh->m_pVehicleInfo->explodeFX )
|
||||
{
|
||||
G_PlayEffect( pVeh->m_pVehicleInfo->explodeFX, parent->currentOrigin );
|
||||
//trace down and place mark
|
||||
VectorCopy( parent->currentOrigin, bottom );
|
||||
bottom[2] -= 80;
|
||||
gi.trace( &trace, parent->currentOrigin, vec3_origin, vec3_origin, bottom, parent->s.number, CONTENTS_SOLID );
|
||||
if ( trace.fraction < 1.0f )
|
||||
{
|
||||
VectorCopy( trace.endpos, bottom );
|
||||
bottom[2] += 2;
|
||||
G_PlayEffect( "ships/ship_explosion_mark", trace.endpos );
|
||||
}
|
||||
}
|
||||
|
||||
parent->takedamage = qfalse;//so we don't recursively damage ourselves
|
||||
if ( pVeh->m_pVehicleInfo->explosionRadius > 0 && pVeh->m_pVehicleInfo->explosionDamage > 0 )
|
||||
{
|
||||
VectorCopy( parent->mins, mins );
|
||||
mins[2] = -4;//to keep it off the ground a *little*
|
||||
VectorCopy( parent->maxs, maxs );
|
||||
VectorCopy( parent->currentOrigin, bottom );
|
||||
bottom[2] += parent->mins[2] - 32;
|
||||
gi.trace( &trace, parent->currentOrigin, mins, maxs, bottom, parent->s.number, CONTENTS_SOLID );
|
||||
G_RadiusDamage( trace.endpos, NULL, pVeh->m_pVehicleInfo->explosionDamage, pVeh->m_pVehicleInfo->explosionRadius, NULL, MOD_EXPLOSIVE );//FIXME: extern damage and radius or base on fuel
|
||||
}
|
||||
|
||||
parent->e_ThinkFunc = thinkF_G_FreeEntity;
|
||||
parent->nextthink = level.time + FRAMETIME;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
// Like a think or move command, this updates various vehicle properties.
|
||||
static bool Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
|
||||
{
|
||||
return g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd );
|
||||
}
|
||||
#endif //QAGAME
|
||||
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_begin.h"
|
||||
#endif
|
||||
|
||||
//MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
|
||||
//If you really need to violate this rule for SP, then use ifdefs.
|
||||
//By BG-compatible, I mean no use of game-specific data - ONLY use
|
||||
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
|
||||
//as a gentity, but the MP-compatible access restrictions are based
|
||||
//on the bgEntity structure in the MP codebase) -rww
|
||||
// ProcessMoveCommands the Vehicle.
|
||||
static void ProcessMoveCommands( Vehicle_t *pVeh )
|
||||
{
|
||||
/************************************************************************************/
|
||||
/* BEGIN Here is where we move the vehicle (forward or back or whatever). BEGIN */
|
||||
/************************************************************************************/
|
||||
|
||||
//Client sets ucmds and such for speed alterations
|
||||
float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax;
|
||||
float fWalkSpeedMax;
|
||||
int curTime;
|
||||
bgEntity_t *parent = pVeh->m_pParentEntity;
|
||||
#ifdef _JK2MP
|
||||
playerState_t *parentPS = parent->playerState;
|
||||
#else
|
||||
playerState_t *parentPS = &parent->client->ps;
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP//SP
|
||||
curTime = level.time;
|
||||
#elif QAGAME//MP GAME
|
||||
curTime = level.time;
|
||||
#elif CGAME//MP CGAME
|
||||
//FIXME: pass in ucmd? Not sure if this is reliable...
|
||||
curTime = pm->cmd.serverTime;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef _JK2MP //bad for prediction - fixme
|
||||
// Bucking so we can't do anything.
|
||||
if ( pVeh->m_ulFlags & VEH_BUCKING || pVeh->m_ulFlags & VEH_FLYING || pVeh->m_ulFlags & VEH_CRASHING )
|
||||
{
|
||||
//#ifdef QAGAME //this was in Update above
|
||||
// ((gentity_t *)parent)->client->ps.speed = 0;
|
||||
//#endif
|
||||
parentPS->speed = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
|
||||
speedMax = pVeh->m_pVehicleInfo->speedMax;
|
||||
|
||||
speedIdle = pVeh->m_pVehicleInfo->speedIdle;
|
||||
speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
|
||||
speedMin = pVeh->m_pVehicleInfo->speedMin;
|
||||
|
||||
|
||||
|
||||
if ( pVeh->m_pPilot /*&& (pilotPS->weapon == WP_NONE || pilotPS->weapon == WP_MELEE )*/ &&
|
||||
(pVeh->m_ucmd.buttons & BUTTON_ALT_ATTACK) && pVeh->m_pVehicleInfo->turboSpeed )
|
||||
{
|
||||
if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge)
|
||||
{
|
||||
pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration);
|
||||
#ifndef _JK2MP //kill me now
|
||||
if (pVeh->m_pVehicleInfo->soundTurbo)
|
||||
{
|
||||
G_SoundIndexOnEnt(pVeh->m_pParentEntity, CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo);
|
||||
}
|
||||
#endif
|
||||
parentPS->speed = pVeh->m_pVehicleInfo->turboSpeed; // Instantly Jump To Turbo Speed
|
||||
}
|
||||
}
|
||||
|
||||
if ( curTime < pVeh->m_iTurboTime )
|
||||
{
|
||||
speedMax = pVeh->m_pVehicleInfo->turboSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
speedMax = pVeh->m_pVehicleInfo->speedMax;
|
||||
}
|
||||
|
||||
#ifdef _JK2MP
|
||||
if ( !parentPS->m_iVehicleNum )
|
||||
#else
|
||||
if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
|
||||
#endif
|
||||
{//drifts to a stop
|
||||
speedInc = speedIdle * pVeh->m_fTimeModifier;
|
||||
VectorClear( parentPS->moveDir );
|
||||
//m_ucmd.forwardmove = 127;
|
||||
parentPS->speed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
|
||||
}
|
||||
|
||||
if ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE ||
|
||||
pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 )
|
||||
{
|
||||
if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
|
||||
{
|
||||
parentPS->speed += speedInc;
|
||||
}
|
||||
else if ( pVeh->m_ucmd.forwardmove < 0 )
|
||||
{
|
||||
if ( parentPS->speed > speedIdle )
|
||||
{
|
||||
parentPS->speed -= speedInc;
|
||||
}
|
||||
else if ( parentPS->speed > speedMin )
|
||||
{
|
||||
parentPS->speed -= speedIdleDec;
|
||||
}
|
||||
}
|
||||
// No input, so coast to stop.
|
||||
else if ( parentPS->speed > 0.0f )
|
||||
{
|
||||
parentPS->speed -= speedIdleDec;
|
||||
if ( parentPS->speed < 0.0f )
|
||||
{
|
||||
parentPS->speed = 0.0f;
|
||||
}
|
||||
}
|
||||
else if ( parentPS->speed < 0.0f )
|
||||
{
|
||||
parentPS->speed += speedIdleDec;
|
||||
if ( parentPS->speed > 0.0f )
|
||||
{
|
||||
parentPS->speed = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pVeh->m_ucmd.forwardmove < 0 )
|
||||
{
|
||||
pVeh->m_ucmd.forwardmove = 0;
|
||||
}
|
||||
if ( pVeh->m_ucmd.upmove < 0 )
|
||||
{
|
||||
pVeh->m_ucmd.upmove = 0;
|
||||
}
|
||||
|
||||
//pVeh->m_ucmd.rightmove = 0;
|
||||
|
||||
/*if ( !pVeh->m_pVehicleInfo->strafePerc
|
||||
|| (!g_speederControlScheme->value && !parent->s.number) )
|
||||
{//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme
|
||||
pVeh->m_ucmd.rightmove = 0;
|
||||
}*/
|
||||
}
|
||||
|
||||
fWalkSpeedMax = speedMax * 0.275f;
|
||||
if ( curTime>pVeh->m_iTurboTime && (pVeh->m_ucmd.buttons & BUTTON_WALKING) && parentPS->speed > fWalkSpeedMax )
|
||||
{
|
||||
parentPS->speed = fWalkSpeedMax;
|
||||
}
|
||||
else if ( parentPS->speed > speedMax )
|
||||
{
|
||||
parentPS->speed = speedMax;
|
||||
}
|
||||
else if ( parentPS->speed < speedMin )
|
||||
{
|
||||
parentPS->speed = speedMin;
|
||||
}
|
||||
|
||||
/********************************************************************************/
|
||||
/* END Here is where we move the vehicle (forward or back or whatever). END */
|
||||
/********************************************************************************/
|
||||
}
|
||||
|
||||
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
|
||||
//If you really need to violate this rule for SP, then use ifdefs.
|
||||
//By BG-compatible, I mean no use of game-specific data - ONLY use
|
||||
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
|
||||
//as a gentity, but the MP-compatible access restrictions are based
|
||||
//on the bgEntity structure in the MP codebase) -rww
|
||||
// ProcessOrientCommands the Vehicle.
|
||||
static void ProcessOrientCommands( Vehicle_t *pVeh )
|
||||
{
|
||||
/********************************************************************************/
|
||||
/* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */
|
||||
/********************************************************************************/
|
||||
bgEntity_t *parent = pVeh->m_pParentEntity;
|
||||
playerState_t *parentPS, *riderPS;
|
||||
|
||||
#ifdef _JK2MP
|
||||
bgEntity_t *rider = NULL;
|
||||
if (parent->s.owner != ENTITYNUM_NONE)
|
||||
{
|
||||
rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
|
||||
}
|
||||
#else
|
||||
gentity_t *rider = parent->owner;
|
||||
#endif
|
||||
|
||||
// Bucking so we can't do anything.
|
||||
#ifndef _JK2MP //bad for prediction - fixme
|
||||
if ( pVeh->m_ulFlags & VEH_BUCKING || pVeh->m_ulFlags & VEH_FLYING || pVeh->m_ulFlags & VEH_CRASHING )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
if ( !rider )
|
||||
#else
|
||||
if ( !rider || !rider->client )
|
||||
#endif
|
||||
{
|
||||
rider = parent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _JK2MP
|
||||
parentPS = parent->playerState;
|
||||
riderPS = rider->playerState;
|
||||
#else
|
||||
parentPS = &parent->client->ps;
|
||||
riderPS = &rider->client->ps;
|
||||
#endif
|
||||
|
||||
if (rider)
|
||||
{
|
||||
#ifdef _JK2MP
|
||||
float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
|
||||
if (parentPS && parentPS->speed)
|
||||
{
|
||||
float s = parentPS->speed;
|
||||
float maxDif = pVeh->m_pVehicleInfo->turningSpeed*4.0f; //magic number hackery
|
||||
if (s < 0.0f)
|
||||
{
|
||||
s = -s;
|
||||
}
|
||||
angDif *= s/pVeh->m_pVehicleInfo->speedMax;
|
||||
if (angDif > maxDif)
|
||||
{
|
||||
angDif = maxDif;
|
||||
}
|
||||
else if (angDif < -maxDif)
|
||||
{
|
||||
angDif = -maxDif;
|
||||
}
|
||||
pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
|
||||
}
|
||||
#else
|
||||
pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* speed = VectorLength( parentPS->velocity );
|
||||
|
||||
// If the player is the rider...
|
||||
if ( rider->s.number < MAX_CLIENTS )
|
||||
{//FIXME: use the vehicle's turning stat in this calc
|
||||
pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
|
||||
}
|
||||
else
|
||||
{
|
||||
float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed;
|
||||
if ( !pVeh->m_pVehicleInfo->turnWhenStopped
|
||||
&& !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove?
|
||||
{//can't turn when not moving
|
||||
//FIXME: or ramp up to max turnSpeed?
|
||||
turnSpeed = 0.0f;
|
||||
}
|
||||
#ifdef _JK2MP
|
||||
if (rider->s.eType == ET_NPC)
|
||||
#else
|
||||
if ( !rider || rider->NPC )
|
||||
#endif
|
||||
{//help NPCs out some
|
||||
turnSpeed *= 2.0f;
|
||||
#ifdef _JK2MP
|
||||
if (parentPS->speed > 200.0f)
|
||||
#else
|
||||
if ( parent->client->ps.speed > 200.0f )
|
||||
#endif
|
||||
{
|
||||
turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f;
|
||||
}
|
||||
}
|
||||
turnSpeed *= pVeh->m_fTimeModifier;
|
||||
|
||||
//default control scheme: strafing turns, mouselook aims
|
||||
if ( pVeh->m_ucmd.rightmove < 0 )
|
||||
{
|
||||
pVeh->m_vOrientation[YAW] += turnSpeed;
|
||||
}
|
||||
else if ( pVeh->m_ucmd.rightmove > 0 )
|
||||
{
|
||||
pVeh->m_vOrientation[YAW] -= turnSpeed;
|
||||
}
|
||||
|
||||
if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel )
|
||||
{//damaged badly
|
||||
}
|
||||
}*/
|
||||
|
||||
/********************************************************************************/
|
||||
/* END Here is where make sure the vehicle is properly oriented. END */
|
||||
/********************************************************************************/
|
||||
}
|
||||
|
||||
#ifdef _JK2MP //temp hack til mp speeder controls are sorted -rww
|
||||
void AnimalProcessOri(Vehicle_t *pVeh)
|
||||
{
|
||||
ProcessOrientCommands(pVeh);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME //back to our game-only functions
|
||||
static void AnimateVehicle( Vehicle_t *pVeh )
|
||||
{
|
||||
animNumber_t Anim = BOTH_VT_IDLE;
|
||||
int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
|
||||
gentity_t * pilot = (gentity_t *)pVeh->m_pPilot;
|
||||
gentity_t * parent = (gentity_t *)pVeh->m_pParentEntity;
|
||||
playerState_t * pilotPS;
|
||||
playerState_t * parentPS;
|
||||
float fSpeedPercToMax;
|
||||
|
||||
#ifdef _JK2MP
|
||||
pilotPS = (pilot)?(pilot->playerState):(0);
|
||||
parentPS = parent->playerState;
|
||||
#else
|
||||
pilotPS = (pilot)?(&pilot->client->ps):(0);
|
||||
parentPS = &parent->client->ps;
|
||||
#endif
|
||||
|
||||
// We're dead (boarding is reused here so I don't have to make another variable :-).
|
||||
if ( parent->health <= 0 )
|
||||
{
|
||||
/*
|
||||
if ( pVeh->m_iBoarding != -999 ) // Animate the death just once!
|
||||
{
|
||||
pVeh->m_iBoarding = -999;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
||||
|
||||
// FIXME! Why do you keep repeating over and over!!?!?!? Bastard!
|
||||
//Vehicle_SetAnim( parent, SETANIM_LEGS, BOTH_VT_DEATH1, iFlags, iBlend );
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// If they're bucking, play the animation and leave...
|
||||
if ( parent->client->ps.legsAnim == BOTH_VT_BUCK )
|
||||
{
|
||||
// Done with animation? Erase the flag.
|
||||
if ( parent->client->ps.legsAnimTimer <= 0 )
|
||||
{
|
||||
pVeh->m_ulFlags &= ~VEH_BUCKING;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( pVeh->m_ulFlags & VEH_BUCKING )
|
||||
{
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
||||
Anim = BOTH_VT_BUCK;
|
||||
iBlend = 500;
|
||||
Vehicle_SetAnim( parent, SETANIM_LEGS, BOTH_VT_BUCK, iFlags, iBlend );
|
||||
return;
|
||||
}
|
||||
|
||||
// Boarding animation.
|
||||
if ( pVeh->m_iBoarding != 0 )
|
||||
{
|
||||
// We've just started boarding, set the amount of time it will take to finish boarding.
|
||||
if ( pVeh->m_iBoarding < 0 )
|
||||
{
|
||||
int iAnimLen;
|
||||
|
||||
// Boarding from left...
|
||||
if ( pVeh->m_iBoarding == -1 )
|
||||
{
|
||||
Anim = BOTH_VT_MOUNT_L;
|
||||
}
|
||||
else if ( pVeh->m_iBoarding == -2 )
|
||||
{
|
||||
Anim = BOTH_VT_MOUNT_R;
|
||||
}
|
||||
else if ( pVeh->m_iBoarding == -3 )
|
||||
{
|
||||
Anim = BOTH_VT_MOUNT_B;
|
||||
}
|
||||
|
||||
// Set the delay time (which happens to be the time it takes for the animation to complete).
|
||||
// NOTE: Here I made it so the delay is actually 70% (0.7f) of the animation time.
|
||||
#ifdef _JK2MP
|
||||
iAnimLen = BG_AnimLength( parent->localAnimIndex, Anim ) * 0.7f;
|
||||
#else
|
||||
iAnimLen = PM_AnimLength( parent->client->clientInfo.animFileIndex, Anim ) * 0.7f;
|
||||
#endif
|
||||
pVeh->m_iBoarding = level.time + iAnimLen;
|
||||
|
||||
// Set the animation, which won't be interrupted until it's completed.
|
||||
// TODO: But what if he's killed? Should the animation remain persistant???
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
||||
|
||||
Vehicle_SetAnim( parent, SETANIM_LEGS, Anim, iFlags, iBlend );
|
||||
if (pilot)
|
||||
{
|
||||
Vehicle_SetAnim(pilot, SETANIM_BOTH, Anim, iFlags, iBlend );
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Otherwise we're done.
|
||||
else if ( pVeh->m_iBoarding <= level.time )
|
||||
{
|
||||
pVeh->m_iBoarding = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Percentage of maximum speed relative to current speed.
|
||||
//float fSpeed = VectorLength( client->ps.velocity );
|
||||
fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax;
|
||||
|
||||
|
||||
// Going in reverse...
|
||||
if ( fSpeedPercToMax < -0.01f )
|
||||
{
|
||||
Anim = BOTH_VT_WALK_REV;
|
||||
iBlend = 600;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool Turbo = (fSpeedPercToMax>0.0f && level.time<pVeh->m_iTurboTime);
|
||||
bool Walking = (fSpeedPercToMax>0.0f && ((pVeh->m_ucmd.buttons&BUTTON_WALKING) || fSpeedPercToMax<=0.275f));
|
||||
bool Running = (fSpeedPercToMax>0.275f);
|
||||
|
||||
|
||||
// Remove Crashing Flag
|
||||
//----------------------
|
||||
pVeh->m_ulFlags &= ~VEH_CRASHING;
|
||||
|
||||
if (Turbo)
|
||||
{// Kicked In Turbo
|
||||
iBlend = 50;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE;
|
||||
Anim = BOTH_VT_TURBO;
|
||||
}
|
||||
else
|
||||
{// No Special Moves
|
||||
iBlend = 300;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS;
|
||||
Anim = (Walking)?(BOTH_VT_WALK_FWD ):((Running)?(BOTH_VT_RUN_FWD ):(BOTH_VT_IDLE1));
|
||||
}
|
||||
}
|
||||
Vehicle_SetAnim( parent, SETANIM_LEGS, Anim, iFlags, iBlend );
|
||||
}
|
||||
|
||||
//rwwFIXMEFIXME: This is all going to have to be predicted I think, or it will feel awful
|
||||
//and lagged
|
||||
// This function makes sure that the rider's in this vehicle are properly animated.
|
||||
static void AnimateRiders( Vehicle_t *pVeh )
|
||||
{
|
||||
animNumber_t Anim = BOTH_VT_IDLE;
|
||||
int iFlags = SETANIM_FLAG_NORMAL, iBlend = 500;
|
||||
gentity_t *pilot = (gentity_t *)pVeh->m_pPilot;
|
||||
gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
|
||||
playerState_t *pilotPS;
|
||||
playerState_t *parentPS;
|
||||
float fSpeedPercToMax;
|
||||
|
||||
#ifdef _JK2MP
|
||||
pilotPS = pVeh->m_pPilot->playerState;
|
||||
parentPS = pVeh->m_pPilot->playerState;
|
||||
#else
|
||||
pilotPS = &pVeh->m_pPilot->client->ps;
|
||||
parentPS = &pVeh->m_pParentEntity->client->ps;
|
||||
#endif
|
||||
|
||||
// Boarding animation.
|
||||
if ( pVeh->m_iBoarding != 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Percentage of maximum speed relative to current speed.
|
||||
fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax;
|
||||
|
||||
// Going in reverse...
|
||||
#ifdef _JK2MP //handled in pmove in mp
|
||||
if (0)
|
||||
#else
|
||||
if ( fSpeedPercToMax < -0.01f )
|
||||
#endif
|
||||
{
|
||||
Anim = BOTH_VT_WALK_REV;
|
||||
iBlend = 600;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool HasWeapon = ((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE));
|
||||
bool Attacking = (HasWeapon && !!(pVeh->m_ucmd.buttons&BUTTON_ATTACK));
|
||||
bool Right = (pVeh->m_ucmd.rightmove>0);
|
||||
bool Left = (pVeh->m_ucmd.rightmove<0);
|
||||
bool Turbo = (fSpeedPercToMax>0.0f && level.time<pVeh->m_iTurboTime);
|
||||
bool Walking = (fSpeedPercToMax>0.0f && ((pVeh->m_ucmd.buttons&BUTTON_WALKING) || fSpeedPercToMax<=0.275f));
|
||||
bool Running = (fSpeedPercToMax>0.275f);
|
||||
EWeaponPose WeaponPose = WPOSE_NONE;
|
||||
|
||||
|
||||
// Remove Crashing Flag
|
||||
//----------------------
|
||||
pVeh->m_ulFlags &= ~VEH_CRASHING;
|
||||
|
||||
|
||||
// Put Away Saber When It Is Not Active
|
||||
//--------------------------------------
|
||||
#ifndef _JK2MP
|
||||
if (HasWeapon && (Turbo || (pilotPS->weapon==WP_SABER && !pilotPS->SaberActive())))
|
||||
{
|
||||
if (pVeh->m_pPilot->s.number<MAX_CLIENTS)
|
||||
{
|
||||
CG_ChangeWeapon(WP_NONE);
|
||||
}
|
||||
|
||||
pVeh->m_pPilot->client->ps.weapon = WP_NONE;
|
||||
G_RemoveWeaponModels(pVeh->m_pPilot);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't Interrupt Attack Anims
|
||||
//------------------------------
|
||||
#ifdef _JK2MP
|
||||
if (pilotPS->weaponTime>0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (pilotPS->torsoAnim>=BOTH_VT_ATL_S && pilotPS->torsoAnim<=BOTH_VT_ATF_G)
|
||||
{
|
||||
float bodyCurrent = 0.0f;
|
||||
int bodyEnd = 0;
|
||||
if (!!gi.G2API_GetBoneAnimIndex(&pVeh->m_pPilot->ghoul2[pVeh->m_pPilot->playerModel], pVeh->m_pPilot->rootBone, level.time, &bodyCurrent, NULL, &bodyEnd, NULL, NULL, NULL))
|
||||
{
|
||||
if (bodyCurrent<=((float)(bodyEnd)-1.5f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compute The Weapon Pose
|
||||
//--------------------------
|
||||
if (pilotPS->weapon==WP_BLASTER)
|
||||
{
|
||||
WeaponPose = WPOSE_BLASTER;
|
||||
}
|
||||
else if (pilotPS->weapon==WP_SABER)
|
||||
{
|
||||
if ( (pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATL_TO_R_S)
|
||||
{
|
||||
pVeh->m_ulFlags &= ~VEH_SABERINLEFTHAND;
|
||||
}
|
||||
if (!(pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATR_TO_L_S)
|
||||
{
|
||||
pVeh->m_ulFlags |= VEH_SABERINLEFTHAND;
|
||||
}
|
||||
WeaponPose = (pVeh->m_ulFlags&VEH_SABERINLEFTHAND)?(WPOSE_SABERLEFT):(WPOSE_SABERRIGHT);
|
||||
}
|
||||
|
||||
|
||||
if (Attacking && WeaponPose)
|
||||
{// Attack!
|
||||
iBlend = 100;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART;
|
||||
|
||||
if (Turbo)
|
||||
{
|
||||
Right = true;
|
||||
Left = false;
|
||||
}
|
||||
|
||||
// Auto Aiming
|
||||
//===============================================
|
||||
if (!Left && !Right) // Allow player strafe keys to override
|
||||
{
|
||||
#ifndef _JK2MP
|
||||
if (pVeh->m_pPilot->enemy)
|
||||
{
|
||||
vec3_t toEnemy;
|
||||
float toEnemyDistance;
|
||||
vec3_t actorRight;
|
||||
float actorRightDot;
|
||||
|
||||
VectorSubtract(pVeh->m_pPilot->currentOrigin, pVeh->m_pPilot->enemy->currentOrigin, toEnemy);
|
||||
toEnemyDistance = VectorNormalize(toEnemy);
|
||||
|
||||
AngleVectors(pVeh->m_pParentEntity->currentAngles, 0, actorRight, 0);
|
||||
actorRightDot = DotProduct(toEnemy, actorRight);
|
||||
|
||||
if (fabsf(actorRightDot)>0.5f || pilotPS->weapon==WP_SABER)
|
||||
{
|
||||
Left = (actorRightDot>0.0f);
|
||||
Right = !Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
Right = Left = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (pilotPS->weapon==WP_SABER && !Left && !Right)
|
||||
{
|
||||
Left = (WeaponPose==WPOSE_SABERLEFT);
|
||||
Right = !Left;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Left)
|
||||
{// Attack Left
|
||||
switch(WeaponPose)
|
||||
{
|
||||
case WPOSE_BLASTER: Anim = BOTH_VT_ATL_G; break;
|
||||
case WPOSE_SABERLEFT: Anim = BOTH_VT_ATL_S; break;
|
||||
case WPOSE_SABERRIGHT: Anim = BOTH_VT_ATR_TO_L_S; break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
else if (Right)
|
||||
{// Attack Right
|
||||
switch(WeaponPose)
|
||||
{
|
||||
case WPOSE_BLASTER: Anim = BOTH_VT_ATR_G; break;
|
||||
case WPOSE_SABERLEFT: Anim = BOTH_VT_ATL_TO_R_S; break;
|
||||
case WPOSE_SABERRIGHT: Anim = BOTH_VT_ATR_S; break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{// Attack Ahead
|
||||
switch(WeaponPose)
|
||||
{
|
||||
case WPOSE_BLASTER: Anim = BOTH_VT_ATF_G; break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Turbo)
|
||||
{// Kicked In Turbo
|
||||
iBlend = 50;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE;
|
||||
Anim = BOTH_VT_TURBO;
|
||||
}
|
||||
else
|
||||
{// No Special Moves
|
||||
iBlend = 300;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS;
|
||||
|
||||
if (WeaponPose==WPOSE_NONE)
|
||||
{
|
||||
if (Walking)
|
||||
{
|
||||
Anim = BOTH_VT_WALK_FWD;
|
||||
}
|
||||
else if (Running)
|
||||
{
|
||||
Anim = BOTH_VT_RUN_FWD;
|
||||
}
|
||||
else
|
||||
{
|
||||
Anim = BOTH_VT_IDLE1;//(Q_irand(0,1)==0)?(BOTH_VT_IDLE):(BOTH_VT_IDLE1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(WeaponPose)
|
||||
{
|
||||
case WPOSE_BLASTER: Anim = BOTH_VT_IDLE_G; break;
|
||||
case WPOSE_SABERLEFT: Anim = BOTH_VT_IDLE_SL; break;
|
||||
case WPOSE_SABERRIGHT: Anim = BOTH_VT_IDLE_SR; break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
}// No Special Moves
|
||||
}
|
||||
|
||||
Vehicle_SetAnim( pilot, SETANIM_BOTH, Anim, iFlags, iBlend );
|
||||
}
|
||||
#endif //QAGAME
|
||||
|
||||
#ifndef QAGAME
|
||||
void AttachRidersGeneric( Vehicle_t *pVeh );
|
||||
#endif
|
||||
|
||||
//on the client this function will only set up the process command funcs
|
||||
void G_SetAnimalVehicleFunctions( vehicleInfo_t *pVehInfo )
|
||||
{
|
||||
#ifdef QAGAME
|
||||
pVehInfo->AnimateVehicle = AnimateVehicle;
|
||||
pVehInfo->AnimateRiders = AnimateRiders;
|
||||
// pVehInfo->ValidateBoard = ValidateBoard;
|
||||
// pVehInfo->SetParent = SetParent;
|
||||
// pVehInfo->SetPilot = SetPilot;
|
||||
// pVehInfo->AddPassenger = AddPassenger;
|
||||
// pVehInfo->Animate = Animate;
|
||||
// pVehInfo->Board = Board;
|
||||
// pVehInfo->Eject = Eject;
|
||||
// pVehInfo->EjectAll = EjectAll;
|
||||
// pVehInfo->StartDeathDelay = StartDeathDelay;
|
||||
pVehInfo->DeathUpdate = DeathUpdate;
|
||||
// pVehInfo->RegisterAssets = RegisterAssets;
|
||||
// pVehInfo->Initialize = Initialize;
|
||||
pVehInfo->Update = Update;
|
||||
// pVehInfo->UpdateRider = UpdateRider;
|
||||
#endif //QAGAME
|
||||
pVehInfo->ProcessMoveCommands = ProcessMoveCommands;
|
||||
pVehInfo->ProcessOrientCommands = ProcessOrientCommands;
|
||||
|
||||
#ifndef QAGAME //cgame prediction attachment func
|
||||
pVehInfo->AttachRiders = AttachRidersGeneric;
|
||||
#endif
|
||||
// pVehInfo->AttachRiders = AttachRiders;
|
||||
// pVehInfo->Ghost = Ghost;
|
||||
// pVehInfo->UnGhost = UnGhost;
|
||||
// pVehInfo->Inhabited = Inhabited;
|
||||
}
|
||||
|
||||
// Following is only in game, not in namespace
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_end.h"
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME
|
||||
extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_begin.h"
|
||||
#endif
|
||||
|
||||
// Create/Allocate a new Animal Vehicle (initializing it as well).
|
||||
//this is a BG function too in MP so don't un-bg-compatibilify it -rww
|
||||
void G_CreateAnimalNPC( Vehicle_t **pVeh, const char *strAnimalType )
|
||||
{
|
||||
// Allocate the Vehicle.
|
||||
#ifdef _JK2MP
|
||||
#ifdef QAGAME
|
||||
//these will remain on entities on the client once allocated because the pointer is
|
||||
//never stomped. on the server, however, when an ent is freed, the entity struct is
|
||||
//memset to 0, so this memory would be lost..
|
||||
G_AllocateVehicleObject(pVeh);
|
||||
#else
|
||||
if (!*pVeh)
|
||||
{ //only allocate a new one if we really have to
|
||||
(*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
|
||||
}
|
||||
#endif
|
||||
memset(*pVeh, 0, sizeof(Vehicle_t));
|
||||
(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
|
||||
#else
|
||||
(*pVeh) = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qtrue );
|
||||
(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _JK2MP
|
||||
|
||||
#include "../namespace_end.h"
|
||||
|
||||
//get rid of all the crazy defs we added for this file
|
||||
#undef currentAngles
|
||||
#undef currentOrigin
|
||||
#undef mins
|
||||
#undef maxs
|
||||
#undef legsAnimTimer
|
||||
#undef torsoAnimTimer
|
||||
#undef bool
|
||||
#undef false
|
||||
#undef true
|
||||
|
||||
#undef sqrtf
|
||||
#undef Q_flrand
|
||||
|
||||
#undef MOD_EXPLOSIVE
|
||||
#endif
|
||||
1961
codemp/game/FighterNPC.c
Normal file
1961
codemp/game/FighterNPC.c
Normal file
File diff suppressed because it is too large
Load Diff
3
codemp/game/JK2_game.def
Normal file
3
codemp/game/JK2_game.def
Normal file
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
dllEntry
|
||||
vmMain
|
||||
650
codemp/game/JK2_game.dsp
Normal file
650
codemp/game/JK2_game.dsp
Normal file
@@ -0,0 +1,650 @@
|
||||
# Microsoft Developer Studio Project File - Name="JK2game" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=JK2game - Win32 Release JK2
|
||||
!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 "JK2_game.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 "JK2_game.mak" CFG="JK2game - Win32 Release JK2"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "JK2game - Win32 Release JK2" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "JK2game - Win32 Debug JK2" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "JK2game - Win32 Final JK2" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""$/jedi/codemp/game", EGAAAAAA"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "JK2game - Win32 Release JK2"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "JK2game___Win32_Release_TA"
|
||||
# PROP BASE Intermediate_Dir "JK2game___Win32_Release_TA"
|
||||
# PROP BASE Ignore_Export_Lib 0
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "../Release"
|
||||
# PROP Intermediate_Dir "../Release/JK2game"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G6 /W4 /GX /O2 /D "WIN32" /D "NDebug" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD CPP /nologo /G6 /W4 /GX /Zi /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "MISSIONPACK" /D "QAGAME" /D "_JK2" /YX /FD /c
|
||||
# SUBTRACT CPP /Fr
|
||||
# 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 winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /machine:I386 /out:"..\Release/qaJK2gamex86.dll"
|
||||
# SUBTRACT BASE LINK32 /incremental:yes /debug
|
||||
# ADD LINK32 kernel32.lib user32.lib winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /debug /machine:I386 /def:".\JK2_game.def" /out:"../Release/jk2mpgamex86.dll"
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "JK2game - Win32 Debug JK2"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "JK2game___Win32_Debug_TA"
|
||||
# PROP BASE Intermediate_Dir "JK2game___Win32_Debug_TA"
|
||||
# PROP BASE Ignore_Export_Lib 0
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "../Debug"
|
||||
# PROP Intermediate_Dir "../Debug/JK2game"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G5 /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_Debug" /D "_WINDOWS" /D "BUILDING_REF_GL" /D "Debug" /FR /YX /FD /c
|
||||
# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "BUILDING_REF_GL" /D "Debug" /D "WIN32" /D "_WINDOWS" /D "MISSIONPACK" /D "QAGAME" /D "_JK2" /D "JK2AWARDS" /FR /YX /FD /GZ /c
|
||||
# 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 winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /debug /machine:I386 /out:"..\Debug/qaJK2gamex86.dll"
|
||||
# SUBTRACT BASE LINK32 /incremental:no
|
||||
# ADD LINK32 kernel32.lib user32.lib winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /debug /machine:I386 /def:".\JK2_game.def" /out:"..\Debug\jk2mpgamex86.dll"
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "JK2game - Win32 Final JK2"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "../Final"
|
||||
# PROP BASE Intermediate_Dir "../Final/JK2game"
|
||||
# PROP BASE Ignore_Export_Lib 0
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "../Final"
|
||||
# PROP Intermediate_Dir "../Final/JK2game"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G6 /W4 /GX /Zi /O2 /I ".." /I "../../jk2/game" /D "NDebug2" /D "WIN32" /D "_WINDOWS" /D "MISSIONPACK" /D "QAGAME" /D "_JK2" /YX /FD /c
|
||||
# SUBTRACT BASE CPP /Fr
|
||||
# ADD CPP /nologo /G6 /W4 /GX /O2 /D "NDEBUG" /D "_WINDOWS" /D "MISSIONPACK" /D "QAGAME" /D "WIN32" /D "_JK2" /D "FINAL_BUILD" /YX /FD /c
|
||||
# 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 winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /debug /machine:I386 /def:".\JK2_game.def" /out:"../../Release/jk2mpgamex86.dll"
|
||||
# SUBTRACT BASE LINK32 /pdb:none
|
||||
# ADD LINK32 kernel32.lib user32.lib winmm.lib /nologo /base:"0x20000000" /subsystem:windows /dll /map /machine:I386 /def:".\JK2_game.def" /out:"../Final/jk2mpgamex86.dll"
|
||||
# SUBTRACT LINK32 /pdb:none /debug
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "JK2game - Win32 Release JK2"
|
||||
# Name "JK2game - Win32 Debug JK2"
|
||||
# Name "JK2game - Win32 Final JK2"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ai_main.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ai_util.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ai_wpnav.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_lib.c
|
||||
# PROP BASE Exclude_From_Build 1
|
||||
# PROP Exclude_From_Build 1
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_misc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_panimate.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_pmove.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_saber.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_slidemove.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_weapons.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_active.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_arenas.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_bot.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_client.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_cmds.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_combat.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_exphysics.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_ICARUScb.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_items.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_log.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_main.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_mem.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_misc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_missile.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_mover.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_nav.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_navnew.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_object.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_saga.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_session.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_spawn.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_strap.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_svcmds.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_syscalls.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_target.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_team.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_timer.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_trigger.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_utils.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_weapon.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Atst.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Default.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Droid.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_GalakMech.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Grenadier.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Howler.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_ImperialProbe.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Interrogator.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Jedi.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Mark1.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Mark2.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_MineMonster.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Remote.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Seeker.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Sentry.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Sniper.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Stormtrooper.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_AI_Utils.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_behavior.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_combat.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_goal.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_misc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_move.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_reactions.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_senses.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_sounds.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_spawn.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_stats.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\NPC_utils.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\q_math.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\q_shared.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\w_force.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\w_saber.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
|
||||
# Begin Group "ICARUS Headers"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\icarus\interpreter.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\icarus\Q3_Interface.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ai.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\ai_main.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\anims.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\cgame\animtable.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\b_local.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\b_public.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_aas.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_char.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_chat.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_gen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_goal.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_move.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ai_weap.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\be_ea.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_lib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_local.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_public.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_saga.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_strap.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\bg_weapons.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\botlib.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\cgame\cg_local.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\cgame\cg_public.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\chars.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\qcommon\disablewarnings.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\ghoul2\G2.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_ICARUScb.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_local.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_nav.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_public.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\g_team.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\inv.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\JK2_game.def
|
||||
|
||||
!IF "$(CFG)" == "JK2game - Win32 Release JK2"
|
||||
|
||||
# PROP Exclude_From_Build 1
|
||||
|
||||
!ELSEIF "$(CFG)" == "JK2game - Win32 Debug JK2"
|
||||
|
||||
# PROP Exclude_From_Build 1
|
||||
|
||||
!ELSEIF "$(CFG)" == "JK2game - Win32 Final JK2"
|
||||
|
||||
# PROP BASE Exclude_From_Build 1
|
||||
# PROP Exclude_From_Build 1
|
||||
|
||||
!ENDIF
|
||||
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\match.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\ui\menudef.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\npc_headers.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\q_shared.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\say.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\surfaceflags.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\syn.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\qcommon\tags.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\teams.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\cgame\tr_types.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\w_saber.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\game.bat
|
||||
# PROP Exclude_From_Build 1
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\game.q3asm
|
||||
# PROP Exclude_From_Build 1
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
||||
744
codemp/game/JK2_game.vcproj
Normal file
744
codemp/game/JK2_game.vcproj
Normal file
@@ -0,0 +1,744 @@
|
||||
<?xml version="1.0" encoding = "Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="7.00"
|
||||
Name="JK2game"
|
||||
SccProjectName=""$/jedi/codemp/game", EGAAAAAA"
|
||||
SccAuxPath=""
|
||||
SccLocalPath="."
|
||||
SccProvider="MSSCCI:Microsoft Visual SourceSafe">
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"/>
|
||||
</Platforms>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\../Release"
|
||||
IntermediateDirectory=".\../Release/JK2game"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
ImproveFloatingPointConsistency="TRUE"
|
||||
FavorSizeOrSpeed="1"
|
||||
OptimizeForProcessor="2"
|
||||
OptimizeForWindowsApplication="TRUE"
|
||||
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;MISSIONPACK;QAGAME;_JK2"
|
||||
StringPooling="TRUE"
|
||||
MinimalRebuild="TRUE"
|
||||
RuntimeLibrary="4"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderFile=".\../Release/JK2game/JK2_game.pch"
|
||||
AssemblerListingLocation=".\../Release/JK2game/"
|
||||
ObjectFile=".\../Release/JK2game/"
|
||||
ProgramDataBaseFileName=".\../Release/JK2game/"
|
||||
WarningLevel="4"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="3"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="winmm.lib"
|
||||
OutputFile="../Release/jampgamex86.dll"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ModuleDefinitionFile=".\JK2_game.def"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\../Release/jampgamex86.pdb"
|
||||
GenerateMapFile="TRUE"
|
||||
MapFileName="../Release/jampgamex86.map"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
SetChecksum="TRUE"
|
||||
BaseAddress="0x20000000"
|
||||
ImportLibrary=".\../Release/jampgamex86.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="NDebug"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\../Release/JK2_game.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDebug"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Final|Win32"
|
||||
OutputDirectory=".\../Final"
|
||||
IntermediateDirectory=".\../Final/JK2game"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
InlineFunctionExpansion="2"
|
||||
EnableIntrinsicFunctions="TRUE"
|
||||
ImproveFloatingPointConsistency="TRUE"
|
||||
FavorSizeOrSpeed="1"
|
||||
OptimizeForProcessor="2"
|
||||
OptimizeForWindowsApplication="TRUE"
|
||||
PreprocessorDefinitions="NDEBUG;_WINDOWS;MISSIONPACK;QAGAME;WIN32;_JK2;FINAL_BUILD"
|
||||
StringPooling="TRUE"
|
||||
RuntimeLibrary="4"
|
||||
BufferSecurityCheck="FALSE"
|
||||
EnableFunctionLevelLinking="TRUE"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderFile=".\../Final/JK2game/JK2_game.pch"
|
||||
AssemblerListingLocation=".\../Final/JK2game/"
|
||||
ObjectFile=".\../Final/JK2game/"
|
||||
ProgramDataBaseFileName=".\../Final/JK2game/"
|
||||
WarningLevel="4"
|
||||
SuppressStartupBanner="TRUE"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="winmm.lib"
|
||||
OutputFile="../Final/jampgamex86.dll"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ModuleDefinitionFile=".\JK2_game.def"
|
||||
ProgramDatabaseFile=""
|
||||
GenerateMapFile="TRUE"
|
||||
MapFileName=".\../Final/jampgamex86.map"
|
||||
MapLines="TRUE"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
SetChecksum="TRUE"
|
||||
BaseAddress="0x20000000"
|
||||
ImportLibrary=".\../Final/jampgamex86.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="NDebug"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\../Final/JK2_game.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDebug"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\../Debug"
|
||||
IntermediateDirectory=".\../Debug/JK2game"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
OptimizeForProcessor="2"
|
||||
PreprocessorDefinitions="_DEBUG;BUILDING_REF_GL;Debug;WIN32;_WINDOWS;MISSIONPACK;QAGAME;_JK2;JK2AWARDS"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderFile=".\../Debug/JK2game/JK2_game.pch"
|
||||
AssemblerListingLocation=".\../Debug/JK2game/"
|
||||
ObjectFile=".\../Debug/JK2game/"
|
||||
ProgramDataBaseFileName=".\../Debug/JK2game/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="winmm.lib"
|
||||
OutputFile="..\Debug\jampgamex86.dll"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ModuleDefinitionFile=".\JK2_game.def"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\../Debug/jampgamex86.pdb"
|
||||
GenerateMapFile="TRUE"
|
||||
MapFileName="../Debug/jampgamex86.map"
|
||||
SubSystem="2"
|
||||
BaseAddress="0x20000000"
|
||||
ImportLibrary=".\../Debug/jampgamex86.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_Debug"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\../Debug/JK2_game.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_Debug"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug(SH)|Win32"
|
||||
OutputDirectory="Debug(SH)"
|
||||
IntermediateDirectory="Debug(SH)"
|
||||
ConfigurationType="2"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="FALSE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
OptimizeForProcessor="2"
|
||||
PreprocessorDefinitions="_DEBUG;BUILDING_REF_GL;Debug;WIN32;_WINDOWS;MISSIONPACK;QAGAME;_JK2;JK2AWARDS"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
UsePrecompiledHeader="2"
|
||||
PrecompiledHeaderFile=".\../Debug/JK2game/JK2_game.pch"
|
||||
AssemblerListingLocation=".\../Debug/JK2game/"
|
||||
ObjectFile=".\../Debug/JK2game/"
|
||||
ProgramDataBaseFileName=".\../Debug/JK2game/"
|
||||
BrowseInformation="1"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="TRUE"
|
||||
DebugInformationFormat="4"/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalOptions="/MACHINE:I386"
|
||||
AdditionalDependencies="winmm.lib"
|
||||
OutputFile="..\Debug\jampgamex86.dll"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="TRUE"
|
||||
ModuleDefinitionFile=".\JK2_game.def"
|
||||
GenerateDebugInformation="TRUE"
|
||||
ProgramDatabaseFile=".\../Debug/jampgamex86.pdb"
|
||||
GenerateMapFile="TRUE"
|
||||
MapFileName=".\../Debug/jampgamex86.map"
|
||||
SubSystem="2"
|
||||
BaseAddress="0x20000000"
|
||||
ImportLibrary=".\../Debug/jampgamex86.lib"/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
PreprocessorDefinitions="_Debug"
|
||||
MkTypLibCompatible="TRUE"
|
||||
SuppressStartupBanner="TRUE"
|
||||
TargetEnvironment="1"
|
||||
TypeLibraryName=".\../Debug/JK2_game.tlb"/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_Debug"
|
||||
Culture="1033"/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90">
|
||||
<File
|
||||
RelativePath="AnimalNPC.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="FighterNPC.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Atst.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Default.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Droid.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_GalakMech.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Grenadier.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Howler.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_ImperialProbe.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Interrogator.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Jedi.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Mark1.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Mark2.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_MineMonster.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NPC_AI_Rancor.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Remote.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Seeker.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Sentry.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Sniper.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Stormtrooper.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_AI_Utils.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="NPC_AI_Wampa.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_behavior.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_combat.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_goal.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_misc.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_move.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_reactions.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_senses.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_sounds.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_spawn.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_stats.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NPC_utils.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="SpeederNPC.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="WalkerNPC.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ai_main.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ai_util.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ai_wpnav.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="bg_g2_utils.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_lib.c">
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Final|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Debug(SH)|Win32"
|
||||
ExcludedFromBuild="TRUE">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_misc.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_panimate.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_pmove.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_saber.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="bg_saberLoad.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="bg_saga.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_slidemove.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="bg_vehicleLoad.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_weapons.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_ICARUScb.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_active.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_arenas.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_bot.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_client.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_cmds.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_combat.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_exphysics.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_items.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_log.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_main.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_mem.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_misc.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_missile.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_mover.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_nav.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_navnew.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_object.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_saga.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_session.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_spawn.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_strap.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_svcmds.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_syscalls.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_target.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_team.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_timer.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_trigger.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="g_turret.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="g_turret_G2.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_utils.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="g_vehicleTurret.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="g_vehicles.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_weapon.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\q_math.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\q_shared.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="tri_coll_test.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\w_force.c">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\w_saber.c">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;fi;fd">
|
||||
<File
|
||||
RelativePath="..\ghoul2\G2.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\JK2_game.def">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ai.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ai_main.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\anims.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\cgame\animtable.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\b_local.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\b_public.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_aas.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_char.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_chat.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_gen.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_goal.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_move.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ai_weap.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\be_ea.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_lib.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_local.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_public.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_saga.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_strap.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="bg_vehicles.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\bg_weapons.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\botlib.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\cgame\cg_local.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\cgame\cg_public.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\chars.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\qcommon\disablewarnings.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_ICARUScb.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="g_headers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_local.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_nav.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_public.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\g_team.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\inv.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\match.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\ui\menudef.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\npc_headers.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\q_shared.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\say.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\surfaceflags.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\syn.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\qcommon\tags.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\teams.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\cgame\tr_types.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\w_saber.h">
|
||||
</File>
|
||||
<Filter
|
||||
Name="ICARUS Headers"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\icarus\Q3_Interface.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\icarus\interpreter.h">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="win32"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\win32\JK2game.rc">
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath=".\game.bat">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\game.q3asm">
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
2127
codemp/game/NPC.c
Normal file
2127
codemp/game/NPC.c
Normal file
File diff suppressed because it is too large
Load Diff
308
codemp/game/NPC_AI_Atst.c
Normal file
308
codemp/game/NPC_AI_Atst.c
Normal file
@@ -0,0 +1,308 @@
|
||||
#include "b_local.h"
|
||||
|
||||
#define MIN_MELEE_RANGE 640
|
||||
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
|
||||
|
||||
#define MIN_DISTANCE 128
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define TURN_OFF 0x00000100//G2SURFACEFLAG_NODESCENDANTS
|
||||
|
||||
#define LEFT_ARM_HEALTH 40
|
||||
#define RIGHT_ARM_HEALTH 40
|
||||
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ATST_Precache
|
||||
-------------------------
|
||||
*/
|
||||
#if 0
|
||||
void NPC_ATST_Precache(void)
|
||||
{
|
||||
G_SoundIndex( "sound/chars/atst/atst_damaged1" );
|
||||
G_SoundIndex( "sound/chars/atst/atst_damaged2" );
|
||||
|
||||
// RegisterItem( BG_FindItemForWeapon( WP_ATST_MAIN )); //precache the weapon
|
||||
//rwwFIXMEFIXME: add this weapon
|
||||
RegisterItem( BG_FindItemForWeapon( WP_BOWCASTER )); //precache the weapon
|
||||
RegisterItem( BG_FindItemForWeapon( WP_ROCKET_LAUNCHER )); //precache the weapon
|
||||
|
||||
G_EffectIndex( "env/med_explode2" );
|
||||
// G_EffectIndex( "smaller_chunks" );
|
||||
G_EffectIndex( "blaster/smoke_bolton" );
|
||||
G_EffectIndex( "explosions/droidexplosion1" );
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
#if 0
|
||||
static void ATST_PlayEffect( gentity_t *self, const int boltID, const char *fx )
|
||||
{
|
||||
if ( boltID >=0 && fx && fx[0] )
|
||||
{
|
||||
mdxaBone_t boltMatrix;
|
||||
vec3_t org, dir;
|
||||
|
||||
trap_G2API_GetBoltMatrix( self->ghoul2, 0,
|
||||
boltID,
|
||||
&boltMatrix, self->r.currentAngles, self->r.currentOrigin, level.time,
|
||||
NULL, self->modelScale );
|
||||
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, org );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir );
|
||||
|
||||
G_PlayEffectID( G_EffectIndex((char *)fx), org, dir );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
G_ATSTCheckPain
|
||||
|
||||
Called by NPC's and player in an ATST
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void G_ATSTCheckPain( gentity_t *self, gentity_t *other, int damage )
|
||||
{
|
||||
//int newBolt;
|
||||
//int hitLoc = gPainHitLoc;
|
||||
|
||||
if ( rand() & 1 )
|
||||
{
|
||||
G_SoundOnEnt( self, CHAN_LESS_ATTEN, "sound/chars/atst/atst_damaged1" );
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SoundOnEnt( self, CHAN_LESS_ATTEN, "sound/chars/atst/atst_damaged2" );
|
||||
}
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ATST_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_ATST_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
G_ATSTCheckPain( self, attacker, damage );
|
||||
NPC_Pain( self, attacker, damage );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ATST_Hunt
|
||||
-------------------------`
|
||||
*/
|
||||
/*
|
||||
void ATST_Hunt( qboolean visible, qboolean advance )
|
||||
{
|
||||
|
||||
if ( NPCInfo->goalEntity == NULL )
|
||||
{//hunt
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
}
|
||||
|
||||
NPCInfo->combatMove = qtrue;
|
||||
|
||||
NPC_MoveToGoal( qtrue );
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ATST_Ranged
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void ATST_Ranged( qboolean visible, qboolean advance, qboolean altAttack )
|
||||
{
|
||||
|
||||
if ( TIMER_Done( NPC, "atkDelay" ) && visible ) // Attack?
|
||||
{
|
||||
TIMER_Set( NPC, "atkDelay", Q_irand( 500, 3000 ) );
|
||||
|
||||
if (altAttack)
|
||||
{
|
||||
ucmd.buttons |= BUTTON_ATTACK|BUTTON_ALT_ATTACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ucmd.buttons |= BUTTON_ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
ATST_Hunt( visible, advance );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ATST_Attack
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void ATST_Attack( void )
|
||||
{
|
||||
qboolean altAttack=qfalse;
|
||||
int blasterTest,chargerTest,weapon;
|
||||
float distance;
|
||||
distance_e distRate;
|
||||
qboolean visible;
|
||||
qboolean advance;
|
||||
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )//!NPC->enemy )//
|
||||
{
|
||||
NPC->enemy = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
// Rate our distance to the target, and our visibilty
|
||||
distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
|
||||
visible = NPC_ClearLOS4( NPC->enemy );
|
||||
advance = (qboolean)(distance > MIN_DISTANCE_SQR);
|
||||
|
||||
// If we cannot see our target, move to see it
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
ATST_Hunt( visible, advance );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide what type of attack to do
|
||||
switch ( distRate )
|
||||
{
|
||||
case DIST_MELEE:
|
||||
// NPC_ChangeWeapon( WP_ATST_MAIN );
|
||||
break;
|
||||
|
||||
case DIST_LONG:
|
||||
|
||||
// NPC_ChangeWeapon( WP_ATST_SIDE );
|
||||
//rwwFIXMEFIXME: make atst weaps work.
|
||||
|
||||
// See if the side weapons are there
|
||||
blasterTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "head_light_blaster_cann" );
|
||||
chargerTest = trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "head_concussion_charger" );
|
||||
|
||||
// It has both side weapons
|
||||
if ( blasterTest != -1
|
||||
&& !(blasterTest&TURN_OFF)
|
||||
&& chargerTest != -1
|
||||
&& !(chargerTest&TURN_OFF))
|
||||
{
|
||||
weapon = Q_irand( 0, 1); // 0 is blaster, 1 is charger (ALT SIDE)
|
||||
|
||||
if (weapon) // Fire charger
|
||||
{
|
||||
altAttack = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
altAttack = qfalse;
|
||||
}
|
||||
|
||||
}
|
||||
else if (blasterTest != -1
|
||||
&& !(blasterTest & TURN_OFF)) // Blaster is on
|
||||
{
|
||||
altAttack = qfalse;
|
||||
}
|
||||
else if (chargerTest != -1
|
||||
&&!(chargerTest & TURN_OFF)) // Blaster is on
|
||||
{
|
||||
altAttack = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_ChangeWeapon( WP_NONE );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
ATST_Ranged( visible, advance,altAttack );
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ATST_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void ATST_Patrol( void )
|
||||
{
|
||||
if ( NPC_CheckPlayerTeamStealth() )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if (!NPC->enemy)
|
||||
{
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ATST_Idle
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void ATST_Idle( void )
|
||||
{
|
||||
|
||||
NPC_BSIdle();
|
||||
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_NORMAL );
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSDroid_Default
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void NPC_BSATST_Default( void )
|
||||
{
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
if( (NPCInfo->scriptFlags & SCF_CHASE_ENEMIES) )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
}
|
||||
ATST_Attack();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
ATST_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
ATST_Idle();
|
||||
}
|
||||
}
|
||||
*/
|
||||
957
codemp/game/NPC_AI_Default.c
Normal file
957
codemp/game/NPC_AI_Default.c
Normal file
@@ -0,0 +1,957 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
#include "../icarus/Q3_Interface.h"
|
||||
|
||||
//#include "anims.h"
|
||||
//extern int PM_AnimLength( int index, animNumber_t anim );
|
||||
//extern int PM_AnimLength( int index, animNumber_t anim );
|
||||
//#define MAX_IDLE_ANIMS 8
|
||||
|
||||
extern qboolean NPC_SomeoneLookingAtMe(gentity_t *ent);
|
||||
|
||||
/*
|
||||
void NPC_LostEnemyDecideChase(void)
|
||||
|
||||
We lost our enemy and want to drop him but see if we should chase him if we are in the proper bState
|
||||
*/
|
||||
|
||||
void NPC_LostEnemyDecideChase(void)
|
||||
{
|
||||
switch( NPCInfo->behaviorState )
|
||||
{
|
||||
case BS_HUNT_AND_KILL:
|
||||
//We were chasing him and lost him, so try to find him
|
||||
if ( NPC->enemy == NPCInfo->goalEntity && NPC->enemy->lastWaypoint != WAYPOINT_NONE )
|
||||
{//Remember his last valid Wp, then check it out
|
||||
//FIXME: Should we only do this if there's no other enemies or we've got LOCKED_ENEMY on?
|
||||
NPC_BSSearchStart( NPC->enemy->lastWaypoint, BS_SEARCH );
|
||||
}
|
||||
//If he's not our goalEntity, we're running somewhere else, so lose him
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
G_ClearEnemy( NPC );
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
NPC_StandIdle
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_StandIdle( void )
|
||||
{
|
||||
/*
|
||||
//Must be done with any other animations
|
||||
if ( NPC->client->ps.legsAnimTimer != 0 )
|
||||
return;
|
||||
|
||||
//Not ready to do another one
|
||||
if ( TIMER_Done( NPC, "idleAnim" ) == false )
|
||||
return;
|
||||
|
||||
int anim = NPC->client->ps.legsAnim;
|
||||
|
||||
if ( anim != BOTH_STAND1 && anim != BOTH_STAND2 )
|
||||
return;
|
||||
|
||||
//FIXME: Account for STAND1 or STAND2 here and set the base anim accordingly
|
||||
int baseSeq = ( anim == BOTH_STAND1 ) ? BOTH_STAND1_RANDOM1 : BOTH_STAND2_RANDOM1;
|
||||
|
||||
//Must have at least one random idle animation
|
||||
//NOTENOTE: This relies on proper ordering of animations, which SHOULD be okay
|
||||
if ( PM_HasAnimation( NPC, baseSeq ) == false )
|
||||
return;
|
||||
|
||||
int newIdle = Q_irand( 0, MAX_IDLE_ANIMS-1 );
|
||||
|
||||
//FIXME: Technically this could never complete.. but that's not really too likely
|
||||
while( 1 )
|
||||
{
|
||||
if ( PM_HasAnimation( NPC, baseSeq + newIdle ) )
|
||||
break;
|
||||
|
||||
newIdle = Q_irand( 0, MAX_IDLE_ANIMS );
|
||||
}
|
||||
|
||||
//Start that animation going
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, baseSeq + newIdle, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
|
||||
int newTime = PM_AnimLength( NPC->client->clientInfo.animFileIndex, (animNumber_t) (baseSeq + newIdle) );
|
||||
|
||||
//Don't do this again for a random amount of time
|
||||
TIMER_Set( NPC, "idleAnim", newTime + Q_irand( 2000, 10000 ) );
|
||||
*/
|
||||
}
|
||||
|
||||
qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck)
|
||||
{
|
||||
qboolean attack_ok = qfalse;
|
||||
qboolean duck_ok = qfalse;
|
||||
qboolean faced = qfalse;
|
||||
float attack_scale = 1.0;
|
||||
|
||||
//First see if we're hurt bad- if so, duck
|
||||
//FIXME: if even when ducked, we can shoot someone, we should.
|
||||
//Maybe is can be shot even when ducked, we should run away to the nearest cover?
|
||||
if ( canDuck )
|
||||
{
|
||||
if ( NPC->health < 20 )
|
||||
{
|
||||
// if( NPC->svFlags&SVF_HEALING || random() )
|
||||
if( random() )
|
||||
{
|
||||
duck_ok = qtrue;
|
||||
}
|
||||
}
|
||||
else if ( NPC->health < 40 )
|
||||
{
|
||||
// if ( NPC->svFlags&SVF_HEALING )
|
||||
// {//Medic is on the way, get down!
|
||||
// duck_ok = qtrue;
|
||||
// }
|
||||
// no more borg
|
||||
/// if ( NPC->client->playerTeam!= TEAM_BORG )
|
||||
// {//Borg don't care if they're about to die
|
||||
//attack_scale will be a max of .66
|
||||
// attack_scale = NPC->health/60;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//NPC_CheckEnemy( qtrue, qfalse, qtrue );
|
||||
|
||||
if ( !duck_ok )
|
||||
{//made this whole part a function call
|
||||
attack_ok = NPC_CheckCanAttack( attack_scale, qtrue );
|
||||
faced = qtrue;
|
||||
}
|
||||
|
||||
if ( canDuck && (duck_ok || (!attack_ok && client->ps.weaponTime <= 0)) && ucmd.upmove != -127 )
|
||||
{//if we didn't attack check to duck if we're not already
|
||||
if( !duck_ok )
|
||||
{
|
||||
if ( NPC->enemy->client )
|
||||
{
|
||||
if ( NPC->enemy->enemy == NPC )
|
||||
{
|
||||
if ( NPC->enemy->client->buttons & BUTTON_ATTACK )
|
||||
{//FIXME: determine if enemy fire angles would hit me or get close
|
||||
if ( NPC_CheckDefend( 1.0 ) )//FIXME: Check self-preservation? Health?
|
||||
{
|
||||
duck_ok = qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( duck_ok )
|
||||
{//duck and don't shoot
|
||||
attack_ok = qfalse;
|
||||
ucmd.upmove = -127;
|
||||
NPCInfo->duckDebounceTime = level.time + 1000;//duck for a full second
|
||||
}
|
||||
}
|
||||
|
||||
return faced;
|
||||
}
|
||||
|
||||
|
||||
void NPC_BSIdle( void )
|
||||
{
|
||||
//FIXME if there is no nav data, we need to do something else
|
||||
// if we're stuck, try to move around it
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
if ( ( ucmd.forwardmove == 0 ) && ( ucmd.rightmove == 0 ) && ( ucmd.upmove == 0 ) )
|
||||
{
|
||||
// NPC_StandIdle();
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
|
||||
void NPC_BSRun (void)
|
||||
{
|
||||
//FIXME if there is no nav data, we need to do something else
|
||||
// if we're stuck, try to move around it
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
void NPC_BSStandGuard (void)
|
||||
{
|
||||
//FIXME: Use Snapshot info
|
||||
if ( NPC->enemy == NULL )
|
||||
{//Possible to pick one up by being shot
|
||||
if( random() < 0.5 )
|
||||
{
|
||||
if(NPC->client->enemyTeam)
|
||||
{
|
||||
gentity_t *newenemy = NPC_PickEnemy(NPC, NPC->client->enemyTeam, (NPC->cantHitEnemyCounter < 10), (NPC->client->enemyTeam == NPCTEAM_PLAYER), qtrue);
|
||||
//only checks for vis if couldn't hit last enemy
|
||||
if(newenemy)
|
||||
{
|
||||
G_SetEnemy( NPC, newenemy );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->enemy != NULL )
|
||||
{
|
||||
if( NPCInfo->tempBehavior == BS_STAND_GUARD )
|
||||
{
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
|
||||
if( NPCInfo->behaviorState == BS_STAND_GUARD )
|
||||
{
|
||||
NPCInfo->behaviorState = BS_STAND_AND_SHOOT;
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSHuntAndKill
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_BSHuntAndKill( void )
|
||||
{
|
||||
qboolean turned = qfalse;
|
||||
vec3_t vec;
|
||||
float enemyDist;
|
||||
visibility_t oEVis;
|
||||
int curAnim;
|
||||
|
||||
NPC_CheckEnemy( NPCInfo->tempBehavior != BS_HUNT_AND_KILL, qfalse, qtrue );//don't find new enemy if this is tempbehav
|
||||
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
oEVis = enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|//CHECK_PVS|
|
||||
if(enemyVisibility > VIS_PVS)
|
||||
{
|
||||
if ( !NPC_EnemyTooFar( NPC->enemy, 0, qtrue ) )
|
||||
{//Enemy is close enough to shoot - FIXME: this next func does this also, but need to know here for info on whether ot not to turn later
|
||||
NPC_CheckCanAttack( 1.0, qfalse );
|
||||
turned = qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
curAnim = NPC->client->ps.legsAnim;
|
||||
if(curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 )
|
||||
{//Don't move toward enemy if we're in a full-body attack anim
|
||||
//FIXME, use IdealDistance to determin if we need to close distance
|
||||
VectorSubtract(NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec);
|
||||
enemyDist = VectorLength(vec);
|
||||
if( enemyDist > 48 && ((enemyDist*1.5)*(enemyDist*1.5) >= NPC_MaxDistSquaredForWeapon() ||
|
||||
oEVis != VIS_SHOOT ||
|
||||
//!(ucmd.buttons & BUTTON_ATTACK) ||
|
||||
enemyDist > IdealDistance(NPC)*3 ) )
|
||||
{//We should close in?
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
else if(enemyDist < IdealDistance(NPC))
|
||||
{//We should back off?
|
||||
//if(ucmd.buttons & BUTTON_ATTACK)
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 12;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
|
||||
ucmd.forwardmove *= -1;
|
||||
ucmd.rightmove *= -1;
|
||||
VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir );
|
||||
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
}//otherwise, stay where we are
|
||||
}
|
||||
}
|
||||
else
|
||||
{//ok, stand guard until we find an enemy
|
||||
if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
|
||||
{
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->tempBehavior = BS_STAND_GUARD;
|
||||
NPC_BSStandGuard();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!turned)
|
||||
{
|
||||
NPC_UpdateAngles(qtrue, qtrue);
|
||||
}
|
||||
}
|
||||
|
||||
void NPC_BSStandAndShoot (void)
|
||||
{
|
||||
//FIXME:
|
||||
//When our numbers outnumber enemies 3 to 1, or only one of them,
|
||||
//go into hunt and kill mode
|
||||
|
||||
//FIXME:
|
||||
//When they're all dead, go to some script or wander off to sickbay?
|
||||
|
||||
if(NPC->client->playerTeam && NPC->client->enemyTeam)
|
||||
{
|
||||
//FIXME: don't realize this right away- or else enemies show up and we're standing around
|
||||
/*
|
||||
if( teamNumbers[NPC->enemyTeam] == 0 )
|
||||
{//ok, stand guard until we find another enemy
|
||||
//reset our rush counter
|
||||
teamCounter[NPC->playerTeam] = 0;
|
||||
NPCInfo->tempBehavior = BS_STAND_GUARD;
|
||||
NPC_BSStandGuard();
|
||||
return;
|
||||
}*/
|
||||
/*
|
||||
//FIXME: whether to do this or not should be settable
|
||||
else if( NPC->playerTeam != TEAM_BORG )//Borg don't rush
|
||||
{
|
||||
//FIXME: In case reinforcements show up, we should wait a few seconds
|
||||
//and keep checking before rushing!
|
||||
//Also: what if not everyone on our team is going after playerTeam?
|
||||
//Also: our team count includes medics!
|
||||
if(NPC->health > 25)
|
||||
{//Can we rush the enemy?
|
||||
if(teamNumbers[NPC->enemyTeam] == 1 ||
|
||||
teamNumbers[NPC->playerTeam] >= teamNumbers[NPC->enemyTeam]*3)
|
||||
{//Only one of them or we outnumber 3 to 1
|
||||
if(teamStrength[NPC->playerTeam] >= 75 ||
|
||||
(teamStrength[NPC->playerTeam] >= 50 && teamStrength[NPC->playerTeam] > teamStrength[NPC->enemyTeam]))
|
||||
{//Our team is strong enough to rush
|
||||
teamCounter[NPC->playerTeam]++;
|
||||
if(teamNumbers[NPC->playerTeam] * 17 <= teamCounter[NPC->playerTeam])
|
||||
{//ok, we waited 1.7 think cycles on average and everyone is go, let's do it!
|
||||
//FIXME: Should we do this to everyone on our team?
|
||||
NPCInfo->behaviorState = BS_HUNT_AND_KILL;
|
||||
//FIXME: if the tide changes, we should retreat!
|
||||
//FIXME: when do we reset the counter?
|
||||
NPC_BSHuntAndKill ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else//Oops! Something's wrong, reset the counter to rush
|
||||
teamCounter[NPC->playerTeam] = 0;
|
||||
}
|
||||
else//Oops! Something's wrong, reset the counter to rush
|
||||
teamCounter[NPC->playerTeam] = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
NPC_CheckEnemy(qtrue, qfalse, qtrue);
|
||||
|
||||
if(NPCInfo->duckDebounceTime > level.time && NPC->client->ps.weapon != WP_SABER )
|
||||
{
|
||||
ucmd.upmove = -127;
|
||||
if(NPC->enemy)
|
||||
{
|
||||
NPC_CheckCanAttack(1.0, qtrue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(NPC->enemy)
|
||||
{
|
||||
if(!NPC_StandTrackAndShoot( NPC, qtrue ))
|
||||
{//That func didn't update our angles
|
||||
NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
|
||||
NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
|
||||
NPC_UpdateAngles(qtrue, qtrue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->desiredYaw = NPC->client->ps.viewangles[YAW];
|
||||
NPCInfo->desiredPitch = NPC->client->ps.viewangles[PITCH];
|
||||
NPC_UpdateAngles(qtrue, qtrue);
|
||||
// NPC_BSIdle();//only moves if we have a goal
|
||||
}
|
||||
}
|
||||
|
||||
void NPC_BSRunAndShoot (void)
|
||||
{
|
||||
/*if(NPC->playerTeam && NPC->enemyTeam)
|
||||
{
|
||||
//FIXME: don't realize this right away- or else enemies show up and we're standing around
|
||||
if( teamNumbers[NPC->enemyTeam] == 0 )
|
||||
{//ok, stand guard until we find another enemy
|
||||
//reset our rush counter
|
||||
teamCounter[NPC->playerTeam] = 0;
|
||||
NPCInfo->tempBehavior = BS_STAND_GUARD;
|
||||
NPC_BSStandGuard();
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
//NOTE: are we sure we want ALL run and shoot people to move this way?
|
||||
//Shouldn't it check to see if we have an enemy and our enemy is our goal?!
|
||||
//Moved that check into NPC_MoveToGoal
|
||||
//NPCInfo->combatMove = qtrue;
|
||||
|
||||
NPC_CheckEnemy( qtrue, qfalse, qtrue );
|
||||
|
||||
if ( NPCInfo->duckDebounceTime > level.time ) // && NPCInfo->hidingGoal )
|
||||
{
|
||||
ucmd.upmove = -127;
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
NPC_CheckCanAttack( 1.0, qfalse );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
int monitor = NPC->cantHitEnemyCounter;
|
||||
NPC_StandTrackAndShoot( NPC, qfalse );//(NPCInfo->hidingGoal != NULL) );
|
||||
|
||||
if ( !(ucmd.buttons & BUTTON_ATTACK) && ucmd.upmove >= 0 && NPC->cantHitEnemyCounter > monitor )
|
||||
{//not crouching and not firing
|
||||
vec3_t vec;
|
||||
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, vec );
|
||||
vec[2] = 0;
|
||||
if ( VectorLength( vec ) > 128 || NPC->cantHitEnemyCounter >= 10 )
|
||||
{//run at enemy if too far away
|
||||
//The cantHitEnemyCounter getting high has other repercussions
|
||||
//100 (10 seconds) will make you try to pick a new enemy...
|
||||
//But we're chasing, so we clamp it at 50 here
|
||||
if ( NPC->cantHitEnemyCounter > 60 )
|
||||
{
|
||||
NPC->cantHitEnemyCounter = 60;
|
||||
}
|
||||
|
||||
if ( NPC->cantHitEnemyCounter >= (NPCInfo->stats.aggression+1) * 10 )
|
||||
{
|
||||
NPC_LostEnemyDecideChase();
|
||||
}
|
||||
|
||||
//chase and face
|
||||
ucmd.angles[YAW] = 0;
|
||||
ucmd.angles[PITCH] = 0;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 12;
|
||||
//NAV_ClearLastRoute(NPC);
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPC_UpdateAngles(qtrue, qtrue);
|
||||
}
|
||||
else
|
||||
{
|
||||
//FIXME: this could happen if they're just on the other side
|
||||
//of a thin wall or something else blocking out shot. That
|
||||
//would make us just stand there and not go around it...
|
||||
//but maybe it's okay- might look like we're waiting for
|
||||
//him to come out...?
|
||||
//Current solution: runs around if cantHitEnemyCounter gets
|
||||
//to 10 (1 second).
|
||||
}
|
||||
}
|
||||
else
|
||||
{//Clear the can't hit enemy counter here
|
||||
NPC->cantHitEnemyCounter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPCInfo->tempBehavior == BS_HUNT_AND_KILL )
|
||||
{//lost him, go back to what we were doing before
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
// NPC_BSRun();//only moves if we have a goal
|
||||
}
|
||||
}
|
||||
|
||||
//Simply turn until facing desired angles
|
||||
void NPC_BSFace (void)
|
||||
{
|
||||
//FIXME: once you stop sending turning info, they reset to whatever their delta_angles was last????
|
||||
//Once this is over, it snaps back to what it was facing before- WHY???
|
||||
if( NPC_UpdateAngles ( qtrue, qtrue ) )
|
||||
{
|
||||
trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
|
||||
|
||||
NPCInfo->desiredYaw = client->ps.viewangles[YAW];
|
||||
NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
|
||||
|
||||
NPCInfo->aimTime = 0;//ok to turn normally now
|
||||
}
|
||||
}
|
||||
|
||||
void NPC_BSPointShoot (qboolean shoot)
|
||||
{//FIXME: doesn't check for clear shot...
|
||||
vec3_t muzzle, dir, angles, org;
|
||||
|
||||
if ( !NPC->enemy || !NPC->enemy->inuse || (NPC->enemy->NPC && NPC->enemy->health <= 0) )
|
||||
{//FIXME: should still keep shooting for a second or two after they actually die...
|
||||
trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
|
||||
goto finished;
|
||||
return;
|
||||
}
|
||||
|
||||
CalcEntitySpot(NPC, SPOT_WEAPON, muzzle);
|
||||
CalcEntitySpot(NPC->enemy, SPOT_HEAD, org);//Was spot_org
|
||||
//Head is a little high, so let's aim for the chest:
|
||||
if ( NPC->enemy->client )
|
||||
{
|
||||
org[2] -= 12;//NOTE: is this enough?
|
||||
}
|
||||
|
||||
VectorSubtract(org, muzzle, dir);
|
||||
vectoangles(dir, angles);
|
||||
|
||||
switch( NPC->client->ps.weapon )
|
||||
{
|
||||
case WP_NONE:
|
||||
// case WP_TRICORDER:
|
||||
case WP_STUN_BATON:
|
||||
case WP_SABER:
|
||||
//don't do any pitch change if not holding a firing weapon
|
||||
break;
|
||||
default:
|
||||
NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]);
|
||||
break;
|
||||
}
|
||||
|
||||
NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]);
|
||||
|
||||
if ( NPC_UpdateAngles ( qtrue, qtrue ) )
|
||||
{//FIXME: if angles clamped, this may never work!
|
||||
//NPCInfo->shotTime = NPC->attackDebounceTime = 0;
|
||||
|
||||
if ( shoot )
|
||||
{//FIXME: needs to hold this down if using a weapon that requires it, like phaser...
|
||||
ucmd.buttons |= BUTTON_ATTACK;
|
||||
}
|
||||
|
||||
//if ( !shoot || !(NPC->svFlags & SVF_LOCKEDENEMY) )
|
||||
if (1)
|
||||
{//If locked_enemy is on, dont complete until it is destroyed...
|
||||
trap_ICARUS_TaskIDComplete( NPC, TID_BSTATE );
|
||||
goto finished;
|
||||
}
|
||||
}
|
||||
//else if ( shoot && (NPC->svFlags & SVF_LOCKEDENEMY) )
|
||||
if (0)
|
||||
{//shooting them till their dead, not aiming right at them yet...
|
||||
/*
|
||||
qboolean movingTarget = qfalse;
|
||||
|
||||
if ( NPC->enemy->client )
|
||||
{
|
||||
if ( VectorLengthSquared( NPC->enemy->client->ps.velocity ) )
|
||||
{
|
||||
movingTarget = qtrue;
|
||||
}
|
||||
}
|
||||
else if ( VectorLengthSquared( NPC->enemy->s.pos.trDelta ) )
|
||||
{
|
||||
movingTarget = qtrue;
|
||||
}
|
||||
|
||||
if (movingTarget )
|
||||
*/
|
||||
{
|
||||
float dist = VectorLength( dir );
|
||||
float yawMiss, yawMissAllow = NPC->enemy->r.maxs[0];
|
||||
float pitchMiss, pitchMissAllow = (NPC->enemy->r.maxs[2] - NPC->enemy->r.mins[2])/2;
|
||||
|
||||
if ( yawMissAllow < 8.0f )
|
||||
{
|
||||
yawMissAllow = 8.0f;
|
||||
}
|
||||
|
||||
if ( pitchMissAllow < 8.0f )
|
||||
{
|
||||
pitchMissAllow = 8.0f;
|
||||
}
|
||||
|
||||
yawMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw ))) * dist;
|
||||
pitchMiss = tan(DEG2RAD(AngleDelta ( NPC->client->ps.viewangles[PITCH], NPCInfo->desiredPitch))) * dist;
|
||||
|
||||
if ( yawMissAllow >= yawMiss && pitchMissAllow > pitchMiss )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_ATTACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
finished:
|
||||
NPCInfo->desiredYaw = client->ps.viewangles[YAW];
|
||||
NPCInfo->desiredPitch = client->ps.viewangles[PITCH];
|
||||
|
||||
NPCInfo->aimTime = 0;//ok to turn normally now
|
||||
}
|
||||
|
||||
/*
|
||||
void NPC_BSMove(void)
|
||||
Move in a direction, face another
|
||||
*/
|
||||
void NPC_BSMove(void)
|
||||
{
|
||||
gentity_t *goal = NULL;
|
||||
|
||||
NPC_CheckEnemy(qtrue, qfalse, qtrue);
|
||||
if(NPC->enemy)
|
||||
{
|
||||
NPC_CheckCanAttack(1.0, qfalse);
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_UpdateAngles(qtrue, qtrue);
|
||||
}
|
||||
|
||||
goal = UpdateGoal();
|
||||
if(goal)
|
||||
{
|
||||
// NPCInfo->moveToGoalMod = 1.0;
|
||||
|
||||
NPC_SlideMoveToGoal();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void NPC_BSShoot(void)
|
||||
Move in a direction, face another
|
||||
*/
|
||||
|
||||
void NPC_BSShoot(void)
|
||||
{
|
||||
// NPC_BSMove();
|
||||
|
||||
enemyVisibility = VIS_SHOOT;
|
||||
|
||||
if ( client->ps.weaponstate != WEAPON_READY && client->ps.weaponstate != WEAPON_FIRING )
|
||||
{
|
||||
client->ps.weaponstate = WEAPON_READY;
|
||||
}
|
||||
|
||||
WeaponThink(qtrue);
|
||||
}
|
||||
|
||||
/*
|
||||
void NPC_BSPatrol( void )
|
||||
|
||||
Same as idle, but you look for enemies every "vigilance"
|
||||
using your angles, HFOV, VFOV and visrange, and listen for sounds within earshot...
|
||||
*/
|
||||
void NPC_BSPatrol( void )
|
||||
{
|
||||
//int alertEventNum;
|
||||
|
||||
if(level.time > NPCInfo->enemyCheckDebounceTime)
|
||||
{
|
||||
NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 1000);
|
||||
NPC_CheckEnemy(qtrue, qfalse, qtrue);
|
||||
if(NPC->enemy)
|
||||
{//FIXME: do anger script
|
||||
NPCInfo->behaviorState = BS_HUNT_AND_KILL;
|
||||
//NPC_AngerSound();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: Implement generic sound alerts
|
||||
/*
|
||||
alertEventNum = NPC_CheckAlertEvents( qtrue, qtrue );
|
||||
if( alertEventNum != -1 )
|
||||
{//If we heard something, see if we should check it out
|
||||
if ( NPC_CheckInvestigate( alertEventNum ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
NPCInfo->investigateSoundDebounceTime = 0;
|
||||
//FIXME if there is no nav data, we need to do something else
|
||||
// if we're stuck, try to move around it
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
|
||||
/*
|
||||
void NPC_BSDefault(void)
|
||||
uses various scriptflags to determine how an npc should behave
|
||||
*/
|
||||
extern void NPC_CheckGetNewWeapon( void );
|
||||
extern void NPC_BSST_Attack( void );
|
||||
|
||||
void NPC_BSDefault( void )
|
||||
{
|
||||
// vec3_t enemyDir;
|
||||
// float enemyDist;
|
||||
// float shootDist;
|
||||
// qboolean enemyFOV = qfalse;
|
||||
// qboolean enemyShotFOV = qfalse;
|
||||
// qboolean enemyPVS = qfalse;
|
||||
// vec3_t enemyHead;
|
||||
// vec3_t muzzle;
|
||||
// qboolean enemyLOS = qfalse;
|
||||
// qboolean enemyCS = qfalse;
|
||||
qboolean move = qtrue;
|
||||
// qboolean shoot = qfalse;
|
||||
|
||||
|
||||
if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
|
||||
{
|
||||
WeaponThink( qtrue );
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
|
||||
{//being forced to walk
|
||||
if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START )
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD );
|
||||
}
|
||||
}
|
||||
//look for a new enemy if don't have one and are allowed to look, validate current enemy if have one
|
||||
NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse, qtrue );
|
||||
if ( !NPC->enemy )
|
||||
{//still don't have an enemy
|
||||
if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
|
||||
{//check for alert events
|
||||
//FIXME: Check Alert events, see if we should investigate or just look at it
|
||||
int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED );
|
||||
|
||||
//There is an event to look at
|
||||
if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
|
||||
{//heard/saw something
|
||||
if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) )
|
||||
{//was a big event
|
||||
if ( level.alertEvents[alertEvent].owner && level.alertEvents[alertEvent].owner->client && level.alertEvents[alertEvent].owner->health >= 0 && level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
|
||||
{//an enemy
|
||||
G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//FIXME: investigate lesser events
|
||||
}
|
||||
}
|
||||
//FIXME: also check our allies' condition?
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) )
|
||||
{
|
||||
// just use the stormtrooper attack AI...
|
||||
NPC_CheckGetNewWeapon();
|
||||
if ( NPC->client->leader
|
||||
&& NPCInfo->goalEntity == NPC->client->leader
|
||||
&& !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
|
||||
{
|
||||
NPC_ClearGoal();
|
||||
}
|
||||
NPC_BSST_Attack();
|
||||
return;
|
||||
/*
|
||||
//have an enemy
|
||||
//FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest?
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir );
|
||||
enemyDist = VectorNormalize( enemyDir );
|
||||
enemyDist *= enemyDist;
|
||||
shootDist = NPC_MaxDistSquaredForWeapon();
|
||||
|
||||
enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov );
|
||||
enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 );
|
||||
enemyPVS = gi.inPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin );
|
||||
|
||||
if ( enemyPVS )
|
||||
{//in the pvs
|
||||
trace_t tr;
|
||||
|
||||
CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead );
|
||||
enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f );
|
||||
CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
|
||||
enemyLOS = NPC_ClearLOS( muzzle, enemyHead );
|
||||
|
||||
gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT );
|
||||
enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue );
|
||||
}
|
||||
else
|
||||
{//skip thr 2 traces since they would have to fail
|
||||
enemyLOS = qfalse;
|
||||
enemyCS = qfalse;
|
||||
}
|
||||
|
||||
if ( enemyCS && enemyShotFOV )
|
||||
{//can hit enemy if we want
|
||||
NPC->cantHitEnemyCounter = 0;
|
||||
}
|
||||
else
|
||||
{//can't hit
|
||||
NPC->cantHitEnemyCounter++;
|
||||
}
|
||||
|
||||
if ( enemyCS && enemyShotFOV && enemyDist < shootDist )
|
||||
{//can shoot
|
||||
shoot = qtrue;
|
||||
if ( NPCInfo->goalEntity == NPC->enemy )
|
||||
{//my goal is my enemy and I have a clear shot, no need to chase right now
|
||||
move = qfalse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{//don't shoot yet, keep chasing
|
||||
shoot = qfalse;
|
||||
move = qtrue;
|
||||
}
|
||||
|
||||
//shoot decision
|
||||
if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) )
|
||||
{//try to shoot
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
if ( shoot )
|
||||
{
|
||||
if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
|
||||
{
|
||||
WeaponThink( qtrue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//chase decision
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{//go after him
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
//FIXME: don't need to chase when have a clear shot and in range?
|
||||
if ( !enemyCS && NPC->cantHitEnemyCounter > 60 )
|
||||
{//haven't been able to shoot enemy for about 6 seconds, need to do something
|
||||
//FIXME: combat points? Just chase?
|
||||
if ( enemyPVS )
|
||||
{//in my PVS, just pick a combat point
|
||||
//FIXME: implement
|
||||
}
|
||||
else
|
||||
{//just chase him
|
||||
}
|
||||
}
|
||||
//FIXME: in normal behavior, should we use combat Points? Do we care? Is anyone actually going to ever use this AI?
|
||||
}
|
||||
else if ( NPC->cantHitEnemyCounter > 60 )
|
||||
{//pick a new one
|
||||
NPC_CheckEnemy( qtrue, qfalse, qtrue );
|
||||
}
|
||||
|
||||
if ( enemyPVS && enemyLOS )//&& !enemyShotFOV )
|
||||
{//have a clear LOS to him//, but not looking at him
|
||||
//Find the desired angles
|
||||
vec3_t angles;
|
||||
|
||||
GetAnglesForDirection( muzzle, enemyHead, angles );
|
||||
|
||||
NPCInfo->desiredYaw = AngleNormalize180( angles[YAW] );
|
||||
NPCInfo->desiredPitch = AngleNormalize180( angles[PITCH] );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if ( UpdateGoal() )
|
||||
{//have a goal
|
||||
if ( !NPC->enemy
|
||||
&& NPC->client->leader
|
||||
&& NPCInfo->goalEntity == NPC->client->leader
|
||||
&& !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) )
|
||||
{
|
||||
NPC_BSFollowLeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
//set angles
|
||||
if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy )
|
||||
{//face direction of movement, NOTE: default behavior when not chasing enemy
|
||||
NPCInfo->combatMove = qfalse;
|
||||
}
|
||||
else
|
||||
{//face goal.. FIXME: what if have a navgoal but want to face enemy while moving? Will this do that?
|
||||
vec3_t dir, angles;
|
||||
|
||||
NPCInfo->combatMove = qfalse;
|
||||
|
||||
VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir );
|
||||
vectoangles( dir, angles );
|
||||
NPCInfo->desiredYaw = angles[YAW];
|
||||
if ( NPCInfo->goalEntity == NPC->enemy )
|
||||
{
|
||||
NPCInfo->desiredPitch = angles[PITCH];
|
||||
}
|
||||
}
|
||||
|
||||
//set movement
|
||||
//override default walk/run behavior
|
||||
//NOTE: redundant, done in NPC_ApplyScriptFlags
|
||||
if ( NPCInfo->scriptFlags & SCF_RUNNING )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_WALKING )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
else if ( NPCInfo->goalEntity == NPC->enemy )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
}
|
||||
else
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH )
|
||||
{//being forced to walk
|
||||
//if ( g_crosshairEntNum != NPC->s.number )
|
||||
if (!NPC_SomeoneLookingAtMe(NPC))
|
||||
{//don't walk if player isn't aiming at me
|
||||
move = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if ( move )
|
||||
{
|
||||
//move toward goal
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( !NPC->enemy && NPC->client->leader )
|
||||
{
|
||||
NPC_BSFollowLeader();
|
||||
}
|
||||
|
||||
//update angles
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
621
codemp/game/NPC_AI_Droid.c
Normal file
621
codemp/game/NPC_AI_Droid.c
Normal file
@@ -0,0 +1,621 @@
|
||||
#include "b_local.h"
|
||||
|
||||
//static void R5D2_LookAround( void );
|
||||
float NPC_GetPainChance( gentity_t *self, int damage );
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
|
||||
#define TURN_OFF 0x00000100
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
LSTATE_BACKINGUP,
|
||||
LSTATE_SPINNING,
|
||||
LSTATE_PAIN,
|
||||
LSTATE_DROP
|
||||
};
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
R2D2_PartsMove
|
||||
-------------------------
|
||||
*/
|
||||
void R2D2_PartsMove(void)
|
||||
{
|
||||
// Front 'eye' lense
|
||||
if ( TIMER_Done(NPC,"eyeDelay") )
|
||||
{
|
||||
NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);
|
||||
|
||||
NPC->pos1[0]+=Q_irand( -20, 20 ); // Roll
|
||||
NPC->pos1[1]=Q_irand( -20, 20 );
|
||||
NPC->pos1[2]=Q_irand( -20, 20 );
|
||||
|
||||
/*
|
||||
if (NPC->genericBone1)
|
||||
{
|
||||
gi.G2API_SetBoneAnglesIndex( &NPC->ghoul2[NPC->playerModel], NPC->genericBone1, NPC->pos1, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL );
|
||||
}
|
||||
*/
|
||||
NPC_SetBoneAngles(NPC, "f_eye", NPC->pos1);
|
||||
|
||||
|
||||
TIMER_Set( NPC, "eyeDelay", Q_irand( 100, 1000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSDroid_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Droid_Idle( void )
|
||||
{
|
||||
// VectorCopy( NPCInfo->investigateGoal, lookPos );
|
||||
|
||||
// NPC_FacePosition( lookPos );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
R2D2_TurnAnims
|
||||
-------------------------
|
||||
*/
|
||||
void R2D2_TurnAnims ( void )
|
||||
{
|
||||
float turndelta;
|
||||
int anim;
|
||||
|
||||
turndelta = AngleDelta(NPC->r.currentAngles[YAW], NPCInfo->desiredYaw);
|
||||
|
||||
if ((fabs(turndelta) > 20) && ((NPC->client->NPC_class == CLASS_R2D2) || (NPC->client->NPC_class == CLASS_R5D2)))
|
||||
{
|
||||
anim = NPC->client->ps.legsAnim;
|
||||
if (turndelta<0)
|
||||
{
|
||||
if (anim != BOTH_TURN_LEFT1)
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURN_LEFT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anim != BOTH_TURN_RIGHT1)
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURN_RIGHT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Droid_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void Droid_Patrol( void )
|
||||
{
|
||||
|
||||
NPC->pos1[1] = AngleNormalize360( NPC->pos1[1]);
|
||||
|
||||
if ( NPC->client && NPC->client->NPC_class != CLASS_GONK )
|
||||
{
|
||||
if (NPC->client->NPC_class != CLASS_R5D2)
|
||||
{ //he doesn't have an eye.
|
||||
R2D2_PartsMove(); // Get his eye moving.
|
||||
}
|
||||
R2D2_TurnAnims();
|
||||
}
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
|
||||
if( NPC->client && NPC->client->NPC_class == CLASS_MOUSE )
|
||||
{
|
||||
NPCInfo->desiredYaw += sin(level.time*.5) * 25; // Weaves side to side a little
|
||||
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/mouse/misc/mousego%d.wav", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
else if( NPC->client && NPC->client->NPC_class == CLASS_R2D2 )
|
||||
{
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/r2d2/misc/r2d2talk0%d.wav", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
else if( NPC->client && NPC->client->NPC_class == CLASS_R5D2 )
|
||||
{
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/r5d2/misc/r5talk%d.wav", Q_irand(1, 4)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
if( NPC->client && NPC->client->NPC_class == CLASS_GONK )
|
||||
{
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/gonk/misc/gonktalk%d.wav", Q_irand(1, 2)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// R5D2_LookAround();
|
||||
// }
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Droid_Run
|
||||
-------------------------
|
||||
*/
|
||||
void Droid_Run( void )
|
||||
{
|
||||
R2D2_PartsMove();
|
||||
|
||||
if ( NPCInfo->localState == LSTATE_BACKINGUP )
|
||||
{
|
||||
ucmd.forwardmove = -127;
|
||||
NPCInfo->desiredYaw += 5;
|
||||
|
||||
NPCInfo->localState = LSTATE_NONE; // So he doesn't constantly backup.
|
||||
}
|
||||
else
|
||||
{
|
||||
ucmd.forwardmove = 64;
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
if (NPC_MoveToGoal( qfalse ))
|
||||
{
|
||||
NPCInfo->desiredYaw += sin(level.time*.5) * 5; // Weaves side to side a little
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
void Droid_Spin( void )
|
||||
-------------------------
|
||||
*/
|
||||
void Droid_Spin( void )
|
||||
{
|
||||
vec3_t dir = {0,0,1};
|
||||
|
||||
R2D2_TurnAnims();
|
||||
|
||||
|
||||
// Head is gone, spin and spark
|
||||
if ( NPC->client->NPC_class == CLASS_R5D2
|
||||
|| NPC->client->NPC_class == CLASS_R2D2 )
|
||||
{
|
||||
// No head?
|
||||
if (trap_G2API_GetSurfaceRenderStatus( NPC->ghoul2, 0, "head" )>0)
|
||||
{
|
||||
if (TIMER_Done(NPC,"smoke") && !TIMER_Done(NPC,"droidsmoketotal"))
|
||||
{
|
||||
TIMER_Set( NPC, "smoke", 100);
|
||||
G_PlayEffectID( G_EffectIndex("volumetric/droid_smoke") , NPC->r.currentOrigin,dir);
|
||||
}
|
||||
|
||||
if (TIMER_Done(NPC,"droidspark"))
|
||||
{
|
||||
TIMER_Set( NPC, "droidspark", Q_irand(100,500));
|
||||
G_PlayEffectID( G_EffectIndex("sparks/spark"), NPC->r.currentOrigin,dir);
|
||||
}
|
||||
|
||||
ucmd.forwardmove = Q_irand( -64, 64);
|
||||
|
||||
if (TIMER_Done(NPC,"roam"))
|
||||
{
|
||||
TIMER_Set( NPC, "roam", Q_irand( 250, 1000 ) );
|
||||
NPCInfo->desiredYaw = Q_irand( 0, 360 ); // Go in random directions
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TIMER_Done(NPC,"roam"))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TIMER_Done(NPC,"roam"))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->desiredYaw = AngleNormalize360(NPCInfo->desiredYaw + 40); // Spin around
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSDroid_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Droid_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
gentity_t *other = attacker;
|
||||
int anim;
|
||||
int mod = gPainMOD;
|
||||
float pain_chance;
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
if ( self->client->NPC_class == CLASS_R5D2 )
|
||||
{
|
||||
pain_chance = NPC_GetPainChance( self, damage );
|
||||
|
||||
// Put it in pain
|
||||
if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this
|
||||
{
|
||||
// Health is between 0-30 or was hit by a DEMP2 so pop his head
|
||||
if ( !self->s.m_iVehicleNum
|
||||
&& ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) )
|
||||
{
|
||||
if (!(self->spawnflags & 2)) // Doesn't have to ALWAYSDIE
|
||||
{
|
||||
if ((self->NPC->localState != LSTATE_SPINNING) &&
|
||||
(!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" )))
|
||||
{
|
||||
NPC_SetSurfaceOnOff( self, "head", TURN_OFF );
|
||||
|
||||
if ( self->client->ps.m_iVehicleNum )
|
||||
{
|
||||
vec3_t up;
|
||||
AngleVectors( self->r.currentAngles, NULL, NULL, up );
|
||||
G_PlayEffectID( G_EffectIndex("chunks/r5d2head_veh"), self->r.currentOrigin, up );
|
||||
}
|
||||
else
|
||||
{
|
||||
G_PlayEffectID( G_EffectIndex("small_chunks") , self->r.currentOrigin, vec3_origin );
|
||||
G_PlayEffectID( G_EffectIndex("chunks/r5d2head"), self->r.currentOrigin, vec3_origin );
|
||||
}
|
||||
|
||||
//self->s.powerups |= ( 1 << PW_SHOCKED );
|
||||
//self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
|
||||
self->client->ps.electrifyTime = level.time + 3000;
|
||||
|
||||
TIMER_Set( self, "droidsmoketotal", 5000);
|
||||
TIMER_Set( self, "droidspark", 100);
|
||||
self->NPC->localState = LSTATE_SPINNING;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just give him normal pain for a little while
|
||||
else
|
||||
{
|
||||
anim = self->client->ps.legsAnim;
|
||||
|
||||
if ( anim == BOTH_STAND2 ) // On two legs?
|
||||
{
|
||||
anim = BOTH_PAIN1;
|
||||
}
|
||||
else // On three legs
|
||||
{
|
||||
anim = BOTH_PAIN2;
|
||||
}
|
||||
|
||||
NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
|
||||
// Spin around in pain
|
||||
self->NPC->localState = LSTATE_SPINNING;
|
||||
TIMER_Set( self, "roam", Q_irand(1000,2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (self->client->NPC_class == CLASS_MOUSE)
|
||||
{
|
||||
if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
|
||||
{
|
||||
self->NPC->localState = LSTATE_SPINNING;
|
||||
//self->s.powerups |= ( 1 << PW_SHOCKED );
|
||||
//self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
|
||||
self->client->ps.electrifyTime = level.time + 3000;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->NPC->localState = LSTATE_BACKINGUP;
|
||||
}
|
||||
|
||||
self->NPC->scriptFlags &= ~SCF_LOOK_FOR_ENEMIES;
|
||||
}
|
||||
else if ((self->client->NPC_class == CLASS_R2D2))
|
||||
{
|
||||
|
||||
pain_chance = NPC_GetPainChance( self, damage );
|
||||
|
||||
if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this
|
||||
{
|
||||
// Health is between 0-30 or was hit by a DEMP2 so pop his head
|
||||
if ( !self->s.m_iVehicleNum
|
||||
&& ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) )
|
||||
{
|
||||
if (!(self->spawnflags & 2)) // Doesn't have to ALWAYSDIE
|
||||
{
|
||||
if ((self->NPC->localState != LSTATE_SPINNING) &&
|
||||
(!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "head" )))
|
||||
{
|
||||
NPC_SetSurfaceOnOff( self, "head", TURN_OFF );
|
||||
|
||||
if ( self->client->ps.m_iVehicleNum )
|
||||
{
|
||||
vec3_t up;
|
||||
AngleVectors( self->r.currentAngles, NULL, NULL, up );
|
||||
G_PlayEffectID( G_EffectIndex("chunks/r2d2head_veh"), self->r.currentOrigin, up );
|
||||
}
|
||||
else
|
||||
{
|
||||
G_PlayEffectID( G_EffectIndex("small_chunks") , self->r.currentOrigin, vec3_origin );
|
||||
G_PlayEffectID( G_EffectIndex("chunks/r2d2head"), self->r.currentOrigin, vec3_origin );
|
||||
}
|
||||
|
||||
//self->s.powerups |= ( 1 << PW_SHOCKED );
|
||||
//self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
|
||||
self->client->ps.electrifyTime = level.time + 3000;
|
||||
|
||||
TIMER_Set( self, "droidsmoketotal", 5000);
|
||||
TIMER_Set( self, "droidspark", 100);
|
||||
self->NPC->localState = LSTATE_SPINNING;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just give him normal pain for a little while
|
||||
else
|
||||
{
|
||||
anim = self->client->ps.legsAnim;
|
||||
|
||||
if ( anim == BOTH_STAND2 ) // On two legs?
|
||||
{
|
||||
anim = BOTH_PAIN1;
|
||||
}
|
||||
else // On three legs
|
||||
{
|
||||
anim = BOTH_PAIN2;
|
||||
}
|
||||
|
||||
NPC_SetAnim( self, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
|
||||
// Spin around in pain
|
||||
self->NPC->localState = LSTATE_SPINNING;
|
||||
TIMER_Set( self, "roam", Q_irand(1000,2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( self->client->NPC_class == CLASS_INTERROGATOR && ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) && other )
|
||||
{
|
||||
vec3_t dir;
|
||||
|
||||
VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
|
||||
self->client->ps.velocity[2] -= 127;
|
||||
}
|
||||
|
||||
NPC_Pain( self, attacker, damage);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Droid_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void Droid_Pain(void)
|
||||
{
|
||||
if (TIMER_Done(NPC,"droidpain")) //He's done jumping around
|
||||
{
|
||||
NPCInfo->localState = LSTATE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Mouse_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Mouse_Precache( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < 4; i++)
|
||||
{
|
||||
G_SoundIndex( va( "sound/chars/mouse/misc/mousego%d.wav", i ) );
|
||||
}
|
||||
|
||||
G_EffectIndex( "env/small_explode" );
|
||||
G_SoundIndex( "sound/chars/mouse/misc/death1" );
|
||||
G_SoundIndex( "sound/chars/mouse/misc/mouse_lp" );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_R5D2_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_R5D2_Precache(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 1; i < 5; i++)
|
||||
{
|
||||
G_SoundIndex( va( "sound/chars/r5d2/misc/r5talk%d.wav", i ) );
|
||||
}
|
||||
//G_SoundIndex( "sound/chars/r5d2/misc/falling1.wav" );
|
||||
G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" ); // ??
|
||||
G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" );
|
||||
G_EffectIndex( "env/med_explode");
|
||||
G_EffectIndex( "volumetric/droid_smoke" );
|
||||
G_EffectIndex("sparks/spark");
|
||||
G_EffectIndex( "chunks/r5d2head");
|
||||
G_EffectIndex( "chunks/r5d2head_veh");
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_R2D2_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_R2D2_Precache(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 1; i < 4; i++)
|
||||
{
|
||||
G_SoundIndex( va( "sound/chars/r2d2/misc/r2d2talk0%d.wav", i ) );
|
||||
}
|
||||
//G_SoundIndex( "sound/chars/r2d2/misc/falling1.wav" );
|
||||
G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" ); // ??
|
||||
G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" );
|
||||
G_EffectIndex( "env/med_explode");
|
||||
G_EffectIndex( "volumetric/droid_smoke" );
|
||||
G_EffectIndex("sparks/spark");
|
||||
G_EffectIndex( "chunks/r2d2head");
|
||||
G_EffectIndex( "chunks/r2d2head_veh");
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Gonk_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Gonk_Precache( void )
|
||||
{
|
||||
G_SoundIndex("sound/chars/gonk/misc/gonktalk1.wav");
|
||||
G_SoundIndex("sound/chars/gonk/misc/gonktalk2.wav");
|
||||
|
||||
G_SoundIndex("sound/chars/gonk/misc/death1.wav");
|
||||
G_SoundIndex("sound/chars/gonk/misc/death2.wav");
|
||||
G_SoundIndex("sound/chars/gonk/misc/death3.wav");
|
||||
|
||||
G_EffectIndex( "env/med_explode");
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Protocol_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Protocol_Precache( void )
|
||||
{
|
||||
G_SoundIndex( "sound/chars/mark2/misc/mark2_explo" );
|
||||
G_EffectIndex( "env/med_explode");
|
||||
}
|
||||
|
||||
/*
|
||||
static void R5D2_OffsetLook( float offset, vec3_t out )
|
||||
{
|
||||
vec3_t angles, forward, temp;
|
||||
|
||||
GetAnglesForDirection( NPC->r.currentOrigin, NPCInfo->investigateGoal, angles );
|
||||
angles[YAW] += offset;
|
||||
AngleVectors( angles, forward, NULL, NULL );
|
||||
VectorMA( NPC->r.currentOrigin, 64, forward, out );
|
||||
|
||||
CalcEntitySpot( NPC, SPOT_HEAD, temp );
|
||||
out[2] = temp[2];
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
R5D2_LookAround
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
static void R5D2_LookAround( void )
|
||||
{
|
||||
vec3_t lookPos;
|
||||
float perc = (float) ( level.time - NPCInfo->pauseTime ) / (float) NPCInfo->investigateDebounceTime;
|
||||
|
||||
//Keep looking at the spot
|
||||
if ( perc < 0.25 )
|
||||
{
|
||||
VectorCopy( NPCInfo->investigateGoal, lookPos );
|
||||
}
|
||||
else if ( perc < 0.5f ) //Look up but straight ahead
|
||||
{
|
||||
R5D2_OffsetLook( 0.0f, lookPos );
|
||||
}
|
||||
else if ( perc < 0.75f ) //Look right
|
||||
{
|
||||
R5D2_OffsetLook( 45.0f, lookPos );
|
||||
}
|
||||
else //Look left
|
||||
{
|
||||
R5D2_OffsetLook( -45.0f, lookPos );
|
||||
}
|
||||
|
||||
NPC_FacePosition( lookPos );
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSDroid_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSDroid_Default( void )
|
||||
{
|
||||
|
||||
if ( NPCInfo->localState == LSTATE_SPINNING )
|
||||
{
|
||||
Droid_Spin();
|
||||
}
|
||||
else if ( NPCInfo->localState == LSTATE_PAIN )
|
||||
{
|
||||
Droid_Pain();
|
||||
}
|
||||
else if ( NPCInfo->localState == LSTATE_DROP )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
ucmd.upmove = crandom() * 64;
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
Droid_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Droid_Run();
|
||||
}
|
||||
}
|
||||
679
codemp/game/NPC_AI_Grenadier.c
Normal file
679
codemp/game/NPC_AI_Grenadier.c
Normal file
@@ -0,0 +1,679 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
#include "anims.h"
|
||||
//#include "g_navigator.h"
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern qboolean BG_SabersOff( playerState_t *ps );
|
||||
#include "../namespace_end.h"
|
||||
|
||||
extern void CG_DrawAlert( vec3_t origin, float rating );
|
||||
extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
extern void NPC_TempLookTarget( gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime );
|
||||
extern qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask );
|
||||
extern void NPC_AimAdjust( int change );
|
||||
extern qboolean FlyingCreature( gentity_t *ent );
|
||||
|
||||
#define MAX_VIEW_DIST 1024
|
||||
#define MAX_VIEW_SPEED 250
|
||||
#define MAX_LIGHT_INTENSITY 255
|
||||
#define MIN_LIGHT_THRESHOLD 0.1
|
||||
|
||||
#define DISTANCE_SCALE 0.25f
|
||||
#define DISTANCE_THRESHOLD 0.075f
|
||||
#define SPEED_SCALE 0.25f
|
||||
#define FOV_SCALE 0.5f
|
||||
#define LIGHT_SCALE 0.25f
|
||||
|
||||
#define REALIZE_THRESHOLD 0.6f
|
||||
#define CAUTIOUS_THRESHOLD ( REALIZE_THRESHOLD * 0.75 )
|
||||
|
||||
qboolean NPC_CheckPlayerTeamStealth( void );
|
||||
|
||||
static qboolean enemyLOS3;
|
||||
static qboolean enemyCS3;
|
||||
static qboolean faceEnemy3;
|
||||
static qboolean move3;
|
||||
static qboolean shoot3;
|
||||
static float enemyDist3;
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
LSTATE_UNDERFIRE,
|
||||
LSTATE_INVESTIGATE,
|
||||
};
|
||||
|
||||
void Grenadier_ClearTimers( gentity_t *ent )
|
||||
{
|
||||
TIMER_Set( ent, "chatter", 0 );
|
||||
TIMER_Set( ent, "duck", 0 );
|
||||
TIMER_Set( ent, "stand", 0 );
|
||||
TIMER_Set( ent, "shuffleTime", 0 );
|
||||
TIMER_Set( ent, "sleepTime", 0 );
|
||||
TIMER_Set( ent, "enemyLastVisible", 0 );
|
||||
TIMER_Set( ent, "roamTime", 0 );
|
||||
TIMER_Set( ent, "hideTime", 0 );
|
||||
TIMER_Set( ent, "attackDelay", 0 ); //FIXME: Slant for difficulty levels
|
||||
TIMER_Set( ent, "stick", 0 );
|
||||
TIMER_Set( ent, "scoutTime", 0 );
|
||||
TIMER_Set( ent, "flee", 0 );
|
||||
}
|
||||
|
||||
void NPC_Grenadier_PlayConfusionSound( gentity_t *self )
|
||||
{//FIXME: make this a custom sound in sound set
|
||||
if ( self->health > 0 )
|
||||
{
|
||||
G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
|
||||
}
|
||||
//reset him to be totally unaware again
|
||||
TIMER_Set( self, "enemyLastVisible", 0 );
|
||||
TIMER_Set( self, "flee", 0 );
|
||||
self->NPC->squadState = SQUAD_IDLE;
|
||||
self->NPC->tempBehavior = BS_DEFAULT;
|
||||
|
||||
//self->NPC->behaviorState = BS_PATROL;
|
||||
G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
|
||||
|
||||
self->NPC->investigateCount = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ST_Pain
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_Grenadier_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
self->NPC->localState = LSTATE_UNDERFIRE;
|
||||
|
||||
TIMER_Set( self, "duck", -1 );
|
||||
TIMER_Set( self, "stand", 2000 );
|
||||
|
||||
NPC_Pain( self, attacker, damage );
|
||||
|
||||
if ( !damage && self->health > 0 )
|
||||
{//FIXME: better way to know I was pushed
|
||||
G_AddVoiceEvent( self, Q_irand(EV_PUSHED1, EV_PUSHED3), 2000 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_HoldPosition
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Grenadier_HoldPosition( void )
|
||||
{
|
||||
NPC_FreeCombatPoint( NPCInfo->combatPoint, qtrue );
|
||||
NPCInfo->goalEntity = NULL;
|
||||
|
||||
/*if ( TIMER_Done( NPC, "stand" ) )
|
||||
{//FIXME: what if can't shoot from this pos?
|
||||
TIMER_Set( NPC, "duck", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_Move
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static qboolean Grenadier_Move( void )
|
||||
{
|
||||
qboolean moved;
|
||||
navInfo_t info;
|
||||
|
||||
NPCInfo->combatMove = qtrue;//always move straight toward our goal
|
||||
moved = NPC_MoveToGoal( qtrue );
|
||||
|
||||
//Get the move info
|
||||
NAV_GetLastMove( &info );
|
||||
|
||||
//FIXME: if we bump into another one of our guys and can't get around him, just stop!
|
||||
//If we hit our target, then stop and fire!
|
||||
if ( info.flags & NIF_COLLISION )
|
||||
{
|
||||
if ( info.blocker == NPC->enemy )
|
||||
{
|
||||
Grenadier_HoldPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//If our move failed, then reset
|
||||
if ( moved == qfalse )
|
||||
{//couldn't get to enemy
|
||||
if ( (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) && NPC->client->ps.weapon == WP_THERMAL && NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy )
|
||||
{//we were running after enemy
|
||||
//Try to find a combat point that can hit the enemy
|
||||
int cpFlags = (CP_CLEAR|CP_HAS_ROUTE);
|
||||
int cp;
|
||||
|
||||
if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST )
|
||||
{
|
||||
cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST);
|
||||
cpFlags |= CP_NEAREST;
|
||||
}
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->r.currentOrigin, cpFlags, 32, -1 );
|
||||
if ( cp == -1 && !(NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) )
|
||||
{//okay, try one by the enemy
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->enemy->r.currentOrigin, CP_CLEAR|CP_HAS_ROUTE|CP_HORZ_DIST_COLL, 32, -1 );
|
||||
}
|
||||
//NOTE: there may be a perfectly valid one, just not one within CP_COLLECT_RADIUS of either me or him...
|
||||
if ( cp != -1 )
|
||||
{//found a combat point that has a clear shot to enemy
|
||||
NPC_SetCombatPoint( cp );
|
||||
NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL );
|
||||
return moved;
|
||||
}
|
||||
}
|
||||
//just hang here
|
||||
Grenadier_HoldPosition();
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSGrenadier_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_BSGrenadier_Patrol( void )
|
||||
{//FIXME: pick up on bodies of dead buddies?
|
||||
if ( NPCInfo->confusionTime < level.time )
|
||||
{
|
||||
//Look for any enemies
|
||||
if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
if ( NPC_CheckPlayerTeamStealth() )
|
||||
{
|
||||
//NPCInfo->behaviorState = BS_HUNT_AND_KILL;//should be automatic now
|
||||
//NPC_AngerSound();
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
|
||||
{
|
||||
//Is there danger nearby
|
||||
int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS );
|
||||
if ( NPC_CheckForDanger( alertEvent ) )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{//check for other alert events
|
||||
//There is an event to look at
|
||||
if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
|
||||
{
|
||||
NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID;
|
||||
if ( level.alertEvents[alertEvent].level == AEL_DISCOVERED )
|
||||
{
|
||||
if ( level.alertEvents[alertEvent].owner &&
|
||||
level.alertEvents[alertEvent].owner->client &&
|
||||
level.alertEvents[alertEvent].owner->health >= 0 &&
|
||||
level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
|
||||
{//an enemy
|
||||
G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
|
||||
//NPCInfo->enemyLastSeenTime = level.time;
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//FIXME: get more suspicious over time?
|
||||
//Save the position for movement (if necessary)
|
||||
VectorCopy( level.alertEvents[alertEvent].position, NPCInfo->investigateGoal );
|
||||
NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 );
|
||||
if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS )
|
||||
{//suspicious looks longer
|
||||
NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->investigateDebounceTime > level.time )
|
||||
{//FIXME: walk over to it, maybe? Not if not chase enemies
|
||||
//NOTE: stops walking or doing anything else below
|
||||
vec3_t dir, angles;
|
||||
float o_yaw, o_pitch;
|
||||
|
||||
VectorSubtract( NPCInfo->investigateGoal, NPC->client->renderInfo.eyePoint, dir );
|
||||
vectoangles( dir, angles );
|
||||
|
||||
o_yaw = NPCInfo->desiredYaw;
|
||||
o_pitch = NPCInfo->desiredPitch;
|
||||
NPCInfo->desiredYaw = angles[YAW];
|
||||
NPCInfo->desiredPitch = angles[PITCH];
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
NPCInfo->desiredYaw = o_yaw;
|
||||
NPCInfo->desiredPitch = o_pitch;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSGrenadier_Idle
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void NPC_BSGrenadier_Idle( void )
|
||||
{
|
||||
//FIXME: check for other alert events?
|
||||
|
||||
//Is there danger nearby?
|
||||
if ( NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
TIMER_Set( NPC, "roamTime", 2000 + Q_irand( 1000, 2000 ) );
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
*/
|
||||
/*
|
||||
-------------------------
|
||||
ST_CheckMoveState
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Grenadier_CheckMoveState( void )
|
||||
{
|
||||
//See if we're a scout
|
||||
if ( !(NPCInfo->scriptFlags & SCF_CHASE_ENEMIES) )//behaviorState == BS_STAND_AND_SHOOT )
|
||||
{
|
||||
if ( NPCInfo->goalEntity == NPC->enemy )
|
||||
{
|
||||
move3 = qfalse;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//See if we're running away
|
||||
else if ( NPCInfo->squadState == SQUAD_RETREAT )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "flee" ) )
|
||||
{
|
||||
NPCInfo->squadState = SQUAD_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
faceEnemy3 = qfalse;
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if ( NPCInfo->squadState == SQUAD_IDLE )
|
||||
{
|
||||
if ( !NPCInfo->goalEntity )
|
||||
{
|
||||
move3 = qfalse;
|
||||
return;
|
||||
}
|
||||
//Should keep moving toward player when we're out of range... right?
|
||||
}
|
||||
*/
|
||||
|
||||
//See if we're moving towards a goal, not the enemy
|
||||
if ( ( NPCInfo->goalEntity != NPC->enemy ) && ( NPCInfo->goalEntity != NULL ) )
|
||||
{
|
||||
//Did we make it?
|
||||
if ( NAV_HitNavGoal( NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPCInfo->goalEntity->r.currentOrigin, 16, FlyingCreature( NPC ) ) ||
|
||||
( NPCInfo->squadState == SQUAD_SCOUT && enemyLOS3 && enemyDist3 <= 10000 ) )
|
||||
{
|
||||
int newSquadState = SQUAD_STAND_AND_SHOOT;
|
||||
//we got where we wanted to go, set timers based on why we were running
|
||||
switch ( NPCInfo->squadState )
|
||||
{
|
||||
case SQUAD_RETREAT://was running away
|
||||
TIMER_Set( NPC, "duck", (NPC->client->pers.maxHealth - NPC->health) * 100 );
|
||||
TIMER_Set( NPC, "hideTime", Q_irand( 3000, 7000 ) );
|
||||
newSquadState = SQUAD_COVER;
|
||||
break;
|
||||
case SQUAD_TRANSITION://was heading for a combat point
|
||||
TIMER_Set( NPC, "hideTime", Q_irand( 2000, 4000 ) );
|
||||
break;
|
||||
case SQUAD_SCOUT://was running after player
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
NPC_ReachedGoal();
|
||||
//don't attack right away
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 250, 500 ) ); //FIXME: Slant for difficulty levels
|
||||
//don't do something else just yet
|
||||
TIMER_Set( NPC, "roamTime", Q_irand( 1000, 4000 ) );
|
||||
//stop fleeing
|
||||
if ( NPCInfo->squadState == SQUAD_RETREAT )
|
||||
{
|
||||
TIMER_Set( NPC, "flee", -level.time );
|
||||
NPCInfo->squadState = SQUAD_IDLE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//keep going, hold of roamTimer until we get there
|
||||
TIMER_Set( NPC, "roamTime", Q_irand( 4000, 8000 ) );
|
||||
}
|
||||
|
||||
if ( !NPCInfo->goalEntity )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_CheckFireState
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Grenadier_CheckFireState( void )
|
||||
{
|
||||
if ( enemyCS3 )
|
||||
{//if have a clear shot, always try
|
||||
return;
|
||||
}
|
||||
|
||||
if ( NPCInfo->squadState == SQUAD_RETREAT || NPCInfo->squadState == SQUAD_TRANSITION || NPCInfo->squadState == SQUAD_SCOUT )
|
||||
{//runners never try to fire at the last pos
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) )
|
||||
{//if moving at all, don't do this
|
||||
return;
|
||||
}
|
||||
|
||||
//continue to fire on their last position
|
||||
/*
|
||||
if ( !Q_irand( 0, 1 ) && NPCInfo->enemyLastSeenTime && level.time - NPCInfo->enemyLastSeenTime < 4000 )
|
||||
{
|
||||
//Fire on the last known position
|
||||
vec3_t muzzle, dir, angles;
|
||||
|
||||
CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
|
||||
VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir );
|
||||
|
||||
VectorNormalize( dir );
|
||||
|
||||
vectoangles( dir, angles );
|
||||
|
||||
NPCInfo->desiredYaw = angles[YAW];
|
||||
NPCInfo->desiredPitch = angles[PITCH];
|
||||
//FIXME: they always throw toward enemy, so this will be very odd...
|
||||
shoot3 = qtrue;
|
||||
faceEnemy3 = qfalse;
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
qboolean Grenadier_EvaluateShot( int hit )
|
||||
{
|
||||
if ( !NPC->enemy )
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( hit == NPC->enemy->s.number || (&g_entities[hit] != NULL && (g_entities[hit].r.svFlags&SVF_GLASS_BRUSH)) )
|
||||
{//can hit enemy or will hit glass, so shoot anyway
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSGrenadier_Attack
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_BSGrenadier_Attack( void )
|
||||
{
|
||||
//Don't do anything if we're hurt
|
||||
if ( NPC->painDebounceTime > level.time )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
//NPC_CheckEnemy( qtrue, qfalse );
|
||||
//If we don't have an enemy, just idle
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )//!NPC->enemy )//
|
||||
{
|
||||
NPC->enemy = NULL;
|
||||
NPC_BSGrenadier_Patrol();//FIXME: or patrol?
|
||||
return;
|
||||
}
|
||||
|
||||
if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
|
||||
{//going to run
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !NPC->enemy )
|
||||
{//WTF? somehow we lost our enemy?
|
||||
NPC_BSGrenadier_Patrol();//FIXME: or patrol?
|
||||
return;
|
||||
}
|
||||
|
||||
enemyLOS3 = enemyCS3 = qfalse;
|
||||
move3 = qtrue;
|
||||
faceEnemy3 = qfalse;
|
||||
shoot3 = qfalse;
|
||||
enemyDist3 = DistanceSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin );
|
||||
|
||||
//See if we should switch to melee attack
|
||||
if ( enemyDist3 < 16384 //128
|
||||
&& (!NPC->enemy->client
|
||||
|| NPC->enemy->client->ps.weapon != WP_SABER
|
||||
|| BG_SabersOff( &NPC->enemy->client->ps )
|
||||
)
|
||||
)
|
||||
{//enemy is close and not using saber
|
||||
if ( NPC->client->ps.weapon == WP_THERMAL )
|
||||
{//grenadier
|
||||
trace_t trace;
|
||||
trap_Trace ( &trace, NPC->r.currentOrigin, NPC->enemy->r.mins, NPC->enemy->r.maxs, NPC->enemy->r.currentOrigin, NPC->s.number, NPC->enemy->clipmask );
|
||||
if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->enemy->s.number ) )
|
||||
{//I can get right to him
|
||||
//reset fire-timing variables
|
||||
NPC_ChangeWeapon( WP_STUN_BATON );
|
||||
if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//NPCInfo->behaviorState == BS_STAND_AND_SHOOT )
|
||||
{//FIXME: should we be overriding scriptFlags?
|
||||
NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( enemyDist3 > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && !NPC->enemy->client->ps.saberHolstered) )//256
|
||||
{//enemy is far or using saber
|
||||
if ( NPC->client->ps.weapon == WP_STUN_BATON && (NPC->client->ps.stats[STAT_WEAPONS]&(1<<WP_THERMAL)) )
|
||||
{//fisticuffs, make switch to thermal if have it
|
||||
//reset fire-timing variables
|
||||
NPC_ChangeWeapon( WP_THERMAL );
|
||||
}
|
||||
}
|
||||
|
||||
//can we see our target?
|
||||
if ( NPC_ClearLOS4( NPC->enemy ) )
|
||||
{
|
||||
NPCInfo->enemyLastSeenTime = level.time;
|
||||
enemyLOS3 = qtrue;
|
||||
|
||||
if ( NPC->client->ps.weapon == WP_STUN_BATON )
|
||||
{
|
||||
if ( enemyDist3 <= 4096 && InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 90, 45 ) )//within 64 & infront
|
||||
{
|
||||
VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
|
||||
enemyCS3 = qtrue;
|
||||
}
|
||||
}
|
||||
else if ( InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 45, 90 ) )
|
||||
{//in front of me
|
||||
//can we shoot our target?
|
||||
//FIXME: how accurate/necessary is this check?
|
||||
int hit = NPC_ShotEntity( NPC->enemy, NULL );
|
||||
gentity_t *hitEnt = &g_entities[hit];
|
||||
if ( hit == NPC->enemy->s.number
|
||||
|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) )
|
||||
{
|
||||
float enemyHorzDist;
|
||||
|
||||
VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
|
||||
enemyHorzDist = DistanceHorizontalSquared( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin );
|
||||
if ( enemyHorzDist < 1048576 )
|
||||
{//within 1024
|
||||
enemyCS3 = qtrue;
|
||||
NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
|
||||
}
|
||||
/*
|
||||
else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
|
||||
{
|
||||
NPCInfo->enemyLastSeenTime = level.time;
|
||||
faceEnemy3 = qtrue;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( enemyLOS3 )
|
||||
{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
|
||||
faceEnemy3 = qtrue;
|
||||
}
|
||||
|
||||
if ( enemyCS3 )
|
||||
{
|
||||
shoot3 = qtrue;
|
||||
if ( NPC->client->ps.weapon == WP_THERMAL )
|
||||
{//don't chase and throw
|
||||
move3 = qfalse;
|
||||
}
|
||||
else if ( NPC->client->ps.weapon == WP_STUN_BATON && enemyDist3 < (NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+16)*(NPC->r.maxs[0]+NPC->enemy->r.maxs[0]+16) )
|
||||
{//close enough
|
||||
move3 = qfalse;
|
||||
}
|
||||
}//this should make him chase enemy when out of range...?
|
||||
|
||||
//Check for movement to take care of
|
||||
Grenadier_CheckMoveState();
|
||||
|
||||
//See if we should override shooting decision with any special considerations
|
||||
Grenadier_CheckFireState();
|
||||
|
||||
if ( move3 )
|
||||
{//move toward goal
|
||||
if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist3 > 10000 ) )//100 squared
|
||||
{
|
||||
move3 = Grenadier_Move();
|
||||
}
|
||||
else
|
||||
{
|
||||
move3 = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !move3 )
|
||||
{
|
||||
if ( !TIMER_Done( NPC, "duck" ) )
|
||||
{
|
||||
ucmd.upmove = -127;
|
||||
}
|
||||
//FIXME: what about leaning?
|
||||
}
|
||||
else
|
||||
{//stop ducking!
|
||||
TIMER_Set( NPC, "duck", -1 );
|
||||
}
|
||||
|
||||
if ( !faceEnemy3 )
|
||||
{//we want to face in the dir we're running
|
||||
if ( move3 )
|
||||
{//don't run away and shoot
|
||||
NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
|
||||
NPCInfo->desiredPitch = 0;
|
||||
shoot3 = qfalse;
|
||||
}
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
else// if ( faceEnemy3 )
|
||||
{//face the enemy
|
||||
NPC_FaceEnemy(qtrue);
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags&SCF_DONT_FIRE )
|
||||
{
|
||||
shoot3 = qfalse;
|
||||
}
|
||||
|
||||
//FIXME: don't shoot right away!
|
||||
if ( shoot3 )
|
||||
{//try to shoot if it's time
|
||||
if ( TIMER_Done( NPC, "attackDelay" ) )
|
||||
{
|
||||
if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
|
||||
{
|
||||
WeaponThink( qtrue );
|
||||
TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NPC_BSGrenadier_Default( void )
|
||||
{
|
||||
if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
|
||||
{
|
||||
WeaponThink( qtrue );
|
||||
}
|
||||
|
||||
if( !NPC->enemy )
|
||||
{//don't have an enemy, look for one
|
||||
NPC_BSGrenadier_Patrol();
|
||||
}
|
||||
else//if ( NPC->enemy )
|
||||
{//have an enemy
|
||||
NPC_BSGrenadier_Attack();
|
||||
}
|
||||
}
|
||||
218
codemp/game/NPC_AI_Howler.c
Normal file
218
codemp/game/NPC_AI_Howler.c
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "b_local.h"
|
||||
|
||||
// These define the working combat range for these suckers
|
||||
#define MIN_DISTANCE 54
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define MAX_DISTANCE 128
|
||||
#define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
|
||||
|
||||
#define LSTATE_CLEAR 0
|
||||
#define LSTATE_WAITING 1
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Howler_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Howler_Precache( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Howler_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Howler_Idle( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Howler_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void Howler_Patrol( void )
|
||||
{
|
||||
vec3_t dif;
|
||||
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done( NPC, "patrolTime" ))
|
||||
{
|
||||
TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
|
||||
}
|
||||
}
|
||||
|
||||
//rwwFIXMEFIXME: Care about all clients, not just client 0
|
||||
VectorSubtract( g_entities[0].r.currentOrigin, NPC->r.currentOrigin, dif );
|
||||
|
||||
if ( VectorLengthSquared( dif ) < 256 * 256 )
|
||||
{
|
||||
G_SetEnemy( NPC, &g_entities[0] );
|
||||
}
|
||||
|
||||
if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
|
||||
{
|
||||
Howler_Idle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Howler_Move
|
||||
-------------------------
|
||||
*/
|
||||
void Howler_Move( qboolean visible )
|
||||
{
|
||||
if ( NPCInfo->localState != LSTATE_WAITING )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
void Howler_TryDamage( gentity_t *enemy, int damage )
|
||||
{
|
||||
vec3_t end, dir;
|
||||
trace_t tr;
|
||||
|
||||
if ( !enemy )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
|
||||
VectorMA( NPC->r.currentOrigin, MIN_DISTANCE, dir, end );
|
||||
|
||||
// Should probably trace from the mouth, but, ah well.
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
|
||||
|
||||
if ( tr.entityNum != ENTITYNUM_WORLD )
|
||||
{
|
||||
G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
void Howler_Attack( void )
|
||||
{
|
||||
if ( !TIMER_Exists( NPC, "attacking" ))
|
||||
{
|
||||
// Going to do ATTACK1
|
||||
TIMER_Set( NPC, "attacking", 1700 + random() * 200 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
TIMER_Set( NPC, "attack_dmg", 200 ); // level two damage
|
||||
}
|
||||
|
||||
// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
|
||||
if ( TIMER_Done2( NPC, "attack_dmg", qtrue ))
|
||||
{
|
||||
Howler_TryDamage( NPC->enemy, 5 );
|
||||
}
|
||||
|
||||
// Just using this to remove the attacking flag at the right time
|
||||
TIMER_Done2( NPC, "attacking", qtrue );
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
void Howler_Combat( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean advance;
|
||||
|
||||
// If we cannot see our target or we have somewhere to go, then do that
|
||||
if ( !NPC_ClearLOS4( NPC->enemy ) || UpdateGoal( ))
|
||||
{
|
||||
NPCInfo->combatMove = qtrue;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
|
||||
NPC_MoveToGoal( qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
distance = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse );
|
||||
|
||||
if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
|
||||
{
|
||||
if ( TIMER_Done2( NPC, "takingPain", qtrue ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Howler_Move( 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Howler_Attack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Howler_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Howler_Pain( gentity_t *self, gentity_t *attacker, int damage )
|
||||
{
|
||||
if ( damage >= 10 )
|
||||
{
|
||||
TIMER_Remove( self, "attacking" );
|
||||
TIMER_Set( self, "takingPain", 2900 );
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
if ( self->NPC )
|
||||
{
|
||||
self->NPC->localState = LSTATE_WAITING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSHowler_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSHowler_Default( void )
|
||||
{
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
Howler_Combat();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
Howler_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Howler_Idle();
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
609
codemp/game/NPC_AI_ImperialProbe.c
Normal file
609
codemp/game/NPC_AI_ImperialProbe.c
Normal file
@@ -0,0 +1,609 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
gitem_t *BG_FindItemForAmmo( ammo_t ammo );
|
||||
#include "../namespace_end.h"
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
LSTATE_BACKINGUP,
|
||||
LSTATE_SPINNING,
|
||||
LSTATE_PAIN,
|
||||
LSTATE_DROP
|
||||
};
|
||||
|
||||
void ImperialProbe_Idle( void );
|
||||
|
||||
void NPC_Probe_Precache(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 1; i < 4; i++)
|
||||
{
|
||||
G_SoundIndex( va( "sound/chars/probe/misc/probetalk%d", i ) );
|
||||
}
|
||||
G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
|
||||
G_SoundIndex("sound/chars/probe/misc/anger1");
|
||||
G_SoundIndex("sound/chars/probe/misc/fire");
|
||||
|
||||
G_EffectIndex( "chunks/probehead" );
|
||||
G_EffectIndex( "env/med_explode2" );
|
||||
G_EffectIndex( "explosions/probeexplosion1");
|
||||
G_EffectIndex( "bryar/muzzle_flash" );
|
||||
|
||||
RegisterItem( BG_FindItemForAmmo( AMMO_BLASTER ));
|
||||
RegisterItem( BG_FindItemForWeapon( WP_BRYAR_PISTOL ) );
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
Hunter_MaintainHeight
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
#define VELOCITY_DECAY 0.85f
|
||||
|
||||
void ImperialProbe_MaintainHeight( void )
|
||||
{
|
||||
float dif;
|
||||
// vec3_t endPos;
|
||||
// trace_t trace;
|
||||
|
||||
// Update our angles regardless
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
// If we have an enemy, we should try to hover at about enemy eye level
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
// Find the height difference
|
||||
dif = NPC->enemy->r.currentOrigin[2] - NPC->r.currentOrigin[2];
|
||||
|
||||
// cap to prevent dramatic height shifts
|
||||
if ( fabs( dif ) > 8 )
|
||||
{
|
||||
if ( fabs( dif ) > 16 )
|
||||
{
|
||||
dif = ( dif < 0 ? -16 : 16 );
|
||||
}
|
||||
|
||||
NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gentity_t *goal = NULL;
|
||||
|
||||
if ( NPCInfo->goalEntity ) // Is there a goal?
|
||||
{
|
||||
goal = NPCInfo->goalEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
goal = NPCInfo->lastGoalEntity;
|
||||
}
|
||||
if ( goal )
|
||||
{
|
||||
dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
|
||||
|
||||
if ( fabs( dif ) > 24 )
|
||||
{
|
||||
ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply friction
|
||||
else if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Stay at a given height until we take on an enemy
|
||||
/* VectorSet( endPos, NPC->r.currentOrigin[0], NPC->r.currentOrigin[1], NPC->r.currentOrigin[2] - 512 );
|
||||
trap_Trace( &trace, NPC->r.currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
|
||||
|
||||
if ( trace.fraction != 1.0f )
|
||||
{
|
||||
float length = ( trace.fraction * 512 );
|
||||
|
||||
if ( length < 80 )
|
||||
{
|
||||
ucmd.upmove = 32;
|
||||
}
|
||||
else if ( length > 120 )
|
||||
{
|
||||
ucmd.upmove = -32;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
// Apply friction
|
||||
if ( NPC->client->ps.velocity[0] )
|
||||
{
|
||||
NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->client->ps.velocity[1] )
|
||||
{
|
||||
NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_Strafe
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
#define HUNTER_STRAFE_VEL 256
|
||||
#define HUNTER_STRAFE_DIS 200
|
||||
#define HUNTER_UPWARD_PUSH 32
|
||||
|
||||
void ImperialProbe_Strafe( void )
|
||||
{
|
||||
int dir;
|
||||
vec3_t end, right;
|
||||
trace_t tr;
|
||||
|
||||
AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
|
||||
|
||||
// Pick a random strafe direction, then check to see if doing a strafe would be
|
||||
// reasonable valid
|
||||
dir = ( rand() & 1 ) ? -1 : 1;
|
||||
VectorMA( NPC->r.currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
|
||||
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
|
||||
|
||||
// Close enough
|
||||
if ( tr.fraction > 0.9f )
|
||||
{
|
||||
VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
|
||||
|
||||
// Add a slight upward push
|
||||
NPC->client->ps.velocity[2] += HUNTER_UPWARD_PUSH;
|
||||
|
||||
// Set the strafe start time so we can do a controlled roll
|
||||
//NPC->fx_time = level.time;
|
||||
NPCInfo->standTime = level.time + 3000 + random() * 500;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_Hunt
|
||||
-------------------------`
|
||||
*/
|
||||
|
||||
#define HUNTER_FORWARD_BASE_SPEED 10
|
||||
#define HUNTER_FORWARD_MULTIPLIER 5
|
||||
|
||||
void ImperialProbe_Hunt( qboolean visible, qboolean advance )
|
||||
{
|
||||
float distance, speed;
|
||||
vec3_t forward;
|
||||
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
|
||||
//If we're not supposed to stand still, pursue the player
|
||||
if ( NPCInfo->standTime < level.time )
|
||||
{
|
||||
// Only strafe when we can see the player
|
||||
if ( visible )
|
||||
{
|
||||
ImperialProbe_Strafe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If we don't want to advance, stop here
|
||||
if ( advance == qfalse )
|
||||
return;
|
||||
|
||||
//Only try and navigate if the player is visible
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
// Move towards our goal
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 12;
|
||||
|
||||
//Get our direction from the navigator if we can't see our target
|
||||
if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
|
||||
distance = VectorNormalize( forward );
|
||||
}
|
||||
|
||||
speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill.integer;
|
||||
VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_FireBlaster
|
||||
-------------------------
|
||||
*/
|
||||
void ImperialProbe_FireBlaster(void)
|
||||
{
|
||||
vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1;
|
||||
static vec3_t forward, vright, up;
|
||||
static vec3_t muzzle;
|
||||
int genBolt1;
|
||||
gentity_t *missile;
|
||||
mdxaBone_t boltMatrix;
|
||||
|
||||
genBolt1 = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash");
|
||||
|
||||
//FIXME: use {0, NPC->client->ps.legsYaw, 0}
|
||||
trap_G2API_GetBoltMatrix( NPC->ghoul2, 0,
|
||||
genBolt1,
|
||||
&boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
|
||||
NULL, NPC->modelScale );
|
||||
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle1 );
|
||||
|
||||
G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle1, vec3_origin );
|
||||
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/probe/misc/fire" ));
|
||||
|
||||
if (NPC->health)
|
||||
{
|
||||
CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 );
|
||||
enemy_org1[0]+= Q_irand(0,10);
|
||||
enemy_org1[1]+= Q_irand(0,10);
|
||||
VectorSubtract (enemy_org1, muzzle1, delta1);
|
||||
vectoangles ( delta1, angleToEnemy1 );
|
||||
AngleVectors (angleToEnemy1, forward, vright, up);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors (NPC->r.currentAngles, forward, vright, up);
|
||||
}
|
||||
|
||||
missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC, qfalse );
|
||||
|
||||
missile->classname = "bryar_proj";
|
||||
missile->s.weapon = WP_BRYAR_PISTOL;
|
||||
|
||||
if ( g_spskill.integer <= 1 )
|
||||
{
|
||||
missile->damage = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
missile->damage = 10;
|
||||
}
|
||||
|
||||
|
||||
missile->dflags = DAMAGE_DEATH_KNOCKBACK;
|
||||
missile->methodOfDeath = MOD_UNKNOWN;
|
||||
missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_Ranged
|
||||
-------------------------
|
||||
*/
|
||||
void ImperialProbe_Ranged( qboolean visible, qboolean advance )
|
||||
{
|
||||
int delay_min,delay_max;
|
||||
|
||||
if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
|
||||
{
|
||||
|
||||
if ( g_spskill.integer == 0 )
|
||||
{
|
||||
delay_min = 500;
|
||||
delay_max = 3000;
|
||||
}
|
||||
else if ( g_spskill.integer > 1 )
|
||||
{
|
||||
delay_min = 500;
|
||||
delay_max = 2000;
|
||||
}
|
||||
else
|
||||
{
|
||||
delay_min = 300;
|
||||
delay_max = 1500;
|
||||
}
|
||||
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
|
||||
ImperialProbe_FireBlaster();
|
||||
// ucmd.buttons |= BUTTON_ATTACK;
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
ImperialProbe_Hunt( visible, advance );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_AttackDecision
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
#define MIN_MELEE_RANGE 320
|
||||
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
|
||||
|
||||
#define MIN_DISTANCE 128
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
void ImperialProbe_AttackDecision( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean visible;
|
||||
qboolean advance;
|
||||
|
||||
// Always keep a good height off the ground
|
||||
ImperialProbe_MaintainHeight();
|
||||
|
||||
//randomly talk
|
||||
if ( TIMER_Done(NPC,"patrolNoise") )
|
||||
{
|
||||
if (TIMER_Done(NPC,"angerNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have an enemy, just idle
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )
|
||||
{
|
||||
ImperialProbe_Idle();
|
||||
return;
|
||||
}
|
||||
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL);
|
||||
|
||||
// Rate our distance to the target, and our visibilty
|
||||
distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
visible = NPC_ClearLOS4( NPC->enemy );
|
||||
advance = (qboolean)(distance > MIN_DISTANCE_SQR);
|
||||
|
||||
// If we cannot see our target, move to see it
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
ImperialProbe_Hunt( visible, advance );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
// Decide what type of attack to do
|
||||
ImperialProbe_Ranged( visible, advance );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSDroid_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Probe_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
float pain_chance;
|
||||
gentity_t *other = attacker;
|
||||
int mod = gPainMOD;
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) // demp2 always messes them up real good
|
||||
{
|
||||
vec3_t endPos;
|
||||
trace_t trace;
|
||||
|
||||
VectorSet( endPos, self->r.currentOrigin[0], self->r.currentOrigin[1], self->r.currentOrigin[2] - 128 );
|
||||
trap_Trace( &trace, self->r.currentOrigin, NULL, NULL, endPos, self->s.number, MASK_SOLID );
|
||||
|
||||
if ( trace.fraction == 1.0f || mod == MOD_DEMP2 ) // demp2 always does this
|
||||
{
|
||||
/*
|
||||
if (self->client->clientInfo.headModel != 0)
|
||||
{
|
||||
vec3_t origin;
|
||||
|
||||
VectorCopy(self->r.currentOrigin,origin);
|
||||
origin[2] +=50;
|
||||
// G_PlayEffect( "small_chunks", origin );
|
||||
G_PlayEffect( "chunks/probehead", origin );
|
||||
G_PlayEffect( "env/med_explode2", origin );
|
||||
self->client->clientInfo.headModel = 0;
|
||||
self->client->moveType = MT_RUNJUMP;
|
||||
self->client->ps.gravity = g_gravity->value*.1;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) && other )
|
||||
{
|
||||
vec3_t dir;
|
||||
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
|
||||
|
||||
VectorSubtract( self->r.currentOrigin, other->r.currentOrigin, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
|
||||
self->client->ps.velocity[2] -= 127;
|
||||
}
|
||||
|
||||
//self->s.powerups |= ( 1 << PW_SHOCKED );
|
||||
//self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
|
||||
self->client->ps.electrifyTime = level.time + 3000;
|
||||
|
||||
self->NPC->localState = LSTATE_DROP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pain_chance = NPC_GetPainChance( self, damage );
|
||||
|
||||
if ( random() < pain_chance ) // Spin around in pain?
|
||||
{
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE);
|
||||
}
|
||||
}
|
||||
|
||||
NPC_Pain( self, attacker, damage );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_Idle
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void ImperialProbe_Idle( void )
|
||||
{
|
||||
ImperialProbe_MaintainHeight();
|
||||
|
||||
NPC_BSIdle();
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSImperialProbe_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void ImperialProbe_Patrol( void )
|
||||
{
|
||||
ImperialProbe_MaintainHeight();
|
||||
|
||||
if ( NPC_CheckPlayerTeamStealth() )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if (!NPC->enemy)
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL );
|
||||
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
//start loop sound once we move
|
||||
NPC->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
//randomly talk
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
else // He's got an enemy. Make him angry.
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/probe/misc/anger1" );
|
||||
TIMER_Set( NPC, "angerNoise", Q_irand( 2000, 4000 ) );
|
||||
//NPCInfo->behaviorState = BS_HUNT_AND_KILL;
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ImperialProbe_Wait
|
||||
-------------------------
|
||||
*/
|
||||
void ImperialProbe_Wait(void)
|
||||
{
|
||||
if ( NPCInfo->localState == LSTATE_DROP )
|
||||
{
|
||||
vec3_t endPos;
|
||||
trace_t trace;
|
||||
|
||||
NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->desiredYaw + 25 );
|
||||
|
||||
VectorSet( endPos, NPC->r.currentOrigin[0], NPC->r.currentOrigin[1], NPC->r.currentOrigin[2] - 32 );
|
||||
trap_Trace( &trace, NPC->r.currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
|
||||
|
||||
if ( trace.fraction != 1.0f )
|
||||
{
|
||||
G_Damage(NPC, NPC->enemy, NPC->enemy, NULL, NULL, 2000, 0,MOD_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSImperialProbe_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSImperialProbe_Default( void )
|
||||
{
|
||||
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
ImperialProbe_AttackDecision();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
ImperialProbe_Patrol();
|
||||
}
|
||||
else if ( NPCInfo->localState == LSTATE_DROP )
|
||||
{
|
||||
ImperialProbe_Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImperialProbe_Idle();
|
||||
}
|
||||
}
|
||||
6168
codemp/game/NPC_AI_Jedi.c
Normal file
6168
codemp/game/NPC_AI_Jedi.c
Normal file
File diff suppressed because it is too large
Load Diff
278
codemp/game/NPC_AI_MineMonster.c
Normal file
278
codemp/game/NPC_AI_MineMonster.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "b_local.h"
|
||||
|
||||
// These define the working combat range for these suckers
|
||||
#define MIN_DISTANCE 54
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define MAX_DISTANCE 128
|
||||
#define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
|
||||
|
||||
#define LSTATE_CLEAR 0
|
||||
#define LSTATE_WAITING 1
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_MineMonster_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_MineMonster_Precache( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < 4; i++ )
|
||||
{
|
||||
G_SoundIndex( va("sound/chars/mine/misc/bite%i.wav", i+1 ));
|
||||
G_SoundIndex( va("sound/chars/mine/misc/miss%i.wav", i+1 ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
MineMonster_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void MineMonster_Idle( void )
|
||||
{
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
MineMonster_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void MineMonster_Patrol( void )
|
||||
{
|
||||
vec3_t dif;
|
||||
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done( NPC, "patrolTime" ))
|
||||
{
|
||||
TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
|
||||
}
|
||||
}
|
||||
|
||||
//rwwFIXMEFIXME: Care about all clients, not just client 0
|
||||
VectorSubtract( g_entities[0].r.currentOrigin, NPC->r.currentOrigin, dif );
|
||||
|
||||
if ( VectorLengthSquared( dif ) < 256 * 256 )
|
||||
{
|
||||
G_SetEnemy( NPC, &g_entities[0] );
|
||||
}
|
||||
|
||||
if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
|
||||
{
|
||||
MineMonster_Idle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
MineMonster_Move
|
||||
-------------------------
|
||||
*/
|
||||
void MineMonster_Move( qboolean visible )
|
||||
{
|
||||
if ( NPCInfo->localState != LSTATE_WAITING )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
void MineMonster_TryDamage( gentity_t *enemy, int damage )
|
||||
{
|
||||
vec3_t end, dir;
|
||||
trace_t tr;
|
||||
|
||||
if ( !enemy )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AngleVectors( NPC->client->ps.viewangles, dir, NULL, NULL );
|
||||
VectorMA( NPC->r.currentOrigin, MIN_DISTANCE, dir, end );
|
||||
|
||||
// Should probably trace from the mouth, but, ah well.
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT );
|
||||
|
||||
if ( tr.entityNum >= 0 && tr.entityNum < ENTITYNUM_NONE )
|
||||
{
|
||||
G_Damage( &g_entities[tr.entityNum], NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
G_Sound( NPC, CHAN_AUTO, G_EffectIndex(va("sound/chars/mine/misc/bite%i.wav", Q_irand(1,4))));
|
||||
}
|
||||
else
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_EffectIndex(va("sound/chars/mine/misc/miss%i.wav", Q_irand(1,4))));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
void MineMonster_Attack( void )
|
||||
{
|
||||
if ( !TIMER_Exists( NPC, "attacking" ))
|
||||
{
|
||||
// usually try and play a jump attack if the player somehow got above them....or just really rarely
|
||||
if ( NPC->enemy && ((NPC->enemy->r.currentOrigin[2] - NPC->r.currentOrigin[2] > 10 && random() > 0.1f )
|
||||
|| random() > 0.8f ))
|
||||
{
|
||||
// Going to do ATTACK4
|
||||
TIMER_Set( NPC, "attacking", 1750 + random() * 200 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
TIMER_Set( NPC, "attack2_dmg", 950 ); // level two damage
|
||||
}
|
||||
else if ( random() > 0.5f )
|
||||
{
|
||||
if ( random() > 0.8f )
|
||||
{
|
||||
// Going to do ATTACK3, (rare)
|
||||
TIMER_Set( NPC, "attacking", 850 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
TIMER_Set( NPC, "attack2_dmg", 400 ); // level two damage
|
||||
}
|
||||
else
|
||||
{
|
||||
// Going to do ATTACK1
|
||||
TIMER_Set( NPC, "attacking", 850 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
TIMER_Set( NPC, "attack1_dmg", 450 ); // level one damage
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Going to do ATTACK2
|
||||
TIMER_Set( NPC, "attacking", 1250 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
TIMER_Set( NPC, "attack1_dmg", 700 ); // level one damage
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
|
||||
if ( TIMER_Done2( NPC, "attack1_dmg", qtrue ))
|
||||
{
|
||||
MineMonster_TryDamage( NPC->enemy, 5 );
|
||||
}
|
||||
else if ( TIMER_Done2( NPC, "attack2_dmg", qtrue ))
|
||||
{
|
||||
MineMonster_TryDamage( NPC->enemy, 10 );
|
||||
}
|
||||
}
|
||||
|
||||
// Just using this to remove the attacking flag at the right time
|
||||
TIMER_Done2( NPC, "attacking", qtrue );
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
void MineMonster_Combat( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean advance;
|
||||
|
||||
// If we cannot see our target or we have somewhere to go, then do that
|
||||
if ( !NPC_ClearLOS4( NPC->enemy ) || UpdateGoal( ))
|
||||
{
|
||||
NPCInfo->combatMove = qtrue;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
|
||||
NPC_MoveToGoal( qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
distance = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
|
||||
advance = (qboolean)( distance > MIN_DISTANCE_SQR ? qtrue : qfalse );
|
||||
|
||||
if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
|
||||
{
|
||||
if ( TIMER_Done2( NPC, "takingPain", qtrue ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
MineMonster_Move( 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MineMonster_Attack();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_MineMonster_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_MineMonster_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
G_AddEvent( self, EV_PAIN, floor((float)self->health/self->client->pers.maxHealth*100.0f) );
|
||||
|
||||
if ( damage >= 10 )
|
||||
{
|
||||
TIMER_Remove( self, "attacking" );
|
||||
TIMER_Remove( self, "attacking1_dmg" );
|
||||
TIMER_Remove( self, "attacking2_dmg" );
|
||||
TIMER_Set( self, "takingPain", 1350 );
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
|
||||
if ( self->NPC )
|
||||
{
|
||||
self->NPC->localState = LSTATE_WAITING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSMineMonster_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSMineMonster_Default( void )
|
||||
{
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
MineMonster_Combat();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
MineMonster_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
MineMonster_Idle();
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
961
codemp/game/NPC_AI_Rancor.c
Normal file
961
codemp/game/NPC_AI_Rancor.c
Normal file
@@ -0,0 +1,961 @@
|
||||
// leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
|
||||
#include "g_headers.h"
|
||||
|
||||
|
||||
#include "b_local.h"
|
||||
|
||||
extern void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex );
|
||||
|
||||
// These define the working combat range for these suckers
|
||||
#define MIN_DISTANCE 128
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define MAX_DISTANCE 1024
|
||||
#define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
|
||||
|
||||
#define LSTATE_CLEAR 0
|
||||
#define LSTATE_WAITING 1
|
||||
|
||||
void Rancor_SetBolts( gentity_t *self )
|
||||
{
|
||||
if ( self && self->client )
|
||||
{
|
||||
renderInfo_t *ri = &self->client->renderInfo;
|
||||
ri->handRBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_hand" );
|
||||
ri->handLBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_hand" );
|
||||
ri->headBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*head_eyes" );
|
||||
ri->torsoBolt = trap_G2API_AddBolt( self->ghoul2, 0, "jaw_bone" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Rancor_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Rancor_Precache( void )
|
||||
{
|
||||
int i;
|
||||
for ( i = 1; i < 3; i ++ )
|
||||
{
|
||||
G_SoundIndex( va("sound/chars/rancor/snort_%d.wav", i) );
|
||||
}
|
||||
G_SoundIndex( "sound/chars/rancor/swipehit.wav" );
|
||||
G_SoundIndex( "sound/chars/rancor/chomp.wav" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Rancor_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Rancor_Idle( void )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qboolean Rancor_CheckRoar( gentity_t *self )
|
||||
{
|
||||
if ( !self->wait )
|
||||
{//haven't ever gotten mad yet
|
||||
self->wait = 1;//do this only once
|
||||
self->client->ps.eFlags2 |= EF2_ALERTED;
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_STAND1TO2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( self, "rageTime", self->client->ps.legsTimer );
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
Rancor_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void Rancor_Patrol( void )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done( NPC, "patrolTime" ))
|
||||
{
|
||||
TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
|
||||
{
|
||||
Rancor_Idle();
|
||||
return;
|
||||
}
|
||||
Rancor_CheckRoar( NPC );
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Rancor_Move
|
||||
-------------------------
|
||||
*/
|
||||
void Rancor_Move( qboolean visible )
|
||||
{
|
||||
if ( NPCInfo->localState != LSTATE_WAITING )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
if ( !NPC_MoveToGoal( qtrue ) )
|
||||
{
|
||||
NPCInfo->consecutiveBlockedMoves++;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->consecutiveBlockedMoves = 0;
|
||||
}
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
|
||||
extern void G_Knockdown( gentity_t *victim );
|
||||
extern void G_Dismember( gentity_t *ent, gentity_t *enemy, vec3_t point, int limbType, float limbRollBase, float limbPitchBase, int deathAnim, qboolean postDeath );
|
||||
//extern qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force );
|
||||
extern float NPC_EntRangeFromBolt( gentity_t *targEnt, int boltIndex );
|
||||
extern int NPC_GetEntsNearBolt( int *radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
|
||||
|
||||
void Rancor_DropVictim( gentity_t *self )
|
||||
{
|
||||
//FIXME: if Rancor dies, it should drop its victim.
|
||||
//FIXME: if Rancor is removed, it must remove its victim.
|
||||
if ( self->activator )
|
||||
{
|
||||
if ( self->activator->client )
|
||||
{
|
||||
self->activator->client->ps.eFlags2 &= ~EF2_HELD_BY_MONSTER;
|
||||
self->activator->client->ps.hasLookTarget = qfalse;
|
||||
self->activator->client->ps.lookTarget = ENTITYNUM_NONE;
|
||||
self->activator->client->ps.viewangles[ROLL] = 0;
|
||||
SetClientViewAngle( self->activator, self->activator->client->ps.viewangles );
|
||||
self->activator->r.currentAngles[PITCH] = self->activator->r.currentAngles[ROLL] = 0;
|
||||
G_SetAngles( self->activator, self->activator->r.currentAngles );
|
||||
}
|
||||
if ( self->activator->health <= 0 )
|
||||
{
|
||||
//if ( self->activator->s.number )
|
||||
{//never free player
|
||||
if ( self->count == 1 )
|
||||
{//in my hand, just drop them
|
||||
if ( self->activator->client )
|
||||
{
|
||||
self->activator->client->ps.legsTimer = self->activator->client->ps.torsoTimer = 0;
|
||||
//FIXME: ragdoll?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self->activator->client )
|
||||
{
|
||||
self->activator->client->ps.eFlags |= EF_NODRAW;//so his corpse doesn't drop out of me...
|
||||
}
|
||||
//G_FreeEntity( self->activator );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self->activator->NPC )
|
||||
{//start thinking again
|
||||
self->activator->NPC->nextBStateThink = level.time;
|
||||
}
|
||||
//clear their anim and let them fall
|
||||
self->activator->client->ps.legsTimer = self->activator->client->ps.torsoTimer = 0;
|
||||
}
|
||||
if ( self->enemy == self->activator )
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
self->activator = NULL;
|
||||
}
|
||||
self->count = 0;//drop him
|
||||
}
|
||||
|
||||
void Rancor_Swing( qboolean tryGrab )
|
||||
{
|
||||
int radiusEntNums[128];
|
||||
int numEnts;
|
||||
const float radius = 88;
|
||||
const float radiusSquared = (radius*radius);
|
||||
int i;
|
||||
vec3_t boltOrg;
|
||||
|
||||
numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, NPC->client->renderInfo.handRBolt, boltOrg );
|
||||
|
||||
for ( i = 0; i < numEnts; i++ )
|
||||
{
|
||||
gentity_t *radiusEnt = &g_entities[radiusEntNums[i]];
|
||||
if ( !radiusEnt->inuse )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt == NPC )
|
||||
{//Skip the rancor ent
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt->client == NULL )
|
||||
{//must be a client
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
|
||||
{//can't be one already being held
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared )
|
||||
{
|
||||
if ( tryGrab
|
||||
&& NPC->count != 1 //don't have one in hand or in mouth already - FIXME: allow one in hand and any number in mouth!
|
||||
&& radiusEnt->client->NPC_class != CLASS_RANCOR
|
||||
&& radiusEnt->client->NPC_class != CLASS_GALAKMECH
|
||||
&& radiusEnt->client->NPC_class != CLASS_ATST
|
||||
&& radiusEnt->client->NPC_class != CLASS_GONK
|
||||
&& radiusEnt->client->NPC_class != CLASS_R2D2
|
||||
&& radiusEnt->client->NPC_class != CLASS_R5D2
|
||||
&& radiusEnt->client->NPC_class != CLASS_MARK1
|
||||
&& radiusEnt->client->NPC_class != CLASS_MARK2
|
||||
&& radiusEnt->client->NPC_class != CLASS_MOUSE
|
||||
&& radiusEnt->client->NPC_class != CLASS_PROBE
|
||||
&& radiusEnt->client->NPC_class != CLASS_SEEKER
|
||||
&& radiusEnt->client->NPC_class != CLASS_REMOTE
|
||||
&& radiusEnt->client->NPC_class != CLASS_SENTRY
|
||||
&& radiusEnt->client->NPC_class != CLASS_INTERROGATOR
|
||||
&& radiusEnt->client->NPC_class != CLASS_VEHICLE )
|
||||
{//grab
|
||||
if ( NPC->count == 2 )
|
||||
{//have one in my mouth, remove him
|
||||
TIMER_Remove( NPC, "clearGrabbed" );
|
||||
Rancor_DropVictim( NPC );
|
||||
}
|
||||
NPC->enemy = radiusEnt;//make him my new best friend
|
||||
radiusEnt->client->ps.eFlags2 |= EF2_HELD_BY_MONSTER;
|
||||
//FIXME: this makes it so that the victim can't hit us with shots! Just use activator or something
|
||||
radiusEnt->client->ps.hasLookTarget = qtrue;
|
||||
radiusEnt->client->ps.lookTarget = NPC->s.number;
|
||||
NPC->activator = radiusEnt;//remember him
|
||||
NPC->count = 1;//in my hand
|
||||
//wait to attack
|
||||
TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + Q_irand(500, 2500) );
|
||||
if ( radiusEnt->health > 0 && radiusEnt->pain )
|
||||
{//do pain on enemy
|
||||
radiusEnt->pain( radiusEnt, NPC, 100 );
|
||||
//GEntity_PainFunc( radiusEnt, NPC, NPC, radiusEnt->r.currentOrigin, 0, MOD_CRUSH );
|
||||
}
|
||||
else if ( radiusEnt->client )
|
||||
{
|
||||
radiusEnt->client->ps.forceHandExtend = HANDEXTEND_NONE;
|
||||
radiusEnt->client->ps.forceHandExtendTime = 0;
|
||||
NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//smack
|
||||
vec3_t pushDir;
|
||||
vec3_t angs;
|
||||
|
||||
G_Sound( radiusEnt, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
|
||||
//actually push the enemy
|
||||
/*
|
||||
//VectorSubtract( radiusEnt->r.currentOrigin, boltOrg, pushDir );
|
||||
VectorSubtract( radiusEnt->r.currentOrigin, NPC->r.currentOrigin, pushDir );
|
||||
pushDir[2] = Q_flrand( 100, 200 );
|
||||
VectorNormalize( pushDir );
|
||||
*/
|
||||
VectorCopy( NPC->client->ps.viewangles, angs );
|
||||
angs[YAW] += flrand( 25, 50 );
|
||||
angs[PITCH] = flrand( -25, -15 );
|
||||
AngleVectors( angs, pushDir, NULL, NULL );
|
||||
if ( radiusEnt->client->NPC_class != CLASS_RANCOR
|
||||
&& radiusEnt->client->NPC_class != CLASS_ATST )
|
||||
{
|
||||
G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, Q_irand( 25, 40 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
G_Throw( radiusEnt, pushDir, 250 );
|
||||
if ( radiusEnt->health > 0 )
|
||||
{//do pain on enemy
|
||||
G_Knockdown( radiusEnt );//, NPC, pushDir, 100, qtrue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rancor_Smash( void )
|
||||
{
|
||||
int radiusEntNums[128];
|
||||
int numEnts;
|
||||
const float radius = 128;
|
||||
const float halfRadSquared = ((radius/2)*(radius/2));
|
||||
const float radiusSquared = (radius*radius);
|
||||
float distSq;
|
||||
int i;
|
||||
vec3_t boltOrg;
|
||||
|
||||
AddSoundEvent( NPC, NPC->r.currentOrigin, 512, AEL_DANGER, qfalse );//, qtrue );
|
||||
|
||||
numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, NPC->client->renderInfo.handLBolt, boltOrg );
|
||||
|
||||
for ( i = 0; i < numEnts; i++ )
|
||||
{
|
||||
gentity_t *radiusEnt = &g_entities[radiusEntNums[i]];
|
||||
if ( !radiusEnt->inuse )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt == NPC )
|
||||
{//Skip the rancor ent
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt->client == NULL )
|
||||
{//must be a client
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
|
||||
{//can't be one being held
|
||||
continue;
|
||||
}
|
||||
|
||||
distSq = DistanceSquared( radiusEnt->r.currentOrigin, boltOrg );
|
||||
if ( distSq <= radiusSquared )
|
||||
{
|
||||
G_Sound( radiusEnt, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
|
||||
if ( distSq < halfRadSquared )
|
||||
{//close enough to do damage, too
|
||||
G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, Q_irand( 10, 25 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
}
|
||||
if ( radiusEnt->health > 0
|
||||
&& radiusEnt->client
|
||||
&& radiusEnt->client->NPC_class != CLASS_RANCOR
|
||||
&& radiusEnt->client->NPC_class != CLASS_ATST )
|
||||
{
|
||||
if ( distSq < halfRadSquared
|
||||
|| radiusEnt->client->ps.groundEntityNum != ENTITYNUM_NONE )
|
||||
{//within range of my fist or withing ground-shaking range and not in the air
|
||||
G_Knockdown( radiusEnt );//, NPC, vec3_origin, 100, qtrue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rancor_Bite( void )
|
||||
{
|
||||
int radiusEntNums[128];
|
||||
int numEnts;
|
||||
const float radius = 100;
|
||||
const float radiusSquared = (radius*radius);
|
||||
int i;
|
||||
vec3_t boltOrg;
|
||||
|
||||
numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, NPC->client->renderInfo.crotchBolt, boltOrg );//was gutBolt?
|
||||
|
||||
for ( i = 0; i < numEnts; i++ )
|
||||
{
|
||||
gentity_t *radiusEnt = &g_entities[radiusEntNums[i]];
|
||||
if ( !radiusEnt->inuse )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt == NPC )
|
||||
{//Skip the rancor ent
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt->client == NULL )
|
||||
{//must be a client
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) )
|
||||
{//can't be one already being held
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared )
|
||||
{
|
||||
G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, Q_irand( 15, 30 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
if ( radiusEnt->health <= 0 && radiusEnt->client )
|
||||
{//killed them, chance of dismembering
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{//bite something off
|
||||
int hitLoc = Q_irand( G2_MODELPART_HEAD, G2_MODELPART_RLEG );
|
||||
if ( hitLoc == G2_MODELPART_HEAD )
|
||||
{
|
||||
NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATH17, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
else if ( hitLoc == G2_MODELPART_WAIST )
|
||||
{
|
||||
NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATHBACKWARD2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
//radiusEnt->client->dismembered = qfalse;
|
||||
//FIXME: the limb should just disappear, cuz I ate it
|
||||
G_Dismember( radiusEnt, NPC, radiusEnt->r.currentOrigin, hitLoc, 90, 0, radiusEnt->client->ps.torsoAnim, qtrue);
|
||||
//G_DoDismemberment( radiusEnt, radiusEnt->r.currentOrigin, MOD_SABER, 1000, hitLoc, qtrue );
|
||||
}
|
||||
}
|
||||
G_Sound( radiusEnt, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------
|
||||
extern void TossClientItems( gentity_t *self );
|
||||
void Rancor_Attack( float distance, qboolean doCharge )
|
||||
{
|
||||
if ( !TIMER_Exists( NPC, "attacking" ) )
|
||||
{
|
||||
if ( NPC->count == 2 && NPC->activator )
|
||||
{
|
||||
}
|
||||
else if ( NPC->count == 1 && NPC->activator )
|
||||
{//holding enemy
|
||||
if ( NPC->activator->health > 0 && Q_irand( 0, 1 ) )
|
||||
{//quick bite
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 450 );
|
||||
}
|
||||
else
|
||||
{//full eat
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 900 );
|
||||
//Make victim scream in fright
|
||||
if ( NPC->activator->health > 0 && NPC->activator->client )
|
||||
{
|
||||
G_AddEvent( NPC->activator, Q_irand(EV_DEATH1, EV_DEATH3), 0 );
|
||||
NPC_SetAnim( NPC->activator, SETANIM_TORSO, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
if ( NPC->activator->NPC )
|
||||
{//no more thinking for you
|
||||
TossClientItems( NPC );
|
||||
NPC->activator->NPC->nextBStateThink = Q3_INFINITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( NPC->enemy->health > 0 && doCharge )
|
||||
{//charge
|
||||
vec3_t fwd, yawAng;
|
||||
VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 );
|
||||
AngleVectors( yawAng, fwd, NULL, NULL );
|
||||
VectorScale( fwd, distance*1.5f, NPC->client->ps.velocity );
|
||||
NPC->client->ps.velocity[2] = 150;
|
||||
NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
|
||||
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_MELEE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 1250 );
|
||||
}
|
||||
else if ( !Q_irand(0, 1) )
|
||||
{//smash
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 1000 );
|
||||
}
|
||||
else
|
||||
{//try to grab
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 1000 );
|
||||
}
|
||||
|
||||
TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + random() * 200 );
|
||||
}
|
||||
|
||||
// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
|
||||
|
||||
if ( TIMER_Done2( NPC, "attack_dmg", qtrue ) )
|
||||
{
|
||||
vec3_t shakePos;
|
||||
switch ( NPC->client->ps.legsAnim )
|
||||
{
|
||||
case BOTH_MELEE1:
|
||||
Rancor_Smash();
|
||||
G_GetBoltPosition( NPC, NPC->client->renderInfo.handLBolt, shakePos, 0 );
|
||||
G_ScreenShake( shakePos, NULL, 4.0f, 1000, qfalse );
|
||||
//CGCam_Shake( 1.0f*playerDist/128.0f, 1000 );
|
||||
break;
|
||||
case BOTH_MELEE2:
|
||||
Rancor_Bite();
|
||||
TIMER_Set( NPC, "attack_dmg2", 450 );
|
||||
break;
|
||||
case BOTH_ATTACK1:
|
||||
if ( NPC->count == 1 && NPC->activator )
|
||||
{
|
||||
G_Damage( NPC->activator, NPC, NPC, vec3_origin, NPC->activator->r.currentOrigin, Q_irand( 25, 40 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
|
||||
if ( NPC->activator->health <= 0 )
|
||||
{//killed him
|
||||
//make it look like we bit his head off
|
||||
//NPC->activator->client->dismembered = qfalse;
|
||||
G_Dismember( NPC->activator, NPC, NPC->activator->r.currentOrigin, G2_MODELPART_HEAD, 90, 0, NPC->activator->client->ps.torsoAnim, qtrue);
|
||||
//G_DoDismemberment( NPC->activator, NPC->activator->r.currentOrigin, MOD_SABER, 1000, HL_HEAD, qtrue );
|
||||
NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
|
||||
NPC->activator->client->ps.forceHandExtendTime = 0;
|
||||
NPC_SetAnim( NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
G_Sound( NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) );
|
||||
}
|
||||
break;
|
||||
case BOTH_ATTACK2:
|
||||
//try to grab
|
||||
Rancor_Swing( qtrue );
|
||||
break;
|
||||
case BOTH_ATTACK3:
|
||||
if ( NPC->count == 1 && NPC->activator )
|
||||
{
|
||||
//cut in half
|
||||
if ( NPC->activator->client )
|
||||
{
|
||||
//NPC->activator->client->dismembered = qfalse;
|
||||
G_Dismember( NPC->activator, NPC, NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPC->activator->client->ps.torsoAnim, qtrue);
|
||||
//G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue );
|
||||
}
|
||||
//KILL
|
||||
G_Damage( NPC->activator, NPC, NPC, vec3_origin, NPC->activator->r.currentOrigin, NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE );//
|
||||
if ( NPC->activator->client )
|
||||
{
|
||||
NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
|
||||
NPC->activator->client->ps.forceHandExtendTime = 0;
|
||||
NPC_SetAnim( NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
TIMER_Set( NPC, "attack_dmg2", 1350 );
|
||||
G_Sound( NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
|
||||
G_AddEvent( NPC->activator, EV_JUMP, NPC->activator->health );
|
||||
}
|
||||
break;
|
||||
default: //something broke!
|
||||
Rancor_DropVictim(NPC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( TIMER_Done2( NPC, "attack_dmg2", qtrue ) )
|
||||
{
|
||||
switch ( NPC->client->ps.legsAnim )
|
||||
{
|
||||
case BOTH_MELEE1:
|
||||
break;
|
||||
case BOTH_MELEE2:
|
||||
Rancor_Bite();
|
||||
break;
|
||||
case BOTH_ATTACK1:
|
||||
break;
|
||||
case BOTH_ATTACK2:
|
||||
break;
|
||||
case BOTH_ATTACK3:
|
||||
if ( NPC->count == 1 && NPC->activator )
|
||||
{//swallow victim
|
||||
G_Sound( NPC->activator, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/chomp.wav" ) );
|
||||
//FIXME: sometimes end up with a live one in our mouths?
|
||||
//just make sure they're dead
|
||||
if ( NPC->activator->health > 0 )
|
||||
{
|
||||
//cut in half
|
||||
//NPC->activator->client->dismembered = qfalse;
|
||||
G_Dismember( NPC->activator, NPC, NPC->activator->r.currentOrigin, G2_MODELPART_WAIST, 90, 0, NPC->activator->client->ps.torsoAnim, qtrue);
|
||||
//G_DoDismemberment( NPC->activator, NPC->enemy->r.currentOrigin, MOD_SABER, 1000, HL_WAIST, qtrue );
|
||||
//KILL
|
||||
G_Damage( NPC->activator, NPC, NPC, vec3_origin, NPC->activator->r.currentOrigin, NPC->enemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_MELEE );//, HL_NONE );
|
||||
NPC->activator->client->ps.forceHandExtend = HANDEXTEND_NONE;
|
||||
NPC->activator->client->ps.forceHandExtendTime = 0;
|
||||
NPC_SetAnim( NPC->activator, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
G_AddEvent( NPC->activator, EV_JUMP, NPC->activator->health );
|
||||
}
|
||||
if ( NPC->activator->client )
|
||||
{//*sigh*, can't get tags right, just remove them?
|
||||
NPC->activator->client->ps.eFlags |= EF_NODRAW;
|
||||
}
|
||||
NPC->count = 2;
|
||||
TIMER_Set( NPC, "clearGrabbed", 2600 );
|
||||
}
|
||||
break;
|
||||
default: //something broke!
|
||||
Rancor_DropVictim(NPC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( NPC->client->ps.legsAnim == BOTH_ATTACK2 )
|
||||
{
|
||||
if ( NPC->client->ps.legsTimer >= 1200 && NPC->client->ps.legsTimer <= 1350 )
|
||||
{
|
||||
if ( Q_irand( 0, 2 ) )
|
||||
{
|
||||
Rancor_Swing( qfalse );
|
||||
}
|
||||
else
|
||||
{
|
||||
Rancor_Swing( qtrue );
|
||||
}
|
||||
}
|
||||
else if ( NPC->client->ps.legsTimer >= 1100 && NPC->client->ps.legsTimer <= 1550 )
|
||||
{
|
||||
Rancor_Swing( qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
// Just using this to remove the attacking flag at the right time
|
||||
TIMER_Done2( NPC, "attacking", qtrue );
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
void Rancor_Combat( void )
|
||||
{
|
||||
if ( NPC->count )
|
||||
{//holding my enemy
|
||||
if ( TIMER_Done2( NPC, "takingPain", qtrue ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rancor_Attack( 0, qfalse );
|
||||
}
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
// If we cannot see our target or we have somewhere to go, then do that
|
||||
if ( !NPC_ClearLOS4( NPC->enemy ) )//|| UpdateGoal( ))
|
||||
{
|
||||
NPCInfo->combatMove = qtrue;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE; // just get us within combat range
|
||||
|
||||
if ( !NPC_MoveToGoal( qtrue ) )
|
||||
{//couldn't go after him? Look for a new one
|
||||
TIMER_Set( NPC, "lookForNewEnemy", 0 );
|
||||
NPCInfo->consecutiveBlockedMoves++;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPCInfo->consecutiveBlockedMoves = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
{
|
||||
float distance;
|
||||
qboolean advance;
|
||||
qboolean doCharge;
|
||||
|
||||
distance = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
advance = (qboolean)( distance > (NPC->r.maxs[0]+MIN_DISTANCE) ? qtrue : qfalse );
|
||||
doCharge = qfalse;
|
||||
|
||||
if ( advance )
|
||||
{//have to get closer
|
||||
vec3_t yawOnlyAngles;
|
||||
VectorSet( yawOnlyAngles, 0, NPC->r.currentAngles[YAW], 0 );
|
||||
if ( NPC->enemy->health > 0
|
||||
&& fabs(distance-250) <= 80
|
||||
&& InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, yawOnlyAngles, 30, 30 ) )
|
||||
{
|
||||
if ( !Q_irand( 0, 9 ) )
|
||||
{//go for the charge
|
||||
doCharge = qtrue;
|
||||
advance = qfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (( advance /*|| NPCInfo->localState == LSTATE_WAITING*/ ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
|
||||
{
|
||||
if ( TIMER_Done2( NPC, "takingPain", qtrue ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rancor_Move( 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Rancor_Attack( distance, doCharge );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Rancor_Pain
|
||||
-------------------------
|
||||
*/
|
||||
//void NPC_Rancor_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
|
||||
void NPC_Rancor_Pain( gentity_t *self, gentity_t *attacker, int damage )
|
||||
{
|
||||
qboolean hitByRancor = qfalse;
|
||||
if ( attacker&&attacker->client&&attacker->client->NPC_class==CLASS_RANCOR )
|
||||
{
|
||||
hitByRancor = qtrue;
|
||||
}
|
||||
if ( attacker
|
||||
&& attacker->inuse
|
||||
&& attacker != self->enemy
|
||||
&& !(attacker->flags&FL_NOTARGET) )
|
||||
{
|
||||
if ( !self->count )
|
||||
{
|
||||
if ( (!attacker->s.number&&!Q_irand(0,3))
|
||||
|| !self->enemy
|
||||
|| self->enemy->health == 0
|
||||
|| (self->enemy->client&&self->enemy->client->NPC_class == CLASS_RANCOR)
|
||||
|| (self->NPC && self->NPC->consecutiveBlockedMoves>=10 && DistanceSquared( attacker->r.currentOrigin, self->r.currentOrigin ) < DistanceSquared( self->enemy->r.currentOrigin, self->r.currentOrigin )) )
|
||||
{//if my enemy is dead (or attacked by player) and I'm not still holding/eating someone, turn on the attacker
|
||||
//FIXME: if can't nav to my enemy, take this guy if I can nav to him
|
||||
G_SetEnemy( self, attacker );
|
||||
TIMER_Set( self, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
if ( hitByRancor )
|
||||
{//stay mad at this Rancor for 2-5 secs before looking for attacker enemies
|
||||
TIMER_Set( self, "rancorInfight", Q_irand( 2000, 5000 ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( (hitByRancor|| (self->count==1&&self->activator&&!Q_irand(0,4)) || Q_irand( 0, 200 ) < damage )//hit by rancor, hit while holding live victim, or took a lot of damage
|
||||
&& self->client->ps.legsAnim != BOTH_STAND1TO2
|
||||
&& TIMER_Done( self, "takingPain" ) )
|
||||
{
|
||||
if ( !Rancor_CheckRoar( self ) )
|
||||
{
|
||||
if ( self->client->ps.legsAnim != BOTH_MELEE1
|
||||
&& self->client->ps.legsAnim != BOTH_MELEE2
|
||||
&& self->client->ps.legsAnim != BOTH_ATTACK2 )
|
||||
{//cant interrupt one of the big attack anims
|
||||
/*
|
||||
if ( self->count != 1
|
||||
|| attacker == self->activator
|
||||
|| (self->client->ps.legsAnim != BOTH_ATTACK1&&self->client->ps.legsAnim != BOTH_ATTACK3) )
|
||||
*/
|
||||
{//if going to bite our victim, only victim can interrupt that anim
|
||||
if ( self->health > 100 || hitByRancor )
|
||||
{
|
||||
TIMER_Remove( self, "attacking" );
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
if ( self->count == 1 )
|
||||
{
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
TIMER_Set( self, "takingPain", self->client->ps.legsTimer+Q_irand(0, 500) );
|
||||
|
||||
if ( self->NPC )
|
||||
{
|
||||
self->NPC->localState = LSTATE_WAITING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//let go
|
||||
/*
|
||||
if ( !Q_irand( 0, 3 ) && self->count == 1 )
|
||||
{
|
||||
Rancor_DropVictim( self );
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void Rancor_CheckDropVictim( void )
|
||||
{
|
||||
vec3_t mins;
|
||||
vec3_t maxs;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
trace_t trace;
|
||||
|
||||
VectorSet( mins, NPC->activator->r.mins[0]-1, NPC->activator->r.mins[1]-1, 0 );
|
||||
VectorSet( maxs, NPC->activator->r.maxs[0]+1, NPC->activator->r.maxs[1]+1, 1 );
|
||||
VectorSet( start, NPC->activator->r.currentOrigin[0], NPC->activator->r.currentOrigin[1], NPC->activator->r.absmin[2] );
|
||||
VectorSet( end, NPC->activator->r.currentOrigin[0], NPC->activator->r.currentOrigin[1], NPC->activator->r.absmax[2]-1 );
|
||||
|
||||
trap_Trace( &trace, start, mins, maxs, end, NPC->activator->s.number, NPC->activator->clipmask );
|
||||
if ( !trace.allsolid && !trace.startsolid && trace.fraction >= 1.0f )
|
||||
{
|
||||
Rancor_DropVictim( NPC );
|
||||
}
|
||||
}
|
||||
|
||||
//if he's stepping on things then crush them -rww
|
||||
void Rancor_Crush(void)
|
||||
{
|
||||
gentity_t *crush;
|
||||
|
||||
if (!NPC ||
|
||||
!NPC->client ||
|
||||
NPC->client->ps.groundEntityNum >= ENTITYNUM_WORLD)
|
||||
{ //nothing to crush
|
||||
return;
|
||||
}
|
||||
|
||||
crush = &g_entities[NPC->client->ps.groundEntityNum];
|
||||
if (crush->inuse && crush->client && !crush->localAnimIndex)
|
||||
{ //a humanoid, smash them good.
|
||||
G_Damage(crush, NPC, NPC, NULL, NPC->r.currentOrigin, 200, 0, MOD_CRUSH);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSRancor_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSRancor_Default( void )
|
||||
{
|
||||
AddSightEvent( NPC, NPC->r.currentOrigin, 1024, AEL_DANGER_GREAT, 50 );
|
||||
|
||||
Rancor_Crush();
|
||||
|
||||
NPC->client->ps.eFlags2 &= ~(EF2_USE_ALT_ANIM|EF2_GENERIC_NPC_FLAG);
|
||||
if ( NPC->count )
|
||||
{//holding someone
|
||||
NPC->client->ps.eFlags2 |= EF2_USE_ALT_ANIM;
|
||||
if ( NPC->count == 2 )
|
||||
{//in my mouth
|
||||
NPC->client->ps.eFlags2 |= EF2_GENERIC_NPC_FLAG;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC->client->ps.eFlags2 &= ~(EF2_USE_ALT_ANIM|EF2_GENERIC_NPC_FLAG);
|
||||
}
|
||||
|
||||
if ( TIMER_Done2( NPC, "clearGrabbed", qtrue ) )
|
||||
{
|
||||
Rancor_DropVictim( NPC );
|
||||
}
|
||||
else if ( NPC->client->ps.legsAnim == BOTH_PAIN2
|
||||
&& NPC->count == 1
|
||||
&& NPC->activator )
|
||||
{
|
||||
if ( !Q_irand( 0, 3 ) )
|
||||
{
|
||||
Rancor_CheckDropVictim();
|
||||
}
|
||||
}
|
||||
if ( !TIMER_Done( NPC, "rageTime" ) )
|
||||
{//do nothing but roar first time we see an enemy
|
||||
AddSoundEvent( NPC, NPC->r.currentOrigin, 1024, AEL_DANGER_GREAT, qfalse );//, qfalse );
|
||||
NPC_FaceEnemy( qtrue );
|
||||
return;
|
||||
}
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
/*
|
||||
if ( NPC->enemy->client //enemy is a client
|
||||
&& (NPC->enemy->client->NPC_class == CLASS_UGNAUGHT || NPC->enemy->client->NPC_class == CLASS_JAWA )//enemy is a lowly jawa or ugnaught
|
||||
&& NPC->enemy->enemy != NPC//enemy's enemy is not me
|
||||
&& (!NPC->enemy->enemy || !NPC->enemy->enemy->client || NPC->enemy->enemy->client->NPC_class!=CLASS_RANCOR) )//enemy's enemy is not a client or is not a rancor (which is as scary as me anyway)
|
||||
{//they should be scared of ME and no-one else
|
||||
G_SetEnemy( NPC->enemy, NPC );
|
||||
}
|
||||
*/
|
||||
if ( TIMER_Done(NPC,"angrynoise") )
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( va("sound/chars/rancor/misc/anger%d.wav", Q_irand(1, 3))) );
|
||||
|
||||
TIMER_Set( NPC, "angrynoise", Q_irand( 5000, 10000 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
AddSoundEvent( NPC, NPC->r.currentOrigin, 512, AEL_DANGER_GREAT, qfalse );//, qfalse );
|
||||
}
|
||||
if ( NPC->count == 2 && NPC->client->ps.legsAnim == BOTH_ATTACK3 )
|
||||
{//we're still chewing our enemy up
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
//else, if he's in our hand, we eat, else if he's on the ground, we keep attacking his dead body for a while
|
||||
if( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_RANCOR )
|
||||
{//got mad at another Rancor, look for a valid enemy
|
||||
if ( TIMER_Done( NPC, "rancorInfight" ) )
|
||||
{
|
||||
NPC_CheckEnemyExt( qtrue );
|
||||
}
|
||||
}
|
||||
else if ( !NPC->count )
|
||||
{
|
||||
if ( ValidEnemy( NPC->enemy ) == qfalse )
|
||||
{
|
||||
TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now
|
||||
if ( !NPC->enemy->inuse || level.time - NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
|
||||
{//it's been a while since the enemy died, or enemy is completely gone, get bored with him
|
||||
NPC->enemy = NULL;
|
||||
Rancor_Patrol();
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( TIMER_Done( NPC, "lookForNewEnemy" ) )
|
||||
{
|
||||
gentity_t *newEnemy, *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy?
|
||||
NPC->enemy = NULL;
|
||||
newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse );
|
||||
NPC->enemy = sav_enemy;
|
||||
if ( newEnemy && newEnemy != sav_enemy )
|
||||
{//picked up a new enemy!
|
||||
NPC->lastEnemy = NPC->enemy;
|
||||
G_SetEnemy( NPC, newEnemy );
|
||||
//hold this one for at least 5-15 seconds
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
}
|
||||
else
|
||||
{//look again in 2-5 secs
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Rancor_Combat();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done(NPC,"idlenoise") )
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( va("sound/chars/rancor/snort_%d.wav", Q_irand(1, 2))) );
|
||||
|
||||
TIMER_Set( NPC, "idlenoise", Q_irand( 2000, 4000 ) );
|
||||
AddSoundEvent( NPC, NPC->r.currentOrigin, 384, AEL_DANGER, qfalse );//, qfalse );
|
||||
}
|
||||
if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
Rancor_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Rancor_Idle();
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
389
codemp/game/NPC_AI_Remote.c
Normal file
389
codemp/game/NPC_AI_Remote.c
Normal file
@@ -0,0 +1,389 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
void Remote_Strafe( void );
|
||||
|
||||
#define VELOCITY_DECAY 0.85f
|
||||
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
};
|
||||
|
||||
void Remote_Idle( void );
|
||||
|
||||
void NPC_Remote_Precache(void)
|
||||
{
|
||||
G_SoundIndex("sound/chars/remote/misc/fire.wav");
|
||||
G_SoundIndex( "sound/chars/remote/misc/hiss.wav");
|
||||
G_EffectIndex( "env/small_explode");
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Remote_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Remote_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
SaveNPCGlobals();
|
||||
SetNPCGlobals( self );
|
||||
Remote_Strafe();
|
||||
RestoreNPCGlobals();
|
||||
|
||||
NPC_Pain( self, attacker, damage );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_MaintainHeight
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_MaintainHeight( void )
|
||||
{
|
||||
float dif;
|
||||
|
||||
// Update our angles regardless
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
// If we have an enemy, we should try to hover at or a little below enemy eye level
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
if (TIMER_Done( NPC, "heightChange"))
|
||||
{
|
||||
TIMER_Set( NPC,"heightChange",Q_irand( 1000, 3000 ));
|
||||
|
||||
// Find the height difference
|
||||
dif = (NPC->enemy->r.currentOrigin[2] + Q_irand( 0, NPC->enemy->r.maxs[2]+8 )) - NPC->r.currentOrigin[2];
|
||||
|
||||
// cap to prevent dramatic height shifts
|
||||
if ( fabs( dif ) > 2 )
|
||||
{
|
||||
if ( fabs( dif ) > 24 )
|
||||
{
|
||||
dif = ( dif < 0 ? -24 : 24 );
|
||||
}
|
||||
dif *= 10;
|
||||
NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
|
||||
// NPC->fx_time = level.time;
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gentity_t *goal = NULL;
|
||||
|
||||
if ( NPCInfo->goalEntity ) // Is there a goal?
|
||||
{
|
||||
goal = NPCInfo->goalEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
goal = NPCInfo->lastGoalEntity;
|
||||
}
|
||||
if ( goal )
|
||||
{
|
||||
dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
|
||||
|
||||
if ( fabs( dif ) > 24 )
|
||||
{
|
||||
dif = ( dif < 0 ? -24 : 24 );
|
||||
NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply friction
|
||||
if ( NPC->client->ps.velocity[0] )
|
||||
{
|
||||
NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->client->ps.velocity[1] )
|
||||
{
|
||||
NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define REMOTE_STRAFE_VEL 256
|
||||
#define REMOTE_STRAFE_DIS 200
|
||||
#define REMOTE_UPWARD_PUSH 32
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Strafe
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Strafe( void )
|
||||
{
|
||||
int dir;
|
||||
vec3_t end, right;
|
||||
trace_t tr;
|
||||
|
||||
AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
|
||||
|
||||
// Pick a random strafe direction, then check to see if doing a strafe would be
|
||||
// reasonable valid
|
||||
dir = ( rand() & 1 ) ? -1 : 1;
|
||||
VectorMA( NPC->r.currentOrigin, REMOTE_STRAFE_DIS * dir, right, end );
|
||||
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
|
||||
|
||||
// Close enough
|
||||
if ( tr.fraction > 0.9f )
|
||||
{
|
||||
VectorMA( NPC->client->ps.velocity, REMOTE_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
|
||||
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/remote/misc/hiss.wav"));
|
||||
|
||||
// Add a slight upward push
|
||||
NPC->client->ps.velocity[2] += REMOTE_UPWARD_PUSH;
|
||||
|
||||
// Set the strafe start time so we can do a controlled roll
|
||||
// NPC->fx_time = level.time;
|
||||
NPCInfo->standTime = level.time + 3000 + random() * 500;
|
||||
}
|
||||
}
|
||||
|
||||
#define REMOTE_FORWARD_BASE_SPEED 10
|
||||
#define REMOTE_FORWARD_MULTIPLIER 5
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Hunt
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Hunt( qboolean visible, qboolean advance, qboolean retreat )
|
||||
{
|
||||
float distance, speed;
|
||||
vec3_t forward;
|
||||
|
||||
//If we're not supposed to stand still, pursue the player
|
||||
if ( NPCInfo->standTime < level.time )
|
||||
{
|
||||
// Only strafe when we can see the player
|
||||
if ( visible )
|
||||
{
|
||||
Remote_Strafe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If we don't want to advance, stop here
|
||||
if ( advance == qfalse && visible == qtrue )
|
||||
return;
|
||||
|
||||
//Only try and navigate if the player is visible
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
// Move towards our goal
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 12;
|
||||
|
||||
//Get our direction from the navigator if we can't see our target
|
||||
if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
|
||||
distance = VectorNormalize( forward );
|
||||
}
|
||||
|
||||
speed = REMOTE_FORWARD_BASE_SPEED + REMOTE_FORWARD_MULTIPLIER * g_spskill.integer;
|
||||
if ( retreat == qtrue )
|
||||
{
|
||||
speed *= -1;
|
||||
}
|
||||
VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Fire
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Fire (void)
|
||||
{
|
||||
vec3_t delta1, enemy_org1, muzzle1;
|
||||
vec3_t angleToEnemy1;
|
||||
static vec3_t forward, vright, up;
|
||||
static vec3_t muzzle;
|
||||
gentity_t *missile;
|
||||
|
||||
CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org1 );
|
||||
VectorCopy( NPC->r.currentOrigin, muzzle1 );
|
||||
|
||||
VectorSubtract (enemy_org1, muzzle1, delta1);
|
||||
|
||||
vectoangles ( delta1, angleToEnemy1 );
|
||||
AngleVectors (angleToEnemy1, forward, vright, up);
|
||||
|
||||
missile = CreateMissile( NPC->r.currentOrigin, forward, 1000, 10000, NPC, qfalse );
|
||||
|
||||
G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), NPC->r.currentOrigin, forward );
|
||||
|
||||
missile->classname = "briar";
|
||||
missile->s.weapon = WP_BRYAR_PISTOL;
|
||||
|
||||
missile->damage = 10;
|
||||
missile->dflags = DAMAGE_DEATH_KNOCKBACK;
|
||||
missile->methodOfDeath = MOD_BRYAR_PISTOL;
|
||||
missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Ranged
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Ranged( qboolean visible, qboolean advance, qboolean retreat )
|
||||
{
|
||||
|
||||
if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
|
||||
{
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
|
||||
Remote_Fire();
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Remote_Hunt( visible, advance, retreat );
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN_MELEE_RANGE 320
|
||||
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
|
||||
|
||||
#define MIN_DISTANCE 80
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Attack
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Attack( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean visible;
|
||||
float idealDist;
|
||||
qboolean advance;
|
||||
qboolean retreat;
|
||||
|
||||
if ( TIMER_Done(NPC,"spin") )
|
||||
{
|
||||
TIMER_Set( NPC, "spin", Q_irand( 250, 1500 ) );
|
||||
NPCInfo->desiredYaw += Q_irand( -200, 200 );
|
||||
}
|
||||
// Always keep a good height off the ground
|
||||
Remote_MaintainHeight();
|
||||
|
||||
// If we don't have an enemy, just idle
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )
|
||||
{
|
||||
Remote_Idle();
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate our distance to the target, and our visibilty
|
||||
distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
visible = NPC_ClearLOS4( NPC->enemy );
|
||||
idealDist = MIN_DISTANCE_SQR+(MIN_DISTANCE_SQR*flrand( 0, 1 ));
|
||||
advance = (qboolean)(distance > idealDist*1.25);
|
||||
retreat = (qboolean)(distance < idealDist*0.75);
|
||||
|
||||
// If we cannot see our target, move to see it
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Remote_Hunt( visible, advance, retreat );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Remote_Ranged( visible, advance, retreat );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Idle( void )
|
||||
{
|
||||
Remote_MaintainHeight();
|
||||
|
||||
NPC_BSIdle();
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Remote_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void Remote_Patrol( void )
|
||||
{
|
||||
Remote_MaintainHeight();
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if (!NPC->enemy)
|
||||
{
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
//start loop sound once we move
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSRemote_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSRemote_Default( void )
|
||||
{
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
Remote_Attack();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
Remote_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Remote_Idle();
|
||||
}
|
||||
}
|
||||
574
codemp/game/NPC_AI_Seeker.c
Normal file
574
codemp/game/NPC_AI_Seeker.c
Normal file
@@ -0,0 +1,574 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
extern void Boba_FireDecide( void );
|
||||
|
||||
void Seeker_Strafe( void );
|
||||
|
||||
#define VELOCITY_DECAY 0.7f
|
||||
|
||||
#define MIN_MELEE_RANGE 320
|
||||
#define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
|
||||
|
||||
#define MIN_DISTANCE 80
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define SEEKER_STRAFE_VEL 100
|
||||
#define SEEKER_STRAFE_DIS 200
|
||||
#define SEEKER_UPWARD_PUSH 32
|
||||
|
||||
#define SEEKER_FORWARD_BASE_SPEED 10
|
||||
#define SEEKER_FORWARD_MULTIPLIER 2
|
||||
|
||||
#define SEEKER_SEEK_RADIUS 1024
|
||||
|
||||
//------------------------------------
|
||||
void NPC_Seeker_Precache(void)
|
||||
{
|
||||
G_SoundIndex("sound/chars/seeker/misc/fire.wav");
|
||||
G_SoundIndex( "sound/chars/seeker/misc/hiss.wav");
|
||||
G_EffectIndex( "env/small_explode");
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void NPC_Seeker_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
if ( !(self->NPC->aiFlags&NPCAI_CUSTOM_GRAVITY ))
|
||||
{//void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod, int hitLoc=HL_NONE );
|
||||
G_Damage( self, NULL, NULL, (float*)vec3_origin, (float*)vec3_origin, 999, 0, MOD_FALLING );
|
||||
}
|
||||
|
||||
SaveNPCGlobals();
|
||||
SetNPCGlobals( self );
|
||||
Seeker_Strafe();
|
||||
RestoreNPCGlobals();
|
||||
NPC_Pain( self, attacker, damage );
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_MaintainHeight( void )
|
||||
{
|
||||
float dif;
|
||||
|
||||
// Update our angles regardless
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
// If we have an enemy, we should try to hover at or a little below enemy eye level
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
if (TIMER_Done( NPC, "heightChange" ))
|
||||
{
|
||||
float difFactor;
|
||||
|
||||
TIMER_Set( NPC,"heightChange",Q_irand( 1000, 3000 ));
|
||||
|
||||
// Find the height difference
|
||||
dif = (NPC->enemy->r.currentOrigin[2] + flrand( NPC->enemy->r.maxs[2]/2, NPC->enemy->r.maxs[2]+8 )) - NPC->r.currentOrigin[2];
|
||||
|
||||
difFactor = 1.0f;
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "flameTime" ) )
|
||||
{
|
||||
difFactor = 10.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// cap to prevent dramatic height shifts
|
||||
if ( fabs( dif ) > 2*difFactor )
|
||||
{
|
||||
if ( fabs( dif ) > 24*difFactor )
|
||||
{
|
||||
dif = ( dif < 0 ? -24*difFactor : 24*difFactor );
|
||||
}
|
||||
|
||||
NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
|
||||
}
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= flrand( 0.85f, 3.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gentity_t *goal = NULL;
|
||||
|
||||
if ( NPCInfo->goalEntity ) // Is there a goal?
|
||||
{
|
||||
goal = NPCInfo->goalEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
goal = NPCInfo->lastGoalEntity;
|
||||
}
|
||||
if ( goal )
|
||||
{
|
||||
dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
|
||||
|
||||
if ( fabs( dif ) > 24 )
|
||||
{
|
||||
ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply friction
|
||||
if ( NPC->client->ps.velocity[0] )
|
||||
{
|
||||
NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->client->ps.velocity[1] )
|
||||
{
|
||||
NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_Strafe( void )
|
||||
{
|
||||
int side;
|
||||
vec3_t end, right, dir;
|
||||
trace_t tr;
|
||||
|
||||
if ( random() > 0.7f || !NPC->enemy || !NPC->enemy->client )
|
||||
{
|
||||
// Do a regular style strafe
|
||||
AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
|
||||
|
||||
// Pick a random strafe direction, then check to see if doing a strafe would be
|
||||
// reasonably valid
|
||||
side = ( rand() & 1 ) ? -1 : 1;
|
||||
VectorMA( NPC->r.currentOrigin, SEEKER_STRAFE_DIS * side, right, end );
|
||||
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
|
||||
|
||||
// Close enough
|
||||
if ( tr.fraction > 0.9f )
|
||||
{
|
||||
float vel = SEEKER_STRAFE_VEL;
|
||||
float upPush = SEEKER_UPWARD_PUSH;
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT )
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
|
||||
}
|
||||
else
|
||||
{
|
||||
vel *= 3.0f;
|
||||
upPush *= 4.0f;
|
||||
}
|
||||
VectorMA( NPC->client->ps.velocity, vel*side, right, NPC->client->ps.velocity );
|
||||
// Add a slight upward push
|
||||
NPC->client->ps.velocity[2] += upPush;
|
||||
|
||||
NPCInfo->standTime = level.time + 1000 + random() * 500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float stDis;
|
||||
|
||||
// Do a strafe to try and keep on the side of their enemy
|
||||
AngleVectors( NPC->enemy->client->renderInfo.eyeAngles, dir, right, NULL );
|
||||
|
||||
// Pick a random side
|
||||
side = ( rand() & 1 ) ? -1 : 1;
|
||||
stDis = SEEKER_STRAFE_DIS;
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
stDis *= 2.0f;
|
||||
}
|
||||
VectorMA( NPC->enemy->r.currentOrigin, stDis * side, right, end );
|
||||
|
||||
// then add a very small bit of random in front of/behind the player action
|
||||
VectorMA( end, crandom() * 25, dir, end );
|
||||
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
|
||||
|
||||
// Close enough
|
||||
if ( tr.fraction > 0.9f )
|
||||
{
|
||||
float dis, upPush;
|
||||
|
||||
VectorSubtract( tr.endpos, NPC->r.currentOrigin, dir );
|
||||
dir[2] *= 0.25; // do less upward change
|
||||
dis = VectorNormalize( dir );
|
||||
|
||||
// Try to move the desired enemy side
|
||||
VectorMA( NPC->client->ps.velocity, dis, dir, NPC->client->ps.velocity );
|
||||
|
||||
upPush = SEEKER_UPWARD_PUSH;
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT )
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
|
||||
}
|
||||
else
|
||||
{
|
||||
upPush *= 4.0f;
|
||||
}
|
||||
|
||||
// Add a slight upward push
|
||||
NPC->client->ps.velocity[2] += upPush;
|
||||
|
||||
NPCInfo->standTime = level.time + 2500 + random() * 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_Hunt( qboolean visible, qboolean advance )
|
||||
{
|
||||
float distance, speed;
|
||||
vec3_t forward;
|
||||
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
// If we're not supposed to stand still, pursue the player
|
||||
if ( NPCInfo->standTime < level.time )
|
||||
{
|
||||
// Only strafe when we can see the player
|
||||
if ( visible )
|
||||
{
|
||||
Seeker_Strafe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't want to advance, stop here
|
||||
if ( advance == qfalse )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Only try and navigate if the player is visible
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
// Move towards our goal
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 24;
|
||||
|
||||
// Get our direction from the navigator if we can't see our target
|
||||
if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
|
||||
distance = VectorNormalize( forward );
|
||||
}
|
||||
|
||||
speed = SEEKER_FORWARD_BASE_SPEED + SEEKER_FORWARD_MULTIPLIER * g_spskill.integer;
|
||||
VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_Fire( void )
|
||||
{
|
||||
vec3_t dir, enemy_org, muzzle;
|
||||
gentity_t *missile;
|
||||
|
||||
CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org );
|
||||
VectorSubtract( enemy_org, NPC->r.currentOrigin, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
// move a bit forward in the direction we shall shoot in so that the bolt doesn't poke out the other side of the seeker
|
||||
VectorMA( NPC->r.currentOrigin, 15, dir, muzzle );
|
||||
|
||||
missile = CreateMissile( muzzle, dir, 1000, 10000, NPC, qfalse );
|
||||
|
||||
G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), NPC->r.currentOrigin, dir );
|
||||
|
||||
missile->classname = "blaster";
|
||||
missile->s.weapon = WP_BLASTER;
|
||||
|
||||
missile->damage = 5;
|
||||
missile->dflags = DAMAGE_DEATH_KNOCKBACK;
|
||||
missile->methodOfDeath = MOD_BLASTER;
|
||||
missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
|
||||
if ( NPC->r.ownerNum < ENTITYNUM_NONE )
|
||||
{
|
||||
missile->r.ownerNum = NPC->r.ownerNum;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_Ranged( qboolean visible, qboolean advance )
|
||||
{
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT )
|
||||
{
|
||||
if ( NPC->count > 0 )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "attackDelay" )) // Attack?
|
||||
{
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 250, 2500 ));
|
||||
Seeker_Fire();
|
||||
NPC->count--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// out of ammo, so let it die...give it a push up so it can fall more and blow up on impact
|
||||
// NPC->client->ps.gravity = 900;
|
||||
// NPC->svFlags &= ~SVF_CUSTOM_GRAVITY;
|
||||
// NPC->client->ps.velocity[2] += 16;
|
||||
G_Damage( NPC, NPC, NPC, NULL, NULL, 999, 0, MOD_UNKNOWN );
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Seeker_Hunt( visible, advance );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_Attack( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean visible;
|
||||
qboolean advance;
|
||||
|
||||
// Always keep a good height off the ground
|
||||
Seeker_MaintainHeight();
|
||||
|
||||
// Rate our distance to the target, and our visibilty
|
||||
distance = DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
visible = NPC_ClearLOS4( NPC->enemy );
|
||||
advance = (qboolean)(distance > MIN_DISTANCE_SQR);
|
||||
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
advance = (qboolean)(distance>(200.0f*200.0f));
|
||||
}
|
||||
|
||||
// If we cannot see our target, move to see it
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Seeker_Hunt( visible, advance );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Seeker_Ranged( visible, advance );
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_FindEnemy( void )
|
||||
{
|
||||
int numFound;
|
||||
float dis, bestDis = SEEKER_SEEK_RADIUS * SEEKER_SEEK_RADIUS + 1;
|
||||
vec3_t mins, maxs;
|
||||
int entityList[MAX_GENTITIES];
|
||||
gentity_t *ent, *best = NULL;
|
||||
int i;
|
||||
|
||||
VectorSet( maxs, SEEKER_SEEK_RADIUS, SEEKER_SEEK_RADIUS, SEEKER_SEEK_RADIUS );
|
||||
VectorScale( maxs, -1, mins );
|
||||
|
||||
numFound = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
|
||||
|
||||
for ( i = 0 ; i < numFound ; i++ )
|
||||
{
|
||||
ent = &g_entities[entityList[i]];
|
||||
|
||||
if ( ent->s.number == NPC->s.number
|
||||
|| !ent->client //&& || !ent->NPC
|
||||
|| ent->health <= 0
|
||||
|| !ent->inuse )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ent->client->playerTeam == NPC->client->playerTeam || ent->client->playerTeam == NPCTEAM_NEUTRAL ) // don't attack same team or bots
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// try to find the closest visible one
|
||||
if ( !NPC_ClearLOS4( ent ))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
dis = DistanceHorizontalSquared( NPC->r.currentOrigin, ent->r.currentOrigin );
|
||||
|
||||
if ( dis <= bestDis )
|
||||
{
|
||||
bestDis = dis;
|
||||
best = ent;
|
||||
}
|
||||
}
|
||||
|
||||
if ( best )
|
||||
{
|
||||
// used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method.
|
||||
NPC->random = random() * 6.3f; // roughly 2pi
|
||||
|
||||
NPC->enemy = best;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void Seeker_FollowOwner( void )
|
||||
{
|
||||
float dis, minDistSqr;
|
||||
vec3_t pt, dir;
|
||||
gentity_t *owner = &g_entities[NPC->s.owner];
|
||||
|
||||
Seeker_MaintainHeight();
|
||||
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
owner = NPC->enemy;
|
||||
}
|
||||
if ( !owner || owner == NPC || !owner->client )
|
||||
{
|
||||
return;
|
||||
}
|
||||
//rwwFIXMEFIXME: Care about all clients not just 0
|
||||
dis = DistanceHorizontalSquared( NPC->r.currentOrigin, owner->r.currentOrigin );
|
||||
|
||||
minDistSqr = MIN_DISTANCE_SQR;
|
||||
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "flameTime" ) )
|
||||
{
|
||||
minDistSqr = 200*200;
|
||||
}
|
||||
}
|
||||
|
||||
if ( dis < minDistSqr )
|
||||
{
|
||||
// generally circle the player closely till we take an enemy..this is our target point
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
pt[0] = owner->r.currentOrigin[0] + cos( level.time * 0.001f + NPC->random ) * 250;
|
||||
pt[1] = owner->r.currentOrigin[1] + sin( level.time * 0.001f + NPC->random ) * 250;
|
||||
if ( NPC->client->jetPackTime < level.time )
|
||||
{
|
||||
pt[2] = NPC->r.currentOrigin[2] - 64;
|
||||
}
|
||||
else
|
||||
{
|
||||
pt[2] = owner->r.currentOrigin[2] + 200;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pt[0] = owner->r.currentOrigin[0] + cos( level.time * 0.001f + NPC->random ) * 56;
|
||||
pt[1] = owner->r.currentOrigin[1] + sin( level.time * 0.001f + NPC->random ) * 56;
|
||||
pt[2] = owner->r.currentOrigin[2] + 40;
|
||||
}
|
||||
|
||||
VectorSubtract( pt, NPC->r.currentOrigin, dir );
|
||||
VectorMA( NPC->client->ps.velocity, 0.8f, dir, NPC->client->ps.velocity );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "seekerhiss" ))
|
||||
{
|
||||
TIMER_Set( NPC, "seekerhiss", 1000 + random() * 1000 );
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
|
||||
}
|
||||
}
|
||||
|
||||
// Hey come back!
|
||||
NPCInfo->goalEntity = owner;
|
||||
NPCInfo->goalRadius = 32;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPC->parent = owner;
|
||||
}
|
||||
|
||||
if ( NPCInfo->enemyCheckDebounceTime < level.time )
|
||||
{
|
||||
// check twice a second to find a new enemy
|
||||
Seeker_FindEnemy();
|
||||
NPCInfo->enemyCheckDebounceTime = level.time + 500;
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
void NPC_BSSeeker_Default( void )
|
||||
{
|
||||
/*
|
||||
if ( in_camera )
|
||||
{
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT )
|
||||
{
|
||||
// cameras make me commit suicide....
|
||||
G_Damage( NPC, NPC, NPC, NULL, NULL, 999, 0, MOD_UNKNOWN );
|
||||
}
|
||||
}
|
||||
*/
|
||||
//N/A for MP.
|
||||
if ( NPC->r.ownerNum < ENTITYNUM_NONE )
|
||||
{
|
||||
gentity_t *owner = &g_entities[0];
|
||||
if ( owner->health <= 0
|
||||
|| (owner->client && owner->client->pers.connected == CON_DISCONNECTED) )
|
||||
{//owner is dead or gone
|
||||
//remove me
|
||||
G_Damage( NPC, NULL, NULL, NULL, NULL, 10000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->random == 0.0f )
|
||||
{
|
||||
// used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method.
|
||||
NPC->random = random() * 6.3f; // roughly 2pi
|
||||
}
|
||||
|
||||
if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse )
|
||||
{
|
||||
if ( NPC->client->NPC_class != CLASS_BOBAFETT && ( NPC->enemy->s.number == 0 || ( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_SEEKER )) )
|
||||
{
|
||||
//hacked to never take the player as an enemy, even if the player shoots at it
|
||||
NPC->enemy = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Seeker_Attack();
|
||||
if ( NPC->client->NPC_class == CLASS_BOBAFETT )
|
||||
{
|
||||
Boba_FireDecide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// In all other cases, follow the player and look for enemies to take on
|
||||
Seeker_FollowOwner();
|
||||
}
|
||||
577
codemp/game/NPC_AI_Sentry.c
Normal file
577
codemp/game/NPC_AI_Sentry.c
Normal file
@@ -0,0 +1,577 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern gitem_t *BG_FindItemForAmmo( ammo_t ammo );
|
||||
#include "../namespace_end.h"
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
|
||||
#define MIN_DISTANCE 256
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define SENTRY_FORWARD_BASE_SPEED 10
|
||||
#define SENTRY_FORWARD_MULTIPLIER 5
|
||||
|
||||
#define SENTRY_VELOCITY_DECAY 0.85f
|
||||
#define SENTRY_STRAFE_VEL 256
|
||||
#define SENTRY_STRAFE_DIS 200
|
||||
#define SENTRY_UPWARD_PUSH 32
|
||||
#define SENTRY_HOVER_HEIGHT 24
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
LSTATE_ASLEEP,
|
||||
LSTATE_WAKEUP,
|
||||
LSTATE_ACTIVE,
|
||||
LSTATE_POWERING_UP,
|
||||
LSTATE_ATTACKING,
|
||||
};
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Sentry_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Sentry_Precache(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_explo" );
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_pain" );
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_open" );
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_shield_close" );
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_1_lp" );
|
||||
G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_2_lp" );
|
||||
|
||||
for ( i = 1; i < 4; i++)
|
||||
{
|
||||
G_SoundIndex( va( "sound/chars/sentry/misc/talk%d", i ) );
|
||||
}
|
||||
|
||||
G_EffectIndex( "bryar/muzzle_flash");
|
||||
G_EffectIndex( "env/med_explode");
|
||||
|
||||
RegisterItem( BG_FindItemForAmmo( AMMO_BLASTER ));
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
sentry_use
|
||||
================
|
||||
*/
|
||||
void sentry_use( gentity_t *self, gentity_t *other, gentity_t *activator)
|
||||
{
|
||||
G_ActivateBehavior(self,BSET_USE);
|
||||
|
||||
self->flags &= ~FL_SHIELDED;
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
// self->NPC->localState = LSTATE_WAKEUP;
|
||||
self->NPC->localState = LSTATE_ACTIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Sentry_Pain
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Sentry_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
int mod = gPainMOD;
|
||||
|
||||
NPC_Pain( self, attacker, damage );
|
||||
|
||||
if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT )
|
||||
{
|
||||
self->NPC->burstCount = 0;
|
||||
TIMER_Set( self, "attackDelay", Q_irand( 9000, 12000) );
|
||||
self->flags |= FL_SHIELDED;
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_FLY_SHIELDED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
G_Sound( self, CHAN_AUTO, G_SoundIndex("sound/chars/sentry/misc/sentry_pain") );
|
||||
|
||||
self->NPC->localState = LSTATE_ACTIVE;
|
||||
}
|
||||
|
||||
// You got hit, go after the enemy
|
||||
// if (self->NPC->localState == LSTATE_ASLEEP)
|
||||
// {
|
||||
// G_Sound( self, G_SoundIndex("sound/chars/sentry/misc/shieldsopen.wav"));
|
||||
//
|
||||
// self->flags &= ~FL_SHIELDED;
|
||||
// NPC_SetAnim( self, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
// self->NPC->localState = LSTATE_WAKEUP;
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_Fire
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_Fire (void)
|
||||
{
|
||||
vec3_t muzzle;
|
||||
static vec3_t forward, vright, up;
|
||||
gentity_t *missile;
|
||||
mdxaBone_t boltMatrix;
|
||||
int bolt;
|
||||
int which;
|
||||
|
||||
NPC->flags &= ~FL_SHIELDED;
|
||||
|
||||
if ( NPCInfo->localState == LSTATE_POWERING_UP )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "powerup" ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_ATTACKING;
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't do anything right now
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ( NPCInfo->localState == LSTATE_ACTIVE )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_POWERING_UP;
|
||||
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex("sound/chars/sentry/misc/sentry_shield_open") );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_POWERUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "powerup", 250 );
|
||||
return;
|
||||
}
|
||||
else if ( NPCInfo->localState != LSTATE_ATTACKING )
|
||||
{
|
||||
// bad because we are uninitialized
|
||||
NPCInfo->localState = LSTATE_ACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Which muzzle to fire from?
|
||||
which = NPCInfo->burstCount % 3;
|
||||
switch( which )
|
||||
{
|
||||
case 0:
|
||||
bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash1");
|
||||
break;
|
||||
case 1:
|
||||
bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash2");
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
bolt = trap_G2API_AddBolt(NPC->ghoul2, 0, "*flash03");
|
||||
}
|
||||
|
||||
trap_G2API_GetBoltMatrix( NPC->ghoul2, 0,
|
||||
bolt,
|
||||
&boltMatrix, NPC->r.currentAngles, NPC->r.currentOrigin, level.time,
|
||||
NULL, NPC->modelScale );
|
||||
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, muzzle );
|
||||
|
||||
AngleVectors( NPC->r.currentAngles, forward, vright, up );
|
||||
// G_Sound( NPC, G_SoundIndex("sound/chars/sentry/misc/shoot.wav"));
|
||||
|
||||
G_PlayEffectID( G_EffectIndex("bryar/muzzle_flash"), muzzle, forward );
|
||||
|
||||
missile = CreateMissile( muzzle, forward, 1600, 10000, NPC, qfalse );
|
||||
|
||||
missile->classname = "bryar_proj";
|
||||
missile->s.weapon = WP_BRYAR_PISTOL;
|
||||
|
||||
missile->dflags = DAMAGE_DEATH_KNOCKBACK;
|
||||
missile->methodOfDeath = MOD_BRYAR_PISTOL;
|
||||
missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
|
||||
|
||||
NPCInfo->burstCount++;
|
||||
NPC->attackDebounceTime = level.time + 50;
|
||||
missile->damage = 5;
|
||||
|
||||
// now scale for difficulty
|
||||
if ( g_spskill.integer == 0 )
|
||||
{
|
||||
NPC->attackDebounceTime += 200;
|
||||
missile->damage = 1;
|
||||
}
|
||||
else if ( g_spskill.integer == 1 )
|
||||
{
|
||||
NPC->attackDebounceTime += 100;
|
||||
missile->damage = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_MaintainHeight
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_MaintainHeight( void )
|
||||
{
|
||||
float dif;
|
||||
|
||||
NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_1_lp" );
|
||||
|
||||
// Update our angles regardless
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
// If we have an enemy, we should try to hover at about enemy eye level
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
// Find the height difference
|
||||
dif = (NPC->enemy->r.currentOrigin[2]+NPC->enemy->r.maxs[2]) - NPC->r.currentOrigin[2];
|
||||
|
||||
// cap to prevent dramatic height shifts
|
||||
if ( fabs( dif ) > 8 )
|
||||
{
|
||||
if ( fabs( dif ) > SENTRY_HOVER_HEIGHT )
|
||||
{
|
||||
dif = ( dif < 0 ? -24 : 24 );
|
||||
}
|
||||
|
||||
NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gentity_t *goal = NULL;
|
||||
|
||||
if ( NPCInfo->goalEntity ) // Is there a goal?
|
||||
{
|
||||
goal = NPCInfo->goalEntity;
|
||||
}
|
||||
else
|
||||
{
|
||||
goal = NPCInfo->lastGoalEntity;
|
||||
}
|
||||
|
||||
if (goal)
|
||||
{
|
||||
dif = goal->r.currentOrigin[2] - NPC->r.currentOrigin[2];
|
||||
|
||||
if ( fabs( dif ) > SENTRY_HOVER_HEIGHT )
|
||||
{
|
||||
ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= SENTRY_VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply friction to Z
|
||||
else if ( NPC->client->ps.velocity[2] )
|
||||
{
|
||||
NPC->client->ps.velocity[2] *= SENTRY_VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply friction
|
||||
if ( NPC->client->ps.velocity[0] )
|
||||
{
|
||||
NPC->client->ps.velocity[0] *= SENTRY_VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC->client->ps.velocity[1] )
|
||||
{
|
||||
NPC->client->ps.velocity[1] *= SENTRY_VELOCITY_DECAY;
|
||||
|
||||
if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
|
||||
{
|
||||
NPC->client->ps.velocity[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
NPC_FaceEnemy( qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_Idle( void )
|
||||
{
|
||||
Sentry_MaintainHeight();
|
||||
|
||||
// Is he waking up?
|
||||
if (NPCInfo->localState == LSTATE_WAKEUP)
|
||||
{
|
||||
if (NPC->client->ps.torsoTimer<=0)
|
||||
{
|
||||
NPCInfo->scriptFlags |= SCF_LOOK_FOR_ENEMIES;
|
||||
NPCInfo->burstCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_SLEEP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
NPC->flags |= FL_SHIELDED;
|
||||
|
||||
NPC_BSIdle();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_Strafe
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_Strafe( void )
|
||||
{
|
||||
int dir;
|
||||
vec3_t end, right;
|
||||
trace_t tr;
|
||||
|
||||
AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
|
||||
|
||||
// Pick a random strafe direction, then check to see if doing a strafe would be
|
||||
// reasonable valid
|
||||
dir = ( rand() & 1 ) ? -1 : 1;
|
||||
VectorMA( NPC->r.currentOrigin, SENTRY_STRAFE_DIS * dir, right, end );
|
||||
|
||||
trap_Trace( &tr, NPC->r.currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
|
||||
|
||||
// Close enough
|
||||
if ( tr.fraction > 0.9f )
|
||||
{
|
||||
VectorMA( NPC->client->ps.velocity, SENTRY_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
|
||||
|
||||
// Add a slight upward push
|
||||
NPC->client->ps.velocity[2] += SENTRY_UPWARD_PUSH;
|
||||
|
||||
// Set the strafe start time so we can do a controlled roll
|
||||
// NPC->fx_time = level.time;
|
||||
NPCInfo->standTime = level.time + 3000 + random() * 500;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_Hunt
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_Hunt( qboolean visible, qboolean advance )
|
||||
{
|
||||
float distance, speed;
|
||||
vec3_t forward;
|
||||
|
||||
//If we're not supposed to stand still, pursue the player
|
||||
if ( NPCInfo->standTime < level.time )
|
||||
{
|
||||
// Only strafe when we can see the player
|
||||
if ( visible )
|
||||
{
|
||||
Sentry_Strafe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If we don't want to advance, stop here
|
||||
if ( !advance && visible )
|
||||
return;
|
||||
|
||||
//Only try and navigate if the player is visible
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
// Move towards our goal
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = 12;
|
||||
|
||||
//Get our direction from the navigator if we can't see our target
|
||||
if ( NPC_GetMoveDirection( forward, &distance ) == qfalse )
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, forward );
|
||||
distance = VectorNormalize( forward );
|
||||
}
|
||||
|
||||
speed = SENTRY_FORWARD_BASE_SPEED + SENTRY_FORWARD_MULTIPLIER * g_spskill.integer;
|
||||
VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_RangedAttack
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_RangedAttack( qboolean visible, qboolean advance )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "attackDelay" ) && NPC->attackDebounceTime < level.time && visible ) // Attack?
|
||||
{
|
||||
if ( NPCInfo->burstCount > 6 )
|
||||
{
|
||||
if ( !NPC->fly_sound_debounce_time )
|
||||
{//delay closing down to give the player an opening
|
||||
NPC->fly_sound_debounce_time = level.time + Q_irand( 500, 2000 );
|
||||
}
|
||||
else if ( NPC->fly_sound_debounce_time < level.time )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_ACTIVE;
|
||||
NPC->fly_sound_debounce_time = NPCInfo->burstCount = 0;
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 2000, 3500) );
|
||||
NPC->flags |= FL_SHIELDED;
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_FLY_SHIELDED, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/sentry/misc/sentry_shield_close" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Sentry_Fire();
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Sentry_Hunt( visible, advance );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Sentry_AttackDecision
|
||||
-------------------------
|
||||
*/
|
||||
void Sentry_AttackDecision( void )
|
||||
{
|
||||
float distance;
|
||||
qboolean visible;
|
||||
qboolean advance;
|
||||
|
||||
// Always keep a good height off the ground
|
||||
Sentry_MaintainHeight();
|
||||
|
||||
NPC->s.loopSound = G_SoundIndex( "sound/chars/sentry/misc/sentry_hover_2_lp" );
|
||||
|
||||
//randomly talk
|
||||
if ( TIMER_Done(NPC,"patrolNoise") )
|
||||
{
|
||||
if (TIMER_Done(NPC,"angerNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/sentry/misc/talk%d", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// He's dead.
|
||||
if (NPC->enemy->health<1)
|
||||
{
|
||||
NPC->enemy = NULL;
|
||||
Sentry_Idle();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have an enemy, just idle
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )
|
||||
{
|
||||
Sentry_Idle();
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate our distance to the target and visibilty
|
||||
distance = (int) DistanceHorizontalSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
visible = NPC_ClearLOS4( NPC->enemy );
|
||||
advance = (qboolean)(distance > MIN_DISTANCE_SQR);
|
||||
|
||||
// If we cannot see our target, move to see it
|
||||
if ( visible == qfalse )
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
|
||||
{
|
||||
Sentry_Hunt( visible, advance );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
Sentry_RangedAttack( visible, advance );
|
||||
}
|
||||
|
||||
qboolean NPC_CheckPlayerTeamStealth( void );
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Sentry_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Sentry_Patrol( void )
|
||||
{
|
||||
Sentry_MaintainHeight();
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if (!NPC->enemy)
|
||||
{
|
||||
if ( NPC_CheckPlayerTeamStealth() )
|
||||
{
|
||||
//NPC_AngerSound();
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
//start loop sound once we move
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
//randomly talk
|
||||
if (TIMER_Done(NPC,"patrolNoise"))
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/sentry/misc/talk%d", Q_irand(1, 3)) );
|
||||
|
||||
TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSSentry_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSSentry_Default( void )
|
||||
{
|
||||
if ( NPC->targetname )
|
||||
{
|
||||
NPC->use = sentry_use;
|
||||
}
|
||||
|
||||
if (( NPC->enemy ) && (NPCInfo->localState != LSTATE_WAKEUP))
|
||||
{
|
||||
// Don't attack if waking up or if no enemy
|
||||
Sentry_AttackDecision();
|
||||
}
|
||||
else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
NPC_Sentry_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Sentry_Idle();
|
||||
}
|
||||
}
|
||||
864
codemp/game/NPC_AI_Sniper.c
Normal file
864
codemp/game/NPC_AI_Sniper.c
Normal file
@@ -0,0 +1,864 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
#include "anims.h"
|
||||
|
||||
extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
|
||||
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
|
||||
extern void NPC_TempLookTarget( gentity_t *self, int lookEntNum, int minLookTime, int maxLookTime );
|
||||
extern qboolean G_ExpandPointToBBox( vec3_t point, const vec3_t mins, const vec3_t maxs, int ignore, int clipmask );
|
||||
extern qboolean FlyingCreature( gentity_t *ent );
|
||||
|
||||
#define SPF_NO_HIDE 2
|
||||
|
||||
#define MAX_VIEW_DIST 1024
|
||||
#define MAX_VIEW_SPEED 250
|
||||
#define MAX_LIGHT_INTENSITY 255
|
||||
#define MIN_LIGHT_THRESHOLD 0.1
|
||||
|
||||
#define DISTANCE_SCALE 0.25f
|
||||
#define DISTANCE_THRESHOLD 0.075f
|
||||
#define SPEED_SCALE 0.25f
|
||||
#define FOV_SCALE 0.5f
|
||||
#define LIGHT_SCALE 0.25f
|
||||
|
||||
#define REALIZE_THRESHOLD 0.6f
|
||||
#define CAUTIOUS_THRESHOLD ( REALIZE_THRESHOLD * 0.75 )
|
||||
|
||||
qboolean NPC_CheckPlayerTeamStealth( void );
|
||||
|
||||
static qboolean enemyLOS2;
|
||||
static qboolean enemyCS2;
|
||||
static qboolean faceEnemy2;
|
||||
static qboolean move2;
|
||||
static qboolean shoot2;
|
||||
static float enemyDist2;
|
||||
|
||||
//Local state enums
|
||||
enum
|
||||
{
|
||||
LSTATE_NONE = 0,
|
||||
LSTATE_UNDERFIRE,
|
||||
LSTATE_INVESTIGATE,
|
||||
};
|
||||
|
||||
void Sniper_ClearTimers( gentity_t *ent )
|
||||
{
|
||||
TIMER_Set( ent, "chatter", 0 );
|
||||
TIMER_Set( ent, "duck", 0 );
|
||||
TIMER_Set( ent, "stand", 0 );
|
||||
TIMER_Set( ent, "shuffleTime", 0 );
|
||||
TIMER_Set( ent, "sleepTime", 0 );
|
||||
TIMER_Set( ent, "enemyLastVisible", 0 );
|
||||
TIMER_Set( ent, "roamTime", 0 );
|
||||
TIMER_Set( ent, "hideTime", 0 );
|
||||
TIMER_Set( ent, "attackDelay", 0 ); //FIXME: Slant for difficulty levels
|
||||
TIMER_Set( ent, "stick", 0 );
|
||||
TIMER_Set( ent, "scoutTime", 0 );
|
||||
TIMER_Set( ent, "flee", 0 );
|
||||
}
|
||||
|
||||
void NPC_Sniper_PlayConfusionSound( gentity_t *self )
|
||||
{//FIXME: make this a custom sound in sound set
|
||||
if ( self->health > 0 )
|
||||
{
|
||||
G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
|
||||
}
|
||||
//reset him to be totally unaware again
|
||||
TIMER_Set( self, "enemyLastVisible", 0 );
|
||||
TIMER_Set( self, "flee", 0 );
|
||||
self->NPC->squadState = SQUAD_IDLE;
|
||||
self->NPC->tempBehavior = BS_DEFAULT;
|
||||
|
||||
//self->NPC->behaviorState = BS_PATROL;
|
||||
G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
|
||||
|
||||
self->NPC->investigateCount = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ST_Pain
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_Sniper_Pain(gentity_t *self, gentity_t *attacker, int damage)
|
||||
{
|
||||
self->NPC->localState = LSTATE_UNDERFIRE;
|
||||
|
||||
TIMER_Set( self, "duck", -1 );
|
||||
TIMER_Set( self, "stand", 2000 );
|
||||
|
||||
NPC_Pain( self, attacker, damage );
|
||||
|
||||
if ( !damage && self->health > 0 )
|
||||
{//FIXME: better way to know I was pushed
|
||||
G_AddVoiceEvent( self, Q_irand(EV_PUSHED1, EV_PUSHED3), 2000 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_HoldPosition
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Sniper_HoldPosition( void )
|
||||
{
|
||||
NPC_FreeCombatPoint( NPCInfo->combatPoint, qtrue );
|
||||
NPCInfo->goalEntity = NULL;
|
||||
|
||||
/*if ( TIMER_Done( NPC, "stand" ) )
|
||||
{//FIXME: what if can't shoot from this pos?
|
||||
TIMER_Set( NPC, "duck", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_Move
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static qboolean Sniper_Move( void )
|
||||
{
|
||||
qboolean moved;
|
||||
navInfo_t info;
|
||||
|
||||
NPCInfo->combatMove = qtrue;//always move straight toward our goal
|
||||
|
||||
moved = NPC_MoveToGoal( qtrue );
|
||||
|
||||
//Get the move info
|
||||
NAV_GetLastMove( &info );
|
||||
|
||||
//FIXME: if we bump into another one of our guys and can't get around him, just stop!
|
||||
//If we hit our target, then stop and fire!
|
||||
if ( info.flags & NIF_COLLISION )
|
||||
{
|
||||
if ( info.blocker == NPC->enemy )
|
||||
{
|
||||
Sniper_HoldPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//If our move failed, then reset
|
||||
if ( moved == qfalse )
|
||||
{//couldn't get to enemy
|
||||
if ( (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) && NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy )
|
||||
{//we were running after enemy
|
||||
//Try to find a combat point that can hit the enemy
|
||||
int cpFlags = (CP_CLEAR|CP_HAS_ROUTE);
|
||||
int cp;
|
||||
if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST )
|
||||
{
|
||||
cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST);
|
||||
cpFlags |= CP_NEAREST;
|
||||
}
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->r.currentOrigin, cpFlags, 32, -1 );
|
||||
if ( cp == -1 && !(NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) )
|
||||
{//okay, try one by the enemy
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->enemy->r.currentOrigin, CP_CLEAR|CP_HAS_ROUTE|CP_HORZ_DIST_COLL, 32, -1 );
|
||||
}
|
||||
//NOTE: there may be a perfectly valid one, just not one within CP_COLLECT_RADIUS of either me or him...
|
||||
if ( cp != -1 )
|
||||
{//found a combat point that has a clear shot to enemy
|
||||
NPC_SetCombatPoint( cp );
|
||||
NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL );
|
||||
return moved;
|
||||
}
|
||||
}
|
||||
//just hang here
|
||||
Sniper_HoldPosition();
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSSniper_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_BSSniper_Patrol( void )
|
||||
{//FIXME: pick up on bodies of dead buddies?
|
||||
NPC->count = 0;
|
||||
|
||||
if ( NPCInfo->confusionTime < level.time )
|
||||
{
|
||||
//Look for any enemies
|
||||
if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
if ( NPC_CheckPlayerTeamStealth() )
|
||||
{
|
||||
//NPCInfo->behaviorState = BS_HUNT_AND_KILL;//Should be auto now
|
||||
//NPC_AngerSound();
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
|
||||
{
|
||||
//Is there danger nearby
|
||||
int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS );
|
||||
if ( NPC_CheckForDanger( alertEvent ) )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{//check for other alert events
|
||||
//There is an event to look at
|
||||
if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
|
||||
{
|
||||
NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID;
|
||||
if ( level.alertEvents[alertEvent].level == AEL_DISCOVERED )
|
||||
{
|
||||
if ( level.alertEvents[alertEvent].owner &&
|
||||
level.alertEvents[alertEvent].owner->client &&
|
||||
level.alertEvents[alertEvent].owner->health >= 0 &&
|
||||
level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
|
||||
{//an enemy
|
||||
G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
|
||||
//NPCInfo->enemyLastSeenTime = level.time;
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( (6-NPCInfo->stats.aim)*100, (6-NPCInfo->stats.aim)*500 ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//FIXME: get more suspicious over time?
|
||||
//Save the position for movement (if necessary)
|
||||
//FIXME: sound?
|
||||
VectorCopy( level.alertEvents[alertEvent].position, NPCInfo->investigateGoal );
|
||||
NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 );
|
||||
if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS )
|
||||
{//suspicious looks longer
|
||||
NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->investigateDebounceTime > level.time )
|
||||
{//FIXME: walk over to it, maybe? Not if not chase enemies flag
|
||||
//NOTE: stops walking or doing anything else below
|
||||
vec3_t dir, angles;
|
||||
float o_yaw, o_pitch;
|
||||
|
||||
VectorSubtract( NPCInfo->investigateGoal, NPC->client->renderInfo.eyePoint, dir );
|
||||
vectoangles( dir, angles );
|
||||
|
||||
o_yaw = NPCInfo->desiredYaw;
|
||||
o_pitch = NPCInfo->desiredPitch;
|
||||
NPCInfo->desiredYaw = angles[YAW];
|
||||
NPCInfo->desiredPitch = angles[PITCH];
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
|
||||
NPCInfo->desiredYaw = o_yaw;
|
||||
NPCInfo->desiredPitch = o_pitch;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSSniper_Idle
|
||||
-------------------------
|
||||
*/
|
||||
/*
|
||||
void NPC_BSSniper_Idle( void )
|
||||
{
|
||||
//reset our shotcount
|
||||
NPC->count = 0;
|
||||
|
||||
//FIXME: check for other alert events?
|
||||
|
||||
//Is there danger nearby?
|
||||
if ( NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue ) ) )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
TIMER_Set( NPC, "roamTime", 2000 + Q_irand( 1000, 2000 ) );
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
*/
|
||||
/*
|
||||
-------------------------
|
||||
ST_CheckMoveState
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Sniper_CheckMoveState( void )
|
||||
{
|
||||
//See if we're a scout
|
||||
if ( !(NPCInfo->scriptFlags & SCF_CHASE_ENEMIES) )//NPCInfo->behaviorState == BS_STAND_AND_SHOOT )
|
||||
{
|
||||
if ( NPCInfo->goalEntity == NPC->enemy )
|
||||
{
|
||||
move2 = qfalse;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//See if we're running away
|
||||
else if ( NPCInfo->squadState == SQUAD_RETREAT )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "flee" ) )
|
||||
{
|
||||
NPCInfo->squadState = SQUAD_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
faceEnemy2 = qfalse;
|
||||
}
|
||||
}
|
||||
else if ( NPCInfo->squadState == SQUAD_IDLE )
|
||||
{
|
||||
if ( !NPCInfo->goalEntity )
|
||||
{
|
||||
move2 = qfalse;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//See if we're moving towards a goal, not the enemy
|
||||
if ( ( NPCInfo->goalEntity != NPC->enemy ) && ( NPCInfo->goalEntity != NULL ) )
|
||||
{
|
||||
//Did we make it?
|
||||
if ( NAV_HitNavGoal( NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPCInfo->goalEntity->r.currentOrigin, 16, FlyingCreature( NPC ) ) ||
|
||||
( NPCInfo->squadState == SQUAD_SCOUT && enemyLOS2 && enemyDist2 <= 10000 ) )
|
||||
{
|
||||
int newSquadState = SQUAD_STAND_AND_SHOOT;
|
||||
//we got where we wanted to go, set timers based on why we were running
|
||||
switch ( NPCInfo->squadState )
|
||||
{
|
||||
case SQUAD_RETREAT://was running away
|
||||
TIMER_Set( NPC, "duck", (NPC->client->pers.maxHealth - NPC->health) * 100 );
|
||||
TIMER_Set( NPC, "hideTime", Q_irand( 3000, 7000 ) );
|
||||
newSquadState = SQUAD_COVER;
|
||||
break;
|
||||
case SQUAD_TRANSITION://was heading for a combat point
|
||||
TIMER_Set( NPC, "hideTime", Q_irand( 2000, 4000 ) );
|
||||
break;
|
||||
case SQUAD_SCOUT://was running after player
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
NPC_ReachedGoal();
|
||||
//don't attack right away
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( (6-NPCInfo->stats.aim)*50, (6-NPCInfo->stats.aim)*100 ) ); //FIXME: Slant for difficulty levels, too?
|
||||
//don't do something else just yet
|
||||
TIMER_Set( NPC, "roamTime", Q_irand( 1000, 4000 ) );
|
||||
//stop fleeing
|
||||
if ( NPCInfo->squadState == SQUAD_RETREAT )
|
||||
{
|
||||
TIMER_Set( NPC, "flee", -level.time );
|
||||
NPCInfo->squadState = SQUAD_IDLE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//keep going, hold of roamTimer until we get there
|
||||
TIMER_Set( NPC, "roamTime", Q_irand( 4000, 8000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
static void Sniper_ResolveBlockedShot( void )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "duck" ) )
|
||||
{//we're not ducking
|
||||
if ( TIMER_Done( NPC, "roamTime" ) )
|
||||
{//not roaming
|
||||
//FIXME: try to find another spot from which to hit the enemy
|
||||
if ( (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) && (!NPCInfo->goalEntity || NPCInfo->goalEntity == NPC->enemy) )
|
||||
{//we were running after enemy
|
||||
//Try to find a combat point that can hit the enemy
|
||||
int cpFlags = (CP_CLEAR|CP_HAS_ROUTE);
|
||||
int cp;
|
||||
|
||||
if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST )
|
||||
{
|
||||
cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST);
|
||||
cpFlags |= CP_NEAREST;
|
||||
}
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->r.currentOrigin, cpFlags, 32, -1 );
|
||||
if ( cp == -1 && !(NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) )
|
||||
{//okay, try one by the enemy
|
||||
cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->enemy->r.currentOrigin, CP_CLEAR|CP_HAS_ROUTE|CP_HORZ_DIST_COLL, 32, -1 );
|
||||
}
|
||||
//NOTE: there may be a perfectly valid one, just not one within CP_COLLECT_RADIUS of either me or him...
|
||||
if ( cp != -1 )
|
||||
{//found a combat point that has a clear shot to enemy
|
||||
NPC_SetCombatPoint( cp );
|
||||
NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL );
|
||||
TIMER_Set( NPC, "duck", -1 );
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000 ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
else
|
||||
{//maybe we should stand
|
||||
if ( TIMER_Done( NPC, "stand" ) )
|
||||
{//stand for as long as we'll be here
|
||||
TIMER_Set( NPC, "stand", Q_irand( 500, 2000 ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
//Hmm, can't resolve this by telling them to duck or telling me to stand
|
||||
//We need to move!
|
||||
TIMER_Set( NPC, "roamTime", -1 );
|
||||
TIMER_Set( NPC, "stick", -1 );
|
||||
TIMER_Set( NPC, "duck", -1 );
|
||||
TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000 ) );
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ST_CheckFireState
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void Sniper_CheckFireState( void )
|
||||
{
|
||||
if ( enemyCS2 )
|
||||
{//if have a clear shot, always try
|
||||
return;
|
||||
}
|
||||
|
||||
if ( NPCInfo->squadState == SQUAD_RETREAT || NPCInfo->squadState == SQUAD_TRANSITION || NPCInfo->squadState == SQUAD_SCOUT )
|
||||
{//runners never try to fire at the last pos
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) )
|
||||
{//if moving at all, don't do this
|
||||
return;
|
||||
}
|
||||
|
||||
//continue to fire on their last position
|
||||
if ( !Q_irand( 0, 1 ) && NPCInfo->enemyLastSeenTime && level.time - NPCInfo->enemyLastSeenTime < ((5-NPCInfo->stats.aim)*1000) )//FIXME: incorporate skill too?
|
||||
{
|
||||
if ( !VectorCompare( vec3_origin, NPCInfo->enemyLastSeenLocation ) )
|
||||
{
|
||||
//Fire on the last known position
|
||||
vec3_t muzzle, dir, angles;
|
||||
|
||||
CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
|
||||
VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir );
|
||||
|
||||
VectorNormalize( dir );
|
||||
|
||||
vectoangles( dir, angles );
|
||||
|
||||
NPCInfo->desiredYaw = angles[YAW];
|
||||
NPCInfo->desiredPitch = angles[PITCH];
|
||||
|
||||
shoot2 = qtrue;
|
||||
//faceEnemy2 = qfalse;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ( level.time - NPCInfo->enemyLastSeenTime > 10000 )
|
||||
{//next time we see him, we'll miss few times first
|
||||
NPC->count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean Sniper_EvaluateShot( int hit )
|
||||
{
|
||||
gentity_t *hitEnt;
|
||||
|
||||
if ( !NPC->enemy )
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
hitEnt = &g_entities[hit];
|
||||
if ( hit == NPC->enemy->s.number
|
||||
|| ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
|
||||
|| ( hitEnt && hitEnt->takedamage && ((hitEnt->r.svFlags&SVF_GLASS_BRUSH)||hitEnt->health < 40||NPC->s.weapon == WP_EMPLACED_GUN) )
|
||||
|| ( hitEnt && (hitEnt->r.svFlags&SVF_GLASS_BRUSH)) )
|
||||
{//can hit enemy or will hit glass, so shoot anyway
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
void Sniper_FaceEnemy( void )
|
||||
{
|
||||
//FIXME: the ones behind kill holes are facing some arbitrary direction and not firing
|
||||
//FIXME: If actually trying to hit enemy, don't fire unless enemy is at least in front of me?
|
||||
//FIXME: need to give designers option to make them not miss first few shots
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
vec3_t muzzle, target, angles, forward, right, up;
|
||||
//Get the positions
|
||||
AngleVectors( NPC->client->ps.viewangles, forward, right, up );
|
||||
CalcMuzzlePoint( NPC, forward, right, up, muzzle );
|
||||
//CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
|
||||
CalcEntitySpot( NPC->enemy, SPOT_ORIGIN, target );
|
||||
|
||||
if ( enemyDist2 > 65536 && NPCInfo->stats.aim < 5 )//is 256 squared, was 16384 (128*128)
|
||||
{
|
||||
if ( NPC->count < (5-NPCInfo->stats.aim) )
|
||||
{//miss a few times first
|
||||
if ( shoot2 && TIMER_Done( NPC, "attackDelay" ) && level.time >= NPCInfo->shotTime )
|
||||
{//ready to fire again
|
||||
qboolean aimError = qfalse;
|
||||
qboolean hit = qtrue;
|
||||
int tryMissCount = 0;
|
||||
trace_t trace;
|
||||
|
||||
GetAnglesForDirection( muzzle, target, angles );
|
||||
AngleVectors( angles, forward, right, up );
|
||||
|
||||
while ( hit && tryMissCount < 10 )
|
||||
{
|
||||
tryMissCount++;
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{
|
||||
aimError = qtrue;
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{
|
||||
VectorMA( target, NPC->enemy->r.maxs[2]*flrand(1.5, 4), right, target );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorMA( target, NPC->enemy->r.mins[2]*flrand(1.5, 4), right, target );
|
||||
}
|
||||
}
|
||||
if ( !aimError || !Q_irand( 0, 1 ) )
|
||||
{
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{
|
||||
VectorMA( target, NPC->enemy->r.maxs[2]*flrand(1.5, 4), up, target );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorMA( target, NPC->enemy->r.mins[2]*flrand(1.5, 4), up, target );
|
||||
}
|
||||
}
|
||||
trap_Trace( &trace, muzzle, vec3_origin, vec3_origin, target, NPC->s.number, MASK_SHOT );
|
||||
hit = Sniper_EvaluateShot( trace.entityNum );
|
||||
}
|
||||
NPC->count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !enemyLOS2 )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{//based on distance, aim value, difficulty and enemy movement, miss
|
||||
//FIXME: incorporate distance as a factor?
|
||||
int missFactor = 8-(NPCInfo->stats.aim+g_spskill.integer) * 3;
|
||||
if ( missFactor > ENEMY_POS_LAG_STEPS )
|
||||
{
|
||||
missFactor = ENEMY_POS_LAG_STEPS;
|
||||
}
|
||||
else if ( missFactor < 0 )
|
||||
{//???
|
||||
missFactor = 0 ;
|
||||
}
|
||||
VectorCopy( NPCInfo->enemyLaggedPos[missFactor], target );
|
||||
}
|
||||
GetAnglesForDirection( muzzle, target, angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
target[2] += flrand( 0, NPC->enemy->r.maxs[2] );
|
||||
//CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, target );
|
||||
GetAnglesForDirection( muzzle, target, angles );
|
||||
}
|
||||
|
||||
NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
|
||||
NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] );
|
||||
}
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
|
||||
void Sniper_UpdateEnemyPos( void )
|
||||
{
|
||||
int index;
|
||||
int i;
|
||||
|
||||
for ( i = MAX_ENEMY_POS_LAG-ENEMY_POS_LAG_INTERVAL; i >= 0; i -= ENEMY_POS_LAG_INTERVAL )
|
||||
{
|
||||
index = i/ENEMY_POS_LAG_INTERVAL;
|
||||
if ( !index )
|
||||
{
|
||||
CalcEntitySpot( NPC->enemy, SPOT_HEAD_LEAN, NPCInfo->enemyLaggedPos[index] );
|
||||
NPCInfo->enemyLaggedPos[index][2] -= flrand( 2, 16 );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( NPCInfo->enemyLaggedPos[index-1], NPCInfo->enemyLaggedPos[index] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSSniper_Attack
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void Sniper_StartHide( void )
|
||||
{
|
||||
int duckTime = Q_irand( 2000, 5000 );
|
||||
|
||||
TIMER_Set( NPC, "duck", duckTime );
|
||||
TIMER_Set( NPC, "watch", 500 );
|
||||
TIMER_Set( NPC, "attackDelay", duckTime + Q_irand( 500, 2000 ) );
|
||||
}
|
||||
|
||||
void NPC_BSSniper_Attack( void )
|
||||
{
|
||||
//Don't do anything if we're hurt
|
||||
if ( NPC->painDebounceTime > level.time )
|
||||
{
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
//NPC_CheckEnemy( qtrue, qfalse );
|
||||
//If we don't have an enemy, just idle
|
||||
if ( NPC_CheckEnemyExt(qfalse) == qfalse )//!NPC->enemy )//
|
||||
{
|
||||
NPC->enemy = NULL;
|
||||
NPC_BSSniper_Patrol();//FIXME: or patrol?
|
||||
return;
|
||||
}
|
||||
|
||||
if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) )
|
||||
{//going to run
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !NPC->enemy )
|
||||
{//WTF? somehow we lost our enemy?
|
||||
NPC_BSSniper_Patrol();//FIXME: or patrol?
|
||||
return;
|
||||
}
|
||||
|
||||
enemyLOS2 = enemyCS2 = qfalse;
|
||||
move2 = qtrue;
|
||||
faceEnemy2 = qfalse;
|
||||
shoot2 = qfalse;
|
||||
enemyDist2 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
if ( enemyDist2 < 16384 )//128 squared
|
||||
{//too close, so switch to primary fire
|
||||
if ( NPC->client->ps.weapon == WP_DISRUPTOR )
|
||||
{//sniping... should be assumed
|
||||
if ( NPCInfo->scriptFlags & SCF_ALT_FIRE )
|
||||
{//use primary fire
|
||||
trace_t trace;
|
||||
trap_Trace ( &trace, NPC->enemy->r.currentOrigin, NPC->enemy->r.mins, NPC->enemy->r.maxs, NPC->r.currentOrigin, NPC->enemy->s.number, NPC->enemy->clipmask );
|
||||
if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->s.number ) )
|
||||
{//he can get right to me
|
||||
NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
|
||||
//reset fire-timing variables
|
||||
NPC_ChangeWeapon( WP_DISRUPTOR );
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
//FIXME: switch back if he gets far away again?
|
||||
}
|
||||
}
|
||||
else if ( enemyDist2 > 65536 )//256 squared
|
||||
{
|
||||
if ( NPC->client->ps.weapon == WP_DISRUPTOR )
|
||||
{//sniping... should be assumed
|
||||
if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) )
|
||||
{//use primary fire
|
||||
NPCInfo->scriptFlags |= SCF_ALT_FIRE;
|
||||
//reset fire-timing variables
|
||||
NPC_ChangeWeapon( WP_DISRUPTOR );
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sniper_UpdateEnemyPos();
|
||||
//can we see our target?
|
||||
if ( NPC_ClearLOS4( NPC->enemy ) )//|| (NPCInfo->stats.aim >= 5 && gi.inPVS( NPC->client->renderInfo.eyePoint, NPC->enemy->currentOrigin )) )
|
||||
{
|
||||
float maxShootDist;
|
||||
|
||||
NPCInfo->enemyLastSeenTime = level.time;
|
||||
VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
|
||||
enemyLOS2 = qtrue;
|
||||
maxShootDist = NPC_MaxDistSquaredForWeapon();
|
||||
if ( enemyDist2 < maxShootDist )
|
||||
{
|
||||
vec3_t fwd, right, up, muzzle, end;
|
||||
trace_t tr;
|
||||
int hit;
|
||||
|
||||
AngleVectors( NPC->client->ps.viewangles, fwd, right, up );
|
||||
CalcMuzzlePoint( NPC, fwd, right, up, muzzle );
|
||||
VectorMA( muzzle, 8192, fwd, end );
|
||||
trap_Trace ( &tr, muzzle, NULL, NULL, end, NPC->s.number, MASK_SHOT );
|
||||
|
||||
hit = tr.entityNum;
|
||||
//can we shoot our target?
|
||||
if ( Sniper_EvaluateShot( hit ) )
|
||||
{
|
||||
enemyCS2 = qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
|
||||
{
|
||||
NPCInfo->enemyLastSeenTime = level.time;
|
||||
faceEnemy2 = qtrue;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( enemyLOS2 )
|
||||
{//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
|
||||
faceEnemy2 = qtrue;
|
||||
}
|
||||
if ( enemyCS2 )
|
||||
{
|
||||
shoot2 = qtrue;
|
||||
}
|
||||
else if ( level.time - NPCInfo->enemyLastSeenTime > 3000 )
|
||||
{//Hmm, have to get around this bastard... FIXME: this NPCInfo->enemyLastSeenTime builds up when ducked seems to make them want to run when they uncrouch
|
||||
Sniper_ResolveBlockedShot();
|
||||
}
|
||||
|
||||
//Check for movement to take care of
|
||||
Sniper_CheckMoveState();
|
||||
|
||||
//See if we should override shooting decision with any special considerations
|
||||
Sniper_CheckFireState();
|
||||
|
||||
if ( move2 )
|
||||
{//move toward goal
|
||||
if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist2 > 10000 ) )//100 squared
|
||||
{
|
||||
move2 = Sniper_Move();
|
||||
}
|
||||
else
|
||||
{
|
||||
move2 = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !move2 )
|
||||
{
|
||||
if ( !TIMER_Done( NPC, "duck" ) )
|
||||
{
|
||||
if ( TIMER_Done( NPC, "watch" ) )
|
||||
{//not while watching
|
||||
ucmd.upmove = -127;
|
||||
}
|
||||
}
|
||||
//FIXME: what about leaning?
|
||||
//FIXME: also, when stop ducking, start looking, if enemy can see me, chance of ducking back down again
|
||||
}
|
||||
else
|
||||
{//stop ducking!
|
||||
TIMER_Set( NPC, "duck", -1 );
|
||||
}
|
||||
|
||||
if ( TIMER_Done( NPC, "duck" )
|
||||
&& TIMER_Done( NPC, "watch" )
|
||||
&& (TIMER_Get( NPC, "attackDelay" )-level.time) > 1000
|
||||
&& NPC->attackDebounceTime < level.time )
|
||||
{
|
||||
if ( enemyLOS2 && (NPCInfo->scriptFlags&SCF_ALT_FIRE) )
|
||||
{
|
||||
if ( NPC->fly_sound_debounce_time < level.time )
|
||||
{
|
||||
NPC->fly_sound_debounce_time = level.time + 2000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !faceEnemy2 )
|
||||
{//we want to face in the dir we're running
|
||||
if ( move2 )
|
||||
{//don't run away and shoot
|
||||
NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
|
||||
NPCInfo->desiredPitch = 0;
|
||||
shoot2 = qfalse;
|
||||
}
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
else// if ( faceEnemy2 )
|
||||
{//face the enemy
|
||||
Sniper_FaceEnemy();
|
||||
}
|
||||
|
||||
if ( NPCInfo->scriptFlags&SCF_DONT_FIRE )
|
||||
{
|
||||
shoot2 = qfalse;
|
||||
}
|
||||
|
||||
//FIXME: don't shoot right away!
|
||||
if ( shoot2 )
|
||||
{//try to shoot if it's time
|
||||
if ( TIMER_Done( NPC, "attackDelay" ) )
|
||||
{
|
||||
WeaponThink( qtrue );
|
||||
if ( ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK) )
|
||||
{
|
||||
G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/null.wav" );
|
||||
}
|
||||
|
||||
//took a shot, now hide
|
||||
if ( !(NPC->spawnflags&SPF_NO_HIDE) && !Q_irand( 0, 1 ) )
|
||||
{
|
||||
//FIXME: do this if in combat point and combat point has duck-type cover... also handle lean-type cover
|
||||
Sniper_StartHide();
|
||||
}
|
||||
else
|
||||
{
|
||||
TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NPC_BSSniper_Default( void )
|
||||
{
|
||||
if( !NPC->enemy )
|
||||
{//don't have an enemy, look for one
|
||||
NPC_BSSniper_Patrol();
|
||||
}
|
||||
else//if ( NPC->enemy )
|
||||
{//have an enemy
|
||||
NPC_BSSniper_Attack();
|
||||
}
|
||||
}
|
||||
2742
codemp/game/NPC_AI_Stormtrooper.c
Normal file
2742
codemp/game/NPC_AI_Stormtrooper.c
Normal file
File diff suppressed because it is too large
Load Diff
1139
codemp/game/NPC_AI_Utils.c
Normal file
1139
codemp/game/NPC_AI_Utils.c
Normal file
File diff suppressed because it is too large
Load Diff
654
codemp/game/NPC_AI_Wampa.c
Normal file
654
codemp/game/NPC_AI_Wampa.c
Normal file
@@ -0,0 +1,654 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
// These define the working combat range for these suckers
|
||||
#define MIN_DISTANCE 48
|
||||
#define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
|
||||
|
||||
#define MAX_DISTANCE 1024
|
||||
#define MAX_DISTANCE_SQR ( MAX_DISTANCE * MAX_DISTANCE )
|
||||
|
||||
#define LSTATE_CLEAR 0
|
||||
#define LSTATE_WAITING 1
|
||||
|
||||
float enemyDist = 0;
|
||||
|
||||
void Wampa_SetBolts( gentity_t *self )
|
||||
{
|
||||
if ( self && self->client )
|
||||
{
|
||||
renderInfo_t *ri = &self->client->renderInfo;
|
||||
ri->headBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*head_eyes");
|
||||
//ri->cervicalBolt = trap_G2API_AddBolt(self->ghoul2, 0, "neck_bone" );
|
||||
//ri->chestBolt = trap_G2API_AddBolt(self->ghoul2, 0, "upper_spine");
|
||||
//ri->gutBolt = trap_G2API_AddBolt(self->ghoul2, 0, "mid_spine");
|
||||
ri->torsoBolt = trap_G2API_AddBolt(self->ghoul2, 0, "lower_spine");
|
||||
ri->crotchBolt = trap_G2API_AddBolt(self->ghoul2, 0, "rear_bone");
|
||||
//ri->elbowLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_arm_elbow");
|
||||
//ri->elbowRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_arm_elbow");
|
||||
ri->handLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_hand");
|
||||
ri->handRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_hand");
|
||||
//ri->kneeLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*hips_l_knee");
|
||||
//ri->kneeRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*hips_r_knee");
|
||||
ri->footLBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*l_leg_foot");
|
||||
ri->footRBolt = trap_G2API_AddBolt(self->ghoul2, 0, "*r_leg_foot");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Wampa_Precache
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_Wampa_Precache( void )
|
||||
{
|
||||
/*
|
||||
int i;
|
||||
for ( i = 1; i < 4; i ++ )
|
||||
{
|
||||
G_SoundIndex( va("sound/chars/wampa/growl%d.wav", i) );
|
||||
}
|
||||
for ( i = 1; i < 3; i ++ )
|
||||
{
|
||||
G_SoundIndex( va("sound/chars/wampa/snort%d.wav", i) );
|
||||
}
|
||||
*/
|
||||
G_SoundIndex( "sound/chars/rancor/swipehit.wav" );
|
||||
//G_SoundIndex( "sound/chars/wampa/chomp.wav" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Wampa_Idle
|
||||
-------------------------
|
||||
*/
|
||||
void Wampa_Idle( void )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
qboolean Wampa_CheckRoar( gentity_t *self )
|
||||
{
|
||||
if ( self->wait < level.time )
|
||||
{
|
||||
self->wait = level.time + Q_irand( 5000, 20000 );
|
||||
NPC_SetAnim( self, SETANIM_BOTH, Q_irand(BOTH_GESTURE1,BOTH_GESTURE2), (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD) );
|
||||
TIMER_Set( self, "rageTime", self->client->ps.legsTimer );
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
Wampa_Patrol
|
||||
-------------------------
|
||||
*/
|
||||
void Wampa_Patrol( void )
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
|
||||
//If we have somewhere to go, then do that
|
||||
if ( UpdateGoal() )
|
||||
{
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_MoveToGoal( qtrue );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done( NPC, "patrolTime" ))
|
||||
{
|
||||
TIMER_Set( NPC, "patrolTime", crandom() * 5000 + 5000 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
|
||||
{
|
||||
Wampa_Idle();
|
||||
return;
|
||||
}
|
||||
Wampa_CheckRoar( NPC );
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
Wampa_Move
|
||||
-------------------------
|
||||
*/
|
||||
void Wampa_Move( qboolean visible )
|
||||
{
|
||||
if ( NPCInfo->localState != LSTATE_WAITING )
|
||||
{
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
|
||||
if ( NPC->enemy )
|
||||
{//pick correct movement speed and anim
|
||||
//run by default
|
||||
ucmd.buttons &= ~BUTTON_WALKING;
|
||||
if ( !TIMER_Done( NPC, "runfar" )
|
||||
|| !TIMER_Done( NPC, "runclose" ) )
|
||||
{//keep running with this anim & speed for a bit
|
||||
}
|
||||
else if ( !TIMER_Done( NPC, "walk" ) )
|
||||
{//keep walking for a bit
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
}
|
||||
else if ( visible && enemyDist > 384 && NPCInfo->stats.runSpeed == 180 )
|
||||
{//fast run, all fours
|
||||
NPCInfo->stats.runSpeed = 300;
|
||||
TIMER_Set( NPC, "runfar", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
else if ( enemyDist > 256 && NPCInfo->stats.runSpeed == 300 )
|
||||
{//slow run, upright
|
||||
NPCInfo->stats.runSpeed = 180;
|
||||
TIMER_Set( NPC, "runclose", Q_irand( 3000, 5000 ) );
|
||||
}
|
||||
else if ( enemyDist < 128 )
|
||||
{//walk
|
||||
NPCInfo->stats.runSpeed = 180;
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
TIMER_Set( NPC, "walk", Q_irand( 4000, 6000 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( NPCInfo->stats.runSpeed == 300 )
|
||||
{//need to use the alternate run - hunched over on all fours
|
||||
NPC->client->ps.eFlags2 |= EF2_USE_ALT_ANIM;
|
||||
}
|
||||
NPC_MoveToGoal( qtrue );
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
//extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
|
||||
extern void G_Knockdown( gentity_t *victim );
|
||||
extern void G_Dismember( gentity_t *ent, gentity_t *enemy, vec3_t point, int limbType, float limbRollBase, float limbPitchBase, int deathAnim, qboolean postDeath );
|
||||
extern int NPC_GetEntsNearBolt( int *radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
|
||||
|
||||
void Wampa_Slash( int boltIndex, qboolean backhand )
|
||||
{
|
||||
int radiusEntNums[128];
|
||||
int numEnts;
|
||||
const float radius = 88;
|
||||
const float radiusSquared = (radius*radius);
|
||||
int i;
|
||||
vec3_t boltOrg;
|
||||
int damage = (backhand)?Q_irand(10,15):Q_irand(20,30);
|
||||
|
||||
numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, boltIndex, boltOrg );
|
||||
|
||||
for ( i = 0; i < numEnts; i++ )
|
||||
{
|
||||
gentity_t *radiusEnt = &g_entities[radiusEntNums[i]];
|
||||
if ( !radiusEnt->inuse )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt == NPC )
|
||||
{//Skip the wampa ent
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( radiusEnt->client == NULL )
|
||||
{//must be a client
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared )
|
||||
{
|
||||
//smack
|
||||
G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, damage, ((backhand)?DAMAGE_NO_ARMOR:(DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK)), MOD_MELEE );
|
||||
if ( backhand )
|
||||
{
|
||||
//actually push the enemy
|
||||
vec3_t pushDir;
|
||||
vec3_t angs;
|
||||
VectorCopy( NPC->client->ps.viewangles, angs );
|
||||
angs[YAW] += flrand( 25, 50 );
|
||||
angs[PITCH] = flrand( -25, -15 );
|
||||
AngleVectors( angs, pushDir, NULL, NULL );
|
||||
if ( radiusEnt->client->NPC_class != CLASS_WAMPA
|
||||
&& radiusEnt->client->NPC_class != CLASS_RANCOR
|
||||
&& radiusEnt->client->NPC_class != CLASS_ATST )
|
||||
{
|
||||
G_Throw( radiusEnt, pushDir, 65 );
|
||||
if ( BG_KnockDownable(&radiusEnt->client->ps) &&
|
||||
radiusEnt->health > 0 && Q_irand( 0, 1 ) )
|
||||
{//do pain on enemy
|
||||
radiusEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
|
||||
radiusEnt->client->ps.forceDodgeAnim = 0;
|
||||
radiusEnt->client->ps.forceHandExtendTime = level.time + 1100;
|
||||
radiusEnt->client->ps.quickerGetup = qfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( radiusEnt->health <= 0 && radiusEnt->client )
|
||||
{//killed them, chance of dismembering
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{//bite something off
|
||||
int hitLoc = Q_irand( G2_MODELPART_HEAD, G2_MODELPART_RLEG );
|
||||
if ( hitLoc == G2_MODELPART_HEAD )
|
||||
{
|
||||
NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATH17, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
else if ( hitLoc == G2_MODELPART_WAIST )
|
||||
{
|
||||
NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATHBACKWARD2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
G_Dismember( radiusEnt, NPC, radiusEnt->r.currentOrigin, hitLoc, 90, 0, radiusEnt->client->ps.torsoAnim, qtrue);
|
||||
}
|
||||
}
|
||||
else if ( !Q_irand( 0, 3 ) && radiusEnt->health > 0 )
|
||||
{//one out of every 4 normal hits does a knockdown, too
|
||||
vec3_t pushDir;
|
||||
vec3_t angs;
|
||||
VectorCopy( NPC->client->ps.viewangles, angs );
|
||||
angs[YAW] += flrand( 25, 50 );
|
||||
angs[PITCH] = flrand( -25, -15 );
|
||||
AngleVectors( angs, pushDir, NULL, NULL );
|
||||
G_Knockdown( radiusEnt );
|
||||
}
|
||||
G_Sound( radiusEnt, CHAN_WEAPON, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
void Wampa_Attack( float distance, qboolean doCharge )
|
||||
{
|
||||
if ( !TIMER_Exists( NPC, "attacking" ) )
|
||||
{
|
||||
if ( Q_irand(0, 2) && !doCharge )
|
||||
{//double slash
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 750 );
|
||||
}
|
||||
else if ( doCharge || (distance > 270 && distance < 430 && !Q_irand(0, 1)) )
|
||||
{//leap
|
||||
vec3_t fwd, yawAng;
|
||||
VectorSet( yawAng, 0, NPC->client->ps.viewangles[YAW], 0 );
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 500 );
|
||||
AngleVectors( yawAng, fwd, NULL, NULL );
|
||||
VectorScale( fwd, distance*1.5f, NPC->client->ps.velocity );
|
||||
NPC->client->ps.velocity[2] = 150;
|
||||
NPC->client->ps.groundEntityNum = ENTITYNUM_NONE;
|
||||
}
|
||||
else
|
||||
{//backhand
|
||||
NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
TIMER_Set( NPC, "attack_dmg", 250 );
|
||||
}
|
||||
|
||||
TIMER_Set( NPC, "attacking", NPC->client->ps.legsTimer + random() * 200 );
|
||||
//allow us to re-evaluate our running speed/anim
|
||||
TIMER_Set( NPC, "runfar", -1 );
|
||||
TIMER_Set( NPC, "runclose", -1 );
|
||||
TIMER_Set( NPC, "walk", -1 );
|
||||
}
|
||||
|
||||
// Need to do delayed damage since the attack animations encapsulate multiple mini-attacks
|
||||
|
||||
if ( TIMER_Done2( NPC, "attack_dmg", qtrue ) )
|
||||
{
|
||||
switch ( NPC->client->ps.legsAnim )
|
||||
{
|
||||
case BOTH_ATTACK1:
|
||||
Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
|
||||
//do second hit
|
||||
TIMER_Set( NPC, "attack_dmg2", 100 );
|
||||
break;
|
||||
case BOTH_ATTACK2:
|
||||
Wampa_Slash( NPC->client->renderInfo.handRBolt, qfalse );
|
||||
TIMER_Set( NPC, "attack_dmg2", 100 );
|
||||
break;
|
||||
case BOTH_ATTACK3:
|
||||
Wampa_Slash( NPC->client->renderInfo.handLBolt, qtrue );
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( TIMER_Done2( NPC, "attack_dmg2", qtrue ) )
|
||||
{
|
||||
switch ( NPC->client->ps.legsAnim )
|
||||
{
|
||||
case BOTH_ATTACK1:
|
||||
Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
|
||||
break;
|
||||
case BOTH_ATTACK2:
|
||||
Wampa_Slash( NPC->client->renderInfo.handLBolt, qfalse );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Just using this to remove the attacking flag at the right time
|
||||
TIMER_Done2( NPC, "attacking", qtrue );
|
||||
|
||||
if ( NPC->client->ps.legsAnim == BOTH_ATTACK1 && distance > (NPC->r.maxs[0]+MIN_DISTANCE) )
|
||||
{//okay to keep moving
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
Wampa_Move( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------
|
||||
void Wampa_Combat( void )
|
||||
{
|
||||
// If we cannot see our target or we have somewhere to go, then do that
|
||||
if ( !NPC_ClearLOS( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) )
|
||||
{
|
||||
if ( !Q_irand( 0, 10 ) )
|
||||
{
|
||||
if ( Wampa_CheckRoar( NPC ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
NPCInfo->combatMove = qtrue;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
|
||||
Wampa_Move( 0 );
|
||||
return;
|
||||
}
|
||||
else if ( UpdateGoal() )
|
||||
{
|
||||
NPCInfo->combatMove = qtrue;
|
||||
NPCInfo->goalEntity = NPC->enemy;
|
||||
NPCInfo->goalRadius = MAX_DISTANCE; // just get us within combat range
|
||||
|
||||
Wampa_Move( 1 );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
float distance = enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
qboolean advance = (qboolean)( distance > (NPC->r.maxs[0]+MIN_DISTANCE) ? qtrue : qfalse );
|
||||
qboolean doCharge = qfalse;
|
||||
|
||||
// Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
|
||||
//FIXME: always seems to face off to the left or right?!!!!
|
||||
NPC_FaceEnemy( qtrue );
|
||||
|
||||
|
||||
if ( advance )
|
||||
{//have to get closer
|
||||
vec3_t yawOnlyAngles;
|
||||
VectorSet( yawOnlyAngles, 0, NPC->r.currentAngles[YAW], 0 );
|
||||
if ( NPC->enemy->health > 0//enemy still alive
|
||||
&& fabs(distance-350) <= 80 //enemy anywhere from 270 to 430 away
|
||||
&& InFOV3( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, yawOnlyAngles, 20, 20 ) )//enemy generally in front
|
||||
{//10% chance of doing charge anim
|
||||
if ( !Q_irand( 0, 9 ) )
|
||||
{//go for the charge
|
||||
doCharge = qtrue;
|
||||
advance = qfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (( advance || NPCInfo->localState == LSTATE_WAITING ) && TIMER_Done( NPC, "attacking" )) // waiting monsters can't attack
|
||||
{
|
||||
if ( TIMER_Done2( NPC, "takingPain", qtrue ))
|
||||
{
|
||||
NPCInfo->localState = LSTATE_CLEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
Wampa_Move( 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !Q_irand( 0, 20 ) )
|
||||
{//FIXME: only do this if we just damaged them or vice-versa?
|
||||
if ( Wampa_CheckRoar( NPC ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{//FIXME: base on skill
|
||||
Wampa_Attack( distance, doCharge );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_Wampa_Pain
|
||||
-------------------------
|
||||
*/
|
||||
//void NPC_Wampa_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
|
||||
void NPC_Wampa_Pain( gentity_t *self, gentity_t *attacker, int damage )
|
||||
{
|
||||
qboolean hitByWampa = qfalse;
|
||||
if ( attacker&&attacker->client&&attacker->client->NPC_class==CLASS_WAMPA )
|
||||
{
|
||||
hitByWampa = qtrue;
|
||||
}
|
||||
if ( attacker
|
||||
&& attacker->inuse
|
||||
&& attacker != self->enemy
|
||||
&& !(attacker->flags&FL_NOTARGET) )
|
||||
{
|
||||
if ( (!attacker->s.number&&!Q_irand(0,3))
|
||||
|| !self->enemy
|
||||
|| self->enemy->health == 0
|
||||
|| (self->enemy->client&&self->enemy->client->NPC_class == CLASS_WAMPA)
|
||||
|| (!Q_irand(0, 4 ) && DistanceSquared( attacker->r.currentOrigin, self->r.currentOrigin ) < DistanceSquared( self->enemy->r.currentOrigin, self->r.currentOrigin )) )
|
||||
{//if my enemy is dead (or attacked by player) and I'm not still holding/eating someone, turn on the attacker
|
||||
//FIXME: if can't nav to my enemy, take this guy if I can nav to him
|
||||
G_SetEnemy( self, attacker );
|
||||
TIMER_Set( self, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
if ( hitByWampa )
|
||||
{//stay mad at this Wampa for 2-5 secs before looking for attacker enemies
|
||||
TIMER_Set( self, "wampaInfight", Q_irand( 2000, 5000 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( (hitByWampa|| Q_irand( 0, 100 ) < damage )//hit by wampa, hit while holding live victim, or took a lot of damage
|
||||
&& self->client->ps.legsAnim != BOTH_GESTURE1
|
||||
&& self->client->ps.legsAnim != BOTH_GESTURE2
|
||||
&& TIMER_Done( self, "takingPain" ) )
|
||||
{
|
||||
if ( !Wampa_CheckRoar( self ) )
|
||||
{
|
||||
if ( self->client->ps.legsAnim != BOTH_ATTACK1
|
||||
&& self->client->ps.legsAnim != BOTH_ATTACK2
|
||||
&& self->client->ps.legsAnim != BOTH_ATTACK3 )
|
||||
{//cant interrupt one of the big attack anims
|
||||
if ( self->health > 100 || hitByWampa )
|
||||
{
|
||||
TIMER_Remove( self, "attacking" );
|
||||
|
||||
VectorCopy( self->NPC->lastPathAngles, self->s.angles );
|
||||
|
||||
if ( !Q_irand( 0, 1 ) )
|
||||
{
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD );
|
||||
}
|
||||
TIMER_Set( self, "takingPain", self->client->ps.legsTimer+Q_irand(0, 500) );
|
||||
//allow us to re-evaluate our running speed/anim
|
||||
TIMER_Set( self, "runfar", -1 );
|
||||
TIMER_Set( self, "runclose", -1 );
|
||||
TIMER_Set( self, "walk", -1 );
|
||||
|
||||
if ( self->NPC )
|
||||
{
|
||||
self->NPC->localState = LSTATE_WAITING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_BSWampa_Default
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_BSWampa_Default( void )
|
||||
{
|
||||
NPC->client->ps.eFlags2 &= ~EF2_USE_ALT_ANIM;
|
||||
//NORMAL ANIMS
|
||||
// stand1 = normal stand
|
||||
// walk1 = normal, non-angry walk
|
||||
// walk2 = injured
|
||||
// run1 = far away run
|
||||
// run2 = close run
|
||||
//VICTIM ANIMS
|
||||
// grabswipe = melee1 - sweep out and grab
|
||||
// stand2 attack = attack4 - while holding victim, swipe at him
|
||||
// walk3_drag = walk5 - walk with drag
|
||||
// stand2 = hold victim
|
||||
// stand2to1 = drop victim
|
||||
if ( !TIMER_Done( NPC, "rageTime" ) )
|
||||
{//do nothing but roar first time we see an enemy
|
||||
NPC_FaceEnemy( qtrue );
|
||||
return;
|
||||
}
|
||||
if ( NPC->enemy )
|
||||
{
|
||||
if ( !TIMER_Done(NPC,"attacking") )
|
||||
{//in middle of attack
|
||||
//face enemy
|
||||
NPC_FaceEnemy( qtrue );
|
||||
//continue attack logic
|
||||
enemyDist = Distance( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
|
||||
Wampa_Attack( enemyDist, qfalse );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done(NPC,"angrynoise") )
|
||||
{
|
||||
G_Sound( NPC, CHAN_VOICE, G_SoundIndex( va("sound/chars/wampa/misc/anger%d.wav", Q_irand(1, 2)) ) );
|
||||
|
||||
TIMER_Set( NPC, "angrynoise", Q_irand( 5000, 10000 ) );
|
||||
}
|
||||
//else, if he's in our hand, we eat, else if he's on the ground, we keep attacking his dead body for a while
|
||||
if( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_WAMPA )
|
||||
{//got mad at another Wampa, look for a valid enemy
|
||||
if ( TIMER_Done( NPC, "wampaInfight" ) )
|
||||
{
|
||||
NPC_CheckEnemyExt( qtrue );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ValidEnemy( NPC->enemy ) == qfalse )
|
||||
{
|
||||
TIMER_Remove( NPC, "lookForNewEnemy" );//make them look again right now
|
||||
if ( !NPC->enemy->inuse || level.time - NPC->enemy->s.time > Q_irand( 10000, 15000 ) )
|
||||
{//it's been a while since the enemy died, or enemy is completely gone, get bored with him
|
||||
NPC->enemy = NULL;
|
||||
Wampa_Patrol();
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
//just lost my enemy
|
||||
if ( (NPC->spawnflags&2) )
|
||||
{//search around me if I don't have an enemy
|
||||
NPC_BSSearchStart( NPC->waypoint, BS_SEARCH );
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
else if ( (NPC->spawnflags&1) )
|
||||
{//wander if I don't have an enemy
|
||||
NPC_BSSearchStart( NPC->waypoint, BS_WANDER );
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( TIMER_Done( NPC, "lookForNewEnemy" ) )
|
||||
{
|
||||
gentity_t *newEnemy, *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy?
|
||||
NPC->enemy = NULL;
|
||||
newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse );
|
||||
NPC->enemy = sav_enemy;
|
||||
if ( newEnemy && newEnemy != sav_enemy )
|
||||
{//picked up a new enemy!
|
||||
NPC->lastEnemy = NPC->enemy;
|
||||
G_SetEnemy( NPC, newEnemy );
|
||||
//hold this one for at least 5-15 seconds
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
}
|
||||
else
|
||||
{//look again in 2-5 secs
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 2000, 5000 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Wampa_Combat();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( TIMER_Done(NPC,"idlenoise") )
|
||||
{
|
||||
G_Sound( NPC, CHAN_AUTO, G_SoundIndex( "sound/chars/wampa/misc/anger3.wav" ) );
|
||||
|
||||
TIMER_Set( NPC, "idlenoise", Q_irand( 2000, 4000 ) );
|
||||
}
|
||||
if ( (NPC->spawnflags&2) )
|
||||
{//search around me if I don't have an enemy
|
||||
if ( NPCInfo->homeWp == WAYPOINT_NONE )
|
||||
{//no homewap, initialize the search behavior
|
||||
NPC_BSSearchStart( WAYPOINT_NONE, BS_SEARCH );
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_BSSearch();//this automatically looks for enemies
|
||||
}
|
||||
else if ( (NPC->spawnflags&1) )
|
||||
{//wander if I don't have an enemy
|
||||
if ( NPCInfo->homeWp == WAYPOINT_NONE )
|
||||
{//no homewap, initialize the wander behavior
|
||||
NPC_BSSearchStart( WAYPOINT_NONE, BS_WANDER );
|
||||
NPCInfo->tempBehavior = BS_DEFAULT;
|
||||
}
|
||||
ucmd.buttons |= BUTTON_WALKING;
|
||||
NPC_BSWander();
|
||||
if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
if ( NPC_CheckEnemyExt( qtrue ) == qfalse )
|
||||
{
|
||||
Wampa_Idle();
|
||||
}
|
||||
else
|
||||
{
|
||||
Wampa_CheckRoar( NPC );
|
||||
TIMER_Set( NPC, "lookForNewEnemy", Q_irand( 5000, 15000 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
|
||||
{
|
||||
Wampa_Patrol();
|
||||
}
|
||||
else
|
||||
{
|
||||
Wampa_Idle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NPC_UpdateAngles( qtrue, qtrue );
|
||||
}
|
||||
1748
codemp/game/NPC_behavior.c
Normal file
1748
codemp/game/NPC_behavior.c
Normal file
File diff suppressed because it is too large
Load Diff
3144
codemp/game/NPC_combat.c
Normal file
3144
codemp/game/NPC_combat.c
Normal file
File diff suppressed because it is too large
Load Diff
267
codemp/game/NPC_goal.c
Normal file
267
codemp/game/NPC_goal.c
Normal file
@@ -0,0 +1,267 @@
|
||||
//b_goal.cpp
|
||||
#include "b_local.h"
|
||||
#include "../icarus/Q3_Interface.h"
|
||||
|
||||
extern qboolean FlyingCreature( gentity_t *ent );
|
||||
/*
|
||||
SetGoal
|
||||
*/
|
||||
|
||||
void SetGoal( gentity_t *goal, float rating )
|
||||
{
|
||||
NPCInfo->goalEntity = goal;
|
||||
// NPCInfo->goalEntityNeed = rating;
|
||||
NPCInfo->goalTime = level.time;
|
||||
// NAV_ClearLastRoute(NPC);
|
||||
if ( goal )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "NPC_SetGoal: %s @ %s (%f)\n", goal->classname, vtos( goal->currentOrigin), rating );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "NPC_SetGoal: NONE\n" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
NPC_SetGoal
|
||||
*/
|
||||
|
||||
void NPC_SetGoal( gentity_t *goal, float rating )
|
||||
{
|
||||
if ( goal == NPCInfo->goalEntity )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !goal )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_ERROR, "NPC_SetGoal: NULL goal\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( goal->client )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_ERROR, "NPC_SetGoal: goal is a client\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( NPCInfo->goalEntity )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "NPC_SetGoal: push %s\n", NPCInfo->goalEntity->classname );
|
||||
NPCInfo->lastGoalEntity = NPCInfo->goalEntity;
|
||||
// NPCInfo->lastGoalEntityNeed = NPCInfo->goalEntityNeed;
|
||||
}
|
||||
|
||||
SetGoal( goal, rating );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
NPC_ClearGoal
|
||||
*/
|
||||
|
||||
void NPC_ClearGoal( void )
|
||||
{
|
||||
gentity_t *goal;
|
||||
|
||||
if ( !NPCInfo->lastGoalEntity )
|
||||
{
|
||||
SetGoal( NULL, 0.0 );
|
||||
return;
|
||||
}
|
||||
|
||||
goal = NPCInfo->lastGoalEntity;
|
||||
NPCInfo->lastGoalEntity = NULL;
|
||||
// NAV_ClearLastRoute(NPC);
|
||||
if ( goal->inuse && !(goal->s.eFlags & EF_NODRAW) )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "NPC_ClearGoal: pop %s\n", goal->classname );
|
||||
SetGoal( goal, 0 );//, NPCInfo->lastGoalEntityNeed
|
||||
return;
|
||||
}
|
||||
|
||||
SetGoal( NULL, 0.0 );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
G_BoundsOverlap
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
qboolean G_BoundsOverlap(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2)
|
||||
{//NOTE: flush up against counts as overlapping
|
||||
if(mins1[0]>maxs2[0])
|
||||
return qfalse;
|
||||
|
||||
if(mins1[1]>maxs2[1])
|
||||
return qfalse;
|
||||
|
||||
if(mins1[2]>maxs2[2])
|
||||
return qfalse;
|
||||
|
||||
if(maxs1[0]<mins2[0])
|
||||
return qfalse;
|
||||
|
||||
if(maxs1[1]<mins2[1])
|
||||
return qfalse;
|
||||
|
||||
if(maxs1[2]<mins2[2])
|
||||
return qfalse;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
void NPC_ReachedGoal( void )
|
||||
{
|
||||
// Debug_NPCPrintf( NPC, debugNPCAI, DEBUG_LEVEL_INFO, "UpdateGoal: reached goal entity\n" );
|
||||
NPC_ClearGoal();
|
||||
NPCInfo->goalTime = level.time;
|
||||
|
||||
//MCG - Begin
|
||||
NPCInfo->aiFlags &= ~NPCAI_MOVING;
|
||||
ucmd.forwardmove = 0;
|
||||
//Return that the goal was reached
|
||||
trap_ICARUS_TaskIDComplete( NPC, TID_MOVE_NAV );
|
||||
//MCG - End
|
||||
}
|
||||
/*
|
||||
ReachedGoal
|
||||
|
||||
id removed checks against waypoints and is now checking surfaces
|
||||
*/
|
||||
//qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, gentity_t *goal, qboolean flying );
|
||||
qboolean ReachedGoal( gentity_t *goal )
|
||||
{
|
||||
//FIXME: For script waypoints, need a special check
|
||||
/*
|
||||
int goalWpNum;
|
||||
vec3_t vec;
|
||||
//vec3_t angles;
|
||||
float delta;
|
||||
|
||||
if ( goal->flags & FL_NAVGOAL )
|
||||
{//waypoint_navgoal
|
||||
return NAV_HitNavGoal( NPC->currentOrigin, NPC->mins, NPC->maxs, goal, FlyingCreature( NPC ) );
|
||||
}
|
||||
|
||||
if ( goal == NPCInfo->tempGoal && !(goal->flags & FL_NAVGOAL))
|
||||
{//MUST touch waypoints, even if moving to it
|
||||
//This is odd, it just checks to see if they are on the same
|
||||
//surface and the tempGoal in in the FOV - does NOT check distance!
|
||||
// are we on same surface?
|
||||
|
||||
//FIXME: NPC->waypoint reset every frame, need to find it first
|
||||
//Should we do that here? (Still will do it only once per frame)
|
||||
if ( NPC->waypoint >= 0 && NPC->waypoint < num_waypoints )
|
||||
{
|
||||
goalWpNum = NAV_FindWaypointAt ( goal->currentOrigin );
|
||||
if ( NPC->waypoint != goalWpNum )
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
VectorSubtract ( NPCInfo->tempGoal->currentOrigin, NPC->currentOrigin, vec);
|
||||
//Who cares if it's in our FOV?!
|
||||
/*
|
||||
// is it in our FOV
|
||||
vectoangles ( vec, angles );
|
||||
delta = AngleDelta ( NPC->client->ps.viewangles[YAW], angles[YAW] );
|
||||
if ( fabs ( delta ) > NPCInfo->stats.hfov )
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
//If in the same waypoint as tempGoal, we're there, right?
|
||||
if ( goal->waypoint >= 0 && goal->waypoint < num_waypoints )
|
||||
{
|
||||
if ( NPC->waypoint == goal->waypoint )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if ( VectorLengthSquared( vec ) < (64*64) )
|
||||
{//Close enough
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
*/
|
||||
if ( NPCInfo->aiFlags & NPCAI_TOUCHED_GOAL )
|
||||
{
|
||||
NPCInfo->aiFlags &= ~NPCAI_TOUCHED_GOAL;
|
||||
return qtrue;
|
||||
}
|
||||
/*
|
||||
if ( goal->s.eFlags & EF_NODRAW )
|
||||
{
|
||||
goalWpNum = NAV_FindWaypointAt( goal->currentOrigin );
|
||||
if ( NPC->waypoint == goalWpNum )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if(goal->client && goal->health <= 0)
|
||||
{//trying to get to dead guy
|
||||
goalWpNum = NAV_FindWaypointAt( goal->currentOrigin );
|
||||
if ( NPC->waypoint == goalWpNum )
|
||||
{
|
||||
VectorSubtract(NPC->currentOrigin, goal->currentOrigin, vec);
|
||||
vec[2] = 0;
|
||||
delta = VectorLengthSquared(vec);
|
||||
if(delta <= 800)
|
||||
{//with 20-30 of other guy's origin
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return NAV_HitNavGoal( NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, goal->r.currentOrigin, NPCInfo->goalRadius, FlyingCreature( NPC ) );
|
||||
}
|
||||
|
||||
/*
|
||||
static gentity_t *UpdateGoal( void )
|
||||
|
||||
Id removed a lot of shit here... doesn't seem to handle waypoints independantly of goalentity
|
||||
|
||||
In fact, doesn't seem to be any waypoint info on entities at all any more?
|
||||
|
||||
MCG - Since goal is ALWAYS goalEntity, took out a lot of sending goal entity pointers around for no reason
|
||||
*/
|
||||
|
||||
gentity_t *UpdateGoal( void )
|
||||
{
|
||||
gentity_t *goal;
|
||||
|
||||
if ( !NPCInfo->goalEntity )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !NPCInfo->goalEntity->inuse )
|
||||
{//Somehow freed it, but didn't clear it
|
||||
NPC_ClearGoal();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
goal = NPCInfo->goalEntity;
|
||||
|
||||
if ( ReachedGoal( goal ) )
|
||||
{
|
||||
NPC_ReachedGoal();
|
||||
goal = NULL;//so they don't keep trying to move to it
|
||||
}//else if fail, need to tell script so?
|
||||
|
||||
return goal;
|
||||
}
|
||||
73
codemp/game/NPC_misc.c
Normal file
73
codemp/game/NPC_misc.c
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// NPC_misc.cpp
|
||||
//
|
||||
#include "b_local.h"
|
||||
#include "q_shared.h"
|
||||
|
||||
/*
|
||||
Debug_Printf
|
||||
*/
|
||||
void Debug_Printf (vmCvar_t *cv, int debugLevel, char *fmt, ...)
|
||||
{
|
||||
char *color;
|
||||
va_list argptr;
|
||||
char msg[1024];
|
||||
|
||||
if (cv->value < debugLevel)
|
||||
return;
|
||||
|
||||
if (debugLevel == DEBUG_LEVEL_DETAIL)
|
||||
color = S_COLOR_WHITE;
|
||||
else if (debugLevel == DEBUG_LEVEL_INFO)
|
||||
color = S_COLOR_GREEN;
|
||||
else if (debugLevel == DEBUG_LEVEL_WARNING)
|
||||
color = S_COLOR_YELLOW;
|
||||
else if (debugLevel == DEBUG_LEVEL_ERROR)
|
||||
color = S_COLOR_RED;
|
||||
else
|
||||
color = S_COLOR_RED;
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsprintf (msg, fmt, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Com_Printf("%s%5i:%s", color, level.time, msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Debug_NPCPrintf
|
||||
*/
|
||||
void Debug_NPCPrintf (gentity_t *printNPC, vmCvar_t *cv, int debugLevel, char *fmt, ...)
|
||||
{
|
||||
int color;
|
||||
va_list argptr;
|
||||
char msg[1024];
|
||||
|
||||
if (cv->value < debugLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if ( debugNPCName.string[0] && Q_stricmp( debugNPCName.string, printNPC->targetname) != 0 )
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (debugLevel == DEBUG_LEVEL_DETAIL)
|
||||
color = COLOR_WHITE;
|
||||
else if (debugLevel == DEBUG_LEVEL_INFO)
|
||||
color = COLOR_GREEN;
|
||||
else if (debugLevel == DEBUG_LEVEL_WARNING)
|
||||
color = COLOR_YELLOW;
|
||||
else if (debugLevel == DEBUG_LEVEL_ERROR)
|
||||
color = COLOR_RED;
|
||||
else
|
||||
color = COLOR_RED;
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsprintf (msg, fmt, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Com_Printf ("%c%c%5i (%s) %s", Q_COLOR_ESCAPE, color, level.time, printNPC->targetname, msg);
|
||||
}
|
||||
505
codemp/game/NPC_move.c
Normal file
505
codemp/game/NPC_move.c
Normal file
@@ -0,0 +1,505 @@
|
||||
//
|
||||
// NPC_move.cpp
|
||||
//
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
#include "anims.h"
|
||||
|
||||
void G_Cylinder( vec3_t start, vec3_t end, float radius, vec3_t color );
|
||||
|
||||
qboolean G_BoundsOverlap(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2);
|
||||
int NAV_Steer( gentity_t *self, vec3_t dir, float distance );
|
||||
extern int GetTime ( int lastTime );
|
||||
|
||||
navInfo_t frameNavInfo;
|
||||
extern qboolean FlyingCreature( gentity_t *ent );
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern qboolean PM_InKnockDown( playerState_t *ps );
|
||||
#include "../namespace_end.h"
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ClearPathToGoal
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
qboolean NPC_ClearPathToGoal( vec3_t dir, gentity_t *goal )
|
||||
{
|
||||
trace_t trace;
|
||||
float radius, dist, tFrac;
|
||||
|
||||
//FIXME: What does do about area portals? THIS IS BROKEN
|
||||
//if ( gi.inPVS( NPC->r.currentOrigin, goal->r.currentOrigin ) == qfalse )
|
||||
// return qfalse;
|
||||
|
||||
//Look ahead and see if we're clear to move to our goal position
|
||||
if ( NAV_CheckAhead( NPC, goal->r.currentOrigin, &trace, ( NPC->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) )
|
||||
{
|
||||
//VectorSubtract( goal->r.currentOrigin, NPC->r.currentOrigin, dir );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (!FlyingCreature(NPC))
|
||||
{
|
||||
//See if we're too far above
|
||||
if ( fabs( NPC->r.currentOrigin[2] - goal->r.currentOrigin[2] ) > 48 )
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//This is a work around
|
||||
radius = ( NPC->r.maxs[0] > NPC->r.maxs[1] ) ? NPC->r.maxs[0] : NPC->r.maxs[1];
|
||||
dist = Distance( NPC->r.currentOrigin, goal->r.currentOrigin );
|
||||
tFrac = 1.0f - ( radius / dist );
|
||||
|
||||
if ( trace.fraction >= tFrac )
|
||||
return qtrue;
|
||||
|
||||
//See if we're looking for a navgoal
|
||||
if ( goal->flags & FL_NAVGOAL )
|
||||
{
|
||||
//Okay, didn't get all the way there, let's see if we got close enough:
|
||||
if ( NAV_HitNavGoal( trace.endpos, NPC->r.mins, NPC->r.maxs, goal->r.currentOrigin, NPCInfo->goalRadius, FlyingCreature( NPC ) ) )
|
||||
{
|
||||
//VectorSubtract(goal->r.currentOrigin, NPC->r.currentOrigin, dir);
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_CheckCombatMove
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
ID_INLINE qboolean NPC_CheckCombatMove( void )
|
||||
{
|
||||
//return NPCInfo->combatMove;
|
||||
if ( ( NPCInfo->goalEntity && NPC->enemy && NPCInfo->goalEntity == NPC->enemy ) || ( NPCInfo->combatMove ) )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if ( NPCInfo->goalEntity && NPCInfo->watchTarget )
|
||||
{
|
||||
if ( NPCInfo->goalEntity != NPCInfo->watchTarget )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_LadderMove
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
static void NPC_LadderMove( vec3_t dir )
|
||||
{
|
||||
//FIXME: this doesn't guarantee we're facing ladder
|
||||
//ALSO: Need to be able to get off at top
|
||||
//ALSO: Need to play an anim
|
||||
//ALSO: Need transitionary anims?
|
||||
|
||||
if ( ( dir[2] > 0 ) || ( dir[2] < 0 && NPC->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
|
||||
{
|
||||
//Set our movement direction
|
||||
ucmd.upmove = (dir[2] > 0) ? 127 : -127;
|
||||
|
||||
//Don't move around on XY
|
||||
ucmd.forwardmove = ucmd.rightmove = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_GetMoveInformation
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
ID_INLINE qboolean NPC_GetMoveInformation( vec3_t dir, float *distance )
|
||||
{
|
||||
//NOTENOTE: Use path stacks!
|
||||
|
||||
//Make sure we have somewhere to go
|
||||
if ( NPCInfo->goalEntity == NULL )
|
||||
return qfalse;
|
||||
|
||||
//Get our move info
|
||||
VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir );
|
||||
*distance = VectorNormalize( dir );
|
||||
|
||||
VectorCopy( NPCInfo->goalEntity->r.currentOrigin, NPCInfo->blockedDest );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAV_GetLastMove
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NAV_GetLastMove( navInfo_t *info )
|
||||
{
|
||||
*info = frameNavInfo;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_GetMoveDirection
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
qboolean NPC_GetMoveDirection( vec3_t out, float *distance )
|
||||
{
|
||||
vec3_t angles;
|
||||
|
||||
//Clear the struct
|
||||
memset( &frameNavInfo, 0, sizeof( frameNavInfo ) );
|
||||
|
||||
//Get our movement, if any
|
||||
if ( NPC_GetMoveInformation( frameNavInfo.direction, &frameNavInfo.distance ) == qfalse )
|
||||
return qfalse;
|
||||
|
||||
//Setup the return value
|
||||
*distance = frameNavInfo.distance;
|
||||
|
||||
//For starters
|
||||
VectorCopy( frameNavInfo.direction, frameNavInfo.pathDirection );
|
||||
|
||||
//If on a ladder, move appropriately
|
||||
if ( NPC->watertype & CONTENTS_LADDER )
|
||||
{
|
||||
NPC_LadderMove( frameNavInfo.direction );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//Attempt a straight move to goal
|
||||
if ( NPC_ClearPathToGoal( frameNavInfo.direction, NPCInfo->goalEntity ) == qfalse )
|
||||
{
|
||||
//See if we're just stuck
|
||||
if ( NAV_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE )
|
||||
{
|
||||
//Can't reach goal, just face
|
||||
vectoangles( frameNavInfo.direction, angles );
|
||||
NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
frameNavInfo.flags |= NIF_MACRO_NAV;
|
||||
}
|
||||
|
||||
//Avoid any collisions on the way
|
||||
if ( NAV_AvoidCollision( NPC, NPCInfo->goalEntity, &frameNavInfo ) == qfalse )
|
||||
{
|
||||
//FIXME: Emit a warning, this is a worst case scenario
|
||||
//FIXME: if we have a clear path to our goal (exluding bodies), but then this
|
||||
// check (against bodies only) fails, shouldn't we fall back
|
||||
// to macro navigation? Like so:
|
||||
if ( !(frameNavInfo.flags&NIF_MACRO_NAV) )
|
||||
{//we had a clear path to goal and didn't try macro nav, but can't avoid collision so try macro nav here
|
||||
//See if we're just stuck
|
||||
if ( NAV_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE )
|
||||
{
|
||||
//Can't reach goal, just face
|
||||
vectoangles( frameNavInfo.direction, angles );
|
||||
NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
frameNavInfo.flags |= NIF_MACRO_NAV;
|
||||
}
|
||||
}
|
||||
|
||||
//Setup the return values
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_GetMoveDirectionAltRoute
|
||||
-------------------------
|
||||
*/
|
||||
extern int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t *info );
|
||||
extern qboolean NAVNEW_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t *info, qboolean setBlockedInfo, int blockedMovesLimit );
|
||||
qboolean NPC_GetMoveDirectionAltRoute( vec3_t out, float *distance, qboolean tryStraight )
|
||||
{
|
||||
vec3_t angles;
|
||||
|
||||
NPCInfo->aiFlags &= ~NPCAI_BLOCKED;
|
||||
|
||||
//Clear the struct
|
||||
memset( &frameNavInfo, 0, sizeof( frameNavInfo ) );
|
||||
|
||||
//Get our movement, if any
|
||||
if ( NPC_GetMoveInformation( frameNavInfo.direction, &frameNavInfo.distance ) == qfalse )
|
||||
return qfalse;
|
||||
|
||||
//Setup the return value
|
||||
*distance = frameNavInfo.distance;
|
||||
|
||||
//For starters
|
||||
VectorCopy( frameNavInfo.direction, frameNavInfo.pathDirection );
|
||||
|
||||
//If on a ladder, move appropriately
|
||||
if ( NPC->watertype & CONTENTS_LADDER )
|
||||
{
|
||||
NPC_LadderMove( frameNavInfo.direction );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//Attempt a straight move to goal
|
||||
if ( !tryStraight || NPC_ClearPathToGoal( frameNavInfo.direction, NPCInfo->goalEntity ) == qfalse )
|
||||
{//blocked
|
||||
//Can't get straight to goal, use macro nav
|
||||
if ( NAVNEW_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE )
|
||||
{
|
||||
//Can't reach goal, just face
|
||||
vectoangles( frameNavInfo.direction, angles );
|
||||
NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
return qfalse;
|
||||
}
|
||||
//else we are on our way
|
||||
frameNavInfo.flags |= NIF_MACRO_NAV;
|
||||
}
|
||||
else
|
||||
{//we have no architectural problems, see if there are ents inthe way and try to go around them
|
||||
//not blocked
|
||||
if ( d_altRoutes.integer )
|
||||
{//try macro nav
|
||||
navInfo_t tempInfo;
|
||||
memcpy( &tempInfo, &frameNavInfo, sizeof( tempInfo ) );
|
||||
if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, &tempInfo, qtrue, 5 ) == qfalse )
|
||||
{//revert to macro nav
|
||||
//Can't get straight to goal, dump tempInfo and use macro nav
|
||||
if ( NAVNEW_MoveToGoal( NPC, &frameNavInfo ) == WAYPOINT_NONE )
|
||||
{
|
||||
//Can't reach goal, just face
|
||||
vectoangles( frameNavInfo.direction, angles );
|
||||
NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] );
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
return qfalse;
|
||||
}
|
||||
//else we are on our way
|
||||
frameNavInfo.flags |= NIF_MACRO_NAV;
|
||||
}
|
||||
else
|
||||
{//otherwise, either clear or can avoid
|
||||
memcpy( &frameNavInfo, &tempInfo, sizeof( frameNavInfo ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//OR: just give up
|
||||
if ( NAVNEW_AvoidCollision( NPC, NPCInfo->goalEntity, &frameNavInfo, qtrue, 30 ) == qfalse )
|
||||
{//give up
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Setup the return values
|
||||
VectorCopy( frameNavInfo.direction, out );
|
||||
*distance = frameNavInfo.distance;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
void G_UcmdMoveForDir( gentity_t *self, usercmd_t *cmd, vec3_t dir )
|
||||
{
|
||||
vec3_t forward, right;
|
||||
float fDot, rDot;
|
||||
|
||||
AngleVectors( self->r.currentAngles, forward, right, NULL );
|
||||
|
||||
dir[2] = 0;
|
||||
VectorNormalize( dir );
|
||||
//NPCs cheat and store this directly because converting movement into a ucmd loses precision
|
||||
VectorCopy( dir, self->client->ps.moveDir );
|
||||
|
||||
fDot = DotProduct( forward, dir ) * 127.0f;
|
||||
rDot = DotProduct( right, dir ) * 127.0f;
|
||||
//Must clamp this because DotProduct is not guaranteed to return a number within -1 to 1, and that would be bad when we're shoving this into a signed byte
|
||||
if ( fDot > 127.0f )
|
||||
{
|
||||
fDot = 127.0f;
|
||||
}
|
||||
if ( fDot < -127.0f )
|
||||
{
|
||||
fDot = -127.0f;
|
||||
}
|
||||
if ( rDot > 127.0f )
|
||||
{
|
||||
rDot = 127.0f;
|
||||
}
|
||||
if ( rDot < -127.0f )
|
||||
{
|
||||
rDot = -127.0f;
|
||||
}
|
||||
cmd->forwardmove = floor(fDot);
|
||||
cmd->rightmove = floor(rDot);
|
||||
|
||||
/*
|
||||
vec3_t wishvel;
|
||||
for ( int i = 0 ; i < 3 ; i++ )
|
||||
{
|
||||
wishvel[i] = forward[i]*cmd->forwardmove + right[i]*cmd->rightmove;
|
||||
}
|
||||
VectorNormalize( wishvel );
|
||||
if ( !VectorCompare( wishvel, dir ) )
|
||||
{
|
||||
Com_Printf( "PRECISION LOSS: %s != %s\n", vtos(wishvel), vtos(dir) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_MoveToGoal
|
||||
|
||||
Now assumes goal is goalEntity, was no reason for it to be otherwise
|
||||
-------------------------
|
||||
*/
|
||||
#if AI_TIMERS
|
||||
extern int navTime;
|
||||
#endif// AI_TIMERS
|
||||
qboolean NPC_MoveToGoal( qboolean tryStraight )
|
||||
{
|
||||
float distance;
|
||||
vec3_t dir;
|
||||
|
||||
#if AI_TIMERS
|
||||
int startTime = GetTime(0);
|
||||
#endif// AI_TIMERS
|
||||
//If taking full body pain, don't move
|
||||
if ( PM_InKnockDown( &NPC->client->ps ) || ( ( NPC->s.legsAnim >= BOTH_PAIN1 ) && ( NPC->s.legsAnim <= BOTH_PAIN18 ) ) )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
if( NPC->s.eFlags & EF_LOCKED_TO_WEAPON )
|
||||
{//If in an emplaced gun, never try to navigate!
|
||||
return qtrue;
|
||||
}
|
||||
*/
|
||||
//rwwFIXMEFIXME: emplaced support
|
||||
|
||||
//FIXME: if can't get to goal & goal is a target (enemy), try to find a waypoint that has line of sight to target, at least?
|
||||
//Get our movement direction
|
||||
#if 1
|
||||
if ( NPC_GetMoveDirectionAltRoute( dir, &distance, tryStraight ) == qfalse )
|
||||
#else
|
||||
if ( NPC_GetMoveDirection( dir, &distance ) == qfalse )
|
||||
#endif
|
||||
return qfalse;
|
||||
|
||||
NPCInfo->distToGoal = distance;
|
||||
|
||||
//Convert the move to angles
|
||||
vectoangles( dir, NPCInfo->lastPathAngles );
|
||||
if ( (ucmd.buttons&BUTTON_WALKING) )
|
||||
{
|
||||
NPC->client->ps.speed = NPCInfo->stats.walkSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC->client->ps.speed = NPCInfo->stats.runSpeed;
|
||||
}
|
||||
|
||||
//FIXME: still getting ping-ponging in certain cases... !!! Nav/avoidance error? WTF???!!!
|
||||
//If in combat move, then move directly towards our goal
|
||||
if ( NPC_CheckCombatMove() )
|
||||
{//keep current facing
|
||||
G_UcmdMoveForDir( NPC, &ucmd, dir );
|
||||
}
|
||||
else
|
||||
{//face our goal
|
||||
//FIXME: strafe instead of turn if change in dir is small and temporary
|
||||
NPCInfo->desiredPitch = 0.0f;
|
||||
NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->lastPathAngles[YAW] );
|
||||
|
||||
//Pitch towards the goal and also update if flying or swimming
|
||||
if ( (NPC->client->ps.eFlags2&EF2_FLYING) )//moveType == MT_FLYSWIM )
|
||||
{
|
||||
NPCInfo->desiredPitch = AngleNormalize360( NPCInfo->lastPathAngles[PITCH] );
|
||||
|
||||
if ( dir[2] )
|
||||
{
|
||||
float scale = (dir[2] * distance);
|
||||
if ( scale > 64 )
|
||||
{
|
||||
scale = 64;
|
||||
}
|
||||
else if ( scale < -64 )
|
||||
{
|
||||
scale = -64;
|
||||
}
|
||||
NPC->client->ps.velocity[2] = scale;
|
||||
//NPC->client->ps.velocity[2] = (dir[2] > 0) ? 64 : -64;
|
||||
}
|
||||
}
|
||||
|
||||
//Set any final info
|
||||
ucmd.forwardmove = 127;
|
||||
}
|
||||
|
||||
#if AI_TIMERS
|
||||
navTime += GetTime( startTime );
|
||||
#endif// AI_TIMERS
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
void NPC_SlideMoveToGoal( void )
|
||||
|
||||
Now assumes goal is goalEntity, if want to use tempGoal, you set that before calling the func
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NPC_SlideMoveToGoal( void )
|
||||
{
|
||||
float saveYaw = NPC->client->ps.viewangles[YAW];
|
||||
qboolean ret;
|
||||
|
||||
NPCInfo->combatMove = qtrue;
|
||||
|
||||
ret = NPC_MoveToGoal( qtrue );
|
||||
|
||||
NPCInfo->desiredYaw = saveYaw;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_ApplyRoff
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void NPC_ApplyRoff(void)
|
||||
{
|
||||
BG_PlayerStateToEntityState( &NPC->client->ps, &NPC->s, qfalse );
|
||||
//VectorCopy ( NPC->r.currentOrigin, NPC->lastOrigin );
|
||||
//rwwFIXMEFIXME: Any significance to this?
|
||||
|
||||
// use the precise origin for linking
|
||||
trap_LinkEntity(NPC);
|
||||
}
|
||||
1127
codemp/game/NPC_reactions.c
Normal file
1127
codemp/game/NPC_reactions.c
Normal file
File diff suppressed because it is too large
Load Diff
934
codemp/game/NPC_senses.c
Normal file
934
codemp/game/NPC_senses.c
Normal file
@@ -0,0 +1,934 @@
|
||||
//NPC_senses.cpp
|
||||
|
||||
#include "b_local.h"
|
||||
|
||||
extern int eventClearTime;
|
||||
/*
|
||||
qboolean G_ClearLineOfSight(const vec3_t point1, const vec3_t point2, int ignore, int clipmask)
|
||||
|
||||
returns true if can see from point 1 to 2, even through glass (1 pane)- doesn't work with portals
|
||||
*/
|
||||
qboolean G_ClearLineOfSight(const vec3_t point1, const vec3_t point2, int ignore, int clipmask)
|
||||
{
|
||||
trace_t tr;
|
||||
gentity_t *hit;
|
||||
|
||||
trap_Trace ( &tr, point1, NULL, NULL, point2, ignore, clipmask );
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
hit = &g_entities[ tr.entityNum ];
|
||||
if(EntIsGlass(hit))
|
||||
{
|
||||
vec3_t newpoint1;
|
||||
VectorCopy(tr.endpos, newpoint1);
|
||||
trap_Trace (&tr, newpoint1, NULL, NULL, point2, hit->s.number, clipmask );
|
||||
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
CanSee
|
||||
determine if NPC can see an entity
|
||||
|
||||
This is a straight line trace check. This function does not look at PVS or FOV,
|
||||
or take any AI related factors (for example, the NPC's reaction time) into account
|
||||
|
||||
FIXME do we need fat and thin version of this?
|
||||
*/
|
||||
qboolean CanSee ( gentity_t *ent )
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t eyes;
|
||||
vec3_t spot;
|
||||
|
||||
CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
CalcEntitySpot( ent, SPOT_ORIGIN, spot );
|
||||
trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
|
||||
ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
CalcEntitySpot( ent, SPOT_HEAD, spot );
|
||||
trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
|
||||
ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
CalcEntitySpot( ent, SPOT_LEGS, spot );
|
||||
trap_Trace ( &tr, eyes, NULL, NULL, spot, NPC->s.number, MASK_OPAQUE );
|
||||
ShotThroughGlass (&tr, ent, spot, MASK_OPAQUE);
|
||||
if ( tr.fraction == 1.0 )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold )
|
||||
{
|
||||
vec3_t dir, forward, angles;
|
||||
float dot;
|
||||
|
||||
VectorSubtract( spot, from, dir );
|
||||
dir[2] = 0;
|
||||
VectorNormalize( dir );
|
||||
|
||||
VectorCopy( fromAngles, angles );
|
||||
angles[0] = 0;
|
||||
AngleVectors( angles, forward, NULL, NULL );
|
||||
|
||||
dot = DotProduct( dir, forward );
|
||||
|
||||
return (dot > threshHold);
|
||||
}
|
||||
|
||||
/*
|
||||
InFOV
|
||||
|
||||
IDEA: further off to side of FOV range, higher chance of failing even if technically in FOV,
|
||||
keep core of 50% to sides as always succeeding
|
||||
*/
|
||||
|
||||
//Position compares
|
||||
|
||||
qboolean InFOV3( vec3_t spot, vec3_t from, vec3_t fromAngles, int hFOV, int vFOV )
|
||||
{
|
||||
vec3_t deltaVector, angles, deltaAngles;
|
||||
|
||||
VectorSubtract ( spot, from, deltaVector );
|
||||
vectoangles ( deltaVector, angles );
|
||||
|
||||
deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
|
||||
deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
|
||||
|
||||
if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//NPC to position
|
||||
|
||||
qboolean InFOV2( vec3_t origin, gentity_t *from, int hFOV, int vFOV )
|
||||
{
|
||||
vec3_t fromAngles, eyes;
|
||||
|
||||
if( from->client )
|
||||
{
|
||||
VectorCopy(from->client->ps.viewangles, fromAngles);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(from->s.angles, fromAngles);
|
||||
}
|
||||
|
||||
CalcEntitySpot( from, SPOT_HEAD, eyes );
|
||||
|
||||
return InFOV3( origin, eyes, fromAngles, hFOV, vFOV );
|
||||
}
|
||||
|
||||
//Entity to entity
|
||||
|
||||
qboolean InFOV ( gentity_t *ent, gentity_t *from, int hFOV, int vFOV )
|
||||
{
|
||||
vec3_t eyes;
|
||||
vec3_t spot;
|
||||
vec3_t deltaVector;
|
||||
vec3_t angles, fromAngles;
|
||||
vec3_t deltaAngles;
|
||||
|
||||
if( from->client )
|
||||
{
|
||||
if( !VectorCompare( from->client->renderInfo.eyeAngles, vec3_origin ) )
|
||||
{//Actual facing of tag_head!
|
||||
//NOTE: Stasis aliens may have a problem with this?
|
||||
VectorCopy( from->client->renderInfo.eyeAngles, fromAngles );
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( from->client->ps.viewangles, fromAngles );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(from->s.angles, fromAngles);
|
||||
}
|
||||
|
||||
CalcEntitySpot( from, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
CalcEntitySpot( ent, SPOT_ORIGIN, spot );
|
||||
VectorSubtract ( spot, eyes, deltaVector);
|
||||
|
||||
vectoangles ( deltaVector, angles );
|
||||
deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
|
||||
deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
|
||||
if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
CalcEntitySpot( ent, SPOT_HEAD, spot );
|
||||
VectorSubtract ( spot, eyes, deltaVector);
|
||||
vectoangles ( deltaVector, angles );
|
||||
deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
|
||||
deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
|
||||
if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
CalcEntitySpot( ent, SPOT_LEGS, spot );
|
||||
VectorSubtract ( spot, eyes, deltaVector);
|
||||
vectoangles ( deltaVector, angles );
|
||||
deltaAngles[PITCH] = AngleDelta ( fromAngles[PITCH], angles[PITCH] );
|
||||
deltaAngles[YAW] = AngleDelta ( fromAngles[YAW], angles[YAW] );
|
||||
if ( fabs ( deltaAngles[PITCH] ) <= vFOV && fabs ( deltaAngles[YAW] ) <= hFOV )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
qboolean InVisrange ( gentity_t *ent )
|
||||
{//FIXME: make a calculate visibility for ents that takes into account
|
||||
//lighting, movement, turning, crouch/stand up, other anims, hide brushes, etc.
|
||||
vec3_t eyes;
|
||||
vec3_t spot;
|
||||
vec3_t deltaVector;
|
||||
float visrange = (NPCInfo->stats.visrange*NPCInfo->stats.visrange);
|
||||
|
||||
CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
CalcEntitySpot( ent, SPOT_ORIGIN, spot );
|
||||
VectorSubtract ( spot, eyes, deltaVector);
|
||||
|
||||
/*if(ent->client)
|
||||
{
|
||||
float vel, avel;
|
||||
if(ent->client->ps.velocity[0] || ent->client->ps.velocity[1] || ent->client->ps.velocity[2])
|
||||
{
|
||||
vel = VectorLength(ent->client->ps.velocity);
|
||||
if(vel > 128)
|
||||
{
|
||||
visrange += visrange * (vel/256);
|
||||
}
|
||||
}
|
||||
|
||||
if(ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
|
||||
{//FIXME: shouldn't they need to have line of sight to you to detect this?
|
||||
avel = VectorLength(ent->avelocity);
|
||||
if(avel > 15)
|
||||
{
|
||||
visrange += visrange * (avel/60);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if(VectorLengthSquared(deltaVector) > visrange)
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
NPC_CheckVisibility
|
||||
*/
|
||||
|
||||
visibility_t NPC_CheckVisibility ( gentity_t *ent, int flags )
|
||||
{
|
||||
// flags should never be 0
|
||||
if ( !flags )
|
||||
{
|
||||
return VIS_NOT;
|
||||
}
|
||||
|
||||
// check PVS
|
||||
if ( flags & CHECK_PVS )
|
||||
{
|
||||
if ( !trap_InPVS ( ent->r.currentOrigin, NPC->r.currentOrigin ) )
|
||||
{
|
||||
return VIS_NOT;
|
||||
}
|
||||
}
|
||||
if ( !(flags & (CHECK_360|CHECK_FOV|CHECK_SHOOT)) )
|
||||
{
|
||||
return VIS_PVS;
|
||||
}
|
||||
|
||||
// check within visrange
|
||||
if (flags & CHECK_VISRANGE)
|
||||
{
|
||||
if( !InVisrange ( ent ) )
|
||||
{
|
||||
return VIS_PVS;
|
||||
}
|
||||
}
|
||||
|
||||
// check 360 degree visibility
|
||||
//Meaning has to be a direct line of site
|
||||
if ( flags & CHECK_360 )
|
||||
{
|
||||
if ( !CanSee ( ent ) )
|
||||
{
|
||||
return VIS_PVS;
|
||||
}
|
||||
}
|
||||
if ( !(flags & (CHECK_FOV|CHECK_SHOOT)) )
|
||||
{
|
||||
return VIS_360;
|
||||
}
|
||||
|
||||
// check FOV
|
||||
if ( flags & CHECK_FOV )
|
||||
{
|
||||
if ( !InFOV ( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov) )
|
||||
{
|
||||
return VIS_360;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(flags & CHECK_SHOOT) )
|
||||
{
|
||||
return VIS_FOV;
|
||||
}
|
||||
|
||||
// check shootability
|
||||
if ( flags & CHECK_SHOOT )
|
||||
{
|
||||
if ( !CanShoot ( ent, NPC ) )
|
||||
{
|
||||
return VIS_FOV;
|
||||
}
|
||||
}
|
||||
|
||||
return VIS_SHOOT;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_CheckSoundEvents
|
||||
-------------------------
|
||||
*/
|
||||
static int G_CheckSoundEvents( gentity_t *self, float maxHearDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
|
||||
{
|
||||
int bestEvent = -1;
|
||||
int bestAlert = -1;
|
||||
int bestTime = -1;
|
||||
int i;
|
||||
float dist, radius;
|
||||
|
||||
maxHearDist *= maxHearDist;
|
||||
|
||||
for ( i = 0; i < level.numAlertEvents; i++ )
|
||||
{
|
||||
//are we purposely ignoring this alert?
|
||||
if ( i == ignoreAlert )
|
||||
continue;
|
||||
//We're only concerned about sounds
|
||||
if ( level.alertEvents[i].type != AET_SOUND )
|
||||
continue;
|
||||
//must be at least this noticable
|
||||
if ( level.alertEvents[i].level < minAlertLevel )
|
||||
continue;
|
||||
//must have an owner?
|
||||
if ( mustHaveOwner && !level.alertEvents[i].owner )
|
||||
continue;
|
||||
//Must be within range
|
||||
dist = DistanceSquared( level.alertEvents[i].position, self->r.currentOrigin );
|
||||
|
||||
//can't hear it
|
||||
if ( dist > maxHearDist )
|
||||
continue;
|
||||
|
||||
radius = level.alertEvents[i].radius * level.alertEvents[i].radius;
|
||||
if ( dist > radius )
|
||||
continue;
|
||||
|
||||
if ( level.alertEvents[i].addLight )
|
||||
{//a quiet sound, must have LOS to hear it
|
||||
if ( G_ClearLOS5( self, level.alertEvents[i].position ) == qfalse )
|
||||
{//no LOS, didn't hear it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//See if this one takes precedence over the previous one
|
||||
if ( level.alertEvents[i].level >= bestAlert //higher alert level
|
||||
|| (level.alertEvents[i].level==bestAlert&&level.alertEvents[i].timestamp >= bestTime) )//same alert level, but this one is newer
|
||||
{//NOTE: equal is better because it's later in the array
|
||||
bestEvent = i;
|
||||
bestAlert = level.alertEvents[i].level;
|
||||
bestTime = level.alertEvents[i].timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return bestEvent;
|
||||
}
|
||||
|
||||
float G_GetLightLevel( vec3_t pos, vec3_t fromDir )
|
||||
{
|
||||
/*
|
||||
vec3_t ambient={0}, directed, lightDir;
|
||||
|
||||
cgi_R_GetLighting( pos, ambient, directed, lightDir );
|
||||
lightLevel = VectorLength( ambient ) + (VectorLength( directed )*DotProduct( lightDir, fromDir ));
|
||||
*/
|
||||
float lightLevel;
|
||||
//rwwFIXMEFIXME: ...this is evil. We can possibly read from the server BSP data, or load the lightmap along
|
||||
//with collision data and whatnot, but is it worth it?
|
||||
lightLevel = 255;
|
||||
|
||||
return lightLevel;
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
NPC_CheckSightEvents
|
||||
-------------------------
|
||||
*/
|
||||
static int G_CheckSightEvents( gentity_t *self, int hFOV, int vFOV, float maxSeeDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
|
||||
{
|
||||
int bestEvent = -1;
|
||||
int bestAlert = -1;
|
||||
int bestTime = -1;
|
||||
int i;
|
||||
float dist, radius;
|
||||
|
||||
maxSeeDist *= maxSeeDist;
|
||||
for ( i = 0; i < level.numAlertEvents; i++ )
|
||||
{
|
||||
//are we purposely ignoring this alert?
|
||||
if ( i == ignoreAlert )
|
||||
continue;
|
||||
//We're only concerned about sounds
|
||||
if ( level.alertEvents[i].type != AET_SIGHT )
|
||||
continue;
|
||||
//must be at least this noticable
|
||||
if ( level.alertEvents[i].level < minAlertLevel )
|
||||
continue;
|
||||
//must have an owner?
|
||||
if ( mustHaveOwner && !level.alertEvents[i].owner )
|
||||
continue;
|
||||
|
||||
//Must be within range
|
||||
dist = DistanceSquared( level.alertEvents[i].position, self->r.currentOrigin );
|
||||
|
||||
//can't see it
|
||||
if ( dist > maxSeeDist )
|
||||
continue;
|
||||
|
||||
radius = level.alertEvents[i].radius * level.alertEvents[i].radius;
|
||||
if ( dist > radius )
|
||||
continue;
|
||||
|
||||
//Must be visible
|
||||
if ( InFOV2( level.alertEvents[i].position, self, hFOV, vFOV ) == qfalse )
|
||||
continue;
|
||||
|
||||
if ( G_ClearLOS5( self, level.alertEvents[i].position ) == qfalse )
|
||||
continue;
|
||||
|
||||
//FIXME: possibly have the light level at this point affect the
|
||||
// visibility/alert level of this event? Would also
|
||||
// need to take into account how bright the event
|
||||
// itself is. A lightsaber would stand out more
|
||||
// in the dark... maybe pass in a light level that
|
||||
// is added to the actual light level at this position?
|
||||
|
||||
//See if this one takes precedence over the previous one
|
||||
if ( level.alertEvents[i].level >= bestAlert //higher alert level
|
||||
|| (level.alertEvents[i].level==bestAlert&&level.alertEvents[i].timestamp >= bestTime) )//same alert level, but this one is newer
|
||||
{//NOTE: equal is better because it's later in the array
|
||||
bestEvent = i;
|
||||
bestAlert = level.alertEvents[i].level;
|
||||
bestTime = level.alertEvents[i].timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return bestEvent;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_CheckAlertEvents
|
||||
|
||||
NOTE: Should all NPCs create alertEvents too so they can detect each other?
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
int G_CheckAlertEvents( gentity_t *self, qboolean checkSight, qboolean checkSound, float maxSeeDist, float maxHearDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
|
||||
{
|
||||
int bestSoundEvent = -1;
|
||||
int bestSightEvent = -1;
|
||||
int bestSoundAlert = -1;
|
||||
int bestSightAlert = -1;
|
||||
|
||||
if ( &g_entities[0] == NULL || g_entities[0].health <= 0 )
|
||||
{
|
||||
//player is dead
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get sound event
|
||||
bestSoundEvent = G_CheckSoundEvents( self, maxHearDist, ignoreAlert, mustHaveOwner, minAlertLevel );
|
||||
//get sound event alert level
|
||||
if ( bestSoundEvent >= 0 )
|
||||
{
|
||||
bestSoundAlert = level.alertEvents[bestSoundEvent].level;
|
||||
}
|
||||
|
||||
//get sight event
|
||||
if ( self->NPC )
|
||||
{
|
||||
bestSightEvent = G_CheckSightEvents( self, self->NPC->stats.hfov, self->NPC->stats.vfov, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel );
|
||||
}
|
||||
else
|
||||
{
|
||||
bestSightEvent = G_CheckSightEvents( self, 80, 80, maxSeeDist, ignoreAlert, mustHaveOwner, minAlertLevel );//FIXME: look at cg_view to get more accurate numbers?
|
||||
}
|
||||
//get sight event alert level
|
||||
if ( bestSightEvent >= 0 )
|
||||
{
|
||||
bestSightAlert = level.alertEvents[bestSightEvent].level;
|
||||
}
|
||||
|
||||
//return the one that has a higher alert (or sound if equal)
|
||||
//FIXME: This doesn't take the distance of the event into account
|
||||
|
||||
if ( bestSightEvent >= 0 && bestSightAlert > bestSoundAlert )
|
||||
{//valid best sight event, more important than the sound event
|
||||
//get the light level of the alert event for this checker
|
||||
vec3_t eyePoint, sightDir;
|
||||
//get eye point
|
||||
CalcEntitySpot( self, SPOT_HEAD_LEAN, eyePoint );
|
||||
VectorSubtract( level.alertEvents[bestSightEvent].position, eyePoint, sightDir );
|
||||
level.alertEvents[bestSightEvent].light = level.alertEvents[bestSightEvent].addLight + G_GetLightLevel( level.alertEvents[bestSightEvent].position, sightDir );
|
||||
//return the sight event
|
||||
return bestSightEvent;
|
||||
}
|
||||
//return the sound event
|
||||
return bestSoundEvent;
|
||||
}
|
||||
|
||||
int NPC_CheckAlertEvents( qboolean checkSight, qboolean checkSound, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel )
|
||||
{
|
||||
return G_CheckAlertEvents( NPC, checkSight, checkSound, NPCInfo->stats.visrange, NPCInfo->stats.earshot, ignoreAlert, mustHaveOwner, minAlertLevel );
|
||||
}
|
||||
|
||||
qboolean G_CheckForDanger( gentity_t *self, int alertEvent )
|
||||
{//FIXME: more bStates need to call this?
|
||||
if ( alertEvent == -1 )
|
||||
{
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( level.alertEvents[alertEvent].level >= AEL_DANGER )
|
||||
{//run away!
|
||||
if ( !level.alertEvents[alertEvent].owner || !level.alertEvents[alertEvent].owner->client || (level.alertEvents[alertEvent].owner!=self&&level.alertEvents[alertEvent].owner->client->playerTeam!=self->client->playerTeam) )
|
||||
{
|
||||
if ( self->NPC )
|
||||
{
|
||||
if ( self->NPC->scriptFlags & SCF_DONT_FLEE )
|
||||
{//can't flee
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
{
|
||||
NPC_StartFlee( level.alertEvents[alertEvent].owner, level.alertEvents[alertEvent].position, level.alertEvents[alertEvent].level, 3000, 6000 );
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
qboolean NPC_CheckForDanger( int alertEvent )
|
||||
{//FIXME: more bStates need to call this?
|
||||
return G_CheckForDanger( NPC, alertEvent );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
AddSoundEvent
|
||||
-------------------------
|
||||
*/
|
||||
qboolean RemoveOldestAlert( void );
|
||||
void AddSoundEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel, qboolean needLOS )
|
||||
{
|
||||
//FIXME: Handle this in another manner?
|
||||
if ( level.numAlertEvents >= MAX_ALERT_EVENTS )
|
||||
{
|
||||
if ( !RemoveOldestAlert() )
|
||||
{//how could that fail?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( owner == NULL && alertLevel < AEL_DANGER ) //allows un-owned danger alerts
|
||||
return;
|
||||
|
||||
//FIXME: if owner is not a player or player ally, and there are no player allies present,
|
||||
// perhaps we don't need to store the alert... unless we want the player to
|
||||
// react to enemy alert events in some way?
|
||||
|
||||
VectorCopy( position, level.alertEvents[ level.numAlertEvents ].position );
|
||||
|
||||
level.alertEvents[ level.numAlertEvents ].radius = radius;
|
||||
level.alertEvents[ level.numAlertEvents ].level = alertLevel;
|
||||
level.alertEvents[ level.numAlertEvents ].type = AET_SOUND;
|
||||
level.alertEvents[ level.numAlertEvents ].owner = owner;
|
||||
if ( needLOS )
|
||||
{//a very low-level sound, when check this sound event, check for LOS
|
||||
level.alertEvents[ level.numAlertEvents ].addLight = 1; //will force an LOS trace on this sound
|
||||
}
|
||||
else
|
||||
{
|
||||
level.alertEvents[ level.numAlertEvents ].addLight = 0; //will force an LOS trace on this sound
|
||||
}
|
||||
level.alertEvents[ level.numAlertEvents ].ID = level.curAlertID++;
|
||||
level.alertEvents[ level.numAlertEvents ].timestamp = level.time;
|
||||
|
||||
level.numAlertEvents++;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
AddSightEvent
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void AddSightEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel, float addLight )
|
||||
{
|
||||
//FIXME: Handle this in another manner?
|
||||
if ( level.numAlertEvents >= MAX_ALERT_EVENTS )
|
||||
{
|
||||
if ( !RemoveOldestAlert() )
|
||||
{//how could that fail?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( owner == NULL && alertLevel < AEL_DANGER ) //allows un-owned danger alerts
|
||||
return;
|
||||
|
||||
//FIXME: if owner is not a player or player ally, and there are no player allies present,
|
||||
// perhaps we don't need to store the alert... unless we want the player to
|
||||
// react to enemy alert events in some way?
|
||||
|
||||
VectorCopy( position, level.alertEvents[ level.numAlertEvents ].position );
|
||||
|
||||
level.alertEvents[ level.numAlertEvents ].radius = radius;
|
||||
level.alertEvents[ level.numAlertEvents ].level = alertLevel;
|
||||
level.alertEvents[ level.numAlertEvents ].type = AET_SIGHT;
|
||||
level.alertEvents[ level.numAlertEvents ].owner = owner;
|
||||
level.alertEvents[ level.numAlertEvents ].addLight = addLight; //will get added to actual light at that point when it's checked
|
||||
level.alertEvents[ level.numAlertEvents ].ID = level.curAlertID++;
|
||||
level.alertEvents[ level.numAlertEvents ].timestamp = level.time;
|
||||
|
||||
level.numAlertEvents++;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
ClearPlayerAlertEvents
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
void ClearPlayerAlertEvents( void )
|
||||
{
|
||||
int curNumAlerts = level.numAlertEvents;
|
||||
int i;
|
||||
//loop through them all (max 32)
|
||||
for ( i = 0; i < curNumAlerts; i++ )
|
||||
{
|
||||
//see if the event is old enough to delete
|
||||
if ( level.alertEvents[i].timestamp && level.alertEvents[i].timestamp + ALERT_CLEAR_TIME < level.time )
|
||||
{//this event has timed out
|
||||
//drop the count
|
||||
level.numAlertEvents--;
|
||||
//shift the rest down
|
||||
if ( level.numAlertEvents > 0 )
|
||||
{//still have more in the array
|
||||
if ( (i+1) < MAX_ALERT_EVENTS )
|
||||
{
|
||||
memmove( &level.alertEvents[i], &level.alertEvents[i+1], sizeof(alertEvent_t)*(MAX_ALERT_EVENTS-(i+1) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//just clear this one... or should we clear the whole array?
|
||||
memset( &level.alertEvents[i], 0, sizeof( alertEvent_t ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
//make sure this never drops below zero... if it does, something very very bad happened
|
||||
assert( level.numAlertEvents >= 0 );
|
||||
|
||||
if ( eventClearTime < level.time )
|
||||
{//this is just a 200ms debouncer so things that generate constant alerts (like corpses and missiles) add an alert every 200 ms
|
||||
eventClearTime = level.time + ALERT_CLEAR_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean RemoveOldestAlert( void )
|
||||
{
|
||||
int oldestEvent = -1, oldestTime = Q3_INFINITE;
|
||||
int i;
|
||||
//loop through them all (max 32)
|
||||
for ( i = 0; i < level.numAlertEvents; i++ )
|
||||
{
|
||||
//see if the event is old enough to delete
|
||||
if ( level.alertEvents[i].timestamp < oldestTime )
|
||||
{
|
||||
oldestEvent = i;
|
||||
oldestTime = level.alertEvents[i].timestamp;
|
||||
}
|
||||
}
|
||||
if ( oldestEvent != -1 )
|
||||
{
|
||||
//drop the count
|
||||
level.numAlertEvents--;
|
||||
//shift the rest down
|
||||
if ( level.numAlertEvents > 0 )
|
||||
{//still have more in the array
|
||||
if ( (oldestEvent+1) < MAX_ALERT_EVENTS )
|
||||
{
|
||||
memmove( &level.alertEvents[oldestEvent], &level.alertEvents[oldestEvent+1], sizeof(alertEvent_t)*(MAX_ALERT_EVENTS-(oldestEvent+1) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//just clear this one... or should we clear the whole array?
|
||||
memset( &level.alertEvents[oldestEvent], 0, sizeof( alertEvent_t ) );
|
||||
}
|
||||
}
|
||||
//make sure this never drops below zero... if it does, something very very bad happened
|
||||
assert( level.numAlertEvents >= 0 );
|
||||
//return true is have room for one now
|
||||
return (level.numAlertEvents<MAX_ALERT_EVENTS);
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
G_ClearLOS
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
// Position to position
|
||||
qboolean G_ClearLOS( gentity_t *self, const vec3_t start, const vec3_t end )
|
||||
{
|
||||
trace_t tr;
|
||||
int traceCount = 0;
|
||||
|
||||
//FIXME: ENTITYNUM_NONE ok?
|
||||
trap_Trace ( &tr, start, NULL, NULL, end, ENTITYNUM_NONE, CONTENTS_OPAQUE/*CONTENTS_SOLID*//*(CONTENTS_SOLID|CONTENTS_MONSTERCLIP)*/ );
|
||||
while ( tr.fraction < 1.0 && traceCount < 3 )
|
||||
{//can see through 3 panes of glass
|
||||
if ( tr.entityNum < ENTITYNUM_WORLD )
|
||||
{
|
||||
if ( &g_entities[tr.entityNum] != NULL && (g_entities[tr.entityNum].r.svFlags&SVF_GLASS_BRUSH) )
|
||||
{//can see through glass, trace again, ignoring me
|
||||
trap_Trace ( &tr, tr.endpos, NULL, NULL, end, tr.entityNum, MASK_OPAQUE );
|
||||
traceCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( tr.fraction == 1.0 )
|
||||
return qtrue;
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//Entity to position
|
||||
qboolean G_ClearLOS2( gentity_t *self, gentity_t *ent, const vec3_t end )
|
||||
{
|
||||
vec3_t eyes;
|
||||
|
||||
CalcEntitySpot( ent, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
return G_ClearLOS( self, eyes, end );
|
||||
}
|
||||
|
||||
//Position to entity
|
||||
qboolean G_ClearLOS3( gentity_t *self, const vec3_t start, gentity_t *ent )
|
||||
{
|
||||
vec3_t spot;
|
||||
|
||||
//Look for the chest first
|
||||
CalcEntitySpot( ent, SPOT_ORIGIN, spot );
|
||||
|
||||
if ( G_ClearLOS( self, start, spot ) )
|
||||
return qtrue;
|
||||
|
||||
//Look for the head next
|
||||
CalcEntitySpot( ent, SPOT_HEAD_LEAN, spot );
|
||||
|
||||
if ( G_ClearLOS( self, start, spot ) )
|
||||
return qtrue;
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//NPC's eyes to entity
|
||||
qboolean G_ClearLOS4( gentity_t *self, gentity_t *ent )
|
||||
{
|
||||
vec3_t eyes;
|
||||
|
||||
//Calculate my position
|
||||
CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
return G_ClearLOS3( self, eyes, ent );
|
||||
}
|
||||
|
||||
//NPC's eyes to position
|
||||
qboolean G_ClearLOS5( gentity_t *self, const vec3_t end )
|
||||
{
|
||||
vec3_t eyes;
|
||||
|
||||
//Calculate the my position
|
||||
CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
|
||||
|
||||
return G_ClearLOS( self, eyes, end );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_GetFOVPercentage
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
float NPC_GetHFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float hFOV )
|
||||
{
|
||||
vec3_t deltaVector, angles;
|
||||
float delta;
|
||||
|
||||
VectorSubtract ( spot, from, deltaVector );
|
||||
|
||||
vectoangles ( deltaVector, angles );
|
||||
|
||||
delta = fabs( AngleDelta ( facing[YAW], angles[YAW] ) );
|
||||
|
||||
if ( delta > hFOV )
|
||||
return 0.0f;
|
||||
|
||||
return ( ( hFOV - delta ) / hFOV );
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NPC_GetVFOVPercentage
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
float NPC_GetVFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float vFOV )
|
||||
{
|
||||
vec3_t deltaVector, angles;
|
||||
float delta;
|
||||
|
||||
VectorSubtract ( spot, from, deltaVector );
|
||||
|
||||
vectoangles ( deltaVector, angles );
|
||||
|
||||
delta = fabs( AngleDelta ( facing[PITCH], angles[PITCH] ) );
|
||||
|
||||
if ( delta > vFOV )
|
||||
return 0.0f;
|
||||
|
||||
return ( ( vFOV - delta ) / vFOV );
|
||||
}
|
||||
|
||||
#define MAX_INTEREST_DIST ( 256 * 256 )
|
||||
/*
|
||||
-------------------------
|
||||
NPC_FindLocalInterestPoint
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
int G_FindLocalInterestPoint( gentity_t *self )
|
||||
{
|
||||
int i, bestPoint = ENTITYNUM_NONE;
|
||||
float dist, bestDist = Q3_INFINITE;
|
||||
vec3_t diffVec, eyes;
|
||||
|
||||
CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes );
|
||||
for ( i = 0; i < level.numInterestPoints; i++ )
|
||||
{
|
||||
//Don't ignore portals? If through a portal, need to look at portal!
|
||||
if ( trap_InPVS( level.interestPoints[i].origin, eyes ) )
|
||||
{
|
||||
VectorSubtract( level.interestPoints[i].origin, eyes, diffVec );
|
||||
if ( (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 &&
|
||||
fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 )
|
||||
{//Too close to look so far up or down
|
||||
continue;
|
||||
}
|
||||
dist = VectorLengthSquared( diffVec );
|
||||
//Some priority to more interesting points
|
||||
//dist -= ((int)level.interestPoints[i].lookMode * 5) * ((int)level.interestPoints[i].lookMode * 5);
|
||||
if ( dist < MAX_INTEREST_DIST && dist < bestDist )
|
||||
{
|
||||
if ( G_ClearLineOfSight( eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE ) )
|
||||
{
|
||||
bestDist = dist;
|
||||
bestPoint = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( bestPoint != ENTITYNUM_NONE && level.interestPoints[bestPoint].target )
|
||||
{
|
||||
G_UseTargets2( self, self, level.interestPoints[bestPoint].target );
|
||||
}
|
||||
return bestPoint;
|
||||
}
|
||||
|
||||
/*QUAKED target_interest (1 0.8 0.5) (-4 -4 -4) (4 4 4)
|
||||
A point that a squadmate will look at if standing still
|
||||
|
||||
target - thing to fire when someone looks at this thing
|
||||
*/
|
||||
|
||||
void SP_target_interest( gentity_t *self )
|
||||
{//FIXME: rename point_interest
|
||||
if(level.numInterestPoints >= MAX_INTEREST_POINTS)
|
||||
{
|
||||
Com_Printf("ERROR: Too many interest points, limit is %d\n", MAX_INTEREST_POINTS);
|
||||
G_FreeEntity(self);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(self->r.currentOrigin, level.interestPoints[level.numInterestPoints].origin);
|
||||
|
||||
if(self->target && self->target[0])
|
||||
{
|
||||
level.interestPoints[level.numInterestPoints].target = G_NewString( self->target );
|
||||
}
|
||||
|
||||
level.numInterestPoints++;
|
||||
|
||||
G_FreeEntity(self);
|
||||
}
|
||||
93
codemp/game/NPC_sounds.c
Normal file
93
codemp/game/NPC_sounds.c
Normal file
@@ -0,0 +1,93 @@
|
||||
//NPC_sounds.cpp
|
||||
#include "b_local.h"
|
||||
#include "../icarus/Q3_Interface.h"
|
||||
|
||||
/*
|
||||
void NPC_AngerSound (void)
|
||||
{
|
||||
if(NPCInfo->investigateSoundDebounceTime)
|
||||
return;
|
||||
|
||||
NPCInfo->investigateSoundDebounceTime = 1;
|
||||
|
||||
// switch((int)NPC->client->race)
|
||||
// {
|
||||
// case RACE_KLINGON:
|
||||
//G_Sound(NPC, G_SoundIndex(va("sound/mgtest/klingon/talk%d.wav", Q_irand(1, 4))));
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
|
||||
extern void G_SpeechEvent( gentity_t *self, int event );
|
||||
void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime )
|
||||
{
|
||||
if ( !self->NPC )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self->client || self->client->ps.pm_type >= PM_DEAD )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self->NPC->blockedSpeechDebounceTime > level.time )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( trap_ICARUS_TaskIDPending( self, TID_CHAN_VOICE ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( (self->NPC->scriptFlags&SCF_NO_COMBAT_TALK) && ( (event >= EV_ANGER1 && event <= EV_VICTORY3) || (event >= EV_CHASE1 && event <= EV_SUSPICIOUS5) ) )//(event < EV_FF_1A || event > EV_FF_3C) && (event < EV_RESPOND1 || event > EV_MISSION3) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (self->NPC->scriptFlags&SCF_NO_ALERT_TALK) && (event >= EV_GIVEUP1 && event <= EV_SUSPICIOUS5) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
//FIXME: Also needs to check for teammates. Don't want
|
||||
// everyone babbling at once
|
||||
|
||||
//NOTE: was losing too many speech events, so we do it directly now, screw networking!
|
||||
//G_AddEvent( self, event, 0 );
|
||||
G_SpeechEvent( self, event );
|
||||
|
||||
//won't speak again for 5 seconds (unless otherwise specified)
|
||||
self->NPC->blockedSpeechDebounceTime = level.time + ((speakDebounceTime==0) ? 5000 : speakDebounceTime);
|
||||
}
|
||||
|
||||
void NPC_PlayConfusionSound( gentity_t *self )
|
||||
{
|
||||
if ( self->health > 0 )
|
||||
{
|
||||
if ( self->enemy ||//was mad
|
||||
!TIMER_Done( self, "enemyLastVisible" ) ||//saw something suspicious
|
||||
self->client->renderInfo.lookTarget == 0//was looking at player
|
||||
)
|
||||
{
|
||||
self->NPC->blockedSpeechDebounceTime = 0;//make sure we say this
|
||||
G_AddVoiceEvent( self, Q_irand( EV_CONFUSE2, EV_CONFUSE3 ), 2000 );
|
||||
}
|
||||
else if ( self->NPC && self->NPC->investigateDebounceTime+self->NPC->pauseTime > level.time )//was checking something out
|
||||
{
|
||||
self->NPC->blockedSpeechDebounceTime = 0;//make sure we say this
|
||||
G_AddVoiceEvent( self, EV_CONFUSE1, 2000 );
|
||||
}
|
||||
//G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
|
||||
}
|
||||
//reset him to be totally unaware again
|
||||
TIMER_Set( self, "enemyLastVisible", 0 );
|
||||
self->NPC->tempBehavior = BS_DEFAULT;
|
||||
|
||||
//self->NPC->behaviorState = BS_PATROL;
|
||||
G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
|
||||
|
||||
self->NPC->investigateCount = 0;
|
||||
}
|
||||
4272
codemp/game/NPC_spawn.c
Normal file
4272
codemp/game/NPC_spawn.c
Normal file
File diff suppressed because it is too large
Load Diff
3352
codemp/game/NPC_stats.c
Normal file
3352
codemp/game/NPC_stats.c
Normal file
File diff suppressed because it is too large
Load Diff
1788
codemp/game/NPC_utils.c
Normal file
1788
codemp/game/NPC_utils.c
Normal file
File diff suppressed because it is too large
Load Diff
1134
codemp/game/SpeederNPC.c
Normal file
1134
codemp/game/SpeederNPC.c
Normal file
File diff suppressed because it is too large
Load Diff
636
codemp/game/WalkerNPC.c
Normal file
636
codemp/game/WalkerNPC.c
Normal file
@@ -0,0 +1,636 @@
|
||||
// leave this line at the top for all g_xxxx.cpp files...
|
||||
#include "g_headers.h"
|
||||
|
||||
//seems to be a compiler bug, it doesn't clean out the #ifdefs between dif-compiles
|
||||
//or something, so the headers spew errors on these defs from the previous compile.
|
||||
//this fixes that. -rww
|
||||
#ifdef _JK2MP
|
||||
//get rid of all the crazy defs we added for this file
|
||||
#undef currentAngles
|
||||
#undef currentOrigin
|
||||
#undef mins
|
||||
#undef maxs
|
||||
#undef legsAnimTimer
|
||||
#undef torsoAnimTimer
|
||||
#undef bool
|
||||
#undef false
|
||||
#undef true
|
||||
|
||||
#undef sqrtf
|
||||
#undef Q_flrand
|
||||
|
||||
#undef MOD_EXPLOSIVE
|
||||
#endif
|
||||
|
||||
#ifdef _JK2 //SP does not have this preprocessor for game like MP does
|
||||
#ifndef _JK2MP
|
||||
#define _JK2MP
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP //if single player
|
||||
#ifndef QAGAME //I don't think we have a QAGAME define
|
||||
#define QAGAME //but define it cause in sp we're always in the game
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^
|
||||
#include "g_local.h"
|
||||
#elif defined _JK2MP
|
||||
#include "bg_public.h"
|
||||
#endif
|
||||
|
||||
#ifndef _JK2MP
|
||||
#include "g_functions.h"
|
||||
#include "g_vehicles.h"
|
||||
#else
|
||||
#include "bg_vehicles.h"
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
//this is really horrible, but it works! just be sure not to use any locals or anything
|
||||
//with these names (exluding bool, false, true). -rww
|
||||
#define currentAngles r.currentAngles
|
||||
#define currentOrigin r.currentOrigin
|
||||
#define mins r.mins
|
||||
#define maxs r.maxs
|
||||
#define legsAnimTimer legsTimer
|
||||
#define torsoAnimTimer torsoTimer
|
||||
#define bool qboolean
|
||||
#define false qfalse
|
||||
#define true qtrue
|
||||
|
||||
#define sqrtf sqrt
|
||||
#define Q_flrand flrand
|
||||
|
||||
#define MOD_EXPLOSIVE MOD_SUICIDE
|
||||
#else
|
||||
#define bgEntity_t gentity_t
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME //we only want a few of these functions for BG
|
||||
|
||||
extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
|
||||
extern vmCvar_t cg_thirdPersonAlpha;
|
||||
extern vec3_t playerMins;
|
||||
extern vec3_t playerMaxs;
|
||||
extern cvar_t *g_speederControlScheme;
|
||||
extern void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
|
||||
extern int PM_AnimLength( int index, animNumber_t anim );
|
||||
extern void Vehicle_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
|
||||
extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
|
||||
extern void G_VehicleTrace( trace_t *results, const vec3_t start, const vec3_t tMins, const vec3_t tMaxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
|
||||
static void RegisterAssets( Vehicle_t *pVeh )
|
||||
{
|
||||
//atst uses turret weapon
|
||||
#ifdef _JK2MP
|
||||
RegisterItem(BG_FindItemForWeapon(WP_TURRET));
|
||||
#else
|
||||
// PUT SOMETHING HERE...
|
||||
#endif
|
||||
|
||||
//call the standard RegisterAssets now
|
||||
g_vehicleInfo[VEHICLE_BASE].RegisterAssets( pVeh );
|
||||
}
|
||||
|
||||
// Like a think or move command, this updates various vehicle properties.
|
||||
/*
|
||||
static bool Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
|
||||
{
|
||||
return g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd );
|
||||
}
|
||||
*/
|
||||
|
||||
// Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
|
||||
static bool Board( Vehicle_t *pVeh, bgEntity_t *pEnt )
|
||||
{
|
||||
if ( !g_vehicleInfo[VEHICLE_BASE].Board( pVeh, pEnt ) )
|
||||
return false;
|
||||
|
||||
// Set the board wait time (they won't be able to do anything, including getting off, for this amount of time).
|
||||
pVeh->m_iBoarding = level.time + 1500;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif //QAGAME
|
||||
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_begin.h"
|
||||
#endif
|
||||
|
||||
//MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
|
||||
//If you really need to violate this rule for SP, then use ifdefs.
|
||||
//By BG-compatible, I mean no use of game-specific data - ONLY use
|
||||
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
|
||||
//as a gentity, but the MP-compatible access restrictions are based
|
||||
//on the bgEntity structure in the MP codebase) -rww
|
||||
// ProcessMoveCommands the Vehicle.
|
||||
static void ProcessMoveCommands( Vehicle_t *pVeh )
|
||||
{
|
||||
/************************************************************************************/
|
||||
/* BEGIN Here is where we move the vehicle (forward or back or whatever). BEGIN */
|
||||
/************************************************************************************/
|
||||
|
||||
//Client sets ucmds and such for speed alterations
|
||||
float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax;
|
||||
float fWalkSpeedMax;
|
||||
bgEntity_t *parent = pVeh->m_pParentEntity;
|
||||
#ifdef _JK2MP
|
||||
playerState_t *parentPS = parent->playerState;
|
||||
#else
|
||||
playerState_t *parentPS = &parent->client->ps;
|
||||
#endif
|
||||
|
||||
speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
|
||||
speedMax = pVeh->m_pVehicleInfo->speedMax;
|
||||
|
||||
speedIdle = pVeh->m_pVehicleInfo->speedIdle;
|
||||
speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
|
||||
speedMin = pVeh->m_pVehicleInfo->speedMin;
|
||||
|
||||
#ifdef _JK2MP
|
||||
if ( !parentPS->m_iVehicleNum )
|
||||
#else
|
||||
if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
|
||||
#endif
|
||||
{//drifts to a stop
|
||||
speedInc = speedIdle * pVeh->m_fTimeModifier;
|
||||
VectorClear( parentPS->moveDir );
|
||||
//m_ucmd.forwardmove = 127;
|
||||
parentPS->speed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
|
||||
}
|
||||
|
||||
if ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE ||
|
||||
pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 )
|
||||
{
|
||||
if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
|
||||
{
|
||||
parentPS->speed += speedInc;
|
||||
}
|
||||
else if ( pVeh->m_ucmd.forwardmove < 0 )
|
||||
{
|
||||
if ( parentPS->speed > speedIdle )
|
||||
{
|
||||
parentPS->speed -= speedInc;
|
||||
}
|
||||
else if ( parentPS->speed > speedMin )
|
||||
{
|
||||
parentPS->speed -= speedIdleDec;
|
||||
}
|
||||
}
|
||||
// No input, so coast to stop.
|
||||
else if ( parentPS->speed > 0.0f )
|
||||
{
|
||||
parentPS->speed -= speedIdleDec;
|
||||
if ( parentPS->speed < 0.0f )
|
||||
{
|
||||
parentPS->speed = 0.0f;
|
||||
}
|
||||
}
|
||||
else if ( parentPS->speed < 0.0f )
|
||||
{
|
||||
parentPS->speed += speedIdleDec;
|
||||
if ( parentPS->speed > 0.0f )
|
||||
{
|
||||
parentPS->speed = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( pVeh->m_ucmd.forwardmove < 0 )
|
||||
{
|
||||
pVeh->m_ucmd.forwardmove = 0;
|
||||
}
|
||||
if ( pVeh->m_ucmd.upmove < 0 )
|
||||
{
|
||||
pVeh->m_ucmd.upmove = 0;
|
||||
}
|
||||
|
||||
pVeh->m_ucmd.rightmove = 0;
|
||||
|
||||
/*if ( !pVeh->m_pVehicleInfo->strafePerc
|
||||
|| (!g_speederControlScheme->value && !parent->s.number) )
|
||||
{//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme
|
||||
pVeh->m_ucmd.rightmove = 0;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (parentPS && parentPS->electrifyTime > pm->cmd.serverTime)
|
||||
{
|
||||
speedMax *= 0.5f;
|
||||
}
|
||||
|
||||
fWalkSpeedMax = speedMax * 0.275f;
|
||||
if ( pVeh->m_ucmd.buttons & BUTTON_WALKING && parentPS->speed > fWalkSpeedMax )
|
||||
{
|
||||
parentPS->speed = fWalkSpeedMax;
|
||||
}
|
||||
else if ( parentPS->speed > speedMax )
|
||||
{
|
||||
parentPS->speed = speedMax;
|
||||
}
|
||||
else if ( parentPS->speed < speedMin )
|
||||
{
|
||||
parentPS->speed = speedMin;
|
||||
}
|
||||
|
||||
if (parentPS->stats[STAT_HEALTH] <= 0)
|
||||
{ //don't keep moving while you're dying!
|
||||
parentPS->speed = 0;
|
||||
}
|
||||
|
||||
/********************************************************************************/
|
||||
/* END Here is where we move the vehicle (forward or back or whatever). END */
|
||||
/********************************************************************************/
|
||||
}
|
||||
|
||||
#ifdef _JK2MP
|
||||
void WalkerYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
|
||||
{
|
||||
float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
|
||||
|
||||
if (parentPS && parentPS->speed)
|
||||
{
|
||||
float s = parentPS->speed;
|
||||
float maxDif = pVeh->m_pVehicleInfo->turningSpeed*1.5f; //magic number hackery
|
||||
|
||||
if (s < 0.0f)
|
||||
{
|
||||
s = -s;
|
||||
}
|
||||
angDif *= s/pVeh->m_pVehicleInfo->speedMax;
|
||||
if (angDif > maxDif)
|
||||
{
|
||||
angDif = maxDif;
|
||||
}
|
||||
else if (angDif < -maxDif)
|
||||
{
|
||||
angDif = -maxDif;
|
||||
}
|
||||
pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void WalkerPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
|
||||
{
|
||||
float angDif = AngleSubtract(pVeh->m_vOrientation[PITCH], riderPS->viewangles[PITCH]);
|
||||
|
||||
if (parentPS && parentPS->speed)
|
||||
{
|
||||
float s = parentPS->speed;
|
||||
float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
|
||||
|
||||
if (s < 0.0f)
|
||||
{
|
||||
s = -s;
|
||||
}
|
||||
angDif *= s/pVeh->m_pVehicleInfo->speedMax;
|
||||
if (angDif > maxDif)
|
||||
{
|
||||
angDif = maxDif;
|
||||
}
|
||||
else if (angDif < -maxDif)
|
||||
{
|
||||
angDif = -maxDif;
|
||||
}
|
||||
pVeh->m_vOrientation[PITCH] = AngleNormalize360(pVeh->m_vOrientation[PITCH] - angDif*(pVeh->m_fTimeModifier*0.2f));
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
|
||||
//If you really need to violate this rule for SP, then use ifdefs.
|
||||
//By BG-compatible, I mean no use of game-specific data - ONLY use
|
||||
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
|
||||
//as a gentity, but the MP-compatible access restrictions are based
|
||||
//on the bgEntity structure in the MP codebase) -rww
|
||||
// ProcessOrientCommands the Vehicle.
|
||||
static void ProcessOrientCommands( Vehicle_t *pVeh )
|
||||
{
|
||||
/********************************************************************************/
|
||||
/* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */
|
||||
/********************************************************************************/
|
||||
float speed;
|
||||
bgEntity_t *parent = pVeh->m_pParentEntity;
|
||||
playerState_t *parentPS, *riderPS;
|
||||
|
||||
#ifdef _JK2MP
|
||||
bgEntity_t *rider = NULL;
|
||||
if (parent->s.owner != ENTITYNUM_NONE)
|
||||
{
|
||||
rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
|
||||
}
|
||||
#else
|
||||
gentity_t *rider = parent->owner;
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
if ( !rider )
|
||||
#else
|
||||
if ( !rider || !rider->client )
|
||||
#endif
|
||||
{
|
||||
rider = parent;
|
||||
}
|
||||
|
||||
#ifdef _JK2MP
|
||||
parentPS = parent->playerState;
|
||||
riderPS = rider->playerState;
|
||||
#else
|
||||
parentPS = &parent->client->ps;
|
||||
riderPS = &rider->client->ps;
|
||||
#endif
|
||||
|
||||
speed = VectorLength( parentPS->velocity );
|
||||
|
||||
// If the player is the rider...
|
||||
if ( rider->s.number < MAX_CLIENTS )
|
||||
{//FIXME: use the vehicle's turning stat in this calc
|
||||
#ifdef _JK2MP
|
||||
WalkerYawAdjust(pVeh, riderPS, parentPS);
|
||||
//FighterPitchAdjust(pVeh, riderPS, parentPS);
|
||||
pVeh->m_vOrientation[PITCH] = riderPS->viewangles[PITCH];
|
||||
#else
|
||||
pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
|
||||
pVeh->m_vOrientation[PITCH] = riderPS->viewangles[PITCH];
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed;
|
||||
if ( !pVeh->m_pVehicleInfo->turnWhenStopped
|
||||
&& !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove?
|
||||
{//can't turn when not moving
|
||||
//FIXME: or ramp up to max turnSpeed?
|
||||
turnSpeed = 0.0f;
|
||||
}
|
||||
#ifdef _JK2MP
|
||||
if (rider->s.eType == ET_NPC)
|
||||
#else
|
||||
if ( !rider || rider->NPC )
|
||||
#endif
|
||||
{//help NPCs out some
|
||||
turnSpeed *= 2.0f;
|
||||
#ifdef _JK2MP
|
||||
if (parentPS->speed > 200.0f)
|
||||
#else
|
||||
if ( parent->client->ps.speed > 200.0f )
|
||||
#endif
|
||||
{
|
||||
turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f;
|
||||
}
|
||||
}
|
||||
turnSpeed *= pVeh->m_fTimeModifier;
|
||||
|
||||
//default control scheme: strafing turns, mouselook aims
|
||||
if ( pVeh->m_ucmd.rightmove < 0 )
|
||||
{
|
||||
pVeh->m_vOrientation[YAW] += turnSpeed;
|
||||
}
|
||||
else if ( pVeh->m_ucmd.rightmove > 0 )
|
||||
{
|
||||
pVeh->m_vOrientation[YAW] -= turnSpeed;
|
||||
}
|
||||
|
||||
if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel )
|
||||
{//damaged badly
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************/
|
||||
/* END Here is where make sure the vehicle is properly oriented. END */
|
||||
/********************************************************************************/
|
||||
}
|
||||
|
||||
#ifdef QAGAME //back to our game-only functions
|
||||
// This function makes sure that the vehicle is properly animated.
|
||||
static void AnimateVehicle( Vehicle_t *pVeh )
|
||||
{
|
||||
animNumber_t Anim = BOTH_STAND1;
|
||||
int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
|
||||
gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
|
||||
float fSpeedPercToMax;
|
||||
|
||||
// We're dead (boarding is reused here so I don't have to make another variable :-).
|
||||
if ( parent->health <= 0 )
|
||||
{
|
||||
/*
|
||||
if ( pVeh->m_iBoarding != -999 ) // Animate the death just once!
|
||||
{
|
||||
pVeh->m_iBoarding = -999;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
||||
|
||||
// FIXME! Why do you keep repeating over and over!!?!?!? Bastard!
|
||||
//Vehicle_SetAnim( parent, SETANIM_LEGS, BOTH_VT_DEATH1, iFlags, iBlend );
|
||||
}
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Following is redundant to g_vehicles.c
|
||||
// if ( pVeh->m_iBoarding )
|
||||
// {
|
||||
// //we have no boarding anim
|
||||
// if (pVeh->m_iBoarding < level.time)
|
||||
// { //we are on now
|
||||
// pVeh->m_iBoarding = 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Percentage of maximum speed relative to current speed.
|
||||
//float fSpeed = VectorLength( client->ps.velocity );
|
||||
fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax;
|
||||
|
||||
// If we're moving...
|
||||
if ( fSpeedPercToMax > 0.0f ) //fSpeedPercToMax >= 0.85f )
|
||||
{
|
||||
float fYawDelta;
|
||||
|
||||
iBlend = 300;
|
||||
iFlags = SETANIM_FLAG_OVERRIDE;
|
||||
fYawDelta = pVeh->m_vPrevOrientation[YAW] - pVeh->m_vOrientation[YAW];
|
||||
|
||||
// NOTE: Mikes suggestion for fixing the stuttering walk (left/right) is to maintain the
|
||||
// current frame between animations. I have no clue how to do this and have to work on other
|
||||
// stuff so good luck to him :-p AReis
|
||||
|
||||
// If we're walking (or our speed is less than .275%)...
|
||||
if ( ( pVeh->m_ucmd.buttons & BUTTON_WALKING ) || fSpeedPercToMax < 0.275f )
|
||||
{
|
||||
// Make them lean if we're turning.
|
||||
/*if ( fYawDelta < -0.0001f )
|
||||
{
|
||||
Anim = BOTH_VT_WALK_FWD_L;
|
||||
}
|
||||
else if ( fYawDelta > 0.0001 )
|
||||
{
|
||||
Anim = BOTH_VT_WALK_FWD_R;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
Anim = BOTH_WALK1;
|
||||
}
|
||||
}
|
||||
// otherwise we're running.
|
||||
else
|
||||
{
|
||||
// Make them lean if we're turning.
|
||||
/*if ( fYawDelta < -0.0001f )
|
||||
{
|
||||
Anim = BOTH_VT_RUN_FWD_L;
|
||||
}
|
||||
else if ( fYawDelta > 0.0001 )
|
||||
{
|
||||
Anim = BOTH_VT_RUN_FWD_R;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
Anim = BOTH_RUN1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Going in reverse...
|
||||
if ( fSpeedPercToMax < -0.018f )
|
||||
{
|
||||
iFlags = SETANIM_FLAG_NORMAL;
|
||||
Anim = BOTH_WALKBACK1;
|
||||
iBlend = 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
//int iChance = Q_irand( 0, 20000 );
|
||||
|
||||
// Every once in a while buck or do a different idle...
|
||||
iFlags = SETANIM_FLAG_NORMAL | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD;
|
||||
iBlend = 600;
|
||||
#ifdef _JK2MP
|
||||
if (parent->client->ps.m_iVehicleNum)
|
||||
#else
|
||||
if ( pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
|
||||
#endif
|
||||
{//occupado
|
||||
Anim = BOTH_STAND1;
|
||||
}
|
||||
else
|
||||
{//wide open for you, baby
|
||||
Anim = BOTH_STAND2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vehicle_SetAnim( parent, SETANIM_LEGS, Anim, iFlags, iBlend );
|
||||
}
|
||||
|
||||
//rwwFIXMEFIXME: This is all going to have to be predicted I think, or it will feel awful
|
||||
//and lagged
|
||||
#endif //QAGAME
|
||||
|
||||
#ifndef QAGAME
|
||||
void AttachRidersGeneric( Vehicle_t *pVeh );
|
||||
#endif
|
||||
|
||||
//on the client this function will only set up the process command funcs
|
||||
void G_SetWalkerVehicleFunctions( vehicleInfo_t *pVehInfo )
|
||||
{
|
||||
#ifdef QAGAME
|
||||
pVehInfo->AnimateVehicle = AnimateVehicle;
|
||||
// pVehInfo->AnimateRiders = AnimateRiders;
|
||||
// pVehInfo->ValidateBoard = ValidateBoard;
|
||||
// pVehInfo->SetParent = SetParent;
|
||||
// pVehInfo->SetPilot = SetPilot;
|
||||
// pVehInfo->AddPassenger = AddPassenger;
|
||||
// pVehInfo->Animate = Animate;
|
||||
pVehInfo->Board = Board;
|
||||
// pVehInfo->Eject = Eject;
|
||||
// pVehInfo->EjectAll = EjectAll;
|
||||
// pVehInfo->StartDeathDelay = StartDeathDelay;
|
||||
// pVehInfo->DeathUpdate = DeathUpdate;
|
||||
pVehInfo->RegisterAssets = RegisterAssets;
|
||||
// pVehInfo->Initialize = Initialize;
|
||||
// pVehInfo->Update = Update;
|
||||
// pVehInfo->UpdateRider = UpdateRider;
|
||||
#endif //QAGAME
|
||||
pVehInfo->ProcessMoveCommands = ProcessMoveCommands;
|
||||
pVehInfo->ProcessOrientCommands = ProcessOrientCommands;
|
||||
|
||||
#ifndef QAGAME //cgame prediction attachment func
|
||||
pVehInfo->AttachRiders = AttachRidersGeneric;
|
||||
#endif
|
||||
// pVehInfo->AttachRiders = AttachRiders;
|
||||
// pVehInfo->Ghost = Ghost;
|
||||
// pVehInfo->UnGhost = UnGhost;
|
||||
// pVehInfo->Inhabited = Inhabited;
|
||||
}
|
||||
|
||||
// Following is only in game, not in namespace
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_end.h"
|
||||
#endif
|
||||
|
||||
#ifdef QAGAME
|
||||
extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
|
||||
#endif
|
||||
|
||||
#ifdef _JK2MP
|
||||
#include "../namespace_begin.h"
|
||||
#endif
|
||||
|
||||
// Create/Allocate a new Animal Vehicle (initializing it as well).
|
||||
//this is a BG function too in MP so don't un-bg-compatibilify it -rww
|
||||
void G_CreateWalkerNPC( Vehicle_t **pVeh, const char *strAnimalType )
|
||||
{
|
||||
// Allocate the Vehicle.
|
||||
#ifdef _JK2MP
|
||||
#ifdef QAGAME
|
||||
//these will remain on entities on the client once allocated because the pointer is
|
||||
//never stomped. on the server, however, when an ent is freed, the entity struct is
|
||||
//memset to 0, so this memory would be lost..
|
||||
G_AllocateVehicleObject(pVeh);
|
||||
#else
|
||||
if (!*pVeh)
|
||||
{ //only allocate a new one if we really have to
|
||||
(*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
|
||||
}
|
||||
#endif
|
||||
memset(*pVeh, 0, sizeof(Vehicle_t));
|
||||
(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
|
||||
#else
|
||||
(*pVeh) = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qtrue );
|
||||
(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _JK2MP
|
||||
|
||||
#include "../namespace_end.h"
|
||||
|
||||
//get rid of all the crazy defs we added for this file
|
||||
#undef currentAngles
|
||||
#undef currentOrigin
|
||||
#undef mins
|
||||
#undef maxs
|
||||
#undef legsAnimTimer
|
||||
#undef torsoAnimTimer
|
||||
#undef bool
|
||||
#undef false
|
||||
#undef true
|
||||
|
||||
#undef sqrtf
|
||||
#undef Q_flrand
|
||||
|
||||
#undef MOD_EXPLOSIVE
|
||||
#endif
|
||||
126
codemp/game/ai.h
Normal file
126
codemp/game/ai.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef __AI__
|
||||
#define __AI__
|
||||
|
||||
//Distance ratings
|
||||
typedef enum
|
||||
{
|
||||
DIST_MELEE,
|
||||
DIST_LONG,
|
||||
} distance_e;
|
||||
|
||||
//Attack types
|
||||
typedef enum
|
||||
{
|
||||
ATTACK_MELEE,
|
||||
ATTACK_RANGE,
|
||||
} attack_e;
|
||||
|
||||
enum
|
||||
{
|
||||
SQUAD_IDLE, //No target found, waiting
|
||||
SQUAD_STAND_AND_SHOOT, //Standing in position and shoot (no cover)
|
||||
SQUAD_RETREAT, //Running away from combat
|
||||
SQUAD_COVER, //Under protective cover
|
||||
SQUAD_TRANSITION, //Moving between points, not firing
|
||||
SQUAD_POINT, //On point, laying down suppressive fire
|
||||
SQUAD_SCOUT, //Poking out to draw enemy
|
||||
NUM_SQUAD_STATES,
|
||||
};
|
||||
|
||||
//sigh... had to move in here for groupInfo
|
||||
typedef enum //# rank_e
|
||||
{
|
||||
RANK_CIVILIAN,
|
||||
RANK_CREWMAN,
|
||||
RANK_ENSIGN,
|
||||
RANK_LT_JG,
|
||||
RANK_LT,
|
||||
RANK_LT_COMM,
|
||||
RANK_COMMANDER,
|
||||
RANK_CAPTAIN
|
||||
} rank_t;
|
||||
|
||||
qboolean NPC_CheckPlayerTeamStealth( void );
|
||||
|
||||
//AI_GRENADIER
|
||||
void NPC_BSGrenadier_Default( void );
|
||||
|
||||
//AI_SNIPER
|
||||
void NPC_BSSniper_Default( void );
|
||||
|
||||
//AI_STORMTROOPER
|
||||
void NPC_BSST_Investigate( void );
|
||||
void NPC_BSST_Default( void );
|
||||
void NPC_BSST_Sleep( void );
|
||||
|
||||
//AI_JEDI
|
||||
void NPC_BSJedi_Investigate( void );
|
||||
void NPC_BSJedi_Default( void );
|
||||
void NPC_BSJedi_FollowLeader( void );
|
||||
|
||||
// AI_DROID
|
||||
void NPC_BSDroid_Default( void );
|
||||
|
||||
// AI_ImperialProbe
|
||||
void NPC_BSImperialProbe_Default( void );
|
||||
|
||||
// AI_atst
|
||||
//void NPC_BSATST_Default( void );
|
||||
|
||||
//void NPC_BSInterrogator_Default( void );
|
||||
|
||||
// AI Mark 1
|
||||
//void NPC_BSMark1_Default( void );
|
||||
|
||||
// AI Mark 2
|
||||
//void NPC_BSMark2_Default( void );
|
||||
|
||||
|
||||
void NPC_BSMineMonster_Default( void );
|
||||
void NPC_BSHowler_Default( void );
|
||||
void NPC_BSRancor_Default( void );
|
||||
|
||||
//Utilities
|
||||
//Group AI
|
||||
#define MAX_FRAME_GROUPS 32
|
||||
// !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!!
|
||||
typedef struct AIGroupMember_s
|
||||
{
|
||||
int number;
|
||||
int waypoint;
|
||||
int pathCostToEnemy;
|
||||
int closestBuddy;
|
||||
} AIGroupMember_t;
|
||||
|
||||
#define MAX_GROUP_MEMBERS 32
|
||||
// !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!!
|
||||
typedef struct AIGroupInfo_s
|
||||
{
|
||||
int numGroup;
|
||||
qboolean processed;
|
||||
team_t team;
|
||||
gentity_t *enemy;
|
||||
int enemyWP;
|
||||
int speechDebounceTime;
|
||||
int lastClearShotTime;
|
||||
int lastSeenEnemyTime;
|
||||
int morale;
|
||||
int moraleAdjust;
|
||||
int moraleDebounce;
|
||||
int memberValidateTime;
|
||||
int activeMemberNum;
|
||||
gentity_t *commander;
|
||||
vec3_t enemyLastSeenPos;
|
||||
int numState[ NUM_SQUAD_STATES ];
|
||||
AIGroupMember_t member[ MAX_GROUP_MEMBERS ];
|
||||
} AIGroupInfo_t;
|
||||
|
||||
int AI_GetGroupSize( vec3_t origin, int radius, team_t playerTeam, gentity_t *avoid );
|
||||
int AI_GetGroupSize2( gentity_t *ent, int radius );
|
||||
|
||||
void AI_GetGroup( gentity_t *self );
|
||||
|
||||
qboolean AI_CheckEnemyCollision( gentity_t *ent, qboolean takeEnemy );
|
||||
gentity_t *AI_DistributeAttack( gentity_t *attacker, gentity_t *enemy, team_t team, int threshold );
|
||||
|
||||
#endif //__AI__
|
||||
7649
codemp/game/ai_main.c
Normal file
7649
codemp/game/ai_main.c
Normal file
File diff suppressed because it is too large
Load Diff
411
codemp/game/ai_main.h
Normal file
411
codemp/game/ai_main.h
Normal file
@@ -0,0 +1,411 @@
|
||||
#include "bg_saga.h"
|
||||
|
||||
#define DEFAULT_FORCEPOWERS "5-1-000000000000000000"
|
||||
|
||||
//#define FORCEJUMP_INSTANTMETHOD 1
|
||||
|
||||
#ifdef _XBOX // No bot has more than 150 bytes of chat right now
|
||||
#define MAX_CHAT_BUFFER_SIZE 256
|
||||
#else
|
||||
#define MAX_CHAT_BUFFER_SIZE 8192
|
||||
#endif
|
||||
#define MAX_CHAT_LINE_SIZE 128
|
||||
|
||||
#define TABLE_BRANCH_DISTANCE 32
|
||||
#define MAX_NODETABLE_SIZE 16384
|
||||
|
||||
#define MAX_LOVED_ONES 4
|
||||
#define MAX_ATTACHMENT_NAME 64
|
||||
|
||||
#define MAX_FORCE_INFO_SIZE 2048
|
||||
|
||||
#define WPFLAG_JUMP 0x00000010 //jump when we hit this
|
||||
#define WPFLAG_DUCK 0x00000020 //duck while moving around here
|
||||
#define WPFLAG_NOVIS 0x00000400 //go here for a bit even with no visibility
|
||||
#define WPFLAG_SNIPEORCAMPSTAND 0x00000800 //a good position to snipe or camp - stand
|
||||
#define WPFLAG_WAITFORFUNC 0x00001000 //wait for a func brushent under this point before moving here
|
||||
#define WPFLAG_SNIPEORCAMP 0x00002000 //a good position to snipe or camp - crouch
|
||||
#define WPFLAG_ONEWAY_FWD 0x00004000 //can only go forward on the trial from here (e.g. went over a ledge)
|
||||
#define WPFLAG_ONEWAY_BACK 0x00008000 //can only go backward on the trail from here
|
||||
#define WPFLAG_GOALPOINT 0x00010000 //make it a goal to get here.. goal points will be decided by setting "weight" values
|
||||
#define WPFLAG_RED_FLAG 0x00020000 //red flag
|
||||
#define WPFLAG_BLUE_FLAG 0x00040000 //blue flag
|
||||
#define WPFLAG_SIEGE_REBELOBJ 0x00080000 //rebel siege objective
|
||||
#define WPFLAG_SIEGE_IMPERIALOBJ 0x00100000 //imperial siege objective
|
||||
#define WPFLAG_NOMOVEFUNC 0x00200000 //don't move over if a func is under
|
||||
|
||||
#define WPFLAG_CALCULATED 0x00400000 //don't calculate it again
|
||||
#define WPFLAG_NEVERONEWAY 0x00800000 //never flag it as one-way
|
||||
|
||||
#define LEVELFLAG_NOPOINTPREDICTION 1 //don't take waypoint beyond current into account when adjusting path view angles
|
||||
#define LEVELFLAG_IGNOREINFALLBACK 2 //ignore enemies when in a fallback navigation routine
|
||||
#define LEVELFLAG_IMUSTNTRUNAWAY 4 //don't be scared
|
||||
|
||||
#define WP_KEEP_FLAG_DIST 128
|
||||
|
||||
#define BWEAPONRANGE_MELEE 1
|
||||
#define BWEAPONRANGE_MID 2
|
||||
#define BWEAPONRANGE_LONG 3
|
||||
#define BWEAPONRANGE_SABER 4
|
||||
|
||||
#define MELEE_ATTACK_RANGE 256
|
||||
#define SABER_ATTACK_RANGE 128
|
||||
#define MAX_CHICKENWUSS_TIME 10000 //wait 10 secs between checking which run-away path to take
|
||||
|
||||
#define BOT_RUN_HEALTH 40
|
||||
#define BOT_WPTOUCH_DISTANCE 32
|
||||
#define ENEMY_FORGET_MS 10000
|
||||
//if our enemy isn't visible within 10000ms (aprx 10sec) then "forget" about him and treat him like every other threat, but still look for
|
||||
//more immediate threats while main enemy is not visible
|
||||
|
||||
#define BOT_PLANT_DISTANCE 256 //plant if within this radius from the last spotted enemy position
|
||||
#define BOT_PLANT_INTERVAL 15000 //only plant once per 15 seconds at max
|
||||
#define BOT_PLANT_BLOW_DISTANCE 256 //blow det packs if enemy is within this radius and I am further away than the enemy
|
||||
|
||||
#define BOT_MAX_WEAPON_GATHER_TIME 1000 //spend a max of 1 second after spawn issuing orders to gather weapons before attacking enemy base
|
||||
#define BOT_MAX_WEAPON_CHASE_TIME 15000 //time to spend gathering the weapon before persuing the enemy base (in case it takes longer than expected)
|
||||
|
||||
#define BOT_MAX_WEAPON_CHASE_CTF 5000 //time to spend gathering the weapon before persuing the enemy base (in case it takes longer than expected) [ctf-only]
|
||||
|
||||
#define BOT_MIN_SIEGE_GOAL_SHOOT 1024
|
||||
#define BOT_MIN_SIEGE_GOAL_TRAVEL 128
|
||||
|
||||
#define BASE_GUARD_DISTANCE 256 //guarding the flag
|
||||
#define BASE_FLAGWAIT_DISTANCE 256 //has the enemy flag and is waiting in his own base for his flag to be returned
|
||||
#define BASE_GETENEMYFLAG_DISTANCE 256 //waiting around to get the enemy's flag
|
||||
|
||||
#define BOT_FLAG_GET_DISTANCE 256
|
||||
|
||||
#define BOT_SABER_THROW_RANGE 800
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CTFSTATE_NONE,
|
||||
CTFSTATE_ATTACKER,
|
||||
CTFSTATE_DEFENDER,
|
||||
CTFSTATE_RETRIEVAL,
|
||||
CTFSTATE_GUARDCARRIER,
|
||||
CTFSTATE_GETFLAGHOME,
|
||||
CTFSTATE_MAXCTFSTATES
|
||||
} bot_ctf_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SIEGESTATE_NONE,
|
||||
SIEGESTATE_ATTACKER,
|
||||
SIEGESTATE_DEFENDER,
|
||||
SIEGESTATE_MAXSIEGESTATES
|
||||
} bot_siege_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TEAMPLAYSTATE_NONE,
|
||||
TEAMPLAYSTATE_FOLLOWING,
|
||||
TEAMPLAYSTATE_ASSISTING,
|
||||
TEAMPLAYSTATE_REGROUP,
|
||||
TEAMPLAYSTATE_MAXTPSTATES
|
||||
} bot_teamplay_state_t;
|
||||
|
||||
typedef struct botattachment_s
|
||||
{
|
||||
int level;
|
||||
char name[MAX_ATTACHMENT_NAME];
|
||||
} botattachment_t;
|
||||
|
||||
typedef struct nodeobject_s
|
||||
{
|
||||
vec3_t origin;
|
||||
// int index;
|
||||
float weight;
|
||||
int flags;
|
||||
#ifdef _XBOX
|
||||
short neighbornum;
|
||||
short inuse;
|
||||
#else
|
||||
int neighbornum;
|
||||
int inuse;
|
||||
#endif
|
||||
} nodeobject_t;
|
||||
|
||||
typedef struct boteventtracker_s
|
||||
{
|
||||
int eventSequence;
|
||||
int events[MAX_PS_EVENTS];
|
||||
float eventTime;
|
||||
} boteventtracker_t;
|
||||
|
||||
typedef struct botskills_s
|
||||
{
|
||||
int reflex;
|
||||
float accuracy;
|
||||
float turnspeed;
|
||||
float turnspeed_combat;
|
||||
float maxturn;
|
||||
int perfectaim;
|
||||
} botskills_t;
|
||||
|
||||
//bot state
|
||||
typedef struct bot_state_s
|
||||
{
|
||||
int inuse; //true if this state is used by a bot client
|
||||
int botthink_residual; //residual for the bot thinks
|
||||
int client; //client number of the bot
|
||||
int entitynum; //entity number of the bot
|
||||
playerState_t cur_ps; //current player state
|
||||
usercmd_t lastucmd; //usercmd from last frame
|
||||
bot_settings_t settings; //several bot settings
|
||||
float thinktime; //time the bot thinks this frame
|
||||
vec3_t origin; //origin of the bot
|
||||
vec3_t velocity; //velocity of the bot
|
||||
vec3_t eye; //eye coordinates of the bot
|
||||
int setupcount; //true when the bot has just been setup
|
||||
float ltime; //local bot time
|
||||
float entergame_time; //time the bot entered the game
|
||||
int ms; //move state of the bot
|
||||
int gs; //goal state of the bot
|
||||
int ws; //weapon state of the bot
|
||||
vec3_t viewangles; //current view angles
|
||||
vec3_t ideal_viewangles; //ideal view angles
|
||||
vec3_t viewanglespeed;
|
||||
|
||||
//rww - new AI values
|
||||
gentity_t *currentEnemy;
|
||||
gentity_t *revengeEnemy;
|
||||
|
||||
gentity_t *squadLeader;
|
||||
|
||||
gentity_t *lastHurt;
|
||||
gentity_t *lastAttacked;
|
||||
|
||||
gentity_t *wantFlag;
|
||||
|
||||
gentity_t *touchGoal;
|
||||
gentity_t *shootGoal;
|
||||
|
||||
gentity_t *dangerousObject;
|
||||
|
||||
vec3_t staticFlagSpot;
|
||||
|
||||
int revengeHateLevel;
|
||||
int isSquadLeader;
|
||||
|
||||
int squadRegroupInterval;
|
||||
int squadCannotLead;
|
||||
|
||||
int lastDeadTime;
|
||||
|
||||
wpobject_t *wpCurrent;
|
||||
wpobject_t *wpDestination;
|
||||
wpobject_t *wpStoreDest;
|
||||
vec3_t goalAngles;
|
||||
vec3_t goalMovedir;
|
||||
vec3_t goalPosition;
|
||||
|
||||
vec3_t lastEnemySpotted;
|
||||
vec3_t hereWhenSpotted;
|
||||
int lastVisibleEnemyIndex;
|
||||
int hitSpotted;
|
||||
|
||||
int wpDirection;
|
||||
|
||||
float destinationGrabTime;
|
||||
float wpSeenTime;
|
||||
float wpTravelTime;
|
||||
float wpDestSwitchTime;
|
||||
float wpSwitchTime;
|
||||
float wpDestIgnoreTime;
|
||||
|
||||
float timeToReact;
|
||||
|
||||
float enemySeenTime;
|
||||
|
||||
float chickenWussCalculationTime;
|
||||
|
||||
float beStill;
|
||||
float duckTime;
|
||||
float jumpTime;
|
||||
float jumpHoldTime;
|
||||
float jumpPrep;
|
||||
float forceJumping;
|
||||
float jDelay;
|
||||
|
||||
float aimOffsetTime;
|
||||
float aimOffsetAmtYaw;
|
||||
float aimOffsetAmtPitch;
|
||||
|
||||
float frame_Waypoint_Len;
|
||||
int frame_Waypoint_Vis;
|
||||
float frame_Enemy_Len;
|
||||
int frame_Enemy_Vis;
|
||||
|
||||
int isCamper;
|
||||
float isCamping;
|
||||
wpobject_t *wpCamping;
|
||||
wpobject_t *wpCampingTo;
|
||||
qboolean campStanding;
|
||||
|
||||
int randomNavTime;
|
||||
int randomNav;
|
||||
|
||||
int saberSpecialist;
|
||||
/*
|
||||
int canChat;
|
||||
int chatFrequency;
|
||||
char currentChat[MAX_CHAT_LINE_SIZE];
|
||||
float chatTime;
|
||||
float chatTime_stored;
|
||||
int doChat;
|
||||
int chatTeam;
|
||||
gentity_t *chatObject;
|
||||
gentity_t *chatAltObject;
|
||||
*/
|
||||
float meleeStrafeTime;
|
||||
int meleeStrafeDir;
|
||||
float meleeStrafeDisable;
|
||||
|
||||
int altChargeTime;
|
||||
|
||||
float escapeDirTime;
|
||||
|
||||
float dontGoBack;
|
||||
|
||||
int doAttack;
|
||||
int doAltAttack;
|
||||
|
||||
int forceWeaponSelect;
|
||||
int virtualWeapon;
|
||||
|
||||
int plantTime;
|
||||
int plantDecided;
|
||||
int plantContinue;
|
||||
int plantKillEmAll;
|
||||
|
||||
int runningLikeASissy;
|
||||
int runningToEscapeThreat;
|
||||
|
||||
//char chatBuffer[MAX_CHAT_BUFFER_SIZE];
|
||||
//Since we're once again not allocating bot structs dynamically,
|
||||
//shoving a 64k chat buffer into one is a bad thing.
|
||||
|
||||
botskills_t skills;
|
||||
|
||||
botattachment_t loved[MAX_LOVED_ONES];
|
||||
int lovednum;
|
||||
|
||||
int loved_death_thresh;
|
||||
|
||||
int deathActivitiesDone;
|
||||
|
||||
float botWeaponWeights[WP_NUM_WEAPONS];
|
||||
|
||||
int ctfState;
|
||||
|
||||
int siegeState;
|
||||
|
||||
int teamplayState;
|
||||
|
||||
int jmState;
|
||||
|
||||
int state_Forced; //set by player ordering menu
|
||||
|
||||
int saberDefending;
|
||||
int saberDefendDecideTime;
|
||||
int saberBFTime;
|
||||
int saberBTime;
|
||||
int saberSTime;
|
||||
int saberThrowTime;
|
||||
|
||||
qboolean saberPower;
|
||||
int saberPowerTime;
|
||||
|
||||
int botChallengingTime;
|
||||
|
||||
char forceinfo[MAX_FORCE_INFO_SIZE];
|
||||
|
||||
#ifndef FORCEJUMP_INSTANTMETHOD
|
||||
int forceJumpChargeTime;
|
||||
#endif
|
||||
|
||||
int doForcePush;
|
||||
|
||||
int noUseTime;
|
||||
qboolean doingFallback;
|
||||
|
||||
int iHaveNoIdeaWhereIAmGoing;
|
||||
vec3_t lastSignificantAreaChange;
|
||||
int lastSignificantChangeTime;
|
||||
|
||||
int forceMove_Forward;
|
||||
int forceMove_Right;
|
||||
int forceMove_Up;
|
||||
//end rww
|
||||
} bot_state_t;
|
||||
|
||||
void *B_TempAlloc(int size);
|
||||
void B_TempFree(int size);
|
||||
|
||||
void *B_Alloc(int size);
|
||||
void B_Free(void *ptr);
|
||||
|
||||
//resets the whole bot state
|
||||
void BotResetState(bot_state_t *bs);
|
||||
//returns the number of bots in the game
|
||||
int NumBots(void);
|
||||
|
||||
void BotUtilizePersonality(bot_state_t *bs);
|
||||
int BotDoChat(bot_state_t *bs, char *section, int always);
|
||||
void StandardBotAI(bot_state_t *bs, float thinktime);
|
||||
void BotWaypointRender(void);
|
||||
int OrgVisibleBox(vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore);
|
||||
int BotIsAChickenWuss(bot_state_t *bs);
|
||||
int GetNearestVisibleWP(vec3_t org, int ignore);
|
||||
int GetBestIdleGoal(bot_state_t *bs);
|
||||
|
||||
char *ConcatArgs( int start );
|
||||
|
||||
extern vmCvar_t bot_forcepowers;
|
||||
extern vmCvar_t bot_forgimmick;
|
||||
extern vmCvar_t bot_honorableduelacceptance;
|
||||
#ifdef _DEBUG
|
||||
extern vmCvar_t bot_nogoals;
|
||||
extern vmCvar_t bot_debugmessages;
|
||||
#endif
|
||||
|
||||
extern vmCvar_t bot_attachments;
|
||||
extern vmCvar_t bot_camp;
|
||||
|
||||
extern vmCvar_t bot_wp_info;
|
||||
extern vmCvar_t bot_wp_edit;
|
||||
extern vmCvar_t bot_wp_clearweight;
|
||||
extern vmCvar_t bot_wp_distconnect;
|
||||
extern vmCvar_t bot_wp_visconnect;
|
||||
|
||||
extern wpobject_t *flagRed;
|
||||
extern wpobject_t *oFlagRed;
|
||||
extern wpobject_t *flagBlue;
|
||||
extern wpobject_t *oFlagBlue;
|
||||
|
||||
extern gentity_t *eFlagRed;
|
||||
extern gentity_t *eFlagBlue;
|
||||
|
||||
//extern char gBotChatBuffer[MAX_CLIENTS][MAX_CHAT_BUFFER_SIZE];
|
||||
extern float gWPRenderTime;
|
||||
extern float gDeactivated;
|
||||
extern float gBotEdit;
|
||||
extern int gWPRenderedFrame;
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern wpobject_t *gWPArray[MAX_WPARRAY_SIZE];
|
||||
extern int gWPNum;
|
||||
#include "../namespace_end.h"
|
||||
|
||||
extern int gLastPrintedIndex;
|
||||
#ifndef _XBOX
|
||||
extern nodeobject_t nodetable[MAX_NODETABLE_SIZE];
|
||||
#endif
|
||||
extern int nodenum;
|
||||
|
||||
extern int gLevelFlags;
|
||||
|
||||
extern float floattime;
|
||||
#define FloatTime() floattime
|
||||
862
codemp/game/ai_util.c
Normal file
862
codemp/game/ai_util.c
Normal file
@@ -0,0 +1,862 @@
|
||||
#include "g_local.h"
|
||||
#include "q_shared.h"
|
||||
#include "botlib.h"
|
||||
#include "ai_main.h"
|
||||
|
||||
#ifdef BOT_ZMALLOC
|
||||
#define MAX_BALLOC 8192
|
||||
|
||||
void *BAllocList[MAX_BALLOC];
|
||||
#endif
|
||||
|
||||
//char gBotChatBuffer[MAX_CLIENTS][MAX_CHAT_BUFFER_SIZE];
|
||||
|
||||
void *B_TempAlloc(int size)
|
||||
{
|
||||
return BG_TempAlloc(size);
|
||||
}
|
||||
|
||||
void B_TempFree(int size)
|
||||
{
|
||||
BG_TempFree(size);
|
||||
}
|
||||
|
||||
|
||||
void *B_Alloc(int size)
|
||||
{
|
||||
#ifdef BOT_ZMALLOC
|
||||
void *ptr = NULL;
|
||||
int i = 0;
|
||||
|
||||
#ifdef BOTMEMTRACK
|
||||
int free = 0;
|
||||
int used = 0;
|
||||
|
||||
while (i < MAX_BALLOC)
|
||||
{
|
||||
if (!BAllocList[i])
|
||||
{
|
||||
free++;
|
||||
}
|
||||
else
|
||||
{
|
||||
used++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
G_Printf("Allocations used: %i\nFree allocation slots: %i\n", used, free);
|
||||
|
||||
i = 0;
|
||||
#endif
|
||||
|
||||
ptr = trap_BotGetMemoryGame(size);
|
||||
|
||||
while (i < MAX_BALLOC)
|
||||
{
|
||||
if (!BAllocList[i])
|
||||
{
|
||||
BAllocList[i] = ptr;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == MAX_BALLOC)
|
||||
{
|
||||
//If this happens we'll have to rely on this chunk being freed manually with B_Free, which it hopefully will be
|
||||
#ifdef DEBUG
|
||||
G_Printf("WARNING: MAXIMUM B_ALLOC ALLOCATIONS EXCEEDED\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return ptr;
|
||||
#else
|
||||
|
||||
return BG_Alloc(size);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void B_Free(void *ptr)
|
||||
{
|
||||
#ifdef BOT_ZMALLOC
|
||||
int i = 0;
|
||||
|
||||
#ifdef BOTMEMTRACK
|
||||
int free = 0;
|
||||
int used = 0;
|
||||
|
||||
while (i < MAX_BALLOC)
|
||||
{
|
||||
if (!BAllocList[i])
|
||||
{
|
||||
free++;
|
||||
}
|
||||
else
|
||||
{
|
||||
used++;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
G_Printf("Allocations used: %i\nFree allocation slots: %i\n", used, free);
|
||||
|
||||
i = 0;
|
||||
#endif
|
||||
|
||||
while (i < MAX_BALLOC)
|
||||
{
|
||||
if (BAllocList[i] == ptr)
|
||||
{
|
||||
BAllocList[i] = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == MAX_BALLOC)
|
||||
{
|
||||
//Likely because the limit was exceeded and we're now freeing the chunk manually as we hoped would happen
|
||||
#ifdef DEBUG
|
||||
G_Printf("WARNING: Freeing allocation which is not in the allocation structure\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
trap_BotFreeMemoryGame(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void B_InitAlloc(void)
|
||||
{
|
||||
#ifdef BOT_ZMALLOC
|
||||
memset(BAllocList, 0, sizeof(BAllocList));
|
||||
#endif
|
||||
|
||||
memset(gWPArray, 0, sizeof(gWPArray));
|
||||
}
|
||||
|
||||
void B_CleanupAlloc(void)
|
||||
{
|
||||
for(int i=0; i<MAX_WPARRAY_SIZE; i++) {
|
||||
if(gWPArray[i]) {
|
||||
Z_Free(gWPArray[i]);
|
||||
gWPArray[i] = NULL;
|
||||
}
|
||||
}
|
||||
#ifdef BOT_ZMALLOC
|
||||
int i = 0;
|
||||
|
||||
while (i < MAX_BALLOC)
|
||||
{
|
||||
if (BAllocList[i])
|
||||
{
|
||||
trap_BotFreeMemoryGame(BAllocList[i]);
|
||||
BAllocList[i] = NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int GetValueGroup(char *buf, char *group, char *outbuf)
|
||||
{
|
||||
char *place, *placesecond;
|
||||
int iplace;
|
||||
int failure;
|
||||
int i;
|
||||
int startpoint, startletter;
|
||||
int subg = 0;
|
||||
|
||||
i = 0;
|
||||
|
||||
iplace = 0;
|
||||
|
||||
place = strstr(buf, group);
|
||||
|
||||
if (!place)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
startpoint = place - buf + strlen(group) + 1;
|
||||
startletter = (place - buf) - 1;
|
||||
|
||||
failure = 0;
|
||||
|
||||
while (buf[startpoint+1] != '{' || buf[startletter] != '\n')
|
||||
{
|
||||
placesecond = strstr(place+1, group);
|
||||
|
||||
if (placesecond)
|
||||
{
|
||||
startpoint += (placesecond - place);
|
||||
startletter += (placesecond - place);
|
||||
place = placesecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
failure = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (failure)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//we have found the proper group name if we made it here, so find the opening brace and read into the outbuf
|
||||
//until hitting the end brace
|
||||
|
||||
while (buf[startpoint] != '{')
|
||||
{
|
||||
startpoint++;
|
||||
}
|
||||
|
||||
startpoint++;
|
||||
|
||||
while (buf[startpoint] != '}' || subg)
|
||||
{
|
||||
if (buf[startpoint] == '{')
|
||||
{
|
||||
subg++;
|
||||
}
|
||||
else if (buf[startpoint] == '}')
|
||||
{
|
||||
subg--;
|
||||
}
|
||||
outbuf[i] = buf[startpoint];
|
||||
i++;
|
||||
startpoint++;
|
||||
}
|
||||
outbuf[i] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetPairedValue(char *buf, char *key, char *outbuf)
|
||||
{
|
||||
char *place, *placesecond;
|
||||
int startpoint, startletter;
|
||||
int i, found;
|
||||
|
||||
if (!buf || !key || !outbuf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (buf[i] && buf[i] != '\0')
|
||||
{
|
||||
if (buf[i] == '/')
|
||||
{
|
||||
if (buf[i+1] && buf[i+1] != '\0' && buf[i+1] == '/')
|
||||
{
|
||||
while (buf[i] != '\n')
|
||||
{
|
||||
buf[i] = '/';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
place = strstr(buf, key);
|
||||
|
||||
if (!place)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//tab == 9
|
||||
startpoint = place - buf + strlen(key);
|
||||
startletter = (place - buf) - 1;
|
||||
|
||||
found = 0;
|
||||
|
||||
while (!found)
|
||||
{
|
||||
if (startletter == 0 || !buf[startletter] || buf[startletter] == '\0' || buf[startletter] == 9 || buf[startletter] == ' ' || buf[startletter] == '\n')
|
||||
{
|
||||
if (buf[startpoint] == '\0' || buf[startpoint] == 9 || buf[startpoint] == ' ' || buf[startpoint] == '\n')
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
placesecond = strstr(place+1, key);
|
||||
|
||||
if (placesecond)
|
||||
{
|
||||
startpoint += placesecond - place;
|
||||
startletter += placesecond - place;
|
||||
place = placesecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
place = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!found || !place || !buf[startpoint] || buf[startpoint] == '\0')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (buf[startpoint] == ' ' || buf[startpoint] == 9 || buf[startpoint] == '\n')
|
||||
{
|
||||
startpoint++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (buf[startpoint] && buf[startpoint] != '\0' && buf[startpoint] != '\n')
|
||||
{
|
||||
outbuf[i] = buf[startpoint];
|
||||
i++;
|
||||
startpoint++;
|
||||
}
|
||||
|
||||
outbuf[i] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
int BotDoChat(bot_state_t *bs, char *section, int always)
|
||||
{
|
||||
char *chatgroup;
|
||||
int rVal;
|
||||
int inc_1;
|
||||
int inc_2;
|
||||
int inc_n;
|
||||
int lines;
|
||||
int checkedline;
|
||||
int getthisline;
|
||||
gentity_t *cobject;
|
||||
|
||||
if (!bs->canChat)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bs->doChat)
|
||||
{ //already have a chat scheduled
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (trap_Cvar_VariableIntegerValue("se_language"))
|
||||
{ //no chatting unless English.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Q_irand(1, 10) > bs->chatFrequency && !always)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bs->chatTeam = 0;
|
||||
|
||||
chatgroup = (char *)B_TempAlloc(MAX_CHAT_BUFFER_SIZE);
|
||||
|
||||
rVal = GetValueGroup(gBotChatBuffer[bs->client], section, chatgroup);
|
||||
|
||||
if (!rVal) //the bot has no group defined for the specified chat event
|
||||
{
|
||||
B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
|
||||
return 0;
|
||||
}
|
||||
|
||||
inc_1 = 0;
|
||||
inc_2 = 2;
|
||||
|
||||
while (chatgroup[inc_2] && chatgroup[inc_2] != '\0')
|
||||
{
|
||||
if (chatgroup[inc_2] != 13 && chatgroup[inc_2] != 9)
|
||||
{
|
||||
chatgroup[inc_1] = chatgroup[inc_2];
|
||||
inc_1++;
|
||||
}
|
||||
inc_2++;
|
||||
}
|
||||
chatgroup[inc_1] = '\0';
|
||||
|
||||
inc_1 = 0;
|
||||
|
||||
lines = 0;
|
||||
|
||||
while (chatgroup[inc_1] && chatgroup[inc_1] != '\0')
|
||||
{
|
||||
if (chatgroup[inc_1] == '\n')
|
||||
{
|
||||
lines++;
|
||||
}
|
||||
inc_1++;
|
||||
}
|
||||
|
||||
if (!lines)
|
||||
{
|
||||
B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
|
||||
return 0;
|
||||
}
|
||||
|
||||
getthisline = Q_irand(0, (lines+1));
|
||||
|
||||
if (getthisline < 1)
|
||||
{
|
||||
getthisline = 1;
|
||||
}
|
||||
if (getthisline > lines)
|
||||
{
|
||||
getthisline = lines;
|
||||
}
|
||||
|
||||
checkedline = 1;
|
||||
|
||||
inc_1 = 0;
|
||||
|
||||
while (checkedline != getthisline)
|
||||
{
|
||||
if (chatgroup[inc_1] && chatgroup[inc_1] != '\0')
|
||||
{
|
||||
if (chatgroup[inc_1] == '\n')
|
||||
{
|
||||
inc_1++;
|
||||
checkedline++;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedline == getthisline)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
inc_1++;
|
||||
}
|
||||
|
||||
//we're at the starting position of the desired line here
|
||||
inc_2 = 0;
|
||||
|
||||
while (chatgroup[inc_1] != '\n')
|
||||
{
|
||||
chatgroup[inc_2] = chatgroup[inc_1];
|
||||
inc_2++;
|
||||
inc_1++;
|
||||
}
|
||||
chatgroup[inc_2] = '\0';
|
||||
|
||||
//trap_EA_Say(bs->client, chatgroup);
|
||||
inc_1 = 0;
|
||||
inc_2 = 0;
|
||||
|
||||
if (strlen(chatgroup) > MAX_CHAT_LINE_SIZE)
|
||||
{
|
||||
B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (chatgroup[inc_1])
|
||||
{
|
||||
if (chatgroup[inc_1] == '%' && chatgroup[inc_1+1] != '%')
|
||||
{
|
||||
inc_1++;
|
||||
|
||||
if (chatgroup[inc_1] == 's' && bs->chatObject)
|
||||
{
|
||||
cobject = bs->chatObject;
|
||||
}
|
||||
else if (chatgroup[inc_1] == 'a' && bs->chatAltObject)
|
||||
{
|
||||
cobject = bs->chatAltObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
cobject = NULL;
|
||||
}
|
||||
|
||||
if (cobject && cobject->client)
|
||||
{
|
||||
inc_n = 0;
|
||||
|
||||
while (cobject->client->pers.netname[inc_n])
|
||||
{
|
||||
bs->currentChat[inc_2] = cobject->client->pers.netname[inc_n];
|
||||
inc_2++;
|
||||
inc_n++;
|
||||
}
|
||||
inc_2--; //to make up for the auto-increment below
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->currentChat[inc_2] = chatgroup[inc_1];
|
||||
}
|
||||
inc_2++;
|
||||
inc_1++;
|
||||
}
|
||||
bs->currentChat[inc_2] = '\0';
|
||||
|
||||
if (strcmp(section, "GeneralGreetings") == 0)
|
||||
{
|
||||
bs->doChat = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->doChat = 1;
|
||||
}
|
||||
bs->chatTime_stored = (strlen(bs->currentChat)*45)+Q_irand(1300, 1500);
|
||||
bs->chatTime = level.time + bs->chatTime_stored;
|
||||
|
||||
B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup
|
||||
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
void ParseEmotionalAttachments(bot_state_t *bs, char *buf)
|
||||
{
|
||||
int i = 0;
|
||||
int i_c = 0;
|
||||
char tbuf[16];
|
||||
|
||||
while (buf[i] && buf[i] != '}')
|
||||
{
|
||||
while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (buf[i] && buf[i] != '}')
|
||||
{
|
||||
i_c = 0;
|
||||
while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n')
|
||||
{
|
||||
bs->loved[bs->lovednum].name[i_c] = buf[i];
|
||||
i_c++;
|
||||
i++;
|
||||
}
|
||||
bs->loved[bs->lovednum].name[i_c] = '\0';
|
||||
|
||||
while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
i_c = 0;
|
||||
|
||||
while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n')
|
||||
{
|
||||
tbuf[i_c] = buf[i];
|
||||
i_c++;
|
||||
i++;
|
||||
}
|
||||
tbuf[i_c] = '\0';
|
||||
|
||||
bs->loved[bs->lovednum].level = atoi(tbuf);
|
||||
|
||||
bs->lovednum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (bs->lovednum >= MAX_LOVED_ONES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int ReadChatGroups(bot_state_t *bs, char *buf)
|
||||
{
|
||||
char *cgroupbegin;
|
||||
int cgbplace;
|
||||
int i;
|
||||
|
||||
cgroupbegin = strstr(buf, "BEGIN_CHAT_GROUPS");
|
||||
|
||||
if (!cgroupbegin)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strlen(cgroupbegin) >= MAX_CHAT_BUFFER_SIZE)
|
||||
{
|
||||
G_Printf(S_COLOR_RED "Error: Personality chat section exceeds max size\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
cgbplace = cgroupbegin - buf+1;
|
||||
|
||||
while (buf[cgbplace] != '\n')
|
||||
{
|
||||
cgbplace++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (buf[cgbplace] && buf[cgbplace] != '\0')
|
||||
{
|
||||
gBotChatBuffer[bs->client][i] = buf[cgbplace];
|
||||
i++;
|
||||
cgbplace++;
|
||||
}
|
||||
|
||||
gBotChatBuffer[bs->client][i] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
void BotUtilizePersonality(bot_state_t *bs)
|
||||
{
|
||||
fileHandle_t f;
|
||||
int len, rlen;
|
||||
int failed;
|
||||
int i;
|
||||
//char buf[131072];
|
||||
char *readbuf, *group;
|
||||
|
||||
len = trap_FS_FOpenFile(bs->settings.personalityfile, &f, FS_READ);
|
||||
|
||||
failed = 0;
|
||||
|
||||
if (!f)
|
||||
{
|
||||
G_Printf(S_COLOR_RED "Error: Specified personality not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *buf = (char *)B_TempAlloc(len + 1);
|
||||
trap_FS_Read(buf, len, f);
|
||||
|
||||
buf[len] = 0;
|
||||
|
||||
readbuf = (char *)B_TempAlloc(1024);
|
||||
group = (char *)B_TempAlloc(len);
|
||||
|
||||
if (!GetValueGroup(buf, "GeneralBotInfo", group))
|
||||
{
|
||||
G_Printf(S_COLOR_RED "Personality file contains no GeneralBotInfo group\n");
|
||||
failed = 1; //set failed so we know to set everything to default values
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "reflex", readbuf))
|
||||
{
|
||||
bs->skills.reflex = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.reflex = 100; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "accuracy", readbuf))
|
||||
{
|
||||
bs->skills.accuracy = atof(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.accuracy = 10; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "turnspeed", readbuf))
|
||||
{
|
||||
bs->skills.turnspeed = atof(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.turnspeed = 0.01f; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "turnspeed_combat", readbuf))
|
||||
{
|
||||
bs->skills.turnspeed_combat = atof(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.turnspeed_combat = 0.05f; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "maxturn", readbuf))
|
||||
{
|
||||
bs->skills.maxturn = atof(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.maxturn = 360; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "perfectaim", readbuf))
|
||||
{
|
||||
bs->skills.perfectaim = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->skills.perfectaim = 0; //default
|
||||
}
|
||||
/*
|
||||
if (!failed && GetPairedValue(group, "chatability", readbuf))
|
||||
{
|
||||
bs->canChat = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->canChat = 0; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "chatfrequency", readbuf))
|
||||
{
|
||||
bs->chatFrequency = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->chatFrequency = 5; //default
|
||||
}
|
||||
*/
|
||||
if (!failed && GetPairedValue(group, "hatelevel", readbuf))
|
||||
{
|
||||
bs->loved_death_thresh = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->loved_death_thresh = 3; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "camper", readbuf))
|
||||
{
|
||||
bs->isCamper = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->isCamper = 0; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "saberspecialist", readbuf))
|
||||
{
|
||||
bs->saberSpecialist = atoi(readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs->saberSpecialist = 0; //default
|
||||
}
|
||||
|
||||
if (!failed && GetPairedValue(group, "forceinfo", readbuf))
|
||||
{
|
||||
Com_sprintf(bs->forceinfo, sizeof(bs->forceinfo), "%s\0", readbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
Com_sprintf(bs->forceinfo, sizeof(bs->forceinfo), "%s\0", DEFAULT_FORCEPOWERS);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
/*
|
||||
while (i < MAX_CHAT_BUFFER_SIZE)
|
||||
{ //clear out the chat buffer for this bot
|
||||
gBotChatBuffer[bs->client][i] = '\0';
|
||||
i++;
|
||||
}
|
||||
|
||||
if (bs->canChat)
|
||||
{
|
||||
if (!ReadChatGroups(bs, buf))
|
||||
{
|
||||
bs->canChat = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (GetValueGroup(buf, "BotWeaponWeights", group))
|
||||
{
|
||||
if (GetPairedValue(group, "WP_STUN_BATON", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_STUN_BATON] = atoi(readbuf);
|
||||
bs->botWeaponWeights[WP_MELEE] = bs->botWeaponWeights[WP_STUN_BATON];
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_SABER", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_SABER] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_BRYAR_PISTOL", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_BRYAR_PISTOL] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_BLASTER", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_BLASTER] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_DISRUPTOR", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_DISRUPTOR] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_BOWCASTER", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_BOWCASTER] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_REPEATER", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_REPEATER] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_DEMP2", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_DEMP2] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_FLECHETTE", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_FLECHETTE] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_ROCKET_LAUNCHER", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_ROCKET_LAUNCHER] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_THERMAL", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_THERMAL] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_TRIP_MINE", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_TRIP_MINE] = atoi(readbuf);
|
||||
}
|
||||
|
||||
if (GetPairedValue(group, "WP_DET_PACK", readbuf))
|
||||
{
|
||||
bs->botWeaponWeights[WP_DET_PACK] = atoi(readbuf);
|
||||
}
|
||||
}
|
||||
|
||||
bs->lovednum = 0;
|
||||
|
||||
if (GetValueGroup(buf, "EmotionalAttachments", group))
|
||||
{
|
||||
ParseEmotionalAttachments(bs, group);
|
||||
}
|
||||
|
||||
B_TempFree(len + 1); //buf
|
||||
B_TempFree(1024); //readbuf
|
||||
B_TempFree(len); //group
|
||||
trap_FS_FCloseFile(f);
|
||||
}
|
||||
3813
codemp/game/ai_wpnav.c
Normal file
3813
codemp/game/ai_wpnav.c
Normal file
File diff suppressed because it is too large
Load Diff
1797
codemp/game/anims.h
Normal file
1797
codemp/game/anims.h
Normal file
File diff suppressed because it is too large
Load Diff
329
codemp/game/b_local.h
Normal file
329
codemp/game/b_local.h
Normal file
@@ -0,0 +1,329 @@
|
||||
//B_local.h
|
||||
//re-added by MCG
|
||||
#ifndef __B_LOCAL_H__
|
||||
#define __B_LOCAL_H__
|
||||
|
||||
#include "g_local.h"
|
||||
#include "b_public.h"
|
||||
#include "say.h"
|
||||
|
||||
#include "ai.h"
|
||||
|
||||
#define AI_TIMERS 0//turn on to see print-outs of AI/nav timing
|
||||
//
|
||||
// Navigation susbsystem
|
||||
//
|
||||
|
||||
#define NAVF_DUCK 0x00000001
|
||||
#define NAVF_JUMP 0x00000002
|
||||
#define NAVF_HOLD 0x00000004
|
||||
#define NAVF_SLOW 0x00000008
|
||||
|
||||
#define DEBUG_LEVEL_DETAIL 4
|
||||
#define DEBUG_LEVEL_INFO 3
|
||||
#define DEBUG_LEVEL_WARNING 2
|
||||
#define DEBUG_LEVEL_ERROR 1
|
||||
#define DEBUG_LEVEL_NONE 0
|
||||
|
||||
#define MAX_GOAL_REACHED_DIST_SQUARED 256//16 squared
|
||||
#define MIN_ANGLE_ERROR 0.01f
|
||||
|
||||
#define MIN_ROCKET_DIST_SQUARED 16384//128*128
|
||||
//
|
||||
// NPC.cpp
|
||||
//
|
||||
// ai debug cvars
|
||||
void SetNPCGlobals( gentity_t *ent );
|
||||
void SaveNPCGlobals(void);
|
||||
void RestoreNPCGlobals(void);
|
||||
extern vmCvar_t debugNPCAI; // used to print out debug info about the NPC AI
|
||||
extern vmCvar_t debugNPCFreeze; // set to disable NPC ai and temporarily freeze them in place
|
||||
extern vmCvar_t debugNPCAimingBeam;
|
||||
extern vmCvar_t debugBreak;
|
||||
extern vmCvar_t debugNoRoam;
|
||||
extern vmCvar_t d_JediAI;
|
||||
extern vmCvar_t d_saberCombat;
|
||||
|
||||
extern void NPC_Think ( gentity_t *self);
|
||||
|
||||
//NPC_reactions.cpp
|
||||
extern void NPC_Pain(gentity_t *self, gentity_t *attacker, int damage);
|
||||
extern void NPC_Touch( gentity_t *self, gentity_t *other, trace_t *trace );
|
||||
extern void NPC_Use( gentity_t *self, gentity_t *other, gentity_t *activator );
|
||||
extern float NPC_GetPainChance( gentity_t *self, int damage );
|
||||
|
||||
//
|
||||
// NPC_misc.cpp
|
||||
//
|
||||
extern void Debug_Printf( vmCvar_t *cv, int level, char *fmt, ... );
|
||||
extern void Debug_NPCPrintf( gentity_t *printNPC, vmCvar_t *cv, int debugLevel, char *fmt, ... );
|
||||
|
||||
//MCG - Begin============================================================
|
||||
//NPC_ai variables - shared by NPC.cpp andf the following modules
|
||||
extern gentity_t *NPC;
|
||||
extern gNPC_t *NPCInfo;
|
||||
extern gclient_t *client;
|
||||
extern usercmd_t ucmd;
|
||||
extern visibility_t enemyVisibility;
|
||||
|
||||
//AI_Default
|
||||
extern qboolean NPC_CheckInvestigate( int alertEventNum );
|
||||
extern qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck);
|
||||
extern void NPC_BSIdle( void );
|
||||
extern void NPC_BSPointShoot(qboolean shoot);
|
||||
extern void NPC_BSStandGuard (void);
|
||||
extern void NPC_BSPatrol (void);
|
||||
extern void NPC_BSHuntAndKill (void);
|
||||
extern void NPC_BSStandAndShoot (void);
|
||||
extern void NPC_BSRunAndShoot (void);
|
||||
extern void NPC_BSWait( void );
|
||||
extern void NPC_BSDefault( void );
|
||||
|
||||
//NPC_behavior
|
||||
extern void NPC_BSAdvanceFight (void);
|
||||
extern void NPC_BSInvestigate (void);
|
||||
extern void NPC_BSSleep( void );
|
||||
extern void NPC_BSFlee (void);
|
||||
extern void NPC_BSFollowLeader (void);
|
||||
extern void NPC_BSJump (void);
|
||||
extern void NPC_BSRemove (void);
|
||||
extern void NPC_BSSearch (void);
|
||||
extern void NPC_BSSearchStart (int homeWp, bState_t bState);
|
||||
extern void NPC_BSWander (void);
|
||||
extern void NPC_BSFlee( void );
|
||||
extern void NPC_StartFlee( gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax );
|
||||
extern void G_StartFlee( gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax );
|
||||
|
||||
//NPC_combat
|
||||
extern int ChooseBestWeapon( void );
|
||||
extern void NPC_ChangeWeapon( int newWeapon );
|
||||
extern void ShootThink( void );
|
||||
extern void WeaponThink( qboolean inCombat );
|
||||
extern qboolean HaveWeapon( int weapon );
|
||||
extern qboolean CanShoot ( gentity_t *ent, gentity_t *shooter );
|
||||
extern void NPC_CheckPossibleEnemy( gentity_t *other, visibility_t vis );
|
||||
extern gentity_t *NPC_PickEnemy (gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest);
|
||||
extern gentity_t *NPC_CheckEnemy (qboolean findNew, qboolean tooFarOk, qboolean setEnemy ); //setEnemy = qtrue
|
||||
extern qboolean NPC_CheckAttack (float scale);
|
||||
extern qboolean NPC_CheckDefend (float scale);
|
||||
extern qboolean NPC_CheckCanAttack (float attack_scale, qboolean stationary);
|
||||
extern int NPC_AttackDebounceForWeapon (void);
|
||||
extern qboolean EntIsGlass (gentity_t *check);
|
||||
extern qboolean ShotThroughGlass (trace_t *tr, gentity_t *target, vec3_t spot, int mask);
|
||||
extern qboolean ValidEnemy (gentity_t *ent);
|
||||
extern void G_ClearEnemy (gentity_t *self);
|
||||
extern gentity_t *NPC_PickAlly ( qboolean facingEachOther, float range, qboolean ignoreGroup, qboolean movingOnly );
|
||||
extern void NPC_LostEnemyDecideChase(void);
|
||||
extern float NPC_MaxDistSquaredForWeapon( void );
|
||||
extern qboolean NPC_EvaluateShot( int hit, qboolean glassOK );
|
||||
extern int NPC_ShotEntity( gentity_t *ent, vec3_t impactPos ); //impactedPos = NULL
|
||||
|
||||
//NPC_formation
|
||||
extern qboolean NPC_SlideMoveToGoal (void);
|
||||
extern float NPC_FindClosestTeammate (gentity_t *self);
|
||||
extern void NPC_CalcClosestFormationSpot(gentity_t *self);
|
||||
extern void G_MaintainFormations (gentity_t *self);
|
||||
extern void NPC_BSFormation (void);
|
||||
extern void NPC_CreateFormation (gentity_t *self);
|
||||
extern void NPC_DropFormation (gentity_t *self);
|
||||
extern void NPC_ReorderFormation (gentity_t *self);
|
||||
extern void NPC_InsertIntoFormation (gentity_t *self);
|
||||
extern void NPC_DeleteFromFormation (gentity_t *self);
|
||||
|
||||
#define COLLISION_RADIUS 32
|
||||
#define NUM_POSITIONS 30
|
||||
|
||||
//NPC spawnflags
|
||||
#define SFB_SMALLHULL 1
|
||||
|
||||
#define SFB_RIFLEMAN 2
|
||||
#define SFB_OLDBORG 2//Borg
|
||||
#define SFB_PHASER 4
|
||||
#define SFB_GUN 4//Borg
|
||||
#define SFB_TRICORDER 8
|
||||
#define SFB_TASER 8//Borg
|
||||
#define SFB_DRILL 16//Borg
|
||||
|
||||
#define SFB_CINEMATIC 32
|
||||
#define SFB_NOTSOLID 64
|
||||
#define SFB_STARTINSOLID 128
|
||||
|
||||
//NPC_goal
|
||||
extern void SetGoal( gentity_t *goal, float rating );
|
||||
extern void NPC_SetGoal( gentity_t *goal, float rating );
|
||||
extern void NPC_ClearGoal( void );
|
||||
extern void NPC_ReachedGoal( void );
|
||||
extern qboolean ReachedGoal( gentity_t *goal );
|
||||
extern gentity_t *UpdateGoal( void );
|
||||
extern qboolean NPC_ClearPathToGoal(vec3_t dir, gentity_t *goal);
|
||||
extern qboolean NPC_MoveToGoal( qboolean tryStraight );
|
||||
|
||||
//NPC_reactions
|
||||
|
||||
//NPC_senses
|
||||
#define ALERT_CLEAR_TIME 200
|
||||
#define CHECK_PVS 1
|
||||
#define CHECK_360 2
|
||||
#define CHECK_FOV 4
|
||||
#define CHECK_SHOOT 8
|
||||
#define CHECK_VISRANGE 16
|
||||
extern qboolean CanSee ( gentity_t *ent );
|
||||
extern qboolean InFOV ( gentity_t *ent, gentity_t *from, int hFOV, int vFOV );
|
||||
extern qboolean InFOV2( vec3_t origin, gentity_t *from, int hFOV, int vFOV );
|
||||
extern qboolean InFOV3( vec3_t spot, vec3_t from, vec3_t fromAngles, int hFOV, int vFOV );
|
||||
extern visibility_t NPC_CheckVisibility ( gentity_t *ent, int flags );
|
||||
extern qboolean InVisrange ( gentity_t *ent );
|
||||
|
||||
//NPC_sounds
|
||||
//extern void NPC_AngerSound(void);
|
||||
|
||||
//NPC_spawn
|
||||
extern void NPC_Spawn ( gentity_t *ent, gentity_t *other, gentity_t *activator );
|
||||
|
||||
//NPC_stats
|
||||
extern int NPC_ReactionTime ( void );
|
||||
extern qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC );
|
||||
extern void NPC_LoadParms( void );
|
||||
|
||||
//NPC_utils
|
||||
extern int teamNumbers[TEAM_NUM_TEAMS];
|
||||
extern int teamStrength[TEAM_NUM_TEAMS];
|
||||
extern int teamCounter[TEAM_NUM_TEAMS];
|
||||
extern void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point );
|
||||
extern qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw );
|
||||
extern void NPC_UpdateShootAngles (vec3_t angles, qboolean doPitch, qboolean doYaw );
|
||||
extern qboolean NPC_UpdateFiringAngles ( qboolean doPitch, qboolean doYaw );
|
||||
extern void SetTeamNumbers (void);
|
||||
extern qboolean G_ActivateBehavior (gentity_t *self, int bset );
|
||||
extern void NPC_AimWiggle( vec3_t enemy_org );
|
||||
extern void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime );
|
||||
|
||||
//g_nav.cpp
|
||||
extern int NAV_FindClosestWaypointForEnt (gentity_t *ent, int targWp);
|
||||
extern qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t *trace, int clipmask );
|
||||
|
||||
//NPC_combat
|
||||
extern float IdealDistance ( gentity_t *self );
|
||||
|
||||
//g_squad
|
||||
extern void NPC_SetSayState (gentity_t *self, gentity_t *to, int saying);
|
||||
|
||||
//g_utils
|
||||
extern qboolean G_CheckInSolid (gentity_t *self, qboolean fix);
|
||||
|
||||
//MCG - End============================================================
|
||||
|
||||
// NPC.cpp
|
||||
extern void NPC_SetAnim(gentity_t *ent, int type, int anim, int priority);
|
||||
extern qboolean NPC_EnemyTooFar(gentity_t *enemy, float dist, qboolean toShoot);
|
||||
|
||||
// ==================================================================
|
||||
|
||||
//rww - special system for sync'ing bone angles between client and server.
|
||||
void NPC_SetBoneAngles(gentity_t *ent, char *bone, vec3_t angles);
|
||||
|
||||
//rww - and another method of automatically managing surface status for the client and server at once
|
||||
void NPC_SetSurfaceOnOff(gentity_t *ent, const char *surfaceName, int surfaceFlags);
|
||||
|
||||
extern qboolean NPC_ClearLOS( const vec3_t start, const vec3_t end );
|
||||
extern qboolean NPC_ClearLOS5( const vec3_t end );
|
||||
extern qboolean NPC_ClearLOS4( gentity_t *ent ) ;
|
||||
extern qboolean NPC_ClearLOS3( const vec3_t start, gentity_t *ent );
|
||||
extern qboolean NPC_ClearLOS2( gentity_t *ent, const vec3_t end );
|
||||
|
||||
extern qboolean NPC_ClearShot( gentity_t *ent );
|
||||
|
||||
extern int NPC_FindCombatPoint( const vec3_t position, const vec3_t avoidPosition, vec3_t enemyPosition, const int flags, const float avoidDist, const int ignorePoint ); //ignorePoint = -1
|
||||
|
||||
|
||||
extern qboolean NPC_ReserveCombatPoint( int combatPointID );
|
||||
extern qboolean NPC_FreeCombatPoint( int combatPointID, qboolean failed ); //failed = qfalse
|
||||
extern qboolean NPC_SetCombatPoint( int combatPointID );
|
||||
|
||||
#define CP_ANY 0 //No flags
|
||||
#define CP_COVER 0x00000001 //The enemy cannot currently shoot this position
|
||||
#define CP_CLEAR 0x00000002 //This cover point has a clear shot to the enemy
|
||||
#define CP_FLEE 0x00000004 //This cover point is marked as a flee point
|
||||
#define CP_DUCK 0x00000008 //This cover point is marked as a duck point
|
||||
#define CP_NEAREST 0x00000010 //Find the nearest combat point
|
||||
#define CP_AVOID_ENEMY 0x00000020 //Avoid our enemy
|
||||
#define CP_INVESTIGATE 0x00000040 //A special point worth enemy investigation if searching
|
||||
#define CP_SQUAD 0x00000080 //Squad path
|
||||
#define CP_AVOID 0x00000100 //Avoid supplied position
|
||||
#define CP_APPROACH_ENEMY 0x00000200 //Try to get closer to enemy
|
||||
#define CP_CLOSEST 0x00000400 //Take the closest combatPoint to the enemy that's available
|
||||
#define CP_FLANK 0x00000800 //Pick a combatPoint behind the enemy
|
||||
#define CP_HAS_ROUTE 0x00001000 //Pick a combatPoint that we have a route to
|
||||
#define CP_SNIPE 0x00002000 //Pick a combatPoint that is marked as a sniper spot
|
||||
#define CP_SAFE 0x00004000 //Pick a combatPoint that is not have dangerTime
|
||||
#define CP_HORZ_DIST_COLL 0x00008000 //Collect combat points within *horizontal* dist
|
||||
#define CP_NO_PVS 0x00010000 //A combat point out of the PVS of enemy pos
|
||||
#define CP_RETREAT 0x00020000 //Try to get farther from enemy
|
||||
|
||||
#define CPF_NONE 0
|
||||
#define CPF_DUCK 0x00000001
|
||||
#define CPF_FLEE 0x00000002
|
||||
#define CPF_INVESTIGATE 0x00000004
|
||||
#define CPF_SQUAD 0x00000008
|
||||
#define CPF_LEAN 0x00000010
|
||||
#define CPF_SNIPE 0x00000020
|
||||
|
||||
#define MAX_COMBAT_POINT_CHECK 32
|
||||
|
||||
extern qboolean NPC_ValidEnemy( gentity_t *ent );
|
||||
extern qboolean NPC_CheckEnemyExt( qboolean checkAlerts ); //checkAlerts = qfalse
|
||||
extern qboolean NPC_FindPlayer( void );
|
||||
extern qboolean NPC_CheckCanAttackExt( void );
|
||||
|
||||
extern int NPC_CheckAlertEvents( qboolean checkSight, qboolean checkSound, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel ); //ignoreAlert = -1, mustHaveOwner = qfalse, minAlertLevel = AEL_MINOR
|
||||
extern qboolean NPC_CheckForDanger( int alertEvent );
|
||||
extern void G_AlertTeam( gentity_t *victim, gentity_t *attacker, float radius, float soundDist );
|
||||
|
||||
extern int NPC_FindSquadPoint( vec3_t position );
|
||||
|
||||
extern void ClearPlayerAlertEvents( void );
|
||||
|
||||
extern qboolean G_BoundsOverlap(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2);
|
||||
extern qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, int radius, qboolean flying );
|
||||
|
||||
extern void NPC_SetMoveGoal( gentity_t *ent, vec3_t point, int radius, qboolean isNavGoal, int combatPoint, gentity_t *targetEnt ); //isNavGoal = qfalse, combatPoint = -1, targetEnt = NULL
|
||||
|
||||
extern qboolean NAV_ClearPathToPoint(gentity_t *self, vec3_t pmins, vec3_t pmaxs, vec3_t point, int clipmask, int okToHitEnt );
|
||||
extern void NPC_ApplyWeaponFireDelay(void);
|
||||
|
||||
//NPC_FaceXXX suite
|
||||
extern qboolean NPC_FacePosition( vec3_t position, qboolean doPitch ); //doPitch = qtrue
|
||||
extern qboolean NPC_FaceEntity( gentity_t *ent, qboolean doPitch ); //doPitch = qtrue
|
||||
extern qboolean NPC_FaceEnemy( qboolean doPitch ); //doPitch = qtrue
|
||||
|
||||
//Skill level cvar
|
||||
extern vmCvar_t g_spskill;
|
||||
|
||||
#define NIF_NONE 0x00000000
|
||||
#define NIF_FAILED 0x00000001 //failed to find a way to the goal
|
||||
#define NIF_MACRO_NAV 0x00000002 //using macro navigation
|
||||
#define NIF_COLLISION 0x00000004 //resolving collision with an entity
|
||||
#define NIF_BLOCKED 0x00000008 //blocked from moving
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
struct navInfo_s
|
||||
-------------------------
|
||||
*/
|
||||
|
||||
typedef struct navInfo_s
|
||||
{
|
||||
gentity_t *blocker;
|
||||
vec3_t direction;
|
||||
vec3_t pathDirection;
|
||||
float distance;
|
||||
trace_t trace;
|
||||
int flags;
|
||||
} navInfo_t;
|
||||
|
||||
extern int NAV_MoveToGoal( gentity_t *self, navInfo_t *info );
|
||||
extern void NAV_GetLastMove( navInfo_t *info );
|
||||
extern qboolean NAV_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t *info );
|
||||
|
||||
|
||||
#endif
|
||||
355
codemp/game/b_public.h
Normal file
355
codemp/game/b_public.h
Normal file
@@ -0,0 +1,355 @@
|
||||
#ifndef __B_PUBLIC_H__
|
||||
#define __B_PUBLIC_H__
|
||||
|
||||
#include "ai.h"
|
||||
|
||||
#define NPCAI_CHECK_WEAPON 0x00000001
|
||||
#define NPCAI_BURST_WEAPON 0x00000002
|
||||
#define NPCAI_MOVING 0x00000004
|
||||
#define NPCAI_TOUCHED_GOAL 0x00000008
|
||||
#define NPCAI_PUSHED 0x00000010
|
||||
#define NPCAI_NO_COLL_AVOID 0x00000020
|
||||
#define NPCAI_BLOCKED 0x00000040
|
||||
#define NPCAI_OFF_PATH 0x00000100
|
||||
#define NPCAI_IN_SQUADPOINT 0x00000200
|
||||
#define NPCAI_STRAIGHT_TO_DESTPOS 0x00000400
|
||||
#define NPCAI_NO_SLOWDOWN 0x00001000
|
||||
#define NPCAI_LOST 0x00002000 //Can't nav to his goal
|
||||
#define NPCAI_SHIELDS 0x00004000 //Has shields, borg can adapt
|
||||
#define NPCAI_GREET_ALLIES 0x00008000 //Say hi to nearby allies
|
||||
#define NPCAI_FORM_TELE_NAV 0x00010000 //Tells formation people to use nav info to get to
|
||||
#define NPCAI_ENROUTE_TO_HOMEWP 0x00020000 //Lets us know to run our lostenemyscript when we get to homeWp
|
||||
#define NPCAI_MATCHPLAYERWEAPON 0x00040000 //Match the player's weapon except when it changes during cinematics
|
||||
#define NPCAI_DIE_ON_IMPACT 0x00100000 //Next time you crashland, die!
|
||||
#define NPCAI_CUSTOM_GRAVITY 0x00200000 //Don't use g_gravity, I fly!
|
||||
|
||||
//Script flags
|
||||
#define SCF_CROUCHED 0x00000001 //Force ucmd.upmove to be -127
|
||||
#define SCF_WALKING 0x00000002 //Force BUTTON_WALKING to be pressed
|
||||
#define SCF_MORELIGHT 0x00000004 //NPC will have a minlight of 96
|
||||
#define SCF_LEAN_RIGHT 0x00000008 //Force rightmove+BUTTON_USE
|
||||
#define SCF_LEAN_LEFT 0x00000010 //Force leftmove+BUTTON_USE
|
||||
#define SCF_RUNNING 0x00000020 //Takes off walking button, overrides SCF_WALKING
|
||||
#define SCF_ALT_FIRE 0x00000040 //Force to use alt-fire when firing
|
||||
#define SCF_NO_RESPONSE 0x00000080 //NPC will not do generic responses to being used
|
||||
#define SCF_FFDEATH 0x00000100 //Just tells player_die to run the friendly fire deathscript
|
||||
#define SCF_NO_COMBAT_TALK 0x00000200 //NPC will not use their generic combat chatter stuff
|
||||
#define SCF_CHASE_ENEMIES 0x00000400 //NPC chase enemies - FIXME: right now this is synonymous with using combat points... should it be?
|
||||
#define SCF_LOOK_FOR_ENEMIES 0x00000800 //NPC be on the lookout for enemies
|
||||
#define SCF_FACE_MOVE_DIR 0x00001000 //NPC face direction it's moving - FIXME: not really implemented right now
|
||||
#define SCF_IGNORE_ALERTS 0x00002000 //NPC ignore alert events
|
||||
#define SCF_DONT_FIRE 0x00004000 //NPC won't shoot
|
||||
#define SCF_DONT_FLEE 0x00008000 //NPC never flees
|
||||
#define SCF_FORCED_MARCH 0x00010000 //NPC that the player must aim at to make him walk
|
||||
#define SCF_NO_GROUPS 0x00020000 //NPC cannot alert groups or be part of a group
|
||||
#define SCF_FIRE_WEAPON 0x00040000 //NPC will fire his (her) weapon
|
||||
#define SCF_NO_MIND_TRICK 0x00080000 //Not succeptible to mind tricks
|
||||
#define SCF_USE_CP_NEAREST 0x00100000 //Will use combat point close to it, not next to player or try and flank player
|
||||
#define SCF_NO_FORCE 0x00200000 //Not succeptible to force powers
|
||||
#define SCF_NO_FALLTODEATH 0x00400000 //NPC will not scream and tumble and fall to hit death over large drops
|
||||
#define SCF_NO_ACROBATICS 0x00800000 //Jedi won't jump, roll or cartwheel
|
||||
#define SCF_USE_SUBTITLES 0x01000000 //Regardless of subtitle setting, this NPC will display subtitles when it speaks lines
|
||||
#define SCF_NO_ALERT_TALK 0x02000000 //Will not say alert sounds, but still can be woken up by alerts
|
||||
|
||||
//#ifdef __DEBUG
|
||||
|
||||
//Debug flag definitions
|
||||
|
||||
#define AID_IDLE 0x00000000 //Nothing is happening
|
||||
#define AID_ACQUIRED 0x00000001 //A target has been found
|
||||
#define AID_LOST 0x00000002 //Alert, but no target is in sight
|
||||
#define AID_CONFUSED 0x00000004 //Is unable to come up with a course of action
|
||||
#define AID_LOSTPATH 0x00000008 //Cannot make a valid movement due to lack of connections
|
||||
|
||||
//#endif //__DEBUG
|
||||
|
||||
//extern qboolean showWaypoints;
|
||||
|
||||
typedef enum {VIS_UNKNOWN, VIS_NOT, VIS_PVS, VIS_360, VIS_FOV, VIS_SHOOT} visibility_t;
|
||||
typedef enum {SPOT_ORIGIN, SPOT_CHEST, SPOT_HEAD, SPOT_HEAD_LEAN, SPOT_WEAPON, SPOT_LEGS, SPOT_GROUND} spot_t;
|
||||
|
||||
typedef enum //# lookMode_e
|
||||
{
|
||||
LM_ENT = 0,
|
||||
LM_INTEREST
|
||||
} lookMode_t;
|
||||
|
||||
typedef enum //# jumpState_e
|
||||
{
|
||||
JS_WAITING = 0,
|
||||
JS_FACING,
|
||||
JS_CROUCHING,
|
||||
JS_JUMPING,
|
||||
JS_LANDING
|
||||
} jumpState_t;
|
||||
|
||||
typedef struct gNPCstats_e
|
||||
{//Stats, loaded in, and can be set by scripts
|
||||
//AI
|
||||
int aggression; // "
|
||||
int aim; // "
|
||||
float earshot; // "
|
||||
int evasion; // "
|
||||
int hfov; // horizontal field of view
|
||||
int intelligence; // "
|
||||
int move; // "
|
||||
int reactions; // 1-5, higher is better
|
||||
float shootDistance; //Maximum range- overrides range set for weapon if nonzero
|
||||
int vfov; // vertical field of view
|
||||
float vigilance; // "
|
||||
float visrange; // "
|
||||
//Movement
|
||||
int runSpeed;
|
||||
int walkSpeed;
|
||||
float yawSpeed; // 1 - whatever, default is 50
|
||||
int health;
|
||||
int acceleration;
|
||||
} gNPCstats_t;
|
||||
|
||||
// NOTE!!! If you add any ptr fields into this structure could you please tell me so I can update the load/save code?
|
||||
// so far the only things I've got to cope with are a bunch of gentity_t*'s, but tell me if any more get added -slc
|
||||
//
|
||||
|
||||
#define MAX_ENEMY_POS_LAG 2400
|
||||
#define ENEMY_POS_LAG_INTERVAL 100
|
||||
#define ENEMY_POS_LAG_STEPS (MAX_ENEMY_POS_LAG/ENEMY_POS_LAG_INTERVAL)
|
||||
typedef struct
|
||||
{
|
||||
//FIXME: Put in playerInfo or something
|
||||
int timeOfDeath; //FIXME do we really need both of these
|
||||
gentity_t *touchedByPlayer;
|
||||
|
||||
visibility_t enemyLastVisibility;
|
||||
|
||||
int aimTime;
|
||||
float desiredYaw;
|
||||
float desiredPitch;
|
||||
float lockedDesiredYaw;
|
||||
float lockedDesiredPitch;
|
||||
gentity_t *aimingBeam; // debugging aid
|
||||
|
||||
vec3_t enemyLastSeenLocation;
|
||||
int enemyLastSeenTime;
|
||||
vec3_t enemyLastHeardLocation;
|
||||
int enemyLastHeardTime;
|
||||
int lastAlertID; //unique ID
|
||||
|
||||
int eFlags;
|
||||
int aiFlags;
|
||||
|
||||
int currentAmmo; // this sucks, need to find a better way
|
||||
int shotTime;
|
||||
int burstCount;
|
||||
int burstMin;
|
||||
int burstMean;
|
||||
int burstMax;
|
||||
int burstSpacing;
|
||||
int attackHold;
|
||||
int attackHoldTime;
|
||||
vec3_t shootAngles; //Angles to where bot is shooting - fixme: make he torso turn to reflect these
|
||||
|
||||
//extra character info
|
||||
rank_t rank; //for pips
|
||||
|
||||
//Behavior state info
|
||||
bState_t behaviorState; //determines what actions he should be doing
|
||||
bState_t defaultBehavior;//State bot will default to if none other set
|
||||
bState_t tempBehavior;//While valid, overrides other behavior
|
||||
|
||||
qboolean ignorePain; //only play pain scripts when take pain
|
||||
|
||||
int duckDebounceTime;//Keeps them ducked for a certain time
|
||||
int walkDebounceTime;
|
||||
int enemyCheckDebounceTime;
|
||||
int investigateDebounceTime;
|
||||
int investigateCount;
|
||||
vec3_t investigateGoal;
|
||||
int investigateSoundDebounceTime;
|
||||
int greetingDebounceTime;//when we can greet someone next
|
||||
gentity_t *eventOwner;
|
||||
|
||||
//bState-specific fields
|
||||
gentity_t *coverTarg;
|
||||
jumpState_t jumpState;
|
||||
float followDist;
|
||||
|
||||
// goal, navigation & pathfinding
|
||||
gentity_t *tempGoal; // used for locational goals (player's last seen/heard position)
|
||||
gentity_t *goalEntity;
|
||||
gentity_t *lastGoalEntity;
|
||||
gentity_t *eventualGoal;
|
||||
gentity_t *captureGoal; //Where we should try to capture
|
||||
gentity_t *defendEnt; //Who we're trying to protect
|
||||
gentity_t *greetEnt; //Who we're greeting
|
||||
int goalTime; //FIXME: This is never actually used
|
||||
qboolean straightToGoal; //move straight at navgoals
|
||||
float distToGoal;
|
||||
int navTime;
|
||||
int blockingEntNum;
|
||||
int blockedSpeechDebounceTime;
|
||||
int lastSideStepSide;
|
||||
int sideStepHoldTime;
|
||||
int homeWp;
|
||||
AIGroupInfo_t *group;
|
||||
|
||||
vec3_t lastPathAngles; //So we know which way to face generally when we stop
|
||||
|
||||
//stats
|
||||
gNPCstats_t stats;
|
||||
int aimErrorDebounceTime;
|
||||
float lastAimErrorYaw;
|
||||
float lastAimErrorPitch;
|
||||
vec3_t aimOfs;
|
||||
int currentAim;
|
||||
int currentAggression;
|
||||
|
||||
//scriptflags
|
||||
int scriptFlags;//in b_local.h
|
||||
|
||||
//moveInfo
|
||||
int desiredSpeed;
|
||||
int currentSpeed;
|
||||
char last_forwardmove;
|
||||
char last_rightmove;
|
||||
vec3_t lastClearOrigin;
|
||||
int consecutiveBlockedMoves;
|
||||
int blockedDebounceTime;
|
||||
int shoveCount;
|
||||
vec3_t blockedDest;
|
||||
|
||||
//
|
||||
int combatPoint;//NPCs in bState BS_COMBAT_POINT will find their closest empty combat_point
|
||||
int lastFailedCombatPoint;//NPCs in bState BS_COMBAT_POINT will find their closest empty combat_point
|
||||
int movementSpeech; //what to say when you first successfully move
|
||||
float movementSpeechChance;//how likely you are to say it
|
||||
|
||||
//Testing physics at 20fps
|
||||
int nextBStateThink;
|
||||
usercmd_t last_ucmd;
|
||||
|
||||
//
|
||||
//JWEIER ADDITIONS START
|
||||
|
||||
qboolean combatMove;
|
||||
int goalRadius;
|
||||
|
||||
//FIXME: These may be redundant
|
||||
|
||||
/*
|
||||
int weaponTime; //Time until refire is valid
|
||||
int jumpTime;
|
||||
*/
|
||||
int pauseTime; //Time to stand still
|
||||
int standTime;
|
||||
|
||||
int localState; //Tracking information local to entity
|
||||
int squadState; //Tracking information for team level interaction
|
||||
|
||||
//JWEIER ADDITIONS END
|
||||
//
|
||||
|
||||
int confusionTime; //Doesn't respond to alerts or pick up enemies (unless shot) until this time is up
|
||||
int charmedTime; //charmed to enemy team
|
||||
int controlledTime; //controlled by player
|
||||
int surrenderTime; //Hands up
|
||||
|
||||
//Lagging enemy position - FIXME: seems awful wasteful...
|
||||
vec3_t enemyLaggedPos[ENEMY_POS_LAG_STEPS];
|
||||
|
||||
gentity_t *watchTarget; //for BS_CINEMATIC, keeps facing this ent
|
||||
|
||||
int ffireCount; //sigh... you'd think I'd be able to find a way to do this without having to use 3 int fields, but...
|
||||
int ffireDebounce;
|
||||
int ffireFadeDebounce;
|
||||
} gNPC_t;
|
||||
|
||||
void G_SquadPathsInit(void);
|
||||
void NPC_InitGame( void );
|
||||
void G_LoadBoltOns( void );
|
||||
void Svcmd_NPC_f( void );
|
||||
void NAV_DebugShowWaypoints (void);
|
||||
void NAV_DebugShowBoxes (void);
|
||||
void NAV_DebugShowSquadPaths (void);
|
||||
/*
|
||||
void Bot_InitGame( void );
|
||||
void Bot_InitPreSpawn( void );
|
||||
void Bot_InitPostSpawn( void );
|
||||
void Bot_Shutdown( void );
|
||||
void Bot_Think( gentity_t *ent, int msec );
|
||||
void Bot_Connect( gentity_t *bot, char *botName );
|
||||
void Bot_Begin( gentity_t *bot );
|
||||
void Bot_Disconnect( gentity_t *bot );
|
||||
void Svcmd_Bot_f( void );
|
||||
void Nav_ItemSpawn( gentity_t *ent, int remaining );
|
||||
*/
|
||||
|
||||
//
|
||||
// This section should be moved to QFILES.H
|
||||
//
|
||||
/*
|
||||
#define NAVFILE_ID (('I')+('N'<<8)+('A'<<16)+('V'<<24))
|
||||
#define NAVFILE_VERSION 6
|
||||
|
||||
typedef struct {
|
||||
unsigned id;
|
||||
unsigned version;
|
||||
unsigned checksum;
|
||||
unsigned surfaceCount;
|
||||
unsigned edgeCount;
|
||||
} navheader_t;
|
||||
|
||||
|
||||
#define MAX_SURFACES 4096
|
||||
|
||||
#define NSF_PUSH 0x00000001
|
||||
#define NSF_WATERLEVEL1 0x00000002
|
||||
#define NSF_WATERLEVEL2 0x00000004
|
||||
#define NSF_WATER_NOAIR 0x00000008
|
||||
#define NSF_DUCK 0x00000010
|
||||
#define NSF_PAIN 0x00000020
|
||||
#define NSF_TELEPORTER 0x00000040
|
||||
#define NSF_PLATHIGH 0x00000080
|
||||
#define NSF_PLATLOW 0x00000100
|
||||
#define NSF_DOOR_FLOOR 0x00000200
|
||||
#define NSF_DOOR_SHOOT 0x00000400
|
||||
#define NSF_DOOR_BUTTON 0x00000800
|
||||
#define NSF_BUTTON 0x00001000
|
||||
|
||||
typedef struct {
|
||||
vec3_t origin;
|
||||
vec2_t absmin;
|
||||
vec2_t absmax;
|
||||
int parm;
|
||||
unsigned flags;
|
||||
unsigned edgeCount;
|
||||
unsigned edgeIndex;
|
||||
} nsurface_t;
|
||||
|
||||
|
||||
#define NEF_DUCK 0x00000001
|
||||
#define NEF_JUMP 0x00000002
|
||||
#define NEF_HOLD 0x00000004
|
||||
#define NEF_WALK 0x00000008
|
||||
#define NEF_RUN 0x00000010
|
||||
#define NEF_NOAIRMOVE 0x00000020
|
||||
#define NEF_LEFTGROUND 0x00000040
|
||||
#define NEF_PLAT 0x00000080
|
||||
#define NEF_FALL1 0x00000100
|
||||
#define NEF_FALL2 0x00000200
|
||||
#define NEF_DOOR_SHOOT 0x00000400
|
||||
#define NEF_DOOR_BUTTON 0x00000800
|
||||
#define NEF_BUTTON 0x00001000
|
||||
|
||||
typedef struct {
|
||||
vec3_t origin;
|
||||
vec2_t absmin; // region within this surface that is the portal to the other surface
|
||||
vec2_t absmax;
|
||||
int surfaceNum;
|
||||
unsigned flags; // jump, prerequisite button, will take falling damage, etc...
|
||||
float cost;
|
||||
int dirIndex;
|
||||
vec3_t endSpot;
|
||||
int parm;
|
||||
} nedge_t;
|
||||
*/
|
||||
#endif
|
||||
205
codemp/game/be_aas.h
Normal file
205
codemp/game/be_aas.h
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_aas.h
|
||||
*
|
||||
* desc: Area Awareness System, stuff exported to the AI
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_aas.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:43:59 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef MAX_STRINGFIELD
|
||||
#define MAX_STRINGFIELD 80
|
||||
#endif
|
||||
|
||||
//travel flags
|
||||
#define TFL_INVALID 0x00000001 //traveling temporary not possible
|
||||
#define TFL_WALK 0x00000002 //walking
|
||||
#define TFL_CROUCH 0x00000004 //crouching
|
||||
#define TFL_BARRIERJUMP 0x00000008 //jumping onto a barrier
|
||||
#define TFL_JUMP 0x00000010 //jumping
|
||||
#define TFL_LADDER 0x00000020 //climbing a ladder
|
||||
#define TFL_WALKOFFLEDGE 0x00000080 //walking of a ledge
|
||||
#define TFL_SWIM 0x00000100 //swimming
|
||||
#define TFL_WATERJUMP 0x00000200 //jumping out of the water
|
||||
#define TFL_TELEPORT 0x00000400 //teleporting
|
||||
#define TFL_ELEVATOR 0x00000800 //elevator
|
||||
#define TFL_ROCKETJUMP 0x00001000 //rocket jumping
|
||||
#define TFL_BFGJUMP 0x00002000 //bfg jumping
|
||||
#define TFL_GRAPPLEHOOK 0x00004000 //grappling hook
|
||||
#define TFL_DOUBLEJUMP 0x00008000 //double jump
|
||||
#define TFL_RAMPJUMP 0x00010000 //ramp jump
|
||||
#define TFL_STRAFEJUMP 0x00020000 //strafe jump
|
||||
#define TFL_JUMPPAD 0x00040000 //jump pad
|
||||
#define TFL_AIR 0x00080000 //travel through air
|
||||
#define TFL_WATER 0x00100000 //travel through water
|
||||
#define TFL_SLIME 0x00200000 //travel through slime
|
||||
#define TFL_LAVA 0x00400000 //travel through lava
|
||||
#define TFL_DONOTENTER 0x00800000 //travel through donotenter area
|
||||
#define TFL_FUNCBOB 0x01000000 //func bobbing
|
||||
#define TFL_FLIGHT 0x02000000 //flight
|
||||
#define TFL_BRIDGE 0x04000000 //move over a bridge
|
||||
//
|
||||
#define TFL_NOTTEAM1 0x08000000 //not team 1
|
||||
#define TFL_NOTTEAM2 0x10000000 //not team 2
|
||||
|
||||
//default travel flags
|
||||
#define TFL_DEFAULT TFL_WALK|TFL_CROUCH|TFL_BARRIERJUMP|\
|
||||
TFL_JUMP|TFL_LADDER|\
|
||||
TFL_WALKOFFLEDGE|TFL_SWIM|TFL_WATERJUMP|\
|
||||
TFL_TELEPORT|TFL_ELEVATOR|\
|
||||
TFL_AIR|TFL_WATER|TFL_JUMPPAD|TFL_FUNCBOB
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SOLID_NOT, // no interaction with other objects
|
||||
SOLID_TRIGGER, // only touch when inside, after moving
|
||||
SOLID_BBOX, // touch on edge
|
||||
SOLID_BSP // bsp clip, touch on edge
|
||||
} solid_t;
|
||||
|
||||
//a trace is returned when a box is swept through the AAS world
|
||||
typedef struct aas_trace_s
|
||||
{
|
||||
qboolean startsolid; // if true, the initial point was in a solid area
|
||||
float fraction; // time completed, 1.0 = didn't hit anything
|
||||
vec3_t endpos; // final position
|
||||
int ent; // entity blocking the trace
|
||||
int lastarea; // last area the trace was in (zero if none)
|
||||
int area; // area blocking the trace (zero if none)
|
||||
int planenum; // number of the plane that was hit
|
||||
} aas_trace_t;
|
||||
|
||||
/* Defined in botlib.h
|
||||
|
||||
//bsp_trace_t hit surface
|
||||
typedef struct bsp_surface_s
|
||||
{
|
||||
char name[16];
|
||||
int flags;
|
||||
int value;
|
||||
} bsp_surface_t;
|
||||
|
||||
//a trace is returned when a box is swept through the BSP world
|
||||
typedef struct bsp_trace_s
|
||||
{
|
||||
qboolean allsolid; // if true, plane is not valid
|
||||
qboolean startsolid; // if true, the initial point was in a solid area
|
||||
float fraction; // time completed, 1.0 = didn't hit anything
|
||||
vec3_t endpos; // final position
|
||||
cplane_t plane; // surface normal at impact
|
||||
float exp_dist; // expanded plane distance
|
||||
int sidenum; // number of the brush side hit
|
||||
bsp_surface_t surface; // hit surface
|
||||
int contents; // contents on other side of surface hit
|
||||
int ent; // number of entity hit
|
||||
} bsp_trace_t;
|
||||
//
|
||||
*/
|
||||
|
||||
//entity info
|
||||
typedef struct aas_entityinfo_s
|
||||
{
|
||||
int valid; // true if updated this frame
|
||||
int type; // entity type
|
||||
int flags; // entity flags
|
||||
float ltime; // local time
|
||||
float update_time; // time between last and current update
|
||||
int number; // number of the entity
|
||||
vec3_t origin; // origin of the entity
|
||||
vec3_t angles; // angles of the model
|
||||
vec3_t old_origin; // for lerping
|
||||
vec3_t lastvisorigin; // last visible origin
|
||||
vec3_t mins; // bounding box minimums
|
||||
vec3_t maxs; // bounding box maximums
|
||||
int groundent; // ground entity
|
||||
int solid; // solid type
|
||||
int modelindex; // model used
|
||||
int modelindex2; // weapons, CTF flags, etc
|
||||
int frame; // model frame number
|
||||
int event; // impulse events -- muzzle flashes, footsteps, etc
|
||||
int eventParm; // even parameter
|
||||
int powerups; // bit flags
|
||||
int weapon; // determines weapon and flash model, etc
|
||||
int legsAnim; // current legs anim
|
||||
int torsoAnim; // current torso anim
|
||||
} aas_entityinfo_t;
|
||||
|
||||
// area info
|
||||
typedef struct aas_areainfo_s
|
||||
{
|
||||
int contents;
|
||||
int flags;
|
||||
int presencetype;
|
||||
int cluster;
|
||||
vec3_t mins;
|
||||
vec3_t maxs;
|
||||
vec3_t center;
|
||||
} aas_areainfo_t;
|
||||
|
||||
// client movement prediction stop events, stop as soon as:
|
||||
#define SE_NONE 0
|
||||
#define SE_HITGROUND 1 // the ground is hit
|
||||
#define SE_LEAVEGROUND 2 // there's no ground
|
||||
#define SE_ENTERWATER 4 // water is entered
|
||||
#define SE_ENTERSLIME 8 // slime is entered
|
||||
#define SE_ENTERLAVA 16 // lava is entered
|
||||
#define SE_HITGROUNDDAMAGE 32 // the ground is hit with damage
|
||||
#define SE_GAP 64 // there's a gap
|
||||
#define SE_TOUCHJUMPPAD 128 // touching a jump pad area
|
||||
#define SE_TOUCHTELEPORTER 256 // touching teleporter
|
||||
#define SE_ENTERAREA 512 // the given stoparea is entered
|
||||
#define SE_HITGROUNDAREA 1024 // a ground face in the area is hit
|
||||
#define SE_HITBOUNDINGBOX 2048 // hit the specified bounding box
|
||||
#define SE_TOUCHCLUSTERPORTAL 4096 // touching a cluster portal
|
||||
|
||||
typedef struct aas_clientmove_s
|
||||
{
|
||||
vec3_t endpos; //position at the end of movement prediction
|
||||
int endarea; //area at end of movement prediction
|
||||
vec3_t velocity; //velocity at the end of movement prediction
|
||||
aas_trace_t trace; //last trace
|
||||
int presencetype; //presence type at end of movement prediction
|
||||
int stopevent; //event that made the prediction stop
|
||||
int endcontents; //contents at the end of movement prediction
|
||||
float time; //time predicted ahead
|
||||
int frames; //number of frames predicted ahead
|
||||
} aas_clientmove_t;
|
||||
|
||||
// alternate route goals
|
||||
#define ALTROUTEGOAL_ALL 1
|
||||
#define ALTROUTEGOAL_CLUSTERPORTALS 2
|
||||
#define ALTROUTEGOAL_VIEWPORTALS 4
|
||||
|
||||
typedef struct aas_altroutegoal_s
|
||||
{
|
||||
vec3_t origin;
|
||||
int areanum;
|
||||
unsigned short starttraveltime;
|
||||
unsigned short goaltraveltime;
|
||||
unsigned short extratraveltime;
|
||||
} aas_altroutegoal_t;
|
||||
|
||||
// route prediction stop events
|
||||
#define RSE_NONE 0
|
||||
#define RSE_NOROUTE 1 //no route to goal
|
||||
#define RSE_USETRAVELTYPE 2 //stop as soon as on of the given travel types is used
|
||||
#define RSE_ENTERCONTENTS 4 //stop when entering the given contents
|
||||
#define RSE_ENTERAREA 8 //stop when entering the given area
|
||||
|
||||
typedef struct aas_predictroute_s
|
||||
{
|
||||
vec3_t endpos; //position at the end of movement prediction
|
||||
int endarea; //area at end of movement prediction
|
||||
int stopevent; //event that made the prediction stop
|
||||
int endcontents; //contents at the end of movement prediction
|
||||
int endtravelflags; //end travel flags
|
||||
int numareas; //number of areas predicted ahead
|
||||
int time; //time predicted ahead (in hundreth of a sec)
|
||||
} aas_predictroute_t;
|
||||
32
codemp/game/be_ai_char.h
Normal file
32
codemp/game/be_ai_char.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_ai_char.h
|
||||
*
|
||||
* desc: bot characters
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_char.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:43:59 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//loads a bot character from a file
|
||||
int BotLoadCharacter(char *charfile, float skill);
|
||||
//frees a bot character
|
||||
void BotFreeCharacter(int character);
|
||||
//returns a float characteristic
|
||||
float Characteristic_Float(int character, int index);
|
||||
//returns a bounded float characteristic
|
||||
float Characteristic_BFloat(int character, int index, float min, float max);
|
||||
//returns an integer characteristic
|
||||
int Characteristic_Integer(int character, int index);
|
||||
//returns a bounded integer characteristic
|
||||
int Characteristic_BInteger(int character, int index, int min, int max);
|
||||
//returns a string characteristic
|
||||
void Characteristic_String(int character, int index, char *buf, int size);
|
||||
//free cached bot characters
|
||||
void BotShutdownCharacters(void);
|
||||
97
codemp/game/be_ai_chat.h
Normal file
97
codemp/game/be_ai_chat.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
/*****************************************************************************
|
||||
* name: be_ai_chat.h
|
||||
*
|
||||
* desc: char AI
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_chat.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:43:59 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define MAX_MESSAGE_SIZE 256
|
||||
#define MAX_CHATTYPE_NAME 32
|
||||
#define MAX_MATCHVARIABLES 8
|
||||
|
||||
#define CHAT_GENDERLESS 0
|
||||
#define CHAT_GENDERFEMALE 1
|
||||
#define CHAT_GENDERMALE 2
|
||||
|
||||
#define CHAT_ALL 0
|
||||
#define CHAT_TEAM 1
|
||||
#define CHAT_TELL 2
|
||||
|
||||
//a console message
|
||||
typedef struct bot_consolemessage_s
|
||||
{
|
||||
int handle;
|
||||
float time; //message time
|
||||
int type; //message type
|
||||
char message[MAX_MESSAGE_SIZE]; //message
|
||||
struct bot_consolemessage_s *prev, *next; //prev and next in list
|
||||
} bot_consolemessage_t;
|
||||
|
||||
//match variable
|
||||
typedef struct bot_matchvariable_s
|
||||
{
|
||||
char offset;
|
||||
int length;
|
||||
} bot_matchvariable_t;
|
||||
//returned to AI when a match is found
|
||||
typedef struct bot_match_s
|
||||
{
|
||||
char string[MAX_MESSAGE_SIZE];
|
||||
int type;
|
||||
int subtype;
|
||||
bot_matchvariable_t variables[MAX_MATCHVARIABLES];
|
||||
} bot_match_t;
|
||||
|
||||
//setup the chat AI
|
||||
int BotSetupChatAI(void);
|
||||
//shutdown the chat AI
|
||||
void BotShutdownChatAI(void);
|
||||
//returns the handle to a newly allocated chat state
|
||||
int BotAllocChatState(void);
|
||||
//frees the chatstate
|
||||
void BotFreeChatState(int handle);
|
||||
//adds a console message to the chat state
|
||||
void BotQueueConsoleMessage(int chatstate, int type, char *message);
|
||||
//removes the console message from the chat state
|
||||
void BotRemoveConsoleMessage(int chatstate, int handle);
|
||||
//returns the next console message from the state
|
||||
int BotNextConsoleMessage(int chatstate, bot_consolemessage_t *cm);
|
||||
//returns the number of console messages currently stored in the state
|
||||
int BotNumConsoleMessages(int chatstate);
|
||||
//selects a chat message of the given type
|
||||
void BotInitialChat(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
|
||||
//returns the number of initial chat messages of the given type
|
||||
int BotNumInitialChats(int chatstate, char *type);
|
||||
//find and select a reply for the given message
|
||||
int BotReplyChat(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
|
||||
//returns the length of the currently selected chat message
|
||||
int BotChatLength(int chatstate);
|
||||
//enters the selected chat message
|
||||
void BotEnterChat(int chatstate, int clientto, int sendto);
|
||||
//get the chat message ready to be output
|
||||
void BotGetChatMessage(int chatstate, char *buf, int size);
|
||||
//checks if the first string contains the second one, returns index into first string or -1 if not found
|
||||
int StringContains(char *str1, char *str2, int casesensitive);
|
||||
//finds a match for the given string using the match templates
|
||||
int BotFindMatch(char *str, bot_match_t *match, unsigned long int context);
|
||||
//returns a variable from a match
|
||||
void BotMatchVariable(bot_match_t *match, int variable, char *buf, int size);
|
||||
//unify all the white spaces in the string
|
||||
void UnifyWhiteSpaces(char *string);
|
||||
//replace all the context related synonyms in the string
|
||||
void BotReplaceSynonyms(char *string, unsigned long int context);
|
||||
//loads a chat file for the chat state
|
||||
int BotLoadChatFile(int chatstate, char *chatfile, char *chatname);
|
||||
//store the gender of the bot in the chat state
|
||||
void BotSetChatGender(int chatstate, int gender);
|
||||
//store the bot name in the chat state
|
||||
void BotSetChatName(int chatstate, char *name, int client);
|
||||
|
||||
17
codemp/game/be_ai_gen.h
Normal file
17
codemp/game/be_ai_gen.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_ai_gen.h
|
||||
*
|
||||
* desc: genetic selection
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_gen.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:44:00 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
int GeneticParentsAndChildSelection(int numranks, float *ranks, int *parent1, int *parent2, int *child);
|
||||
102
codemp/game/be_ai_goal.h
Normal file
102
codemp/game/be_ai_goal.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
/*****************************************************************************
|
||||
* name: be_ai_goal.h
|
||||
*
|
||||
* desc: goal AI
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_goal.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:44:00 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define MAX_AVOIDGOALS 256
|
||||
#define MAX_GOALSTACK 8
|
||||
|
||||
#define GFL_NONE 0
|
||||
#define GFL_ITEM 1
|
||||
#define GFL_ROAM 2
|
||||
#define GFL_DROPPED 4
|
||||
|
||||
//a bot goal
|
||||
typedef struct bot_goal_s
|
||||
{
|
||||
vec3_t origin; //origin of the goal
|
||||
int areanum; //area number of the goal
|
||||
vec3_t mins, maxs; //mins and maxs of the goal
|
||||
int entitynum; //number of the goal entity
|
||||
int number; //goal number
|
||||
int flags; //goal flags
|
||||
int iteminfo; //item information
|
||||
} bot_goal_t;
|
||||
|
||||
//reset the whole goal state, but keep the item weights
|
||||
void BotResetGoalState(int goalstate);
|
||||
//reset avoid goals
|
||||
void BotResetAvoidGoals(int goalstate);
|
||||
//remove the goal with the given number from the avoid goals
|
||||
void BotRemoveFromAvoidGoals(int goalstate, int number);
|
||||
//push a goal onto the goal stack
|
||||
void BotPushGoal(int goalstate, bot_goal_t *goal);
|
||||
//pop a goal from the goal stack
|
||||
void BotPopGoal(int goalstate);
|
||||
//empty the bot's goal stack
|
||||
void BotEmptyGoalStack(int goalstate);
|
||||
//dump the avoid goals
|
||||
void BotDumpAvoidGoals(int goalstate);
|
||||
//dump the goal stack
|
||||
void BotDumpGoalStack(int goalstate);
|
||||
//get the name name of the goal with the given number
|
||||
void BotGoalName(int number, char *name, int size);
|
||||
//get the top goal from the stack
|
||||
int BotGetTopGoal(int goalstate, bot_goal_t *goal);
|
||||
//get the second goal on the stack
|
||||
int BotGetSecondGoal(int goalstate, bot_goal_t *goal);
|
||||
//choose the best long term goal item for the bot
|
||||
int BotChooseLTGItem(int goalstate, vec3_t origin, int *inventory, int travelflags);
|
||||
//choose the best nearby goal item for the bot
|
||||
//the item may not be further away from the current bot position than maxtime
|
||||
//also the travel time from the nearby goal towards the long term goal may not
|
||||
//be larger than the travel time towards the long term goal from the current bot position
|
||||
int BotChooseNBGItem(int goalstate, vec3_t origin, int *inventory, int travelflags,
|
||||
bot_goal_t *ltg, float maxtime);
|
||||
//returns true if the bot touches the goal
|
||||
int BotTouchingGoal(vec3_t origin, bot_goal_t *goal);
|
||||
//returns true if the goal should be visible but isn't
|
||||
int BotItemGoalInVisButNotVisible(int viewer, vec3_t eye, vec3_t viewangles, bot_goal_t *goal);
|
||||
//search for a goal for the given classname, the index can be used
|
||||
//as a start point for the search when multiple goals are available with that same classname
|
||||
int BotGetLevelItemGoal(int index, char *classname, bot_goal_t *goal);
|
||||
//get the next camp spot in the map
|
||||
int BotGetNextCampSpotGoal(int num, bot_goal_t *goal);
|
||||
//get the map location with the given name
|
||||
int BotGetMapLocationGoal(char *name, bot_goal_t *goal);
|
||||
//returns the avoid goal time
|
||||
float BotAvoidGoalTime(int goalstate, int number);
|
||||
//set the avoid goal time
|
||||
void BotSetAvoidGoalTime(int goalstate, int number, float avoidtime);
|
||||
//initializes the items in the level
|
||||
void BotInitLevelItems(void);
|
||||
//regularly update dynamic entity items (dropped weapons, flags etc.)
|
||||
void BotUpdateEntityItems(void);
|
||||
//interbreed the goal fuzzy logic
|
||||
void BotInterbreedGoalFuzzyLogic(int parent1, int parent2, int child);
|
||||
//save the goal fuzzy logic to disk
|
||||
void BotSaveGoalFuzzyLogic(int goalstate, char *filename);
|
||||
//mutate the goal fuzzy logic
|
||||
void BotMutateGoalFuzzyLogic(int goalstate, float range);
|
||||
//loads item weights for the bot
|
||||
int BotLoadItemWeights(int goalstate, char *filename);
|
||||
//frees the item weights of the bot
|
||||
void BotFreeItemWeights(int goalstate);
|
||||
//returns the handle of a newly allocated goal state
|
||||
int BotAllocGoalState(int client);
|
||||
//free the given goal state
|
||||
void BotFreeGoalState(int handle);
|
||||
//setup the goal AI
|
||||
int BotSetupGoalAI(void);
|
||||
//shut down the goal AI
|
||||
void BotShutdownGoalAI(void);
|
||||
126
codemp/game/be_ai_move.h
Normal file
126
codemp/game/be_ai_move.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_ai_move.h
|
||||
*
|
||||
* desc: movement AI
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_move.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:44:00 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//movement types
|
||||
#define MOVE_WALK 1
|
||||
#define MOVE_CROUCH 2
|
||||
#define MOVE_JUMP 4
|
||||
#define MOVE_GRAPPLE 8
|
||||
#define MOVE_ROCKETJUMP 16
|
||||
#define MOVE_BFGJUMP 32
|
||||
//move flags
|
||||
#define MFL_BARRIERJUMP 1 //bot is performing a barrier jump
|
||||
#define MFL_ONGROUND 2 //bot is in the ground
|
||||
#define MFL_SWIMMING 4 //bot is swimming
|
||||
#define MFL_AGAINSTLADDER 8 //bot is against a ladder
|
||||
#define MFL_WATERJUMP 16 //bot is waterjumping
|
||||
#define MFL_TELEPORTED 32 //bot is being teleported
|
||||
#define MFL_GRAPPLEPULL 64 //bot is being pulled by the grapple
|
||||
#define MFL_ACTIVEGRAPPLE 128 //bot is using the grapple hook
|
||||
#define MFL_GRAPPLERESET 256 //bot has reset the grapple
|
||||
#define MFL_WALK 512 //bot should walk slowly
|
||||
// move result flags
|
||||
#define MOVERESULT_MOVEMENTVIEW 1 //bot uses view for movement
|
||||
#define MOVERESULT_SWIMVIEW 2 //bot uses view for swimming
|
||||
#define MOVERESULT_WAITING 4 //bot is waiting for something
|
||||
#define MOVERESULT_MOVEMENTVIEWSET 8 //bot has set the view in movement code
|
||||
#define MOVERESULT_MOVEMENTWEAPON 16 //bot uses weapon for movement
|
||||
#define MOVERESULT_ONTOPOFOBSTACLE 32 //bot is ontop of obstacle
|
||||
#define MOVERESULT_ONTOPOF_FUNCBOB 64 //bot is ontop of a func_bobbing
|
||||
#define MOVERESULT_ONTOPOF_ELEVATOR 128 //bot is ontop of an elevator (func_plat)
|
||||
#define MOVERESULT_BLOCKEDBYAVOIDSPOT 256 //bot is blocked by an avoid spot
|
||||
//
|
||||
#define MAX_AVOIDREACH 1
|
||||
#define MAX_AVOIDSPOTS 32
|
||||
// avoid spot types
|
||||
#define AVOID_CLEAR 0 //clear all avoid spots
|
||||
#define AVOID_ALWAYS 1 //avoid always
|
||||
#define AVOID_DONTBLOCK 2 //never totally block
|
||||
// restult types
|
||||
#define RESULTTYPE_ELEVATORUP 1 //elevator is up
|
||||
#define RESULTTYPE_WAITFORFUNCBOBBING 2 //waiting for func bobbing to arrive
|
||||
#define RESULTTYPE_BADGRAPPLEPATH 4 //grapple path is obstructed
|
||||
#define RESULTTYPE_INSOLIDAREA 8 //stuck in solid area, this is bad
|
||||
|
||||
//structure used to initialize the movement state
|
||||
//the or_moveflags MFL_ONGROUND, MFL_TELEPORTED and MFL_WATERJUMP come from the playerstate
|
||||
typedef struct bot_initmove_s
|
||||
{
|
||||
vec3_t origin; //origin of the bot
|
||||
vec3_t velocity; //velocity of the bot
|
||||
vec3_t viewoffset; //view offset
|
||||
int entitynum; //entity number of the bot
|
||||
int client; //client number of the bot
|
||||
float thinktime; //time the bot thinks
|
||||
int presencetype; //presencetype of the bot
|
||||
vec3_t viewangles; //view angles of the bot
|
||||
int or_moveflags; //values ored to the movement flags
|
||||
} bot_initmove_t;
|
||||
|
||||
//NOTE: the ideal_viewangles are only valid if MFL_MOVEMENTVIEW is set
|
||||
typedef struct bot_moveresult_s
|
||||
{
|
||||
int failure; //true if movement failed all together
|
||||
int type; //failure or blocked type
|
||||
int blocked; //true if blocked by an entity
|
||||
int blockentity; //entity blocking the bot
|
||||
int traveltype; //last executed travel type
|
||||
int flags; //result flags
|
||||
int weapon; //weapon used for movement
|
||||
vec3_t movedir; //movement direction
|
||||
vec3_t ideal_viewangles; //ideal viewangles for the movement
|
||||
} bot_moveresult_t;
|
||||
|
||||
// bk001204: from code/botlib/be_ai_move.c
|
||||
// TTimo 04/12/2001 was moved here to avoid dup defines
|
||||
typedef struct bot_avoidspot_s
|
||||
{
|
||||
vec3_t origin;
|
||||
float radius;
|
||||
int type;
|
||||
} bot_avoidspot_t;
|
||||
|
||||
//resets the whole move state
|
||||
void BotResetMoveState(int movestate);
|
||||
//moves the bot to the given goal
|
||||
void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags);
|
||||
//moves the bot in the specified direction using the specified type of movement
|
||||
int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type);
|
||||
//reset avoid reachability
|
||||
void BotResetAvoidReach(int movestate);
|
||||
//resets the last avoid reachability
|
||||
void BotResetLastAvoidReach(int movestate);
|
||||
//returns a reachability area if the origin is in one
|
||||
int BotReachabilityArea(vec3_t origin, int client);
|
||||
//view target based on movement
|
||||
int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target);
|
||||
//predict the position of a player based on movement towards a goal
|
||||
int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target);
|
||||
//returns the handle of a newly allocated movestate
|
||||
int BotAllocMoveState(void);
|
||||
//frees the movestate with the given handle
|
||||
void BotFreeMoveState(int handle);
|
||||
//initialize movement state before performing any movement
|
||||
void BotInitMoveState(int handle, bot_initmove_t *initmove);
|
||||
//add a spot to avoid (if type == AVOID_CLEAR all spots are removed)
|
||||
void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type);
|
||||
//must be called every map change
|
||||
void BotSetBrushModelTypes(void);
|
||||
//setup movement AI
|
||||
int BotSetupMoveAI(void);
|
||||
//shutdown movement AI
|
||||
void BotShutdownMoveAI(void);
|
||||
|
||||
88
codemp/game/be_ai_weap.h
Normal file
88
codemp/game/be_ai_weap.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_ai_weap.h
|
||||
*
|
||||
* desc: weapon AI
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ai_weap.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:44:00 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//projectile flags
|
||||
#define PFL_WINDOWDAMAGE 1 //projectile damages through window
|
||||
#define PFL_RETURN 2 //set when projectile returns to owner
|
||||
//weapon flags
|
||||
#define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event
|
||||
//damage types
|
||||
#define DAMAGETYPE_IMPACT 1 //damage on impact
|
||||
#define DAMAGETYPE_RADIAL 2 //radial damage
|
||||
#define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile
|
||||
|
||||
typedef struct projectileinfo_s
|
||||
{
|
||||
char name[MAX_STRINGFIELD];
|
||||
char model[MAX_STRINGFIELD];
|
||||
int flags;
|
||||
float gravity;
|
||||
int damage;
|
||||
float radius;
|
||||
int visdamage;
|
||||
int damagetype;
|
||||
int healthinc;
|
||||
float push;
|
||||
float detonation;
|
||||
float bounce;
|
||||
float bouncefric;
|
||||
float bouncestop;
|
||||
} projectileinfo_t;
|
||||
|
||||
typedef struct weaponinfo_s
|
||||
{
|
||||
int valid; //true if the weapon info is valid
|
||||
int number; //number of the weapon
|
||||
char name[MAX_STRINGFIELD];
|
||||
char model[MAX_STRINGFIELD];
|
||||
int level;
|
||||
int weaponindex;
|
||||
int flags;
|
||||
char projectile[MAX_STRINGFIELD];
|
||||
int numprojectiles;
|
||||
float hspread;
|
||||
float vspread;
|
||||
float speed;
|
||||
float acceleration;
|
||||
vec3_t recoil;
|
||||
vec3_t offset;
|
||||
vec3_t angleoffset;
|
||||
float extrazvelocity;
|
||||
int ammoamount;
|
||||
int ammoindex;
|
||||
float activate;
|
||||
float reload;
|
||||
float spinup;
|
||||
float spindown;
|
||||
projectileinfo_t proj; //pointer to the used projectile
|
||||
} weaponinfo_t;
|
||||
|
||||
//setup the weapon AI
|
||||
int BotSetupWeaponAI(void);
|
||||
//shut down the weapon AI
|
||||
void BotShutdownWeaponAI(void);
|
||||
//returns the best weapon to fight with
|
||||
int BotChooseBestFightWeapon(int weaponstate, int *inventory);
|
||||
//returns the information of the current weapon
|
||||
void BotGetWeaponInfo(int weaponstate, int weapon, weaponinfo_t *weaponinfo);
|
||||
//loads the weapon weights
|
||||
int BotLoadWeaponWeights(int weaponstate, char *filename);
|
||||
//returns a handle to a newly allocated weapon state
|
||||
int BotAllocWeaponState(void);
|
||||
//frees the weapon state
|
||||
void BotFreeWeaponState(int weaponstate);
|
||||
//resets the whole weapon state
|
||||
void BotResetWeaponState(int weaponstate);
|
||||
52
codemp/game/be_ea.h
Normal file
52
codemp/game/be_ea.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
/*****************************************************************************
|
||||
* name: be_ea.h
|
||||
*
|
||||
* desc: elementary actions
|
||||
*
|
||||
* $Archive: /source/code/botlib/be_ea.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.4 $
|
||||
* $Modtime: 10/05/99 3:32p $
|
||||
* $Date: 2003/03/15 23:44:00 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
//ClientCommand elementary actions
|
||||
void EA_Say(int client, char *str);
|
||||
void EA_SayTeam(int client, char *str);
|
||||
void EA_Command(int client, char *command );
|
||||
|
||||
void EA_Action(int client, int action);
|
||||
void EA_Crouch(int client);
|
||||
void EA_Walk(int client);
|
||||
void EA_MoveUp(int client);
|
||||
void EA_MoveDown(int client);
|
||||
void EA_MoveForward(int client);
|
||||
void EA_MoveBack(int client);
|
||||
void EA_MoveLeft(int client);
|
||||
void EA_MoveRight(int client);
|
||||
void EA_Attack(int client);
|
||||
void EA_Alt_Attack(int client);
|
||||
void EA_ForcePower(int client);
|
||||
void EA_Respawn(int client);
|
||||
void EA_Talk(int client);
|
||||
void EA_Gesture(int client);
|
||||
void EA_Use(int client);
|
||||
|
||||
//regular elementary actions
|
||||
void EA_SelectWeapon(int client, int weapon);
|
||||
void EA_Jump(int client);
|
||||
void EA_DelayedJump(int client);
|
||||
void EA_Move(int client, vec3_t dir, float speed);
|
||||
void EA_View(int client, vec3_t viewangles);
|
||||
|
||||
//send regular input to the server
|
||||
void EA_EndRegular(int client, float thinktime);
|
||||
void EA_GetInput(int client, float thinktime, bot_input_t *input);
|
||||
void EA_ResetInput(int client);
|
||||
//setup and shutdown routines
|
||||
int EA_Setup(void);
|
||||
void EA_Shutdown(void);
|
||||
124
codemp/game/bg_g2_utils.c
Normal file
124
codemp/game/bg_g2_utils.c
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
// bg_g2_utils.c -- both games misc functions, all completely stateless
|
||||
|
||||
#include "q_shared.h"
|
||||
#include "bg_public.h"
|
||||
#include "bg_strap.h"
|
||||
|
||||
#ifdef QAGAME
|
||||
#include "g_local.h"
|
||||
#endif
|
||||
|
||||
#ifdef UI_EXPORTS
|
||||
#include "../ui/ui_local.h"
|
||||
#endif
|
||||
|
||||
#ifndef UI_EXPORTS
|
||||
#ifndef QAGAME
|
||||
#include "../cgame/cg_local.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
|
||||
void BG_AttachToRancor( void *ghoul2,
|
||||
float rancYaw,
|
||||
vec3_t rancOrigin,
|
||||
int time,
|
||||
qhandle_t *modelList,
|
||||
vec3_t modelScale,
|
||||
qboolean inMouth,
|
||||
vec3_t out_origin,
|
||||
vec3_t out_angles,
|
||||
vec3_t out_axis[3] )
|
||||
{
|
||||
mdxaBone_t boltMatrix;
|
||||
int boltIndex;
|
||||
vec3_t rancAngles;
|
||||
vec3_t temp_angles;
|
||||
// Getting the bolt here
|
||||
if ( inMouth )
|
||||
{//in mouth
|
||||
boltIndex = trap_G2API_AddBolt(ghoul2, 0, "jaw_bone");
|
||||
}
|
||||
else
|
||||
{//in right hand
|
||||
boltIndex = trap_G2API_AddBolt(ghoul2, 0, "*r_hand");
|
||||
}
|
||||
VectorSet( rancAngles, 0, rancYaw, 0 );
|
||||
trap_G2API_GetBoltMatrix( ghoul2, 0, boltIndex,
|
||||
&boltMatrix, rancAngles, rancOrigin, time,
|
||||
modelList, modelScale );
|
||||
// Storing ent position, bolt position, and bolt axis
|
||||
if ( out_origin )
|
||||
{
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, out_origin );
|
||||
}
|
||||
if ( out_axis )
|
||||
{
|
||||
if ( inMouth )
|
||||
{//in mouth
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, out_axis[0] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, out_axis[1] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, out_axis[2] );
|
||||
}
|
||||
else
|
||||
{//in hand
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, out_axis[0] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_X, out_axis[1] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, out_axis[2] );
|
||||
}
|
||||
//FIXME: this is messing up our axis and turning us inside-out?
|
||||
if ( out_angles )
|
||||
{
|
||||
vectoangles( out_axis[0], out_angles );
|
||||
vectoangles( out_axis[2], temp_angles );
|
||||
out_angles[ROLL] = -temp_angles[PITCH];
|
||||
}
|
||||
}
|
||||
else if ( out_angles )
|
||||
{
|
||||
vec3_t temp_axis[3];
|
||||
if ( inMouth )
|
||||
{//in mouth
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, temp_axis[0] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, temp_axis[2] );
|
||||
}
|
||||
else
|
||||
{//in hand
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, temp_axis[0] );
|
||||
BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_Z, temp_axis[2] );
|
||||
}
|
||||
//FIXME: this is messing up our axis and turning us inside-out?
|
||||
vectoangles( temp_axis[0], out_angles );
|
||||
vectoangles( temp_axis[2], temp_angles );
|
||||
out_angles[ROLL] = -temp_angles[PITCH];
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_VARIANTS 8
|
||||
qboolean BG_GetRootSurfNameWithVariant( void *ghoul2, const char *rootSurfName, char *returnSurfName, int returnSize )
|
||||
{
|
||||
if ( !ghoul2 || !trap_G2API_GetSurfaceRenderStatus( ghoul2, 0, rootSurfName ) )
|
||||
{//see if the basic name without variants is on
|
||||
Q_strncpyz( returnSurfName, rootSurfName, returnSize );
|
||||
return qtrue;
|
||||
}
|
||||
else
|
||||
{//check variants
|
||||
int i;
|
||||
for ( i = 0; i < MAX_VARIANTS; i++ )
|
||||
{
|
||||
Com_sprintf( returnSurfName, returnSize, "%s%c", rootSurfName, 'a'+i );
|
||||
if ( !trap_G2API_GetSurfaceRenderStatus( ghoul2, 0, returnSurfName ) )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Q_strncpyz( returnSurfName, rootSurfName, returnSize );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
#include "../namespace_end.h"
|
||||
1318
codemp/game/bg_lib.c
Normal file
1318
codemp/game/bg_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
70
codemp/game/bg_lib.h
Normal file
70
codemp/game/bg_lib.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// bg_lib.h -- standard C library replacement routines used by code
|
||||
// compiled for the virtual machine
|
||||
|
||||
// This file is NOT included on native builds
|
||||
|
||||
typedef int size_t;
|
||||
|
||||
typedef char * va_list;
|
||||
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
|
||||
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
|
||||
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
|
||||
#define va_end(ap) ( ap = (va_list)0 )
|
||||
|
||||
#define CHAR_BIT 8 /* number of bits in a char */
|
||||
#define SCHAR_MIN (-128) /* minimum signed char value */
|
||||
#define SCHAR_MAX 127 /* maximum signed char value */
|
||||
#define UCHAR_MAX 0xff /* maximum unsigned char value */
|
||||
|
||||
#define SHRT_MIN (-32768) /* minimum (signed) short value */
|
||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||
#define USHRT_MAX 0xffff /* maximum unsigned short value */
|
||||
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
|
||||
#define INT_MAX 2147483647 /* maximum (signed) int value */
|
||||
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
|
||||
#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */
|
||||
#define LONG_MAX 2147483647L /* maximum (signed) long value */
|
||||
#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */
|
||||
|
||||
// Misc functions
|
||||
typedef int cmp_t(const void *, const void *);
|
||||
void qsort(void *a, size_t n, size_t es, cmp_t *cmp);
|
||||
void srand( unsigned seed );
|
||||
int rand( void );
|
||||
|
||||
// String functions
|
||||
size_t strlen( const char *string );
|
||||
char *strcat( char *strDestination, const char *strSource );
|
||||
char *strcpy( char *strDestination, const char *strSource );
|
||||
int strcmp( const char *string1, const char *string2 );
|
||||
char *strchr( const char *string, int c );
|
||||
char *strstr( const char *string, const char *strCharSet );
|
||||
char *strncpy( char *strDest, const char *strSource, size_t count );
|
||||
int tolower( int c );
|
||||
int toupper( int c );
|
||||
|
||||
double atof( const char *string );
|
||||
double _atof( const char **stringPtr );
|
||||
int atoi( const char *string );
|
||||
int _atoi( const char **stringPtr );
|
||||
|
||||
int vsprintf( char *buffer, const char *fmt, va_list argptr );
|
||||
int sscanf( const char *buffer, const char *fmt, ... );
|
||||
|
||||
// Memory functions
|
||||
void *memmove( void *dest, const void *src, size_t count );
|
||||
void *memset( void *dest, int c, size_t count );
|
||||
void *memcpy( void *dest, const void *src, size_t count );
|
||||
|
||||
// Math functions
|
||||
double ceil( double x );
|
||||
double floor( double x );
|
||||
double sqrt( double x );
|
||||
double sin( double x );
|
||||
double cos( double x );
|
||||
double atan2( double y, double x );
|
||||
double tan( double x );
|
||||
int abs( int n );
|
||||
double fabs( double x );
|
||||
double acos( double x );
|
||||
|
||||
109
codemp/game/bg_local.h
Normal file
109
codemp/game/bg_local.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
// bg_local.h -- local definitions for the bg (both games) files
|
||||
|
||||
#define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes
|
||||
|
||||
#define TIMER_LAND 130
|
||||
#define TIMER_GESTURE (34*66+50)
|
||||
|
||||
#define OVERCLIP 1.001f
|
||||
|
||||
// all of the locals will be zeroed before each
|
||||
// pmove, just to make damn sure we don't have
|
||||
// any differences when running on client or server
|
||||
typedef struct
|
||||
{
|
||||
vec3_t forward, right, up;
|
||||
float frametime;
|
||||
|
||||
int msec;
|
||||
|
||||
qboolean walking;
|
||||
qboolean groundPlane;
|
||||
trace_t groundTrace;
|
||||
|
||||
float impactSpeed;
|
||||
|
||||
vec3_t previous_origin;
|
||||
vec3_t previous_velocity;
|
||||
int previous_waterlevel;
|
||||
} pml_t;
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
|
||||
extern pml_t pml;
|
||||
|
||||
// movement parameters
|
||||
extern float pm_stopspeed;
|
||||
extern float pm_duckScale;
|
||||
extern float pm_swimScale;
|
||||
extern float pm_wadeScale;
|
||||
|
||||
extern float pm_accelerate;
|
||||
extern float pm_airaccelerate;
|
||||
extern float pm_wateraccelerate;
|
||||
extern float pm_flyaccelerate;
|
||||
|
||||
extern float pm_friction;
|
||||
extern float pm_waterfriction;
|
||||
extern float pm_flightfriction;
|
||||
|
||||
extern int c_pmove;
|
||||
|
||||
extern int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS];
|
||||
|
||||
// Had to add these here because there was no file access within the BG right now.
|
||||
int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode );
|
||||
void trap_FS_Read( void *buffer, int len, fileHandle_t f );
|
||||
void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
|
||||
void trap_FS_FCloseFile( fileHandle_t f );
|
||||
#include "../namespace_end.h"
|
||||
|
||||
//PM anim utility functions:
|
||||
#include "../namespace_begin.h"
|
||||
qboolean PM_SaberInParry( int move );
|
||||
qboolean PM_SaberInKnockaway( int move );
|
||||
qboolean PM_SaberInReflect( int move );
|
||||
qboolean PM_SaberInStart( int move );
|
||||
qboolean PM_InSaberAnim( int anim );
|
||||
qboolean PM_InKnockDown( playerState_t *ps );
|
||||
qboolean PM_PainAnim( int anim );
|
||||
qboolean PM_JumpingAnim( int anim );
|
||||
qboolean PM_LandingAnim( int anim );
|
||||
qboolean PM_SpinningAnim( int anim );
|
||||
qboolean PM_InOnGroundAnim ( int anim );
|
||||
qboolean PM_InRollComplete( playerState_t *ps, int anim );
|
||||
|
||||
int PM_AnimLength( int index, animNumber_t anim );
|
||||
|
||||
int PM_GetSaberStance(void);
|
||||
float PM_GroundDistance(void);
|
||||
qboolean PM_SomeoneInFront(trace_t *tr);
|
||||
saberMoveName_t PM_SaberFlipOverAttackMove(void);
|
||||
saberMoveName_t PM_SaberJumpAttackMove( void );
|
||||
|
||||
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce );
|
||||
void PM_AddTouchEnt( int entityNum );
|
||||
void PM_AddEvent( int newEvent );
|
||||
|
||||
qboolean PM_SlideMove( qboolean gravity );
|
||||
void PM_StepSlideMove( qboolean gravity );
|
||||
|
||||
void PM_StartTorsoAnim( int anim );
|
||||
void PM_ContinueLegsAnim( int anim );
|
||||
void PM_ForceLegsAnim( int anim );
|
||||
|
||||
void PM_BeginWeaponChange( int weapon );
|
||||
void PM_FinishWeaponChange( void );
|
||||
|
||||
void PM_SetAnim(int setAnimParts,int anim,int setAnimFlags, int blendTime);
|
||||
|
||||
void PM_WeaponLightsaber(void);
|
||||
void PM_SetSaberMove(short newMove);
|
||||
|
||||
void PM_SetForceJumpZStart(float value);
|
||||
|
||||
void BG_CycleInven(playerState_t *ps, int direction);
|
||||
|
||||
#include "../namespace_end.h"
|
||||
3415
codemp/game/bg_misc.c
Normal file
3415
codemp/game/bg_misc.c
Normal file
File diff suppressed because it is too large
Load Diff
2967
codemp/game/bg_panimate.c
Normal file
2967
codemp/game/bg_panimate.c
Normal file
File diff suppressed because it is too large
Load Diff
10880
codemp/game/bg_pmove.c
Normal file
10880
codemp/game/bg_pmove.c
Normal file
File diff suppressed because it is too large
Load Diff
1673
codemp/game/bg_public.h
Normal file
1673
codemp/game/bg_public.h
Normal file
File diff suppressed because it is too large
Load Diff
3690
codemp/game/bg_saber.c
Normal file
3690
codemp/game/bg_saber.c
Normal file
File diff suppressed because it is too large
Load Diff
1501
codemp/game/bg_saberLoad.c
Normal file
1501
codemp/game/bg_saberLoad.c
Normal file
File diff suppressed because it is too large
Load Diff
1510
codemp/game/bg_saga.c
Normal file
1510
codemp/game/bg_saga.c
Normal file
File diff suppressed because it is too large
Load Diff
116
codemp/game/bg_saga.h
Normal file
116
codemp/game/bg_saga.h
Normal file
@@ -0,0 +1,116 @@
|
||||
#define MAX_SIEGE_INFO_SIZE 16384
|
||||
|
||||
#define SIEGETEAM_TEAM1 1 //e.g. TEAM_RED
|
||||
#define SIEGETEAM_TEAM2 2 //e.g. TEAM_BLUE
|
||||
|
||||
#define SIEGE_POINTS_OBJECTIVECOMPLETED 20
|
||||
#define SIEGE_POINTS_FINALOBJECTIVECOMPLETED 30
|
||||
#define SIEGE_POINTS_TEAMWONROUND 10
|
||||
|
||||
#define SIEGE_ROUND_BEGIN_TIME 5000 //delay 5 secs after players are in game.
|
||||
|
||||
//#define MAX_SIEGE_CLASSES 128 //up to 128 classes
|
||||
#define MAX_SIEGE_CLASSES 64 //up to 128 classes
|
||||
#define MAX_SIEGE_CLASSES_PER_TEAM 16
|
||||
|
||||
#define MAX_SIEGE_TEAMS 16 //up to 16 diffent teams
|
||||
|
||||
#define MAX_EXDATA_ENTS_TO_SEND MAX_CLIENTS //max number of extended data for ents to send
|
||||
|
||||
// The basic siege player classes
|
||||
typedef enum
|
||||
{
|
||||
SPC_INFANTRY = 0,
|
||||
SPC_VANGUARD,
|
||||
SPC_SUPPORT,
|
||||
SPC_JEDI,
|
||||
SPC_DEMOLITIONIST,
|
||||
SPC_HEAVY_WEAPONS,
|
||||
SPC_MAX
|
||||
} siegePlayerClassFlags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CFL_MORESABERDMG = 0,
|
||||
CFL_STRONGAGAINSTPHYSICAL,
|
||||
CFL_FASTFORCEREGEN,
|
||||
CFL_STATVIEWER,
|
||||
CFL_HEAVYMELEE,
|
||||
CFL_SINGLE_ROCKET,//has only 1 rocket to use with rocketlauncher
|
||||
CFL_CUSTOMSKEL, //class uses a custom skeleton, be sure to load on server etc
|
||||
CFL_EXTRA_AMMO
|
||||
} siegeClassFlags_t;
|
||||
|
||||
|
||||
#ifdef _XBOX
|
||||
#define SIEGE_CLASS_DESC_LEN 512
|
||||
#else
|
||||
#define SIEGE_CLASS_DESC_LEN 4096
|
||||
#endif
|
||||
typedef struct
|
||||
{
|
||||
char desc[SIEGE_CLASS_DESC_LEN];
|
||||
} siegeClassDesc_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[512];
|
||||
char forcedModel[256];
|
||||
char forcedSkin[256];
|
||||
char saber1[64];
|
||||
char saber2[64];
|
||||
int saberStance;
|
||||
int weapons;
|
||||
int forcePowerLevels[NUM_FORCE_POWERS];
|
||||
int classflags;
|
||||
int maxhealth;
|
||||
int starthealth;
|
||||
int maxarmor;
|
||||
int startarmor;
|
||||
float speed;
|
||||
qboolean hasForcedSaberColor;
|
||||
int forcedSaberColor;
|
||||
qboolean hasForcedSaber2Color;
|
||||
int forcedSaber2Color;
|
||||
int invenItems;
|
||||
int powerups;
|
||||
int uiPortraitShader;
|
||||
char uiPortrait[256];
|
||||
int classShader;
|
||||
short playerClass; // SPC_INFANTRY . ..
|
||||
} siegeClass_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[512];
|
||||
siegeClass_t *classes[MAX_SIEGE_CLASSES_PER_TEAM];
|
||||
int numClasses;
|
||||
int friendlyShader;
|
||||
} siegeTeam_t;
|
||||
|
||||
extern siegeClass_t bgSiegeClasses[MAX_SIEGE_CLASSES];
|
||||
extern int bgNumSiegeClasses;
|
||||
|
||||
extern siegeTeam_t bgSiegeTeams[MAX_SIEGE_TEAMS];
|
||||
extern int bgNumSiegeTeams;
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
|
||||
int BG_SiegeGetValueGroup(char *buf, char *group, char *outbuf);
|
||||
int BG_SiegeGetPairedValue(char *buf, char *key, char *outbuf);
|
||||
void BG_SiegeStripTabs(char *buf);
|
||||
|
||||
void BG_SiegeLoadClasses(siegeClassDesc_t *descBuffer);
|
||||
void BG_SiegeLoadTeams(void);
|
||||
|
||||
siegeTeam_t *BG_SiegeFindThemeForTeam(int team);
|
||||
void BG_PrecacheSabersForSiegeTeam(int team);
|
||||
siegeClass_t *BG_SiegeFindClassByName(const char *classname);
|
||||
qboolean BG_SiegeCheckClassLegality(int team, char *classname);
|
||||
void BG_SiegeSetTeamTheme(int team, char *themeName);
|
||||
int BG_SiegeFindClassIndexByName(const char *classname);
|
||||
|
||||
extern char siege_info[MAX_SIEGE_INFO_SIZE];
|
||||
extern int siege_valid;
|
||||
|
||||
#include "../namespace_end.h"
|
||||
1059
codemp/game/bg_slidemove.c
Normal file
1059
codemp/game/bg_slidemove.c
Normal file
File diff suppressed because it is too large
Load Diff
38
codemp/game/bg_strap.h
Normal file
38
codemp/game/bg_strap.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//rww - shared trap call system
|
||||
#include "q_shared.h"
|
||||
#include "bg_public.h"
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale);
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix_NoReconstruct(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale);
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix_NoRecNoRot(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale);
|
||||
|
||||
qboolean strap_G2API_SetBoneAngles(void *ghoul2, int modelIndex, const char *boneName, const vec3_t angles, const int flags,
|
||||
const int up, const int right, const int forward, qhandle_t *modelList,
|
||||
int blendTime , int currentTime );
|
||||
|
||||
qboolean strap_G2API_SetBoneAnim(void *ghoul2, const int modelIndex, const char *boneName, const int startFrame, const int endFrame,
|
||||
const int flags, const float animSpeed, const int currentTime, const float setFrame , const int blendTime );
|
||||
|
||||
qboolean strap_G2API_GetBoneAnim(void *ghoul2, const char *boneName, const int currentTime, float *currentFrame,
|
||||
int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList, const int modelIndex);
|
||||
|
||||
void strap_G2API_SetRagDoll(void *ghoul2, sharedRagDollParams_t *params);
|
||||
|
||||
void strap_G2API_AnimateG2Models(void *ghoul2, int time, sharedRagDollUpdateParams_t *params);
|
||||
|
||||
qboolean strap_G2API_SetBoneIKState(void *ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params);
|
||||
|
||||
qboolean strap_G2API_IKMove(void *ghoul2, int time, sharedIKMoveParams_t *params);
|
||||
|
||||
void strap_TrueMalloc(void **ptr, int size);
|
||||
|
||||
void strap_TrueFree(void **ptr);
|
||||
|
||||
#include "../namespace_end.h"
|
||||
1678
codemp/game/bg_vehicleLoad.c
Normal file
1678
codemp/game/bg_vehicleLoad.c
Normal file
File diff suppressed because it is too large
Load Diff
628
codemp/game/bg_vehicles.h
Normal file
628
codemp/game/bg_vehicles.h
Normal file
@@ -0,0 +1,628 @@
|
||||
#ifndef __BG_VEHICLES_H
|
||||
#define __BG_VEHICLES_H
|
||||
|
||||
#include "q_shared.h"
|
||||
|
||||
typedef struct Vehicle_s Vehicle_t;
|
||||
typedef struct bgEntity_s bgEntity_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VH_NONE = 0, //0 just in case anyone confuses VH_NONE and VEHICLE_NONE below
|
||||
VH_WALKER, //something you ride inside of, it walks like you, like an AT-ST
|
||||
VH_FIGHTER, //something you fly inside of, like an X-Wing or TIE fighter
|
||||
VH_SPEEDER, //something you ride on that hovers, like a speeder or swoop
|
||||
VH_ANIMAL, //animal you ride on top of that walks, like a tauntaun
|
||||
VH_FLIER, //animal you ride on top of that flies, like a giant mynoc?
|
||||
VH_NUM_VEHICLES
|
||||
} vehicleType_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
WPOSE_NONE = 0,
|
||||
WPOSE_BLASTER,
|
||||
WPOSE_SABERLEFT,
|
||||
WPOSE_SABERRIGHT,
|
||||
} EWeaponPose;
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern stringID_table_t VehicleTable[VH_NUM_VEHICLES+1];
|
||||
#include "../namespace_end.h"
|
||||
|
||||
//===========================================================================================================
|
||||
//START VEHICLE WEAPONS
|
||||
//===========================================================================================================
|
||||
typedef struct
|
||||
{
|
||||
//*** IMPORTANT!!! *** the number of variables in the vehWeaponStats_t struct (including all elements of arrays) must be reflected by NUM_VWEAP_PARMS!!!
|
||||
//*** IMPORTANT!!! *** vWeapFields table correponds to this structure!
|
||||
char *name;
|
||||
qboolean bIsProjectile; //traceline or entity?
|
||||
qboolean bHasGravity; //if a projectile, drops
|
||||
qboolean bIonWeapon;//disables ship shields and sends them out of control
|
||||
qboolean bSaberBlockable;//lightsabers can deflect this projectile
|
||||
int iMuzzleFX; //index of Muzzle Effect
|
||||
int iModel; //handle to the model used by this projectile
|
||||
int iShotFX; //index of Shot Effect
|
||||
int iImpactFX; //index of Impact Effect
|
||||
int iG2MarkShaderHandle; //index of shader to use for G2 marks made on other models when hit by this projectile
|
||||
float fG2MarkSize;//size (diameter) of the ghoul2 mark
|
||||
int iLoopSound; //index of loopSound
|
||||
float fSpeed; //speed of projectile/range of traceline
|
||||
float fHoming; //0.0 = not homing, 0.5 = half vel to targ, half cur vel, 1.0 = all vel to targ
|
||||
int iLockOnTime; //0 = no lock time needed, else # of ms needed to lock on
|
||||
int iDamage; //damage done when traceline or projectile directly hits target
|
||||
int iSplashDamage;//damage done to ents in splashRadius of end of traceline or projectile origin on impact
|
||||
float fSplashRadius;//radius that ent must be in to take splashDamage (linear fall-off)
|
||||
int iAmmoPerShot; //how much "ammo" each shot takes
|
||||
int iHealth; //if non-zero, projectile can be shot, takes this much damage before being destroyed
|
||||
float fWidth; //width of traceline or bounding box of projecile (non-rotating!)
|
||||
float fHeight; //height of traceline or bounding box of projecile (non-rotating!)
|
||||
int iLifeTime; //removes itself after this amount of time
|
||||
qboolean bExplodeOnExpire; //when iLifeTime is up, explodes rather than simply removing itself
|
||||
} vehWeaponInfo_t;
|
||||
//NOTE: this MUST stay up to date with the number of variables in the vehFields table!!!
|
||||
#define NUM_VWEAP_PARMS 24
|
||||
|
||||
#define VWFOFS(x) ((int)&(((vehWeaponInfo_t *)0)->x))
|
||||
|
||||
#define MAX_VEH_WEAPONS 16 //sigh... no more than 16 different vehicle weapons
|
||||
#define VEH_WEAPON_BASE 0
|
||||
#define VEH_WEAPON_NONE -1
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern vehWeaponInfo_t g_vehWeaponInfo[MAX_VEH_WEAPONS];
|
||||
extern int numVehicleWeapons;
|
||||
#include "../namespace_end.h"
|
||||
|
||||
//===========================================================================================================
|
||||
//END VEHICLE WEAPONS
|
||||
//===========================================================================================================
|
||||
|
||||
#define MAX_VEHICLE_MUZZLES 12
|
||||
#define MAX_VEHICLE_EXHAUSTS 12
|
||||
#define MAX_VEHICLE_WEAPONS 2
|
||||
#define MAX_VEHICLE_TURRETS 2
|
||||
#define MAX_VEHICLE_TURRET_MUZZLES 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int iWeapon; //what vehWeaponInfo index to use
|
||||
int iDelay; //delay between turret muzzle shots
|
||||
int iAmmoMax; //how much ammo it has
|
||||
int iAmmoRechargeMS; //how many MS between every point of recharged ammo
|
||||
char *yawBone; //bone on ship that this turret uses to yaw
|
||||
char *pitchBone; //bone on ship that this turret uses to pitch
|
||||
int yawAxis; //axis on yawBone to which we should to apply the yaw angles
|
||||
int pitchAxis; //axis on pitchBone to which we should to apply the pitch angles
|
||||
float yawClampLeft; //how far the turret is allowed to turn left
|
||||
float yawClampRight; //how far the turret is allowed to turn right
|
||||
float pitchClampUp; //how far the turret is allowed to title up
|
||||
float pitchClampDown; //how far the turret is allowed to tilt down
|
||||
int iMuzzle[MAX_VEHICLE_TURRET_MUZZLES];//iMuzzle-1 = index of ship's muzzle to fire this turret's 1st and 2nd shots from
|
||||
char *gunnerViewTag;//Where to put the view origin of the gunner (name)
|
||||
float fTurnSpeed; //how quickly the turret can turn
|
||||
qboolean bAI; //whether or not the turret auto-targets enemies when it's not manned
|
||||
qboolean bAILead;//whether
|
||||
float fAIRange; //how far away the AI will look for enemies
|
||||
int passengerNum;//which passenger, if any, has control of this turret (overrides AI)
|
||||
} turretStats_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//*** IMPORTANT!!! *** See note at top of next structure!!! ***
|
||||
// Weapon stuff.
|
||||
int ID;//index into the weapon data
|
||||
// The delay between shots for each weapon.
|
||||
int delay;
|
||||
// Whether or not all the muzzles for each weapon can be linked together (linked delay = weapon delay * number of muzzles linked!)
|
||||
int linkable;
|
||||
// Whether or not to auto-aim the projectiles/tracelines at the thing under the crosshair when we fire
|
||||
qboolean aimCorrect;
|
||||
//maximum ammo
|
||||
int ammoMax;
|
||||
//ammo recharge rate - milliseconds per unit (minimum of 100, which is 10 ammo per second)
|
||||
int ammoRechargeMS;
|
||||
//sound to play when out of ammo (plays default "no ammo" sound if none specified)
|
||||
int soundNoAmmo;
|
||||
} vehWeaponStats_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//*** IMPORTANT!!! *** vehFields table correponds to this structure!
|
||||
char *name; //unique name of the vehicle
|
||||
|
||||
//general data
|
||||
vehicleType_t type; //what kind of vehicle
|
||||
int numHands; //if 2 hands, no weapons, if 1 hand, can use 1-handed weapons, if 0 hands, can use 2-handed weapons
|
||||
float lookPitch; //How far you can look up and down off the forward of the vehicle
|
||||
float lookYaw; //How far you can look left and right off the forward of the vehicle
|
||||
float length; //how long it is - used for body length traces when turning/moving?
|
||||
float width; //how wide it is - used for body length traces when turning/moving?
|
||||
float height; //how tall it is - used for body length traces when turning/moving?
|
||||
vec3_t centerOfGravity;//offset from origin: {forward, right, up} as a modifier on that dimension (-1.0f is all the way back, 1.0f is all the way forward)
|
||||
|
||||
//speed stats
|
||||
float speedMax; //top speed
|
||||
float turboSpeed; //turbo speed
|
||||
float speedMin; //if < 0, can go in reverse
|
||||
float speedIdle; //what speed it drifts to when no accel/decel input is given
|
||||
float accelIdle; //if speedIdle > 0, how quickly it goes up to that speed
|
||||
float acceleration; //when pressing on accelerator
|
||||
float decelIdle; //when giving no input, how quickly it drops to speedIdle
|
||||
float throttleSticks; //if true, speed stays at whatever you accel/decel to, unless you turbo or brake
|
||||
float strafePerc; //multiplier on current speed for strafing. If 1.0f, you can strafe at the same speed as you're going forward, 0.5 is half, 0 is no strafing
|
||||
|
||||
//handling stats
|
||||
float bankingSpeed; //how quickly it pitches and rolls (not under player control)
|
||||
float rollLimit; //how far it can roll to either side
|
||||
float pitchLimit; //how far it can roll forward or backward
|
||||
float braking; //when pressing on decelerator
|
||||
float mouseYaw; // The mouse yaw override.
|
||||
float mousePitch; // The mouse pitch override.
|
||||
float turningSpeed; //how quickly you can turn
|
||||
qboolean turnWhenStopped;//whether or not you can turn when not moving
|
||||
float traction; //how much your command input affects velocity
|
||||
float friction; //how much velocity is cut on its own
|
||||
float maxSlope; //the max slope that it can go up with control
|
||||
qboolean speedDependantTurning;//vehicle turns faster the faster it's going
|
||||
|
||||
//durability stats
|
||||
int mass; //for momentum and impact force (player mass is 10)
|
||||
int armor; //total points of damage it can take
|
||||
int shields; //energy shield damage points
|
||||
int shieldRechargeMS;//energy shield milliseconds per point recharged
|
||||
float toughness; //modifies incoming damage, 1.0 is normal, 0.5 is half, etc. Simulates being made of tougher materials/construction
|
||||
int malfunctionArmorLevel;//when armor drops to or below this point, start malfunctioning
|
||||
int surfDestruction; //can parts of this thing be torn off on impact? -rww
|
||||
|
||||
//individual "area" health -rww
|
||||
int health_front;
|
||||
int health_back;
|
||||
int health_right;
|
||||
int health_left;
|
||||
|
||||
//visuals & sounds
|
||||
char *model; //what model to use - if make it an NPC's primary model, don't need this?
|
||||
char *skin; //what skin to use - if make it an NPC's primary model, don't need this?
|
||||
int g2radius; //render radius for the ghoul2 model
|
||||
int riderAnim; //what animation the rider uses
|
||||
int radarIconHandle;//what icon to show on radar in MP
|
||||
int dmgIndicFrameHandle;//what image to use for the frame of the damage indicator
|
||||
int dmgIndicShieldHandle;//what image to use for the shield of the damage indicator
|
||||
int dmgIndicBackgroundHandle;//what image to use for the background of the damage indicator
|
||||
int iconFrontHandle;//what image to use for the front of the ship on the damage indicator
|
||||
int iconBackHandle; //what image to use for the back of the ship on the damage indicator
|
||||
int iconRightHandle;//what image to use for the right of the ship on the damage indicator
|
||||
int iconLeftHandle; //what image to use for the left of the ship on the damage indicator
|
||||
int crosshairShaderHandle;//what image to use for the left of the ship on the damage indicator
|
||||
int shieldShaderHandle;//What shader to use when drawing the shield shell
|
||||
char *droidNPC; //NPC to attach to *droidunit tag (if it exists in the model)
|
||||
|
||||
int soundOn; //sound to play when get on it
|
||||
int soundTakeOff; //sound to play when ship takes off
|
||||
int soundEngineStart;//sound to play when ship's thrusters first activate
|
||||
int soundLoop; //sound to loop while riding it
|
||||
int soundSpin; //sound to loop while spiraling out of control
|
||||
int soundTurbo; //sound to play when turbo/afterburner kicks in
|
||||
int soundHyper; //sound to play when ship lands
|
||||
int soundLand; //sound to play when ship lands
|
||||
int soundOff; //sound to play when get off
|
||||
int soundFlyBy; //sound to play when they buzz you
|
||||
int soundFlyBy2; //alternate sound to play when they buzz you
|
||||
int soundShift1; //sound to play when accelerating
|
||||
int soundShift2; //sound to play when accelerating
|
||||
int soundShift3; //sound to play when decelerating
|
||||
int soundShift4; //sound to play when decelerating
|
||||
|
||||
int iExhaustFX; //exhaust effect, played from "*exhaust" bolt(s)
|
||||
int iTurboFX; //turbo exhaust effect, played from "*exhaust" bolt(s) when ship is in "turbo" mode
|
||||
int iTurboStartFX; //turbo begin effect, played from "*exhaust" bolts when "turbo" mode begins
|
||||
int iTrailFX; //trail effect, played from "*trail" bolt(s)
|
||||
int iImpactFX; //impact effect, for when it bumps into something
|
||||
int iExplodeFX; //explosion effect, for when it blows up (should have the sound built into explosion effect)
|
||||
int iWakeFX; //effect it makes when going across water
|
||||
int iDmgFX; //effect to play on damage from a weapon or something
|
||||
int iInjureFX;
|
||||
int iNoseFX; //effect for nose piece flying away when blown off
|
||||
int iLWingFX; //effect for left wing piece flying away when blown off
|
||||
int iRWingFX; //effect for right wing piece flying away when blown off
|
||||
|
||||
//Weapon stats
|
||||
vehWeaponStats_t weapon[MAX_VEHICLE_WEAPONS];
|
||||
|
||||
// Which weapon a muzzle fires (has to match one of the weapons this vehicle has). So 1 would be weapon 1,
|
||||
// 2 would be weapon 2 and so on.
|
||||
int weapMuzzle[MAX_VEHICLE_MUZZLES];
|
||||
|
||||
//turrets (if any) on the vehicle
|
||||
turretStats_t turret[MAX_VEHICLE_TURRETS];
|
||||
|
||||
// The max height before this ship (?) starts (auto)landing.
|
||||
float landingHeight;
|
||||
|
||||
//other misc stats
|
||||
int gravity; //normal is 800
|
||||
float hoverHeight; //if 0, it's a ground vehicle
|
||||
float hoverStrength; //how hard it pushes off ground when less than hover height... causes "bounce", like shocks
|
||||
qboolean waterProof; //can drive underwater if it has to
|
||||
float bouyancy; //when in water, how high it floats (1 is neutral bouyancy)
|
||||
int fuelMax; //how much fuel it can hold (capacity)
|
||||
int fuelRate; //how quickly is uses up fuel
|
||||
int turboDuration; //how long turbo lasts
|
||||
int turboRecharge; //how long turbo takes to recharge
|
||||
int visibility; //for sight alerts
|
||||
int loudness; //for sound alerts
|
||||
float explosionRadius;//range of explosion
|
||||
int explosionDamage;//damage of explosion
|
||||
|
||||
int maxPassengers; // The max number of passengers this vehicle may have (Default = 0).
|
||||
qboolean hideRider; // rider (and passengers?) should not be drawn
|
||||
qboolean killRiderOnDeath;//if rider is on vehicle when it dies, they should die
|
||||
qboolean flammable; //whether or not the vehicle should catch on fire before it explodes
|
||||
int explosionDelay; //how long the vehicle should be on fire/dying before it explodes
|
||||
//camera stuff
|
||||
qboolean cameraOverride; //whether or not to use all of the following 3rd person camera override values
|
||||
float cameraRange; //how far back the camera should be - normal is 80
|
||||
float cameraVertOffset;//how high over the vehicle origin the camera should be - normal is 16
|
||||
float cameraHorzOffset;//how far to left/right (negative/positive) of of the vehicle origin the camera should be - normal is 0
|
||||
float cameraPitchOffset;//a modifier on the camera's pitch (up/down angle) to the vehicle - normal is 0
|
||||
float cameraFOV; //third person camera FOV, default is 80
|
||||
float cameraAlpha; //fade out the vehicle to this alpha (0.1-1.0f) if it's in the way of the crosshair
|
||||
qboolean cameraPitchDependantVertOffset;//use the hacky AT-ST pitch dependant vertical offset
|
||||
|
||||
//NOTE: some info on what vehicle weapon to use? Like ATST or TIE bomber or TIE fighter or X-Wing...?
|
||||
|
||||
//===VEH_PARM_MAX========================================================================
|
||||
//*** IMPORTANT!!! *** vehFields table correponds to this structure!
|
||||
|
||||
//THE FOLLOWING FIELDS are not in the vehFields table because they are internal variables, not read in from the .veh file
|
||||
int modelIndex; //set internally, not until this vehicle is spawned into the level
|
||||
|
||||
// NOTE: Please note that most of this stuff has been converted from C++ classes to generic C.
|
||||
// This part of the structure is used to simulate inheritance for vehicles. The basic idea is that all vehicle use
|
||||
// this vehicle interface since they declare their own functions and assign the function pointer to the
|
||||
// corresponding function. Meanwhile, the base logic can still call the appropriate functions. In C++ talk all
|
||||
// of these functions (pointers) are pure virtuals and this is an abstract base class (although it cannot be
|
||||
// inherited from, only contained and reimplemented (through an object and a setup function respectively)). -AReis
|
||||
|
||||
// Makes sure that the vehicle is properly animated.
|
||||
void (*AnimateVehicle)( Vehicle_t *pVeh );
|
||||
|
||||
// Makes sure that the rider's in this vehicle are properly animated.
|
||||
void (*AnimateRiders)( Vehicle_t *pVeh );
|
||||
|
||||
// Determine whether this entity is able to board this vehicle or not.
|
||||
qboolean (*ValidateBoard)( Vehicle_t *pVeh, bgEntity_t *pEnt );
|
||||
|
||||
// Set the parent entity of this Vehicle NPC.
|
||||
void (*SetParent)( Vehicle_t *pVeh, bgEntity_t *pParentEntity );
|
||||
|
||||
// Add a pilot to the vehicle.
|
||||
void (*SetPilot)( Vehicle_t *pVeh, bgEntity_t *pPilot );
|
||||
|
||||
// Add a passenger to the vehicle (false if we're full).
|
||||
qboolean (*AddPassenger)( Vehicle_t *pVeh );
|
||||
|
||||
// Animate the vehicle and it's riders.
|
||||
void (*Animate)( Vehicle_t *pVeh );
|
||||
|
||||
// Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
|
||||
qboolean (*Board)( Vehicle_t *pVeh, bgEntity_t *pEnt );
|
||||
|
||||
// Eject an entity from the vehicle.
|
||||
qboolean (*Eject)( Vehicle_t *pVeh, bgEntity_t *pEnt, qboolean forceEject );
|
||||
|
||||
// Eject all the inhabitants of this vehicle.
|
||||
qboolean (*EjectAll)( Vehicle_t *pVeh );
|
||||
|
||||
// Start a delay until the vehicle dies.
|
||||
void (*StartDeathDelay)( Vehicle_t *pVeh, int iDelayTime );
|
||||
|
||||
// Update death sequence.
|
||||
void (*DeathUpdate)( Vehicle_t *pVeh );
|
||||
|
||||
// Register all the assets used by this vehicle.
|
||||
void (*RegisterAssets)( Vehicle_t *pVeh );
|
||||
|
||||
// Initialize the vehicle (should be called by Spawn?).
|
||||
qboolean (*Initialize)( Vehicle_t *pVeh );
|
||||
|
||||
// Like a think or move command, this updates various vehicle properties.
|
||||
qboolean (*Update)( Vehicle_t *pVeh, const usercmd_t *pUcmd );
|
||||
|
||||
// Update the properties of a Rider (that may reflect what happens to the vehicle).
|
||||
//
|
||||
// [return] bool True if still in vehicle, false if otherwise.
|
||||
qboolean (*UpdateRider)( Vehicle_t *pVeh, bgEntity_t *pRider, usercmd_t *pUcmd );
|
||||
|
||||
// ProcessMoveCommands the Vehicle.
|
||||
void (*ProcessMoveCommands)( Vehicle_t *pVeh );
|
||||
|
||||
// ProcessOrientCommands the Vehicle.
|
||||
void (*ProcessOrientCommands)( Vehicle_t *pVeh );
|
||||
|
||||
// Attachs all the riders of this vehicle to their appropriate position/tag (*driver, *pass1, *pass2, whatever...).
|
||||
void (*AttachRiders)( Vehicle_t *pVeh );
|
||||
|
||||
// Make someone invisible and un-collidable.
|
||||
void (*Ghost)( Vehicle_t *pVeh, bgEntity_t *pEnt );
|
||||
|
||||
// Make someone visible and collidable.
|
||||
void (*UnGhost)( Vehicle_t *pVeh, bgEntity_t *pEnt );
|
||||
|
||||
// Get the pilot of this vehicle.
|
||||
const bgEntity_t *(*GetPilot)( Vehicle_t *pVeh );
|
||||
|
||||
// Whether this vehicle is currently inhabited (by anyone) or not.
|
||||
qboolean (*Inhabited)( Vehicle_t *pVeh );
|
||||
} vehicleInfo_t;
|
||||
|
||||
|
||||
#define VFOFS(x) ((int)&(((vehicleInfo_t *)0)->x))
|
||||
|
||||
#define MAX_VEHICLES 16 //sigh... no more than 64 individual vehicles
|
||||
#define VEHICLE_BASE 0
|
||||
#define VEHICLE_NONE -1
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern vehicleInfo_t g_vehicleInfo[MAX_VEHICLES];
|
||||
extern int numVehicles;
|
||||
#include "../namespace_end.h"
|
||||
|
||||
#define VEH_DEFAULT_SPEED_MAX 800.0f
|
||||
#define VEH_DEFAULT_ACCEL 10.0f
|
||||
#define VEH_DEFAULT_DECEL 10.0f
|
||||
#define VEH_DEFAULT_STRAFE_PERC 0.5f
|
||||
#define VEH_DEFAULT_BANKING_SPEED 0.5f
|
||||
#define VEH_DEFAULT_ROLL_LIMIT 60.0f
|
||||
#define VEH_DEFAULT_PITCH_LIMIT 90.0f
|
||||
#define VEH_DEFAULT_BRAKING 10.0f
|
||||
#define VEH_DEFAULT_TURNING_SPEED 1.0f
|
||||
#define VEH_DEFAULT_TRACTION 8.0f
|
||||
#define VEH_DEFAULT_FRICTION 1.0f
|
||||
#define VEH_DEFAULT_MAX_SLOPE 0.85f
|
||||
#define VEH_DEFAULT_MASS 200
|
||||
#define VEH_DEFAULT_MAX_ARMOR 200
|
||||
#define VEH_DEFAULT_TOUGHNESS 2.5f
|
||||
#define VEH_DEFAULT_GRAVITY 800
|
||||
#define VEH_DEFAULT_HOVER_HEIGHT 64.0f
|
||||
#define VEH_DEFAULT_HOVER_STRENGTH 10.0f
|
||||
#define VEH_DEFAULT_VISIBILITY 0
|
||||
#define VEH_DEFAULT_LOUDNESS 0
|
||||
#define VEH_DEFAULT_EXP_RAD 400.0f
|
||||
#define VEH_DEFAULT_EXP_DMG 1000
|
||||
#define VEH_MAX_PASSENGERS 10
|
||||
|
||||
#define MAX_STRAFE_TIME 2000.0f//FIXME: extern?
|
||||
#define MIN_LANDING_SPEED 200//equal to or less than this and close to ground = auto-slow-down to land
|
||||
#define MIN_LANDING_SLOPE 0.8f//must be pretty flat to land on the surf
|
||||
|
||||
#define VEH_MOUNT_THROW_LEFT -5
|
||||
#define VEH_MOUNT_THROW_RIGHT -6
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VEH_EJECT_LEFT,
|
||||
VEH_EJECT_RIGHT,
|
||||
VEH_EJECT_FRONT,
|
||||
VEH_EJECT_REAR,
|
||||
VEH_EJECT_TOP,
|
||||
VEH_EJECT_BOTTOM
|
||||
};
|
||||
|
||||
// Vehicle flags.
|
||||
typedef enum
|
||||
{
|
||||
VEH_NONE = 0, VEH_FLYING = 0x00000001, VEH_CRASHING = 0x00000002,
|
||||
VEH_LANDING = 0x00000004, VEH_BUCKING = 0x00000010, VEH_WINGSOPEN = 0x00000020,
|
||||
VEH_GEARSOPEN = 0x00000040, VEH_SLIDEBREAKING = 0x00000080, VEH_SPINNING = 0x00000100,
|
||||
VEH_OUTOFCONTROL = 0x00000200,
|
||||
VEH_SABERINLEFTHAND = 0x00000400
|
||||
} vehFlags_t;
|
||||
|
||||
//defines for impact damage surface stuff
|
||||
#define SHIPSURF_FRONT 0
|
||||
#define SHIPSURF_BACK 1
|
||||
#define SHIPSURF_RIGHT 2
|
||||
#define SHIPSURF_LEFT 3
|
||||
|
||||
#define SHIPSURF_DAMAGE_FRONT_LIGHT 0
|
||||
#define SHIPSURF_DAMAGE_BACK_LIGHT 1
|
||||
#define SHIPSURF_DAMAGE_RIGHT_LIGHT 2
|
||||
#define SHIPSURF_DAMAGE_LEFT_LIGHT 3
|
||||
#define SHIPSURF_DAMAGE_FRONT_HEAVY 4
|
||||
#define SHIPSURF_DAMAGE_BACK_HEAVY 5
|
||||
#define SHIPSURF_DAMAGE_RIGHT_HEAVY 6
|
||||
#define SHIPSURF_DAMAGE_LEFT_HEAVY 7
|
||||
|
||||
//generic part bits
|
||||
#define SHIPSURF_BROKEN_A (1<<0) //gear 1
|
||||
#define SHIPSURF_BROKEN_B (1<<1) //gear 1
|
||||
#define SHIPSURF_BROKEN_C (1<<2) //wing 1
|
||||
#define SHIPSURF_BROKEN_D (1<<3) //wing 2
|
||||
#define SHIPSURF_BROKEN_E (1<<4) //wing 3
|
||||
#define SHIPSURF_BROKEN_F (1<<5) //wing 4
|
||||
#define SHIPSURF_BROKEN_G (1<<6) //front
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//linked firing mode
|
||||
qboolean linked;//weapon 1's muzzles are in linked firing mode
|
||||
//current weapon ammo
|
||||
int ammo;
|
||||
//debouncer for ammo recharge
|
||||
int lastAmmoInc;
|
||||
//which muzzle will fire next
|
||||
int nextMuzzle;
|
||||
} vehWeaponStatus_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//current weapon ammo
|
||||
int ammo;
|
||||
//debouncer for ammo recharge
|
||||
int lastAmmoInc;
|
||||
//which muzzle will fire next
|
||||
int nextMuzzle;
|
||||
//which entity they're after
|
||||
int enemyEntNum;
|
||||
//how long to hold on to our current enemy
|
||||
int enemyHoldTime;
|
||||
} vehTurretStatus_t;
|
||||
// This is the implementation of the vehicle interface and any of the other variables needed. This
|
||||
// is what actually represents a vehicle. -AReis.
|
||||
typedef struct Vehicle_s
|
||||
{
|
||||
// The entity who pilots/drives this vehicle.
|
||||
// NOTE: This is redundant (since m_pParentEntity->owner _should_ be the pilot). This makes things clearer though.
|
||||
bgEntity_t *m_pPilot;
|
||||
|
||||
int m_iPilotTime; //if spawnflag to die without pilot and this < level.time then die.
|
||||
int m_iPilotLastIndex; //index to last pilot
|
||||
qboolean m_bHasHadPilot; //qtrue once the vehicle gets its first pilot
|
||||
|
||||
// The passengers of this vehicle.
|
||||
//bgEntity_t **m_ppPassengers;
|
||||
bgEntity_t *m_ppPassengers[VEH_MAX_PASSENGERS];
|
||||
|
||||
//the droid unit NPC for this vehicle, if any
|
||||
bgEntity_t *m_pDroidUnit;
|
||||
|
||||
// The number of passengers currently in this vehicle.
|
||||
int m_iNumPassengers;
|
||||
|
||||
// The entity from which this NPC comes from.
|
||||
bgEntity_t *m_pParentEntity;
|
||||
|
||||
// If not zero, how long to wait before we can do anything with the vehicle (we're getting on still).
|
||||
// -1 = board from left, -2 = board from right, -3 = jump/quick board. -4 & -5 = throw off existing pilot
|
||||
int m_iBoarding;
|
||||
|
||||
// Used to check if we've just started the boarding process
|
||||
qboolean m_bWasBoarding;
|
||||
|
||||
// The speed the vehicle maintains while boarding occurs (often zero)
|
||||
vec3_t m_vBoardingVelocity;
|
||||
|
||||
// Time modifier (must only be used in ProcessMoveCommands() and ProcessOrientCommands() and is updated in Update()).
|
||||
float m_fTimeModifier;
|
||||
|
||||
// Ghoul2 Animation info.
|
||||
//int m_iDriverTag;
|
||||
int m_iLeftExhaustTag;
|
||||
int m_iRightExhaustTag;
|
||||
int m_iGun1Tag;
|
||||
int m_iGun1Bone;
|
||||
int m_iLeftWingBone;
|
||||
int m_iRightWingBone;
|
||||
|
||||
int m_iExhaustTag[MAX_VEHICLE_EXHAUSTS];
|
||||
int m_iMuzzleTag[MAX_VEHICLE_MUZZLES];
|
||||
int m_iDroidUnitTag;
|
||||
int m_iGunnerViewTag[MAX_VEHICLE_TURRETS];//Where to put the view origin of the gunner (index)
|
||||
|
||||
//this stuff is a little bit different from SP, because I am lazy -rww
|
||||
int m_iMuzzleTime[MAX_VEHICLE_MUZZLES];
|
||||
// These are updated every frame and represent the current position and direction for the specific muzzle.
|
||||
vec3_t m_vMuzzlePos[MAX_VEHICLE_MUZZLES], m_vMuzzleDir[MAX_VEHICLE_MUZZLES];
|
||||
|
||||
// This is how long to wait before being able to fire a specific muzzle again. This is based on the firing rate
|
||||
// so that a firing rate of 10 rounds/sec would make this value initially 100 miliseconds.
|
||||
int m_iMuzzleWait[MAX_VEHICLE_MUZZLES];
|
||||
|
||||
// The user commands structure.
|
||||
usercmd_t m_ucmd;
|
||||
|
||||
// The direction an entity will eject from the vehicle towards.
|
||||
int m_EjectDir;
|
||||
|
||||
// Flags that describe the vehicles behavior.
|
||||
unsigned long m_ulFlags;
|
||||
|
||||
// NOTE: Vehicle Type ID, Orientation, and Armor MUST be transmitted over the net.
|
||||
|
||||
// The ID of the type of vehicle this is.
|
||||
int m_iVehicleTypeID;
|
||||
|
||||
// Current angles of this vehicle.
|
||||
//vec3_t m_vOrientation;
|
||||
float *m_vOrientation;
|
||||
//Yeah, since we use the SP code for vehicles, I want to use this value, but I'm going
|
||||
//to make it a pointer to a vec3_t in the playerstate for prediction's sake. -rww
|
||||
|
||||
// How long you have strafed left or right (increments every frame that you strafe to right, decrements every frame you strafe left)
|
||||
int m_fStrafeTime;
|
||||
|
||||
// Previous angles of this vehicle.
|
||||
vec3_t m_vPrevOrientation;
|
||||
|
||||
// Previous viewangles of the rider
|
||||
vec3_t m_vPrevRiderViewAngles;
|
||||
|
||||
// When control is lost on a speeder, current angular velocity is stored here and applied until landing
|
||||
float m_vAngularVelocity;
|
||||
|
||||
vec3_t m_vFullAngleVelocity;
|
||||
|
||||
// Current armor and shields of your vehicle (explodes if armor to 0).
|
||||
int m_iArmor; //hull strength - STAT_HEALTH on NPC
|
||||
int m_iShields; //energy shielding - STAT_ARMOR on NPC
|
||||
|
||||
//mp-specific
|
||||
int m_iHitDebounce;
|
||||
|
||||
// Timer for all cgame-FX...? ex: exhaust?
|
||||
int m_iLastFXTime;
|
||||
|
||||
// When to die.
|
||||
int m_iDieTime;
|
||||
|
||||
// This pointer is to a valid VehicleInfo (which could be an animal, speeder, fighter, whatever). This
|
||||
// contains the functions actually used to do things to this specific kind of vehicle as well as shared
|
||||
// information (max speed, type, etc...).
|
||||
vehicleInfo_t *m_pVehicleInfo;
|
||||
|
||||
// This trace tells us if we're within landing height.
|
||||
trace_t m_LandTrace;
|
||||
|
||||
// TEMP: The wing angles (used to animate it).
|
||||
vec3_t m_vWingAngles;
|
||||
|
||||
//amount of damage done last impact
|
||||
int m_iLastImpactDmg;
|
||||
|
||||
//bitflag of surfaces that have broken off
|
||||
int m_iRemovedSurfaces;
|
||||
|
||||
int m_iDmgEffectTime;
|
||||
|
||||
// the last time this vehicle fired a turbo burst
|
||||
int m_iTurboTime;
|
||||
|
||||
//how long it should drop like a rock for after freed from SUSPEND
|
||||
int m_iDropTime;
|
||||
|
||||
int m_iSoundDebounceTimer;
|
||||
|
||||
//last time we incremented the shields
|
||||
int lastShieldInc;
|
||||
|
||||
//so we don't hold it down and toggle it back and forth
|
||||
qboolean linkWeaponToggleHeld;
|
||||
|
||||
//info about our weapons (linked, ammo, etc.)
|
||||
vehWeaponStatus_t weaponStatus[MAX_VEHICLE_WEAPONS];
|
||||
vehTurretStatus_t turretStatus[MAX_VEHICLE_TURRETS];
|
||||
|
||||
//the guy who was previously the pilot
|
||||
bgEntity_t * m_pOldPilot;
|
||||
|
||||
} Vehicle_t;
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
extern int BG_VehicleGetIndex( const char *vehicleName );
|
||||
#include "../namespace_end.h"
|
||||
|
||||
#endif // __BG_VEHICLES_H
|
||||
402
codemp/game/bg_weapons.c
Normal file
402
codemp/game/bg_weapons.c
Normal file
@@ -0,0 +1,402 @@
|
||||
// Copyright (C) 2001-2002 Raven Software
|
||||
//
|
||||
// bg_weapons.c -- part of bg_pmove functionality
|
||||
|
||||
#include "q_shared.h"
|
||||
#include "bg_public.h"
|
||||
#include "bg_local.h"
|
||||
|
||||
// Muzzle point table...
|
||||
vec3_t WP_MuzzlePoint[WP_NUM_WEAPONS] =
|
||||
{// Fwd, right, up.
|
||||
{0, 0, 0 }, // WP_NONE,
|
||||
{0 , 8, 0 }, // WP_STUN_BATON,
|
||||
{0 , 8, 0 }, // WP_MELEE,
|
||||
{8 , 16, 0 }, // WP_SABER,
|
||||
{12, 6, -6 }, // WP_BRYAR_PISTOL,
|
||||
{12, 6, -6 }, // WP_BLASTER,
|
||||
{12, 6, -6 }, // WP_DISRUPTOR,
|
||||
{12, 2, -6 }, // WP_BOWCASTER,
|
||||
{12, 4.5, -6 }, // WP_REPEATER,
|
||||
{12, 6, -6 }, // WP_DEMP2,
|
||||
{12, 6, -6 }, // WP_FLECHETTE,
|
||||
{12, 8, -4 }, // WP_ROCKET_LAUNCHER,
|
||||
{12, 0, -4 }, // WP_THERMAL,
|
||||
{12, 0, -10 }, // WP_TRIP_MINE,
|
||||
{12, 0, -4 }, // WP_DET_PACK,
|
||||
{12, 6, -6 }, // WP_CONCUSSION
|
||||
{12, 6, -6 }, // WP_BRYAR_OLD,
|
||||
};
|
||||
|
||||
weaponData_t weaponData[WP_NUM_WEAPONS] =
|
||||
{
|
||||
{ // WP_NONE
|
||||
// "No Weapon", // char classname[32]; // Spawning name
|
||||
AMMO_NONE, // int ammoIndex; // Index to proper ammo slot
|
||||
0, // int ammoLow; // Count when ammo is low
|
||||
0, // int energyPerShot; // Amount of energy used per shot
|
||||
0, // int fireTime; // Amount of time between firings
|
||||
0, // int range; // Range of weapon
|
||||
0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
0, // int altFireTime; // Amount of time between alt-firings
|
||||
0, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_STUN_BATON
|
||||
// "Stun Baton", // char classname[32]; // Spawning name
|
||||
AMMO_NONE, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
0, // int energyPerShot; // Amount of energy used per shot
|
||||
400, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_MELEE
|
||||
// "Melee", // char classname[32]; // Spawning name
|
||||
AMMO_NONE, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
0, // int energyPerShot; // Amount of energy used per shot
|
||||
400, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_SABER,
|
||||
// "Lightsaber", // char classname[32]; // Spawning name
|
||||
AMMO_NONE, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
0, // int energyPerShot; // Amount of energy used per shot
|
||||
100, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
100, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_BRYAR_PISTOL,
|
||||
// "Bryar Pistol", // char classname[32]; // Spawning name
|
||||
AMMO_BLASTER, // int ammoIndex; // Index to proper ammo slot
|
||||
0,//15, // int ammoLow; // Count when ammo is low
|
||||
0,//2, // int energyPerShot; // Amount of energy used per shot
|
||||
800,//400, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
0,//2, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
800,//400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0,//200, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0,//1, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0,//1500 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_BLASTER
|
||||
// "E11 Blaster Rifle", // char classname[32]; // Spawning name
|
||||
AMMO_BLASTER, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
2, // int energyPerShot; // Amount of energy used per shot
|
||||
350, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
3, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
150, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_DISRUPTOR
|
||||
// "Tenloss Disruptor Rifle",// char classname[32]; // Spawning name
|
||||
AMMO_POWERCELL, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
5, // int energyPerShot; // Amount of energy used per shot
|
||||
600, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
6, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
1300, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
200, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
3, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
1700 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_BOWCASTER
|
||||
// "Wookiee Bowcaster", // char classname[32]; // Spawning name
|
||||
AMMO_POWERCELL, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
5, // int energyPerShot; // Amount of energy used per shot
|
||||
1000, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
5, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
750, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
400, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
5, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
1700, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_REPEATER
|
||||
// "Imperial Heavy Repeater",// char classname[32]; // Spawning name
|
||||
AMMO_METAL_BOLTS, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
1, // int energyPerShot; // Amount of energy used per shot
|
||||
100, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
15, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
800, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_DEMP2
|
||||
// "DEMP2", // char classname[32]; // Spawning name
|
||||
AMMO_POWERCELL, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
8, // int energyPerShot; // Amount of energy used per shot
|
||||
500, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
6, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
900, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
250, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
3, // int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
2100 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_FLECHETTE
|
||||
// "Golan Arms Flechette", // char classname[32]; // Spawning name
|
||||
AMMO_METAL_BOLTS, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
10, // int energyPerShot; // Amount of energy used per shot
|
||||
700, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
15, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
800, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_ROCKET_LAUNCHER
|
||||
// "Merr-Sonn Missile System", // char classname[32]; // Spawning name
|
||||
AMMO_ROCKETS, // int ammoIndex; // Index to proper ammo slot
|
||||
5, // int ammoLow; // Count when ammo is low
|
||||
1, // int energyPerShot; // Amount of energy used per shot
|
||||
900, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
2, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
1200, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_THERMAL
|
||||
// "Thermal Detonator", // char classname[32]; // Spawning name
|
||||
AMMO_THERMAL, // int ammoIndex; // Index to proper ammo slot
|
||||
0, // int ammoLow; // Count when ammo is low
|
||||
1, // int energyPerShot; // Amount of energy used per shot
|
||||
800, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
1, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_TRIP_MINE
|
||||
// "Trip Mine", // char classname[32]; // Spawning name
|
||||
AMMO_TRIPMINE, // int ammoIndex; // Index to proper ammo slot
|
||||
0, // int ammoLow; // Count when ammo is low
|
||||
1, // int energyPerShot; // Amount of energy used per shot
|
||||
800, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
1, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_DET_PACK
|
||||
// "Det Pack", // char classname[32]; // Spawning name
|
||||
AMMO_DETPACK, // int ammoIndex; // Index to proper ammo slot
|
||||
0, // int ammoLow; // Count when ammo is low
|
||||
1, // int energyPerShot; // Amount of energy used per shot
|
||||
800, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_CONCUSSION
|
||||
// "Concussion Rifle", // char classname[32]; // Spawning name
|
||||
AMMO_METAL_BOLTS, // int ammoIndex; // Index to proper ammo slot
|
||||
40, // int ammoLow; // Count when ammo is low
|
||||
40, // int energyPerShot; // Amount of energy used per shot
|
||||
800, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
50, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
1200, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, // int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_BRYAR_OLD,
|
||||
// "Bryar Pistol", // char classname[32]; // Spawning name
|
||||
AMMO_BLASTER, // int ammoIndex; // Index to proper ammo slot
|
||||
15, // int ammoLow; // Count when ammo is low
|
||||
2, // int energyPerShot; // Amount of energy used per shot
|
||||
400, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
2, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
400, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
200, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
1, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
1500 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_EMPLCACED_GUN
|
||||
// "Emplaced Gun", // char classname[32]; // Spawning name
|
||||
/*AMMO_BLASTER*/0, // int ammoIndex; // Index to proper ammo slot
|
||||
/*5*/0, // int ammoLow; // Count when ammo is low
|
||||
/*2*/0, // int energyPerShot; // Amount of energy used per shot
|
||||
100, // int fireTime; // Amount of time between firings
|
||||
8192, // int range; // Range of weapon
|
||||
/*3*/0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
100, // int altFireTime; // Amount of time between alt-firings
|
||||
8192, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
},
|
||||
{ // WP_TURRET - NOTE NOT ACTUALLY USEABLE BY PLAYER!
|
||||
// "Emplaced Gun", // char classname[32]; // Spawning name
|
||||
/*AMMO_BLASTER*/0, // int ammoIndex; // Index to proper ammo slot
|
||||
/*5*/0, // int ammoLow; // Count when ammo is low
|
||||
/*2*/0, // int energyPerShot; // Amount of energy used per shot
|
||||
0, // int fireTime; // Amount of time between firings
|
||||
0, // int range; // Range of weapon
|
||||
/*3*/0, // int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
0, // int altFireTime; // Amount of time between alt-firings
|
||||
0, // int altRange; // Range of alt-fire
|
||||
0, // int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
0, // int altChargeSubTime; // above for secondary
|
||||
0, // int chargeSub; // amount to subtract during charge on each interval
|
||||
0, //int altChargeSub; // above for secondary
|
||||
0, // int maxCharge; // stop subtracting once charged for this many ms
|
||||
0 // int altMaxCharge; // above for secondary
|
||||
}
|
||||
};
|
||||
|
||||
ammoData_t ammoData[AMMO_MAX] =
|
||||
{
|
||||
{ // AMMO_NONE
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
0 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_FORCE
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
100 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_BLASTER
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
300 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_POWERCELL
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
300 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_METAL_BOLTS
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
300 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_ROCKETS
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
25 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_EMPLACED
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
800 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_THERMAL
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
10 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_TRIPMINE
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
10 // int max; // Max amount player can hold of ammo
|
||||
},
|
||||
{ // AMMO_DETPACK
|
||||
// "", // char icon[32]; // Name of ammo icon file
|
||||
10 // int max; // Max amount player can hold of ammo
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
113
codemp/game/bg_weapons.h
Normal file
113
codemp/game/bg_weapons.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// Filename:- bg_weapons.h
|
||||
//
|
||||
// This crosses both client and server. It could all be crammed into bg_public, but isolation of this type of data is best.
|
||||
|
||||
#ifndef __WEAPONS_H__
|
||||
#define __WEAPONS_H__
|
||||
|
||||
typedef enum {
|
||||
WP_NONE,
|
||||
|
||||
WP_STUN_BATON,
|
||||
WP_MELEE,
|
||||
WP_SABER,
|
||||
WP_BRYAR_PISTOL,
|
||||
WP_BLASTER,
|
||||
WP_DISRUPTOR,
|
||||
WP_BOWCASTER,
|
||||
WP_REPEATER,
|
||||
WP_DEMP2,
|
||||
WP_FLECHETTE,
|
||||
WP_ROCKET_LAUNCHER,
|
||||
WP_THERMAL,
|
||||
WP_TRIP_MINE,
|
||||
WP_DET_PACK,
|
||||
WP_CONCUSSION,
|
||||
WP_BRYAR_OLD,
|
||||
WP_EMPLACED_GUN,
|
||||
WP_TURRET,
|
||||
|
||||
// WP_GAUNTLET,
|
||||
// WP_MACHINEGUN, // Bryar
|
||||
// WP_SHOTGUN, // Blaster
|
||||
// WP_GRENADE_LAUNCHER, // Thermal
|
||||
// WP_LIGHTNING, //
|
||||
// WP_RAILGUN, //
|
||||
// WP_GRAPPLING_HOOK,
|
||||
|
||||
WP_NUM_WEAPONS
|
||||
};
|
||||
typedef int weapon_t;
|
||||
|
||||
//anything > this will be considered not player useable
|
||||
#define LAST_USEABLE_WEAPON WP_BRYAR_OLD
|
||||
|
||||
typedef enum //# ammo_e
|
||||
{
|
||||
AMMO_NONE,
|
||||
AMMO_FORCE, // AMMO_PHASER
|
||||
AMMO_BLASTER, // AMMO_STARFLEET,
|
||||
AMMO_POWERCELL, // AMMO_ALIEN,
|
||||
AMMO_METAL_BOLTS,
|
||||
AMMO_ROCKETS,
|
||||
AMMO_EMPLACED,
|
||||
AMMO_THERMAL,
|
||||
AMMO_TRIPMINE,
|
||||
AMMO_DETPACK,
|
||||
AMMO_MAX
|
||||
} ammo_t;
|
||||
|
||||
|
||||
typedef struct weaponData_s
|
||||
{
|
||||
// char classname[32]; // Spawning name
|
||||
|
||||
int ammoIndex; // Index to proper ammo slot
|
||||
int ammoLow; // Count when ammo is low
|
||||
|
||||
int energyPerShot; // Amount of energy used per shot
|
||||
int fireTime; // Amount of time between firings
|
||||
int range; // Range of weapon
|
||||
|
||||
int altEnergyPerShot; // Amount of energy used for alt-fire
|
||||
int altFireTime; // Amount of time between alt-firings
|
||||
int altRange; // Range of alt-fire
|
||||
|
||||
int chargeSubTime; // ms interval for subtracting ammo during charge
|
||||
int altChargeSubTime; // above for secondary
|
||||
|
||||
int chargeSub; // amount to subtract during charge on each interval
|
||||
int altChargeSub; // above for secondary
|
||||
|
||||
int maxCharge; // stop subtracting once charged for this many ms
|
||||
int altMaxCharge; // above for secondary
|
||||
} weaponData_t;
|
||||
|
||||
|
||||
typedef struct ammoData_s
|
||||
{
|
||||
// char icon[32]; // Name of ammo icon file
|
||||
int max; // Max amount player can hold of ammo
|
||||
} ammoData_t;
|
||||
|
||||
|
||||
extern weaponData_t weaponData[WP_NUM_WEAPONS];
|
||||
extern ammoData_t ammoData[AMMO_MAX];
|
||||
|
||||
|
||||
// Specific weapon information
|
||||
|
||||
#define FIRST_WEAPON WP_BRYAR_PISTOL // this is the first weapon for next and prev weapon switching
|
||||
#define MAX_PLAYER_WEAPONS WP_NUM_WEAPONS-1 // this is the max you can switch to and get with the give all.
|
||||
|
||||
|
||||
#define DEFAULT_SHOTGUN_SPREAD 700
|
||||
#define DEFAULT_SHOTGUN_COUNT 11
|
||||
|
||||
#define LIGHTNING_RANGE 768
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif//#ifndef __WEAPONS_H__
|
||||
508
codemp/game/botlib.h
Normal file
508
codemp/game/botlib.h
Normal file
@@ -0,0 +1,508 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
/*****************************************************************************
|
||||
* name: botlib.h
|
||||
*
|
||||
* desc: bot AI library
|
||||
*
|
||||
* $Archive: /source/code/game/botai.h $
|
||||
* $Author: osman $
|
||||
* $Revision: 1.5 $
|
||||
* $Modtime: 03/01/00 3:32p $
|
||||
* $Date: 2003/09/26 02:43:45 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define BOTLIB_API_VERSION 2
|
||||
|
||||
struct aas_clientmove_s;
|
||||
struct aas_entityinfo_s;
|
||||
struct aas_areainfo_s;
|
||||
struct aas_altroutegoal_s;
|
||||
struct aas_predictroute_s;
|
||||
struct bot_consolemessage_s;
|
||||
struct bot_match_s;
|
||||
struct bot_goal_s;
|
||||
struct bot_moveresult_s;
|
||||
struct bot_initmove_s;
|
||||
struct weaponinfo_s;
|
||||
|
||||
#define BOTFILESBASEFOLDER "botfiles"
|
||||
//debug line colors
|
||||
#define LINECOLOR_NONE -1
|
||||
#define LINECOLOR_RED 1//0xf2f2f0f0L
|
||||
#define LINECOLOR_GREEN 2//0xd0d1d2d3L
|
||||
#define LINECOLOR_BLUE 3//0xf3f3f1f1L
|
||||
#define LINECOLOR_YELLOW 4//0xdcdddedfL
|
||||
#define LINECOLOR_ORANGE 5//0xe0e1e2e3L
|
||||
|
||||
//Print types
|
||||
#define PRT_MESSAGE 1
|
||||
#define PRT_WARNING 2
|
||||
#define PRT_ERROR 3
|
||||
#define PRT_FATAL 4
|
||||
#define PRT_EXIT 5
|
||||
|
||||
//console message types
|
||||
#define CMS_NORMAL 0
|
||||
#define CMS_CHAT 1
|
||||
|
||||
//botlib error codes
|
||||
#define BLERR_NOERROR 0 //no error
|
||||
#define BLERR_LIBRARYNOTSETUP 1 //library not setup
|
||||
#define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number
|
||||
#define BLERR_NOAASFILE 3 //no AAS file available
|
||||
#define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file
|
||||
#define BLERR_WRONGAASFILEID 5 //incorrect AAS file id
|
||||
#define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version
|
||||
#define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump
|
||||
#define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats
|
||||
#define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights
|
||||
#define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config
|
||||
#define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights
|
||||
#define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config
|
||||
|
||||
//action flags
|
||||
#define ACTION_ATTACK 0x0000001
|
||||
#define ACTION_USE 0x0000002
|
||||
#define ACTION_RESPAWN 0x0000008
|
||||
#define ACTION_JUMP 0x0000010
|
||||
#define ACTION_MOVEUP 0x0000020
|
||||
#define ACTION_CROUCH 0x0000080
|
||||
#define ACTION_MOVEDOWN 0x0000100
|
||||
#define ACTION_MOVEFORWARD 0x0000200
|
||||
#define ACTION_MOVEBACK 0x0000800
|
||||
#define ACTION_MOVELEFT 0x0001000
|
||||
#define ACTION_MOVERIGHT 0x0002000
|
||||
#define ACTION_DELAYEDJUMP 0x0008000
|
||||
#define ACTION_TALK 0x0010000
|
||||
#define ACTION_GESTURE 0x0020000
|
||||
#define ACTION_WALK 0x0080000
|
||||
#define ACTION_FORCEPOWER 0x0100000
|
||||
#define ACTION_ALT_ATTACK 0x0200000
|
||||
/*
|
||||
#define ACTION_AFFIRMATIVE 0x0100000
|
||||
#define ACTION_NEGATIVE 0x0200000
|
||||
#define ACTION_GETFLAG 0x0800000
|
||||
#define ACTION_GUARDBASE 0x1000000
|
||||
#define ACTION_PATROL 0x2000000
|
||||
#define ACTION_FOLLOWME 0x8000000
|
||||
*/
|
||||
|
||||
//the bot input, will be converted to an usercmd_t
|
||||
typedef struct bot_input_s
|
||||
{
|
||||
float thinktime; //time since last output (in seconds)
|
||||
vec3_t dir; //movement direction
|
||||
float speed; //speed in the range [0, 400]
|
||||
vec3_t viewangles; //the view angles
|
||||
int actionflags; //one of the ACTION_? flags
|
||||
int weapon; //weapon to use
|
||||
} bot_input_t;
|
||||
|
||||
#ifndef BSPTRACE
|
||||
|
||||
#define BSPTRACE
|
||||
|
||||
//bsp_trace_t hit surface
|
||||
typedef struct bsp_surface_s
|
||||
{
|
||||
char name[16];
|
||||
int flags;
|
||||
int value;
|
||||
} bsp_surface_t;
|
||||
|
||||
//remove the bsp_trace_s structure definition l8r on
|
||||
//a trace is returned when a box is swept through the world
|
||||
typedef struct bsp_trace_s
|
||||
{
|
||||
qboolean allsolid; // if true, plane is not valid
|
||||
qboolean startsolid; // if true, the initial point was in a solid area
|
||||
float fraction; // time completed, 1.0 = didn't hit anything
|
||||
vec3_t endpos; // final position
|
||||
cplane_t plane; // surface normal at impact
|
||||
float exp_dist; // expanded plane distance
|
||||
int sidenum; // number of the brush side hit
|
||||
bsp_surface_t surface; // the hit point surface
|
||||
int contents; // contents on other side of surface hit
|
||||
int ent; // number of entity hit
|
||||
} bsp_trace_t;
|
||||
|
||||
#endif // BSPTRACE
|
||||
|
||||
//entity state
|
||||
typedef struct bot_entitystate_s
|
||||
{
|
||||
int type; // entity type
|
||||
int flags; // entity flags
|
||||
vec3_t origin; // origin of the entity
|
||||
vec3_t angles; // angles of the model
|
||||
vec3_t old_origin; // for lerping
|
||||
vec3_t mins; // bounding box minimums
|
||||
vec3_t maxs; // bounding box maximums
|
||||
int groundent; // ground entity
|
||||
int solid; // solid type
|
||||
int modelindex; // model used
|
||||
int modelindex2; // weapons, CTF flags, etc
|
||||
int frame; // model frame number
|
||||
int event; // impulse events -- muzzle flashes, footsteps, etc
|
||||
int eventParm; // even parameter
|
||||
int powerups; // bit flags
|
||||
int weapon; // determines weapon and flash model, etc
|
||||
int legsAnim;
|
||||
int torsoAnim;
|
||||
} bot_entitystate_t;
|
||||
|
||||
//bot AI library exported functions
|
||||
typedef struct botlib_import_s
|
||||
{
|
||||
//print messages from the bot library
|
||||
void (QDECL *Print)(int type, char *fmt, ...);
|
||||
//trace a bbox through the world
|
||||
void (*Trace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask);
|
||||
//trace a bbox against a specific entity
|
||||
void (*EntityTrace)(bsp_trace_t *trace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int entnum, int contentmask);
|
||||
//retrieve the contents at the given point
|
||||
int (*PointContents)(vec3_t point);
|
||||
//check if the point is in potential visible sight
|
||||
int (*inPVS)(vec3_t p1, vec3_t p2);
|
||||
//retrieve the BSP entity data lump
|
||||
char *(*BSPEntityData)(void);
|
||||
//
|
||||
void (*BSPModelMinsMaxsOrigin)(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin);
|
||||
//send a bot client command
|
||||
void (*BotClientCommand)(int client, char *command);
|
||||
//memory allocation
|
||||
void *(*GetMemory)(int size); // allocate from Zone
|
||||
void (*FreeMemory)(void *ptr); // free memory from Zone
|
||||
int (*AvailableMemory)(void); // available Zone memory
|
||||
void *(*HunkAlloc)(int size); // allocate from hunk
|
||||
//file system access
|
||||
int (*FS_FOpenFile)( const char *qpath, fileHandle_t *file, fsMode_t mode );
|
||||
int (*FS_Read)( void *buffer, int len, fileHandle_t f );
|
||||
int (*FS_Write)( const void *buffer, int len, fileHandle_t f );
|
||||
void (*FS_FCloseFile)( fileHandle_t f );
|
||||
int (*FS_Seek)( fileHandle_t f, long offset, int origin );
|
||||
//debug visualisation stuff
|
||||
int (*DebugLineCreate)(void);
|
||||
void (*DebugLineDelete)(int line);
|
||||
void (*DebugLineShow)(int line, vec3_t start, vec3_t end, int color);
|
||||
//
|
||||
int (*DebugPolygonCreate)(int color, int numPoints, vec3_t *points);
|
||||
void (*DebugPolygonDelete)(int id);
|
||||
} botlib_import_t;
|
||||
|
||||
typedef struct aas_export_s
|
||||
{
|
||||
//-----------------------------------
|
||||
// be_aas_entity.h
|
||||
//-----------------------------------
|
||||
void (*AAS_EntityInfo)(int entnum, struct aas_entityinfo_s *info);
|
||||
//-----------------------------------
|
||||
// be_aas_main.h
|
||||
//-----------------------------------
|
||||
int (*AAS_Initialized)(void);
|
||||
void (*AAS_PresenceTypeBoundingBox)(int presencetype, vec3_t mins, vec3_t maxs);
|
||||
float (*AAS_Time)(void);
|
||||
//--------------------------------------------
|
||||
// be_aas_sample.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_PointAreaNum)(vec3_t point);
|
||||
int (*AAS_PointReachabilityAreaIndex)( vec3_t point );
|
||||
int (*AAS_TraceAreas)(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas);
|
||||
int (*AAS_BBoxAreas)(vec3_t absmins, vec3_t absmaxs, int *areas, int maxareas);
|
||||
int (*AAS_AreaInfo)( int areanum, struct aas_areainfo_s *info );
|
||||
//--------------------------------------------
|
||||
// be_aas_bspq3.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_PointContents)(vec3_t point);
|
||||
int (*AAS_NextBSPEntity)(int ent);
|
||||
int (*AAS_ValueForBSPEpairKey)(int ent, char *key, char *value, int size);
|
||||
int (*AAS_VectorForBSPEpairKey)(int ent, char *key, vec3_t v);
|
||||
int (*AAS_FloatForBSPEpairKey)(int ent, char *key, float *value);
|
||||
int (*AAS_IntForBSPEpairKey)(int ent, char *key, int *value);
|
||||
//--------------------------------------------
|
||||
// be_aas_reach.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_AreaReachability)(int areanum);
|
||||
//--------------------------------------------
|
||||
// be_aas_route.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_AreaTravelTimeToGoalArea)(int areanum, vec3_t origin, int goalareanum, int travelflags);
|
||||
int (*AAS_EnableRoutingArea)(int areanum, int enable);
|
||||
int (*AAS_PredictRoute)(struct aas_predictroute_s *route, int areanum, vec3_t origin,
|
||||
int goalareanum, int travelflags, int maxareas, int maxtime,
|
||||
int stopevent, int stopcontents, int stoptfl, int stopareanum);
|
||||
//--------------------------------------------
|
||||
// be_aas_altroute.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_AlternativeRouteGoals)(vec3_t start, int startareanum, vec3_t goal, int goalareanum, int travelflags,
|
||||
struct aas_altroutegoal_s *altroutegoals, int maxaltroutegoals,
|
||||
int type);
|
||||
//--------------------------------------------
|
||||
// be_aas_move.c
|
||||
//--------------------------------------------
|
||||
int (*AAS_Swimming)(vec3_t origin);
|
||||
int (*AAS_PredictClientMovement)(struct aas_clientmove_s *move,
|
||||
int entnum, vec3_t origin,
|
||||
int presencetype, int onground,
|
||||
vec3_t velocity, vec3_t cmdmove,
|
||||
int cmdframes,
|
||||
int maxframes, float frametime,
|
||||
int stopevent, int stopareanum, int visualize);
|
||||
} aas_export_t;
|
||||
|
||||
typedef struct ea_export_s
|
||||
{
|
||||
//ClientCommand elementary actions
|
||||
void (*EA_Command)(int client, char *command );
|
||||
void (*EA_Say)(int client, char *str);
|
||||
void (*EA_SayTeam)(int client, char *str);
|
||||
//
|
||||
void (*EA_Action)(int client, int action);
|
||||
void (*EA_Gesture)(int client);
|
||||
void (*EA_Talk)(int client);
|
||||
void (*EA_Attack)(int client);
|
||||
void (*EA_Use)(int client);
|
||||
void (*EA_Respawn)(int client);
|
||||
void (*EA_MoveUp)(int client);
|
||||
void (*EA_MoveDown)(int client);
|
||||
void (*EA_MoveForward)(int client);
|
||||
void (*EA_MoveBack)(int client);
|
||||
void (*EA_MoveLeft)(int client);
|
||||
void (*EA_MoveRight)(int client);
|
||||
void (*EA_Crouch)(int client);
|
||||
void (*EA_Alt_Attack)(int client);
|
||||
void (*EA_ForcePower)(int client);
|
||||
|
||||
void (*EA_SelectWeapon)(int client, int weapon);
|
||||
void (*EA_Jump)(int client);
|
||||
void (*EA_DelayedJump)(int client);
|
||||
void (*EA_Move)(int client, vec3_t dir, float speed);
|
||||
void (*EA_View)(int client, vec3_t viewangles);
|
||||
//send regular input to the server
|
||||
void (*EA_EndRegular)(int client, float thinktime);
|
||||
void (*EA_GetInput)(int client, float thinktime, bot_input_t *input);
|
||||
void (*EA_ResetInput)(int client);
|
||||
} ea_export_t;
|
||||
|
||||
typedef struct ai_export_s
|
||||
{
|
||||
//-----------------------------------
|
||||
// be_ai_char.h
|
||||
//-----------------------------------
|
||||
int (*BotLoadCharacter)(char *charfile, float skill);
|
||||
void (*BotFreeCharacter)(int character);
|
||||
float (*Characteristic_Float)(int character, int index);
|
||||
float (*Characteristic_BFloat)(int character, int index, float min, float max);
|
||||
int (*Characteristic_Integer)(int character, int index);
|
||||
int (*Characteristic_BInteger)(int character, int index, int min, int max);
|
||||
void (*Characteristic_String)(int character, int index, char *buf, int size);
|
||||
//-----------------------------------
|
||||
// be_ai_chat.h
|
||||
//-----------------------------------
|
||||
// int (*BotAllocChatState)(void);
|
||||
// void (*BotFreeChatState)(int handle);
|
||||
// void (*BotQueueConsoleMessage)(int chatstate, int type, char *message);
|
||||
// void (*BotRemoveConsoleMessage)(int chatstate, int handle);
|
||||
// int (*BotNextConsoleMessage)(int chatstate, struct bot_consolemessage_s *cm);
|
||||
// int (*BotNumConsoleMessages)(int chatstate);
|
||||
// void (*BotInitialChat)(int chatstate, char *type, int mcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
|
||||
// int (*BotNumInitialChats)(int chatstate, char *type);
|
||||
// int (*BotReplyChat)(int chatstate, char *message, int mcontext, int vcontext, char *var0, char *var1, char *var2, char *var3, char *var4, char *var5, char *var6, char *var7);
|
||||
// int (*BotChatLength)(int chatstate);
|
||||
// void (*BotEnterChat)(int chatstate, int client, int sendto);
|
||||
// void (*BotGetChatMessage)(int chatstate, char *buf, int size);
|
||||
// int (*StringContains)(char *str1, char *str2, int casesensitive);
|
||||
// int (*BotFindMatch)(char *str, struct bot_match_s *match, unsigned long int context);
|
||||
// void (*BotMatchVariable)(struct bot_match_s *match, int variable, char *buf, int size);
|
||||
// void (*UnifyWhiteSpaces)(char *string);
|
||||
// void (*BotReplaceSynonyms)(char *string, unsigned long int context);
|
||||
// int (*BotLoadChatFile)(int chatstate, char *chatfile, char *chatname);
|
||||
// void (*BotSetChatGender)(int chatstate, int gender);
|
||||
// void (*BotSetChatName)(int chatstate, char *name, int client);
|
||||
//-----------------------------------
|
||||
// be_ai_goal.h
|
||||
//-----------------------------------
|
||||
void (*BotResetGoalState)(int goalstate);
|
||||
void (*BotResetAvoidGoals)(int goalstate);
|
||||
void (*BotRemoveFromAvoidGoals)(int goalstate, int number);
|
||||
void (*BotPushGoal)(int goalstate, struct bot_goal_s *goal);
|
||||
void (*BotPopGoal)(int goalstate);
|
||||
void (*BotEmptyGoalStack)(int goalstate);
|
||||
void (*BotDumpAvoidGoals)(int goalstate);
|
||||
void (*BotDumpGoalStack)(int goalstate);
|
||||
void (*BotGoalName)(int number, char *name, int size);
|
||||
int (*BotGetTopGoal)(int goalstate, struct bot_goal_s *goal);
|
||||
int (*BotGetSecondGoal)(int goalstate, struct bot_goal_s *goal);
|
||||
int (*BotChooseLTGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags);
|
||||
int (*BotChooseNBGItem)(int goalstate, vec3_t origin, int *inventory, int travelflags,
|
||||
struct bot_goal_s *ltg, float maxtime);
|
||||
int (*BotTouchingGoal)(vec3_t origin, struct bot_goal_s *goal);
|
||||
int (*BotItemGoalInVisButNotVisible)(int viewer, vec3_t eye, vec3_t viewangles, struct bot_goal_s *goal);
|
||||
int (*BotGetLevelItemGoal)(int index, char *classname, struct bot_goal_s *goal);
|
||||
int (*BotGetNextCampSpotGoal)(int num, struct bot_goal_s *goal);
|
||||
int (*BotGetMapLocationGoal)(char *name, struct bot_goal_s *goal);
|
||||
float (*BotAvoidGoalTime)(int goalstate, int number);
|
||||
void (*BotSetAvoidGoalTime)(int goalstate, int number, float avoidtime);
|
||||
void (*BotInitLevelItems)(void);
|
||||
void (*BotUpdateEntityItems)(void);
|
||||
int (*BotLoadItemWeights)(int goalstate, char *filename);
|
||||
void (*BotFreeItemWeights)(int goalstate);
|
||||
void (*BotInterbreedGoalFuzzyLogic)(int parent1, int parent2, int child);
|
||||
void (*BotSaveGoalFuzzyLogic)(int goalstate, char *filename);
|
||||
void (*BotMutateGoalFuzzyLogic)(int goalstate, float range);
|
||||
int (*BotAllocGoalState)(int client);
|
||||
void (*BotFreeGoalState)(int handle);
|
||||
//-----------------------------------
|
||||
// be_ai_move.h
|
||||
//-----------------------------------
|
||||
void (*BotResetMoveState)(int movestate);
|
||||
void (*BotMoveToGoal)(struct bot_moveresult_s *result, int movestate, struct bot_goal_s *goal, int travelflags);
|
||||
int (*BotMoveInDirection)(int movestate, vec3_t dir, float speed, int type);
|
||||
void (*BotResetAvoidReach)(int movestate);
|
||||
void (*BotResetLastAvoidReach)(int movestate);
|
||||
int (*BotReachabilityArea)(vec3_t origin, int testground);
|
||||
int (*BotMovementViewTarget)(int movestate, struct bot_goal_s *goal, int travelflags, float lookahead, vec3_t target);
|
||||
int (*BotPredictVisiblePosition)(vec3_t origin, int areanum, struct bot_goal_s *goal, int travelflags, vec3_t target);
|
||||
int (*BotAllocMoveState)(void);
|
||||
void (*BotFreeMoveState)(int handle);
|
||||
void (*BotInitMoveState)(int handle, struct bot_initmove_s *initmove);
|
||||
void (*BotAddAvoidSpot)(int movestate, vec3_t origin, float radius, int type);
|
||||
//-----------------------------------
|
||||
// be_ai_weap.h
|
||||
//-----------------------------------
|
||||
int (*BotChooseBestFightWeapon)(int weaponstate, int *inventory);
|
||||
void (*BotGetWeaponInfo)(int weaponstate, int weapon, struct weaponinfo_s *weaponinfo);
|
||||
int (*BotLoadWeaponWeights)(int weaponstate, char *filename);
|
||||
int (*BotAllocWeaponState)(void);
|
||||
void (*BotFreeWeaponState)(int weaponstate);
|
||||
void (*BotResetWeaponState)(int weaponstate);
|
||||
//-----------------------------------
|
||||
// be_ai_gen.h
|
||||
//-----------------------------------
|
||||
int (*GeneticParentsAndChildSelection)(int numranks, float *ranks, int *parent1, int *parent2, int *child);
|
||||
} ai_export_t;
|
||||
|
||||
//bot AI library imported functions
|
||||
typedef struct botlib_export_s
|
||||
{
|
||||
//Area Awareness System functions
|
||||
aas_export_t aas;
|
||||
//Elementary Action functions
|
||||
ea_export_t ea;
|
||||
//AI functions
|
||||
ai_export_t ai;
|
||||
//setup the bot library, returns BLERR_
|
||||
int (*BotLibSetup)(void);
|
||||
//shutdown the bot library, returns BLERR_
|
||||
int (*BotLibShutdown)(void);
|
||||
//sets a library variable returns BLERR_
|
||||
int (*BotLibVarSet)(char *var_name, char *value);
|
||||
//gets a library variable returns BLERR_
|
||||
int (*BotLibVarGet)(char *var_name, char *value, int size);
|
||||
|
||||
//sets a C-like define returns BLERR_
|
||||
int (*PC_AddGlobalDefine)(char *string);
|
||||
int (*PC_LoadSourceHandle)(const char *filename);
|
||||
int (*PC_FreeSourceHandle)(int handle);
|
||||
int (*PC_ReadTokenHandle)(int handle, pc_token_t *pc_token);
|
||||
int (*PC_SourceFileAndLine)(int handle, char *filename, int *line);
|
||||
int (*PC_LoadGlobalDefines)(const char* filename );
|
||||
void (*PC_RemoveAllGlobalDefines) ( void );
|
||||
|
||||
//start a frame in the bot library
|
||||
int (*BotLibStartFrame)(float time);
|
||||
//load a new map in the bot library
|
||||
int (*BotLibLoadMap)(const char *mapname);
|
||||
//entity updates
|
||||
int (*BotLibUpdateEntity)(int ent, bot_entitystate_t *state);
|
||||
//just for testing
|
||||
int (*Test)(int parm0, char *parm1, vec3_t parm2, vec3_t parm3);
|
||||
} botlib_export_t;
|
||||
|
||||
//linking of bot library
|
||||
botlib_export_t *GetBotLibAPI( int apiVersion, botlib_import_t *import );
|
||||
|
||||
/* Library variables:
|
||||
|
||||
name: default: module(s): description:
|
||||
|
||||
"basedir" "" l_utils.c base directory
|
||||
"gamedir" "" l_utils.c game directory
|
||||
"cddir" "" l_utils.c CD directory
|
||||
|
||||
"log" "0" l_log.c enable/disable creating a log file
|
||||
"maxclients" "4" be_interface.c maximum number of clients
|
||||
"maxentities" "1024" be_interface.c maximum number of entities
|
||||
"bot_developer" "0" be_interface.c bot developer mode
|
||||
|
||||
"phys_friction" "6" be_aas_move.c ground friction
|
||||
"phys_stopspeed" "100" be_aas_move.c stop speed
|
||||
"phys_gravity" "800" be_aas_move.c gravity value
|
||||
"phys_waterfriction" "1" be_aas_move.c water friction
|
||||
"phys_watergravity" "400" be_aas_move.c gravity in water
|
||||
"phys_maxvelocity" "320" be_aas_move.c maximum velocity
|
||||
"phys_maxwalkvelocity" "320" be_aas_move.c maximum walk velocity
|
||||
"phys_maxcrouchvelocity" "100" be_aas_move.c maximum crouch velocity
|
||||
"phys_maxswimvelocity" "150" be_aas_move.c maximum swim velocity
|
||||
"phys_walkaccelerate" "10" be_aas_move.c walk acceleration
|
||||
"phys_airaccelerate" "1" be_aas_move.c air acceleration
|
||||
"phys_swimaccelerate" "4" be_aas_move.c swim acceleration
|
||||
"phys_maxstep" "18" be_aas_move.c maximum step height
|
||||
"phys_maxsteepness" "0.7" be_aas_move.c maximum floor steepness
|
||||
"phys_maxbarrier" "32" be_aas_move.c maximum barrier height
|
||||
"phys_maxwaterjump" "19" be_aas_move.c maximum waterjump height
|
||||
"phys_jumpvel" "270" be_aas_move.c jump z velocity
|
||||
"phys_falldelta5" "40" be_aas_move.c
|
||||
"phys_falldelta10" "60" be_aas_move.c
|
||||
"rs_waterjump" "400" be_aas_move.c
|
||||
"rs_teleport" "50" be_aas_move.c
|
||||
"rs_barrierjump" "100" be_aas_move.c
|
||||
"rs_startcrouch" "300" be_aas_move.c
|
||||
"rs_startgrapple" "500" be_aas_move.c
|
||||
"rs_startwalkoffledge" "70" be_aas_move.c
|
||||
"rs_startjump" "300" be_aas_move.c
|
||||
"rs_rocketjump" "500" be_aas_move.c
|
||||
"rs_bfgjump" "500" be_aas_move.c
|
||||
"rs_jumppad" "250" be_aas_move.c
|
||||
"rs_aircontrolledjumppad" "300" be_aas_move.c
|
||||
"rs_funcbob" "300" be_aas_move.c
|
||||
"rs_startelevator" "50" be_aas_move.c
|
||||
"rs_falldamage5" "300" be_aas_move.c
|
||||
"rs_falldamage10" "500" be_aas_move.c
|
||||
"rs_maxjumpfallheight" "450" be_aas_move.c
|
||||
|
||||
"max_aaslinks" "4096" be_aas_sample.c maximum links in the AAS
|
||||
"max_routingcache" "4096" be_aas_route.c maximum routing cache size in KB
|
||||
"forceclustering" "0" be_aas_main.c force recalculation of clusters
|
||||
"forcereachability" "0" be_aas_main.c force recalculation of reachabilities
|
||||
"forcewrite" "0" be_aas_main.c force writing of aas file
|
||||
"aasoptimize" "0" be_aas_main.c enable aas optimization
|
||||
"sv_mapChecksum" "0" be_aas_main.c BSP file checksum
|
||||
"bot_visualizejumppads" "0" be_aas_reach.c visualize jump pads
|
||||
|
||||
"bot_reloadcharacters" "0" - reload bot character files
|
||||
"ai_gametype" "0" be_ai_goal.c game type
|
||||
"droppedweight" "1000" be_ai_goal.c additional dropped item weight
|
||||
"weapindex_rocketlauncher" "5" be_ai_move.c rl weapon index for rocket jumping
|
||||
"weapindex_bfg10k" "9" be_ai_move.c bfg weapon index for bfg jumping
|
||||
"weapindex_grapple" "10" be_ai_move.c grapple weapon index for grappling
|
||||
"entitytypemissile" "3" be_ai_move.c ET_MISSILE
|
||||
"offhandgrapple" "0" be_ai_move.c enable off hand grapple hook
|
||||
"cmd_grappleon" "grappleon" be_ai_move.c command to activate off hand grapple
|
||||
"cmd_grappleoff" "grappleoff" be_ai_move.c command to deactivate off hand grapple
|
||||
"itemconfig" "items.c" be_ai_goal.c item configuration file
|
||||
"weaponconfig" "weapons.c" be_ai_weap.c weapon configuration file
|
||||
"synfile" "syn.c" be_ai_chat.c file with synonyms
|
||||
"rndfile" "rnd.c" be_ai_chat.c file with random strings
|
||||
"matchfile" "match.c" be_ai_chat.c file with match strings
|
||||
"nochat" "0" be_ai_chat.c disable chats
|
||||
"max_messages" "1024" be_ai_chat.c console message heap size
|
||||
"max_weaponinfo" "32" be_ai_weap.c maximum number of weapon info
|
||||
"max_projectileinfo" "32" be_ai_weap.c maximum number of projectile info
|
||||
"max_iteminfo" "256" be_ai_goal.c maximum number of item info
|
||||
"max_levelitems" "256" be_ai_goal.c maximum number of level items
|
||||
|
||||
*/
|
||||
|
||||
124
codemp/game/chars.h
Normal file
124
codemp/game/chars.h
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
//===========================================================================
|
||||
//
|
||||
// Name: chars.h
|
||||
// Function: bot characteristics
|
||||
// Programmer: Mr Elusive (MrElusive@idsoftware.com)
|
||||
// Last update: 1999-09-08
|
||||
// Tab Size: 4 (real tabs)
|
||||
//===========================================================================
|
||||
|
||||
|
||||
//========================================================
|
||||
//========================================================
|
||||
//name
|
||||
#define CHARACTERISTIC_NAME 0 //string
|
||||
//gender of the bot
|
||||
#define CHARACTERISTIC_GENDER 1 //string ("male", "female", "it")
|
||||
//attack skill
|
||||
// > 0.0 && < 0.2 = don't move
|
||||
// > 0.3 && < 1.0 = aim at enemy during retreat
|
||||
// > 0.0 && < 0.4 = only move forward/backward
|
||||
// >= 0.4 && < 1.0 = circle strafing
|
||||
// > 0.7 && < 1.0 = random strafe direction change
|
||||
#define CHARACTERISTIC_ATTACK_SKILL 2 //float [0, 1]
|
||||
//weapon weight file
|
||||
#define CHARACTERISTIC_WEAPONWEIGHTS 3 //string
|
||||
//view angle difference to angle change factor
|
||||
#define CHARACTERISTIC_VIEW_FACTOR 4 //float <0, 1]
|
||||
//maximum view angle change
|
||||
#define CHARACTERISTIC_VIEW_MAXCHANGE 5 //float [1, 360]
|
||||
//reaction time in seconds
|
||||
#define CHARACTERISTIC_REACTIONTIME 6 //float [0, 5]
|
||||
//accuracy when aiming
|
||||
#define CHARACTERISTIC_AIM_ACCURACY 7 //float [0, 1]
|
||||
//weapon specific aim accuracy
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN 8 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_SHOTGUN 9 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER 10 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER 11 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_LIGHTNING 12
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN 13 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_RAILGUN 14
|
||||
#define CHARACTERISTIC_AIM_ACCURACY_BFG10K 15 //float [0, 1]
|
||||
//skill when aiming
|
||||
// > 0.0 && < 0.9 = aim is affected by enemy movement
|
||||
// > 0.4 && <= 0.8 = enemy linear leading
|
||||
// > 0.8 && <= 1.0 = enemy exact movement leading
|
||||
// > 0.5 && <= 1.0 = prediction shots when enemy is not visible
|
||||
// > 0.6 && <= 1.0 = splash damage by shooting nearby geometry
|
||||
#define CHARACTERISTIC_AIM_SKILL 16 //float [0, 1]
|
||||
//weapon specific aim skill
|
||||
#define CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER 17 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER 18 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_SKILL_PLASMAGUN 19 //float [0, 1]
|
||||
#define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1]
|
||||
//========================================================
|
||||
//chat
|
||||
//========================================================
|
||||
//file with chats
|
||||
#define CHARACTERISTIC_CHAT_FILE 21 //string
|
||||
//name of the chat character
|
||||
#define CHARACTERISTIC_CHAT_NAME 22 //string
|
||||
//characters per minute type speed
|
||||
#define CHARACTERISTIC_CHAT_CPM 23 //integer [1, 4000]
|
||||
//tendency to insult/praise
|
||||
#define CHARACTERISTIC_CHAT_INSULT 24 //float [0, 1]
|
||||
//tendency to chat misc
|
||||
#define CHARACTERISTIC_CHAT_MISC 25 //float [0, 1]
|
||||
//tendency to chat at start or end of level
|
||||
#define CHARACTERISTIC_CHAT_STARTENDLEVEL 26 //float [0, 1]
|
||||
//tendency to chat entering or exiting the game
|
||||
#define CHARACTERISTIC_CHAT_ENTEREXITGAME 27 //float [0, 1]
|
||||
//tendency to chat when killed someone
|
||||
#define CHARACTERISTIC_CHAT_KILL 28 //float [0, 1]
|
||||
//tendency to chat when died
|
||||
#define CHARACTERISTIC_CHAT_DEATH 29 //float [0, 1]
|
||||
//tendency to chat when enemy suicides
|
||||
#define CHARACTERISTIC_CHAT_ENEMYSUICIDE 30 //float [0, 1]
|
||||
//tendency to chat when hit while talking
|
||||
#define CHARACTERISTIC_CHAT_HITTALKING 31 //float [0, 1]
|
||||
//tendency to chat when bot was hit but didn't dye
|
||||
#define CHARACTERISTIC_CHAT_HITNODEATH 32 //float [0, 1]
|
||||
//tendency to chat when bot hit the enemy but enemy didn't dye
|
||||
#define CHARACTERISTIC_CHAT_HITNOKILL 33 //float [0, 1]
|
||||
//tendency to randomly chat
|
||||
#define CHARACTERISTIC_CHAT_RANDOM 34 //float [0, 1]
|
||||
//tendency to reply
|
||||
#define CHARACTERISTIC_CHAT_REPLY 35 //float [0, 1]
|
||||
//========================================================
|
||||
//movement
|
||||
//========================================================
|
||||
//tendency to crouch
|
||||
#define CHARACTERISTIC_CROUCHER 36 //float [0, 1]
|
||||
//tendency to jump
|
||||
#define CHARACTERISTIC_JUMPER 37 //float [0, 1]
|
||||
//tendency to walk
|
||||
#define CHARACTERISTIC_WALKER 48 //float [0, 1]
|
||||
//tendency to jump using a weapon
|
||||
#define CHARACTERISTIC_WEAPONJUMPING 38 //float [0, 1]
|
||||
//tendency to use the grapple hook when available
|
||||
#define CHARACTERISTIC_GRAPPLE_USER 39 //float [0, 1] //use this!!
|
||||
//========================================================
|
||||
//goal
|
||||
//========================================================
|
||||
//item weight file
|
||||
#define CHARACTERISTIC_ITEMWEIGHTS 40 //string
|
||||
//the aggression of the bot
|
||||
#define CHARACTERISTIC_AGGRESSION 41 //float [0, 1]
|
||||
//the self preservation of the bot (rockets near walls etc.)
|
||||
#define CHARACTERISTIC_SELFPRESERVATION 42 //float [0, 1]
|
||||
//how likely the bot is to take revenge
|
||||
#define CHARACTERISTIC_VENGEFULNESS 43 //float [0, 1] //use this!!
|
||||
//tendency to camp
|
||||
#define CHARACTERISTIC_CAMPER 44 //float [0, 1]
|
||||
//========================================================
|
||||
//========================================================
|
||||
//tendency to get easy frags
|
||||
#define CHARACTERISTIC_EASY_FRAGGER 45 //float [0, 1]
|
||||
//how alert the bot is (view distance)
|
||||
#define CHARACTERISTIC_ALERTNESS 46 //float [0, 1]
|
||||
//how much the bot fires it's weapon
|
||||
#define CHARACTERISTIC_FIRETHROTTLE 47 //float [0, 1]
|
||||
|
||||
5798
codemp/game/g_ICARUScb.c
Normal file
5798
codemp/game/g_ICARUScb.c
Normal file
File diff suppressed because it is too large
Load Diff
15
codemp/game/g_ICARUScb.h
Normal file
15
codemp/game/g_ICARUScb.h
Normal file
@@ -0,0 +1,15 @@
|
||||
int Q3_PlaySound( int taskID, int entID, const char *name, const char *channel );
|
||||
qboolean Q3_Set( int taskID, int entID, const char *type_name, const char *data );
|
||||
void Q3_Lerp2Pos( int taskID, int entID, vec3_t origin, vec3_t angles, float duration );
|
||||
void Q3_Lerp2Origin( int taskID, int entID, vec3_t origin, float duration );
|
||||
void Q3_Lerp2Angles( int taskID, int entID, vec3_t angles, float duration );
|
||||
int Q3_GetTag( int entID, const char *name, int lookup, vec3_t info );
|
||||
void Q3_Lerp2Start( int entID, int taskID, float duration );
|
||||
void Q3_Lerp2End( int entID, int taskID, float duration );
|
||||
void Q3_Use( int entID, const char *target );
|
||||
void Q3_Kill( int entID, const char *name );
|
||||
void Q3_Remove( int entID, const char *name );
|
||||
void Q3_Play( int taskID, int entID, const char *type, const char *name );
|
||||
int Q3_GetFloat( int entID, int type, const char *name, float *value );
|
||||
int Q3_GetVector( int entID, int type, const char *name, vec3_t value );
|
||||
int Q3_GetString( int entID, int type, const char *name, char **value );
|
||||
3781
codemp/game/g_active.c
Normal file
3781
codemp/game/g_active.c
Normal file
File diff suppressed because it is too large
Load Diff
343
codemp/game/g_arenas.c
Normal file
343
codemp/game/g_arenas.c
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
//
|
||||
// g_arenas.c
|
||||
//
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
gentity_t *podium1;
|
||||
gentity_t *podium2;
|
||||
gentity_t *podium3;
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
UpdateTournamentInfo
|
||||
==================
|
||||
*/
|
||||
void UpdateTournamentInfo( void ) {
|
||||
int i;
|
||||
gentity_t *player;
|
||||
int playerClientNum;
|
||||
int n, accuracy, perfect, msglen;
|
||||
int buflen;
|
||||
int score1, score2;
|
||||
qboolean won;
|
||||
char buf[32];
|
||||
char msg[MAX_STRING_CHARS];
|
||||
|
||||
// find the real player
|
||||
player = NULL;
|
||||
for (i = 0; i < level.maxclients; i++ ) {
|
||||
player = &g_entities[i];
|
||||
if ( !player->inuse ) {
|
||||
continue;
|
||||
}
|
||||
if ( !( player->r.svFlags & SVF_BOT ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this should never happen!
|
||||
if ( !player || i == level.maxclients ) {
|
||||
return;
|
||||
}
|
||||
playerClientNum = i;
|
||||
|
||||
CalculateRanks();
|
||||
|
||||
if ( level.clients[playerClientNum].sess.sessionTeam == TEAM_SPECTATOR ) {
|
||||
Com_sprintf( msg, sizeof(msg), "postgame %i %i 0 0 0 0 0 0 0 0 0 0 0", level.numNonSpectatorClients, playerClientNum );
|
||||
}
|
||||
else {
|
||||
if( player->client->accuracy_shots ) {
|
||||
accuracy = player->client->accuracy_hits * 100 / player->client->accuracy_shots;
|
||||
}
|
||||
else {
|
||||
accuracy = 0;
|
||||
}
|
||||
won = qfalse;
|
||||
if (g_gametype.integer >= GT_CTF) {
|
||||
score1 = level.teamScores[TEAM_RED];
|
||||
score2 = level.teamScores[TEAM_BLUE];
|
||||
if (level.clients[playerClientNum].sess.sessionTeam == TEAM_RED) {
|
||||
won = (level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]);
|
||||
} else {
|
||||
won = (level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED]);
|
||||
}
|
||||
} else {
|
||||
if (&level.clients[playerClientNum] == &level.clients[ level.sortedClients[0] ]) {
|
||||
won = qtrue;
|
||||
score1 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
|
||||
score2 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE];
|
||||
} else {
|
||||
score2 = level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE];
|
||||
score1 = level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE];
|
||||
}
|
||||
}
|
||||
if (won && player->client->ps.persistant[PERS_KILLED] == 0) {
|
||||
perfect = 1;
|
||||
} else {
|
||||
perfect = 0;
|
||||
}
|
||||
Com_sprintf( msg, sizeof(msg), "postgame %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.numNonSpectatorClients, playerClientNum, accuracy,
|
||||
player->client->ps.persistant[PERS_IMPRESSIVE_COUNT], player->client->ps.persistant[PERS_EXCELLENT_COUNT],player->client->ps.persistant[PERS_DEFEND_COUNT],
|
||||
player->client->ps.persistant[PERS_ASSIST_COUNT], player->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], player->client->ps.persistant[PERS_SCORE],
|
||||
perfect, score1, score2, level.time, player->client->ps.persistant[PERS_CAPTURES] );
|
||||
}
|
||||
|
||||
msglen = strlen( msg );
|
||||
for( i = 0; i < level.numNonSpectatorClients; i++ ) {
|
||||
n = level.sortedClients[i];
|
||||
Com_sprintf( buf, sizeof(buf), " %i %i %i", n, level.clients[n].ps.persistant[PERS_RANK], level.clients[n].ps.persistant[PERS_SCORE] );
|
||||
buflen = strlen( buf );
|
||||
if( msglen + buflen + 1 >= sizeof(msg) ) {
|
||||
break;
|
||||
}
|
||||
strcat( msg, buf );
|
||||
}
|
||||
trap_SendConsoleCommand( EXEC_APPEND, msg );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) {
|
||||
gentity_t *body;
|
||||
vec3_t vec;
|
||||
vec3_t f, r, u;
|
||||
|
||||
body = G_Spawn();
|
||||
if ( !body ) {
|
||||
G_Printf( S_COLOR_RED "ERROR: out of gentities\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
body->classname = ent->client->pers.netname;
|
||||
body->client = ent->client;
|
||||
body->s = ent->s;
|
||||
body->s.eType = ET_PLAYER; // could be ET_INVISIBLE
|
||||
body->s.eFlags = 0; // clear EF_TALK, etc
|
||||
body->s.powerups = 0; // clear powerups
|
||||
body->s.loopSound = 0; // clear lava burning
|
||||
body->s.number = body - g_entities;
|
||||
body->timestamp = level.time;
|
||||
body->physicsObject = qtrue;
|
||||
body->physicsBounce = 0; // don't bounce
|
||||
body->s.event = 0;
|
||||
body->s.pos.trType = TR_STATIONARY;
|
||||
body->s.groundEntityNum = ENTITYNUM_WORLD;
|
||||
body->s.legsAnim = WeaponReadyAnim[ent->s.weapon];
|
||||
body->s.torsoAnim = WeaponReadyAnim[ent->s.weapon];
|
||||
if( body->s.weapon == WP_NONE ) {
|
||||
body->s.weapon = WP_BRYAR_PISTOL;
|
||||
}
|
||||
if( body->s.weapon == WP_SABER) {
|
||||
body->s.torsoAnim = BOTH_STAND2;
|
||||
}
|
||||
body->s.event = 0;
|
||||
body->r.svFlags = ent->r.svFlags;
|
||||
VectorCopy (ent->r.mins, body->r.mins);
|
||||
VectorCopy (ent->r.maxs, body->r.maxs);
|
||||
VectorCopy (ent->r.absmin, body->r.absmin);
|
||||
VectorCopy (ent->r.absmax, body->r.absmax);
|
||||
body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
|
||||
body->r.contents = CONTENTS_BODY;
|
||||
body->r.ownerNum = ent->r.ownerNum;
|
||||
body->takedamage = qfalse;
|
||||
|
||||
VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec );
|
||||
vectoangles( vec, body->s.apos.trBase );
|
||||
body->s.apos.trBase[PITCH] = 0;
|
||||
body->s.apos.trBase[ROLL] = 0;
|
||||
|
||||
AngleVectors( body->s.apos.trBase, f, r, u );
|
||||
VectorMA( pad->r.currentOrigin, offset[0], f, vec );
|
||||
VectorMA( vec, offset[1], r, vec );
|
||||
VectorMA( vec, offset[2], u, vec );
|
||||
|
||||
G_SetOrigin( body, vec );
|
||||
|
||||
trap_LinkEntity (body);
|
||||
|
||||
body->count = place;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
static void CelebrateStop( gentity_t *player ) {
|
||||
int anim;
|
||||
|
||||
if( player->s.weapon == WP_SABER) {
|
||||
anim = BOTH_STAND2;
|
||||
}
|
||||
else {
|
||||
anim = WeaponReadyAnim[player->s.weapon];
|
||||
}
|
||||
player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
|
||||
}
|
||||
|
||||
|
||||
#define TIMER_GESTURE (34*66+50)
|
||||
static void CelebrateStart( gentity_t *player ) {
|
||||
player->s.torsoAnim = ( ( player->s.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | BOTH_TALKGESTURE1;
|
||||
player->nextthink = level.time + TIMER_GESTURE;
|
||||
player->think = CelebrateStop;
|
||||
|
||||
|
||||
// player->client->ps.events[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = EV_TAUNT;
|
||||
// player->client->ps.eventParms[player->client->ps.eventSequence & (MAX_PS_EVENTS-1)] = 0;
|
||||
// player->client->ps.eventSequence++;
|
||||
|
||||
G_AddEvent(player, EV_TAUNT, 0);
|
||||
}
|
||||
|
||||
|
||||
static vec3_t offsetFirst = {0, 0, 74};
|
||||
static vec3_t offsetSecond = {-10, 60, 54};
|
||||
static vec3_t offsetThird = {-19, -60, 45};
|
||||
|
||||
static void PodiumPlacementThink( gentity_t *podium ) {
|
||||
vec3_t vec;
|
||||
vec3_t origin;
|
||||
vec3_t f, r, u;
|
||||
|
||||
podium->nextthink = level.time + 100;
|
||||
|
||||
AngleVectors( level.intermission_angle, vec, NULL, NULL );
|
||||
VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin );
|
||||
origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" );
|
||||
G_SetOrigin( podium, origin );
|
||||
|
||||
if( podium1 ) {
|
||||
VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
|
||||
vectoangles( vec, podium1->s.apos.trBase );
|
||||
podium1->s.apos.trBase[PITCH] = 0;
|
||||
podium1->s.apos.trBase[ROLL] = 0;
|
||||
|
||||
AngleVectors( podium1->s.apos.trBase, f, r, u );
|
||||
VectorMA( podium->r.currentOrigin, offsetFirst[0], f, vec );
|
||||
VectorMA( vec, offsetFirst[1], r, vec );
|
||||
VectorMA( vec, offsetFirst[2], u, vec );
|
||||
|
||||
G_SetOrigin( podium1, vec );
|
||||
}
|
||||
|
||||
if( podium2 ) {
|
||||
VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
|
||||
vectoangles( vec, podium2->s.apos.trBase );
|
||||
podium2->s.apos.trBase[PITCH] = 0;
|
||||
podium2->s.apos.trBase[ROLL] = 0;
|
||||
|
||||
AngleVectors( podium2->s.apos.trBase, f, r, u );
|
||||
VectorMA( podium->r.currentOrigin, offsetSecond[0], f, vec );
|
||||
VectorMA( vec, offsetSecond[1], r, vec );
|
||||
VectorMA( vec, offsetSecond[2], u, vec );
|
||||
|
||||
G_SetOrigin( podium2, vec );
|
||||
}
|
||||
|
||||
if( podium3 ) {
|
||||
VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
|
||||
vectoangles( vec, podium3->s.apos.trBase );
|
||||
podium3->s.apos.trBase[PITCH] = 0;
|
||||
podium3->s.apos.trBase[ROLL] = 0;
|
||||
|
||||
AngleVectors( podium3->s.apos.trBase, f, r, u );
|
||||
VectorMA( podium->r.currentOrigin, offsetThird[0], f, vec );
|
||||
VectorMA( vec, offsetThird[1], r, vec );
|
||||
VectorMA( vec, offsetThird[2], u, vec );
|
||||
|
||||
G_SetOrigin( podium3, vec );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gentity_t *SpawnPodium( void ) {
|
||||
gentity_t *podium;
|
||||
vec3_t vec;
|
||||
vec3_t origin;
|
||||
|
||||
podium = G_Spawn();
|
||||
if ( !podium ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
podium->classname = "podium";
|
||||
podium->s.eType = ET_GENERAL;
|
||||
podium->s.number = podium - g_entities;
|
||||
podium->clipmask = CONTENTS_SOLID;
|
||||
podium->r.contents = CONTENTS_SOLID;
|
||||
podium->s.modelindex = G_ModelIndex( SP_PODIUM_MODEL );
|
||||
|
||||
AngleVectors( level.intermission_angle, vec, NULL, NULL );
|
||||
VectorMA( level.intermission_origin, trap_Cvar_VariableIntegerValue( "g_podiumDist" ), vec, origin );
|
||||
origin[2] -= trap_Cvar_VariableIntegerValue( "g_podiumDrop" );
|
||||
G_SetOrigin( podium, origin );
|
||||
|
||||
VectorSubtract( level.intermission_origin, podium->r.currentOrigin, vec );
|
||||
podium->s.apos.trBase[YAW] = vectoyaw( vec );
|
||||
trap_LinkEntity (podium);
|
||||
|
||||
podium->think = PodiumPlacementThink;
|
||||
podium->nextthink = level.time + 100;
|
||||
return podium;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================
|
||||
//SpawnModelsOnVictoryPads
|
||||
//==================
|
||||
|
||||
void SpawnModelsOnVictoryPads( void ) {
|
||||
gentity_t *player;
|
||||
gentity_t *podium;
|
||||
|
||||
podium1 = NULL;
|
||||
podium2 = NULL;
|
||||
podium3 = NULL;
|
||||
|
||||
podium = SpawnPodium();
|
||||
|
||||
player = SpawnModelOnVictoryPad( podium, offsetFirst, &g_entities[level.sortedClients[0]],
|
||||
level.clients[ level.sortedClients[0] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
|
||||
if ( player ) {
|
||||
player->nextthink = level.time + 2000;
|
||||
player->think = CelebrateStart;
|
||||
podium1 = player;
|
||||
}
|
||||
|
||||
player = SpawnModelOnVictoryPad( podium, offsetSecond, &g_entities[level.sortedClients[1]],
|
||||
level.clients[ level.sortedClients[1] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
|
||||
if ( player ) {
|
||||
podium2 = player;
|
||||
}
|
||||
|
||||
if ( level.numNonSpectatorClients > 2 ) {
|
||||
player = SpawnModelOnVictoryPad( podium, offsetThird, &g_entities[level.sortedClients[2]],
|
||||
level.clients[ level.sortedClients[2] ].ps.persistant[PERS_RANK] &~ RANK_TIED_FLAG );
|
||||
if ( player ) {
|
||||
podium3 = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===============
|
||||
//Svcmd_AbortPodium_f
|
||||
//===============
|
||||
|
||||
void Svcmd_AbortPodium_f( void ) {
|
||||
if( g_gametype.integer != GT_SINGLE_PLAYER ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( podium1 ) {
|
||||
podium1->nextthink = level.time;
|
||||
podium1->think = CelebrateStop;
|
||||
}
|
||||
}
|
||||
*/
|
||||
1316
codemp/game/g_bot.c
Normal file
1316
codemp/game/g_bot.c
Normal file
File diff suppressed because it is too large
Load Diff
3935
codemp/game/g_client.c
Normal file
3935
codemp/game/g_client.c
Normal file
File diff suppressed because it is too large
Load Diff
3958
codemp/game/g_cmds.c
Normal file
3958
codemp/game/g_cmds.c
Normal file
File diff suppressed because it is too large
Load Diff
5651
codemp/game/g_combat.c
Normal file
5651
codemp/game/g_combat.c
Normal file
File diff suppressed because it is too large
Load Diff
232
codemp/game/g_exphysics.c
Normal file
232
codemp/game/g_exphysics.c
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright (C) 2000-2002 Raven Software, Inc.
|
||||
//
|
||||
/*****************************************************************************
|
||||
* name: g_exphysics.c
|
||||
*
|
||||
* desc: Custom physics system (Expensive Physics)
|
||||
*
|
||||
* $Author: osman $
|
||||
* $Revision: 1.7 $
|
||||
*
|
||||
*****************************************************************************/
|
||||
#pragma warning(disable : 4701) //local variable may be used without having been initialized
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
#define MAX_GRAVITY_PULL 512
|
||||
|
||||
//Run physics on the object (purely origin-related) using custom epVelocity entity
|
||||
//state value. Origin smoothing on the client is expected to compensate for choppy
|
||||
//movement.
|
||||
void G_RunExPhys(gentity_t *ent, float gravity, float mass, float bounce, qboolean autoKill, int *g2Bolts, int numG2Bolts)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t projectedOrigin;
|
||||
vec3_t vNorm;
|
||||
vec3_t ground;
|
||||
float velScaling = 0.1f;
|
||||
float vTotal = 0.0f;
|
||||
|
||||
assert(mass <= 1.0f && mass >= 0.01f);
|
||||
|
||||
if (gravity)
|
||||
{ //factor it in before we do anything.
|
||||
VectorCopy(ent->r.currentOrigin, ground);
|
||||
ground[2] -= 0.1f;
|
||||
|
||||
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ground, ent->s.number, ent->clipmask);
|
||||
|
||||
if (tr.fraction == 1.0f)
|
||||
{
|
||||
ent->s.groundEntityNum = ENTITYNUM_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.groundEntityNum = tr.entityNum;
|
||||
}
|
||||
|
||||
if (ent->s.groundEntityNum == ENTITYNUM_NONE)
|
||||
{
|
||||
ent->epGravFactor += gravity;
|
||||
|
||||
if (ent->epGravFactor > MAX_GRAVITY_PULL)
|
||||
{ //cap it off if needed
|
||||
ent->epGravFactor = MAX_GRAVITY_PULL;
|
||||
}
|
||||
|
||||
ent->epVelocity[2] -= ent->epGravFactor;
|
||||
}
|
||||
else
|
||||
{ //if we're sitting on something then reset the gravity factor.
|
||||
ent->epGravFactor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ent->epVelocity[0] && !ent->epVelocity[1] && !ent->epVelocity[2])
|
||||
{ //nothing to do if we have no velocity even after gravity.
|
||||
if (ent->touch)
|
||||
{ //call touch if we're in something
|
||||
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, ent->clipmask);
|
||||
if (tr.startsolid || tr.allsolid)
|
||||
{
|
||||
ent->touch(ent, &g_entities[tr.entityNum], &tr);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//get the projected origin based on velocity.
|
||||
VectorMA(ent->r.currentOrigin, velScaling, ent->epVelocity, projectedOrigin);
|
||||
|
||||
VectorScale(ent->epVelocity, 1.0f-mass, ent->epVelocity); //scale it down based on mass
|
||||
|
||||
VectorCopy(ent->epVelocity, vNorm);
|
||||
vTotal = VectorNormalize(vNorm);
|
||||
|
||||
if (vTotal < 1 && ent->s.groundEntityNum != ENTITYNUM_NONE)
|
||||
{ //we've pretty much stopped moving anyway, just clear it out then.
|
||||
VectorClear(ent->epVelocity);
|
||||
ent->epGravFactor = 0;
|
||||
trap_LinkEntity(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->ghoul2 && g2Bolts)
|
||||
{ //Have we been passed a bolt index array to clip against points on the skeleton?
|
||||
vec3_t tMins, tMaxs;
|
||||
vec3_t trajDif;
|
||||
vec3_t gbmAngles;
|
||||
vec3_t boneOrg;
|
||||
vec3_t projectedBoneOrg;
|
||||
vec3_t collisionRootPos;
|
||||
mdxaBone_t matrix;
|
||||
trace_t bestCollision;
|
||||
qboolean hasFirstCollision = qfalse;
|
||||
int i = 0;
|
||||
|
||||
//Maybe we could use a trap call and get the default radius for the bone specified,
|
||||
//but this will do at least for now.
|
||||
VectorSet(tMins, -3, -3, -3);
|
||||
VectorSet(tMaxs, 3, 3, 3);
|
||||
|
||||
gbmAngles[PITCH] = gbmAngles[ROLL] = 0;
|
||||
gbmAngles[YAW] = ent->s.apos.trBase[YAW];
|
||||
|
||||
//Get the difference relative to the entity origin and projected origin, to add to each bolt position.
|
||||
VectorSubtract(ent->r.currentOrigin, projectedOrigin, trajDif);
|
||||
|
||||
while (i < numG2Bolts)
|
||||
{
|
||||
//Get the position of the actual bolt for this frame
|
||||
trap_G2API_GetBoltMatrix(ent->ghoul2, 0, g2Bolts[i], &matrix, gbmAngles, ent->r.currentOrigin, level.time, NULL, ent->modelScale);
|
||||
BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, boneOrg);
|
||||
|
||||
//Now add the projected positional difference into the result
|
||||
VectorAdd(boneOrg, trajDif, projectedBoneOrg);
|
||||
|
||||
trap_Trace(&tr, boneOrg, tMins, tMaxs, projectedBoneOrg, ent->s.number, ent->clipmask);
|
||||
|
||||
if (tr.fraction != 1.0 || tr.startsolid || tr.allsolid)
|
||||
{ //we've hit something
|
||||
//Store the "deepest" collision we have
|
||||
if (!hasFirstCollision)
|
||||
{ //don't have one yet so just use this one
|
||||
bestCollision = tr;
|
||||
VectorCopy(boneOrg, collisionRootPos);
|
||||
hasFirstCollision = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tr.allsolid && !bestCollision.allsolid)
|
||||
{ //If the whole trace is solid then this one is deeper
|
||||
bestCollision = tr;
|
||||
VectorCopy(boneOrg, collisionRootPos);
|
||||
}
|
||||
else if (tr.startsolid && !bestCollision.startsolid && !bestCollision.allsolid)
|
||||
{ //Next deepest is if it's startsolid
|
||||
bestCollision = tr;
|
||||
VectorCopy(boneOrg, collisionRootPos);
|
||||
}
|
||||
else if (!bestCollision.startsolid && !bestCollision.allsolid &&
|
||||
tr.fraction < bestCollision.fraction)
|
||||
{ //and finally, if neither is startsolid/allsolid, but the new one has a smaller fraction, then it's closer to an impact point so we will use it
|
||||
bestCollision = tr;
|
||||
VectorCopy(boneOrg, collisionRootPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (hasFirstCollision)
|
||||
{ //at least one bolt collided
|
||||
//We'll get the offset between the collided bolt and endpos, then trace there
|
||||
//from the origin so that our desired position becomes that point.
|
||||
VectorSubtract(collisionRootPos, bestCollision.endpos, trajDif);
|
||||
|
||||
VectorAdd(ent->r.currentOrigin, trajDif, projectedOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
//If we didn't collide with any bolts projectedOrigin will still be the original desired
|
||||
//projected position so all is well. If we did then projectedOrigin will be modified
|
||||
//to provide us with a relative position which does not place the bolt in a solid.
|
||||
trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, projectedOrigin, ent->s.number, ent->clipmask);
|
||||
|
||||
if (tr.startsolid || tr.allsolid)
|
||||
{ //can't go anywhere from here
|
||||
#ifdef _DEBUG
|
||||
Com_Printf("ExPhys object in solid (%i)\n", ent->s.number);
|
||||
#endif
|
||||
if (autoKill)
|
||||
{
|
||||
ent->think = G_FreeEntity;
|
||||
ent->nextthink = level.time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Go ahead and set it to the trace endpoint regardless of what it hit
|
||||
G_SetOrigin(ent, tr.endpos);
|
||||
trap_LinkEntity(ent);
|
||||
|
||||
if (tr.fraction == 1.0f)
|
||||
{ //Nothing was in the way.
|
||||
return;
|
||||
}
|
||||
|
||||
if (bounce)
|
||||
{
|
||||
vTotal *= bounce; //scale it by bounce
|
||||
|
||||
VectorScale(tr.plane.normal, vTotal, vNorm); //scale the trace plane normal by the bounce factor
|
||||
|
||||
if (vNorm[2] > 0)
|
||||
{
|
||||
ent->epGravFactor -= vNorm[2]*(1.0f-mass); //The lighter it is the more gravity will be reduced by bouncing vertically.
|
||||
if (ent->epGravFactor < 0)
|
||||
{
|
||||
ent->epGravFactor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//call touch first so we can check velocity upon impact if we want
|
||||
if (tr.entityNum != ENTITYNUM_NONE && ent->touch)
|
||||
{ //then call the touch function
|
||||
ent->touch(ent, &g_entities[tr.entityNum], &tr);
|
||||
}
|
||||
|
||||
VectorAdd(ent->epVelocity, vNorm, ent->epVelocity); //add it into the existing velocity.
|
||||
}
|
||||
else
|
||||
{ //if no bounce, kill when it hits something.
|
||||
ent->epVelocity[0] = 0;
|
||||
ent->epVelocity[1] = 0;
|
||||
|
||||
if (!gravity)
|
||||
{
|
||||
ent->epVelocity[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
4
codemp/game/g_headers.h
Normal file
4
codemp/game/g_headers.h
Normal file
@@ -0,0 +1,4 @@
|
||||
//dummy file so as to not break SP's game PCH stuff.
|
||||
//in mp we don't even bother with pch since the point of
|
||||
//the code is to be totally cross-platform (or at least
|
||||
//as much so as possible)
|
||||
3256
codemp/game/g_items.c
Normal file
3256
codemp/game/g_items.c
Normal file
File diff suppressed because it is too large
Load Diff
1976
codemp/game/g_local.h
Normal file
1976
codemp/game/g_local.h
Normal file
File diff suppressed because it is too large
Load Diff
1776
codemp/game/g_log.c
Normal file
1776
codemp/game/g_log.c
Normal file
File diff suppressed because it is too large
Load Diff
4233
codemp/game/g_main.c
Normal file
4233
codemp/game/g_main.c
Normal file
File diff suppressed because it is too large
Load Diff
42
codemp/game/g_mem.c
Normal file
42
codemp/game/g_mem.c
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
//
|
||||
// g_mem.c
|
||||
//
|
||||
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
//#define POOLSIZE (256 * 1024)
|
||||
#define POOLSIZE (128 * 1024)
|
||||
|
||||
static char memoryPool[POOLSIZE];
|
||||
static int allocPoint;
|
||||
|
||||
void *G_Alloc( int size ) {
|
||||
char *p;
|
||||
|
||||
if ( g_debugAlloc.integer ) {
|
||||
G_Printf( "G_Alloc of %i bytes (%i left)\n", size, POOLSIZE - allocPoint - ( ( size + 31 ) & ~31 ) );
|
||||
}
|
||||
|
||||
if ( allocPoint + size > POOLSIZE ) {
|
||||
G_Error( "G_Alloc: failed on allocation of %i bytes\n", size ); // bk010103 - was %u, but is signed
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = &memoryPool[allocPoint];
|
||||
|
||||
allocPoint += ( size + 31 ) & ~31;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void G_InitMemory( void ) {
|
||||
allocPoint = 0;
|
||||
}
|
||||
|
||||
void Svcmd_GameMem_f( void ) {
|
||||
G_Printf( "Game memory status: %i out of %i bytes allocated\n", allocPoint, POOLSIZE );
|
||||
}
|
||||
3287
codemp/game/g_misc.c
Normal file
3287
codemp/game/g_misc.c
Normal file
File diff suppressed because it is too large
Load Diff
1010
codemp/game/g_missile.c
Normal file
1010
codemp/game/g_missile.c
Normal file
File diff suppressed because it is too large
Load Diff
3267
codemp/game/g_mover.c
Normal file
3267
codemp/game/g_mover.c
Normal file
File diff suppressed because it is too large
Load Diff
1928
codemp/game/g_nav.c
Normal file
1928
codemp/game/g_nav.c
Normal file
File diff suppressed because it is too large
Load Diff
79
codemp/game/g_nav.h
Normal file
79
codemp/game/g_nav.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef __G_NAV_H__
|
||||
#define __G_NAV_H__
|
||||
|
||||
//This file is shared by the exe nav code.
|
||||
//If you modify it without recompiling the exe with new code, there could be issues.
|
||||
|
||||
#define WAYPOINT_NONE -1
|
||||
|
||||
//#define MAX_STORED_WAYPOINTS 512//???
|
||||
#define MAX_STORED_WAYPOINTS 64
|
||||
#define MAX_WAYPOINT_REACHED_DIST_SQUARED 1024 //32 squared
|
||||
#define MAX_COLL_AVOID_DIST 128
|
||||
#define NAVGOAL_USE_RADIUS 16384 //Used to force the waypoint_navgoals with a manually set radius to actually do a DistanceSquared check, not just bounds overlap
|
||||
|
||||
#define MIN_STOP_DIST 64
|
||||
#define MIN_BLOCKED_SPEECH_TIME 4000
|
||||
#define MIN_DOOR_BLOCK_DIST 16
|
||||
#define MIN_DOOR_BLOCK_DIST_SQR ( MIN_DOOR_BLOCK_DIST * MIN_DOOR_BLOCK_DIST )
|
||||
#define SHOVE_SPEED 200
|
||||
#define SHOVE_LIFT 10
|
||||
#define MAX_RADIUS_CHECK 1024
|
||||
#define YAW_ITERATIONS 16
|
||||
|
||||
// This is probably wrong - VVFIXME
|
||||
// Some kind of wacky code sharing going on here, but we need these things
|
||||
// in g_navnew.c -- which is now C++ code in the GAME on Xbox, so the
|
||||
// original test fails.
|
||||
#if !defined(__cplusplus) || (defined(_XBOX) && defined(QAGAME)) || (defined(__linux__) && defined(QAGAME))
|
||||
//rww - Rest of defines here are also shared in exe, do not modify.
|
||||
#define __NEWCOLLECT 1
|
||||
|
||||
#define _HARD_CONNECT 1
|
||||
|
||||
//Node flags
|
||||
#define NF_ANY 0
|
||||
//#define NF_CLEAR_LOS 0x00000001
|
||||
#define NF_CLEAR_PATH 0x00000002
|
||||
#define NF_RECALC 0x00000004
|
||||
|
||||
//Edge flags
|
||||
#define EFLAG_NONE 0
|
||||
#define EFLAG_BLOCKED 0x00000001
|
||||
#define EFLAG_FAILED 0x00000002
|
||||
|
||||
//Miscellaneous defines
|
||||
#define NODE_NONE -1
|
||||
#define NAV_HEADER_ID 'JNV5'
|
||||
#define NODE_HEADER_ID 'NODE'
|
||||
|
||||
//this stuff is local and can be modified, don't even show it to the engine.
|
||||
extern qboolean navCalculatePaths;
|
||||
|
||||
extern qboolean NAVDEBUG_showNodes;
|
||||
extern qboolean NAVDEBUG_showRadius;
|
||||
extern qboolean NAVDEBUG_showEdges;
|
||||
extern qboolean NAVDEBUG_showTestPath;
|
||||
extern qboolean NAVDEBUG_showEnemyPath;
|
||||
extern qboolean NAVDEBUG_showCombatPoints;
|
||||
extern qboolean NAVDEBUG_showNavGoals;
|
||||
extern qboolean NAVDEBUG_showCollision;
|
||||
|
||||
extern int NAVDEBUG_curGoal;
|
||||
|
||||
void NAV_Shutdown( void );
|
||||
void NAV_CalculatePaths( const char *filename, int checksum );
|
||||
void NAV_CalculateSquadPaths( const char *filename, int checksum );
|
||||
|
||||
void NAV_ShowDebugInfo( void );
|
||||
|
||||
int NAV_GetNearestNode( gentity_t *self, int lastNode );
|
||||
extern int NAV_TestBestNode( gentity_t *self, int startID, int endID, qboolean failEdge );
|
||||
|
||||
qboolean NPC_GetMoveDirection( vec3_t out, float *distance );
|
||||
void NPC_MoveToGoalExt( vec3_t point );
|
||||
void NAV_FindPlayerWaypoint( int clNum );
|
||||
qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t *trace, int clipmask );
|
||||
#endif
|
||||
|
||||
#endif //#ifndef __G_NAV_H__
|
||||
865
codemp/game/g_navnew.c
Normal file
865
codemp/game/g_navnew.c
Normal file
@@ -0,0 +1,865 @@
|
||||
#include "b_local.h"
|
||||
#include "g_nav.h"
|
||||
|
||||
qboolean NAV_CheckAhead( gentity_t *self, vec3_t end, trace_t *trace, int clipmask );
|
||||
qboolean NAV_TestForBlocked( gentity_t *self, gentity_t *goal, gentity_t *blocker, float distance, int *flags );
|
||||
|
||||
void G_Line( vec3_t start, vec3_t end, vec3_t color, float alpha );
|
||||
void G_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha );
|
||||
void G_CubeOutline( vec3_t mins, vec3_t maxs, int time, unsigned int color, float alpha );
|
||||
void G_DrawEdge( vec3_t start, vec3_t end, int type );
|
||||
void G_DrawNode( vec3_t origin, int type );
|
||||
void G_DrawCombatPoint( vec3_t origin, int type );
|
||||
void TAG_ShowTags( int flags );
|
||||
|
||||
qboolean NAV_CheckNodeFailedForEnt( gentity_t *ent, int nodeNum )
|
||||
{
|
||||
int j;
|
||||
|
||||
//FIXME: must be a better way to do this
|
||||
for ( j = 0; j < MAX_FAILED_NODES; j++ )
|
||||
{
|
||||
if ( ent->failedWaypoints[j] == nodeNum+1 )//+1 because 0 is a valid nodeNum, but also the default
|
||||
{//we failed against this node
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
NPC_UnBlocked
|
||||
-------------------------
|
||||
*/
|
||||
void NPC_ClearBlocked( gentity_t *self )
|
||||
{
|
||||
if ( self->NPC == NULL )
|
||||
return;
|
||||
|
||||
//self->NPC->aiFlags &= ~NPCAI_BLOCKED;
|
||||
self->NPC->blockingEntNum = ENTITYNUM_NONE;
|
||||
}
|
||||
|
||||
void NPC_SetBlocked( gentity_t *self, gentity_t *blocker )
|
||||
{
|
||||
if ( self->NPC == NULL )
|
||||
return;
|
||||
|
||||
//self->NPC->aiFlags |= NPCAI_BLOCKED;
|
||||
self->NPC->blockedSpeechDebounceTime = level.time + MIN_BLOCKED_SPEECH_TIME + ( random() * 4000 );
|
||||
self->NPC->blockingEntNum = blocker->s.number;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_ClearPathBetweenPoints
|
||||
-------------------------
|
||||
*/
|
||||
int NAVNEW_ClearPathBetweenPoints(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int ignore, int clipmask)
|
||||
{
|
||||
trace_t trace;
|
||||
|
||||
//Test if they're even conceivably close to one another
|
||||
if ( !trap_InPVS( start, end ) )
|
||||
{
|
||||
return ENTITYNUM_WORLD;
|
||||
}
|
||||
|
||||
trap_Trace( &trace, start, mins, maxs, end, ignore, clipmask );
|
||||
|
||||
//if( ( ( trace.startsolid == false ) && ( trace.allsolid == false ) ) && ( trace.fraction < 1.0f ) )
|
||||
//{//FIXME: check for drops?
|
||||
//FIXME: if startsolid or allsolid, then the path isn't clear... but returning ENTITYNUM_NONE indicates to CheckFailedEdge that is is clear...?
|
||||
return trace.entityNum;
|
||||
//}
|
||||
|
||||
//return ENTITYNUM_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_PushBlocker
|
||||
-------------------------
|
||||
*/
|
||||
void NAVNEW_PushBlocker( gentity_t *self, gentity_t *blocker, vec3_t right, qboolean setBlockedInfo )
|
||||
{//try pushing blocker to one side
|
||||
trace_t tr;
|
||||
vec3_t mins, end;
|
||||
float rightSucc, leftSucc, moveamt;
|
||||
|
||||
if ( self->NPC->shoveCount > 30 )
|
||||
{//don't push for more than 3 seconds;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !blocker->s.number )
|
||||
{//never push the player
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !blocker->client || !VectorCompare( blocker->client->pushVec, vec3_origin ) )
|
||||
{//someone else is pushing him, wait until they give up?
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( blocker->r.mins, mins );
|
||||
mins[2] += STEPSIZE;
|
||||
|
||||
moveamt = (self->r.maxs[1] + blocker->r.maxs[1]) * 1.2;//yes, magic number
|
||||
|
||||
VectorMA( blocker->r.currentOrigin, -moveamt, right, end );
|
||||
trap_Trace( &tr, blocker->r.currentOrigin, mins, blocker->r.maxs, end, blocker->s.number, blocker->clipmask|CONTENTS_BOTCLIP);
|
||||
if ( !tr.startsolid && !tr.allsolid )
|
||||
{
|
||||
leftSucc = tr.fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSucc = 0.0f;
|
||||
}
|
||||
|
||||
if ( leftSucc >= 1.0f )
|
||||
{//it's clear, shove him that way
|
||||
VectorScale( right, -moveamt, blocker->client->pushVec );
|
||||
blocker->client->pushVecTime = level.time + 2000;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorMA( blocker->r.currentOrigin, moveamt, right, end );
|
||||
trap_Trace( &tr, blocker->r.currentOrigin, mins, blocker->r.maxs, end, blocker->s.number, blocker->clipmask|CONTENTS_BOTCLIP );
|
||||
if ( !tr.startsolid && !tr.allsolid )
|
||||
{
|
||||
rightSucc = tr.fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightSucc = 0.0f;
|
||||
}
|
||||
|
||||
if ( leftSucc == 0.0f && rightSucc == 0.0f )
|
||||
{//both sides failed
|
||||
if ( d_patched.integer )
|
||||
{//use patch-style navigation
|
||||
blocker->client->pushVecTime = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( rightSucc >= 1.0f )
|
||||
{//it's clear, shove him that way
|
||||
VectorScale( right, moveamt, blocker->client->pushVec );
|
||||
blocker->client->pushVecTime = level.time + 2000;
|
||||
}
|
||||
//if neither are enough, we probably can't get around him, but keep trying
|
||||
else if ( leftSucc >= rightSucc )
|
||||
{//favor the left, all things being equal
|
||||
VectorScale( right, -moveamt, blocker->client->pushVec );
|
||||
blocker->client->pushVecTime = level.time + 2000;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorScale( right, moveamt, blocker->client->pushVec );
|
||||
blocker->client->pushVecTime = level.time + 2000;
|
||||
}
|
||||
}
|
||||
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
//we tried pushing
|
||||
self->NPC->shoveCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_DanceWithBlocker
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NAVNEW_DanceWithBlocker( gentity_t *self, gentity_t *blocker, vec3_t movedir, vec3_t right )
|
||||
{//sees if blocker has any lateral movement
|
||||
if ( blocker->client && !VectorCompare( blocker->client->ps.velocity, vec3_origin ) )
|
||||
{
|
||||
vec3_t blocker_movedir;
|
||||
float dot;
|
||||
|
||||
VectorCopy( blocker->client->ps.velocity, blocker_movedir );
|
||||
blocker_movedir[2] = 0;//cancel any vertical motion
|
||||
dot = DotProduct( blocker_movedir, right );
|
||||
if ( dot > 50.0f )
|
||||
{//he's moving to the right of me at a relatively good speed
|
||||
//go to my left
|
||||
VectorMA( movedir, -1, right, movedir );
|
||||
VectorNormalize( movedir );
|
||||
return qtrue;
|
||||
}
|
||||
else if ( dot > -50.0f )
|
||||
{//he's moving to the left of me at a relatively good speed
|
||||
//go to my right
|
||||
VectorAdd( right, movedir, movedir );
|
||||
VectorNormalize( movedir );
|
||||
return qtrue;
|
||||
}
|
||||
/*
|
||||
vec3_t block_pos;
|
||||
trace_t tr;
|
||||
VectorScale( blocker_movedir, -1, blocker_movedir );
|
||||
VectorMA( self->r.currentOrigin, blocked_dist, blocker_movedir, block_pos );
|
||||
if ( NAVNEW_CheckAhead( self, block_pos, tr, ( self->clipmask & ~CONTENTS_BODY )|CONTENTS_BOTCLIP ) )
|
||||
{
|
||||
VectorCopy( blocker_movedir, movedir );
|
||||
return qtrue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_SidestepBlocker
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NAVNEW_SidestepBlocker( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir, float blocked_dist, vec3_t movedir, vec3_t right )
|
||||
{//trace to sides of blocker and see if either is clear
|
||||
trace_t tr;
|
||||
vec3_t avoidAngles;
|
||||
vec3_t avoidRight_dir, avoidLeft_dir, block_pos, mins;
|
||||
float rightSucc, leftSucc, yaw, avoidRadius, arcAngle;
|
||||
|
||||
VectorCopy( self->r.mins, mins );
|
||||
mins[2] += STEPSIZE;
|
||||
|
||||
//Get the blocked direction
|
||||
yaw = vectoyaw( blocked_dir );
|
||||
|
||||
//Get the avoid radius
|
||||
avoidRadius = sqrt( ( blocker->r.maxs[0] * blocker->r.maxs[0] ) + ( blocker->r.maxs[1] * blocker->r.maxs[1] ) ) +
|
||||
sqrt( ( self->r.maxs[0] * self->r.maxs[0] ) + ( self->r.maxs[1] * self->r.maxs[1] ) );
|
||||
|
||||
//See if we're inside our avoidance radius
|
||||
arcAngle = ( blocked_dist <= avoidRadius ) ? 135 : ( ( avoidRadius / blocked_dist ) * 90 );
|
||||
|
||||
/*
|
||||
float dot = DotProduct( blocked_dir, right );
|
||||
|
||||
//Go right on the first try if that works better
|
||||
if ( dot < 0.0f )
|
||||
arcAngle *= -1;
|
||||
*/
|
||||
|
||||
VectorClear( avoidAngles );
|
||||
|
||||
//need to stop it from ping-ponging, so we have a bit of a debounce time on which side you try
|
||||
if ( self->NPC->sideStepHoldTime > level.time )
|
||||
{
|
||||
if ( self->NPC->lastSideStepSide == -1 )//left
|
||||
{
|
||||
arcAngle *= -1;
|
||||
}//else right
|
||||
avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle );
|
||||
AngleVectors( avoidAngles, movedir, NULL, NULL );
|
||||
VectorMA( self->r.currentOrigin, blocked_dist, movedir, block_pos );
|
||||
trap_Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP );
|
||||
return (tr.fraction==1.0&&!tr.allsolid&&!tr.startsolid);
|
||||
}
|
||||
|
||||
//test right
|
||||
avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle );
|
||||
AngleVectors( avoidAngles, avoidRight_dir, NULL, NULL );
|
||||
|
||||
VectorMA( self->r.currentOrigin, blocked_dist, avoidRight_dir, block_pos );
|
||||
|
||||
trap_Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP );
|
||||
|
||||
if ( !tr.allsolid && !tr.startsolid )
|
||||
{
|
||||
if ( tr.fraction >= 1.0f )
|
||||
{//all clear, go for it (favor the right if both are equal)
|
||||
VectorCopy( avoidRight_dir, movedir );
|
||||
self->NPC->lastSideStepSide = 1;
|
||||
self->NPC->sideStepHoldTime = level.time + 2000;
|
||||
return qtrue;
|
||||
}
|
||||
rightSucc = tr.fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightSucc = 0.0f;
|
||||
}
|
||||
|
||||
//now test left
|
||||
arcAngle *= -1;
|
||||
|
||||
avoidAngles[YAW] = AngleNormalize360( yaw + arcAngle );
|
||||
AngleVectors( avoidAngles, avoidLeft_dir, NULL, NULL );
|
||||
|
||||
VectorMA( self->r.currentOrigin, blocked_dist, avoidLeft_dir, block_pos );
|
||||
|
||||
trap_Trace( &tr, self->r.currentOrigin, mins, self->r.maxs, block_pos, self->s.number, self->clipmask|CONTENTS_BOTCLIP );
|
||||
|
||||
if ( !tr.allsolid && !tr.startsolid )
|
||||
{
|
||||
if ( tr.fraction >= 1.0f )
|
||||
{//all clear, go for it (right side would have already succeeded if as good as this)
|
||||
VectorCopy( avoidLeft_dir, movedir );
|
||||
self->NPC->lastSideStepSide = -1;
|
||||
self->NPC->sideStepHoldTime = level.time + 2000;
|
||||
return qtrue;
|
||||
}
|
||||
leftSucc = tr.fraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSucc = 0.0f;
|
||||
}
|
||||
|
||||
if ( leftSucc == 0.0f && rightSucc == 0.0f )
|
||||
{//both sides failed
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if ( rightSucc*blocked_dist >= avoidRadius || leftSucc*blocked_dist >= avoidRadius )
|
||||
{//the traces hit something, but got a relatively good distance
|
||||
if ( rightSucc >= leftSucc )
|
||||
{//favor the right, all things being equal
|
||||
VectorCopy( avoidRight_dir, movedir );
|
||||
self->NPC->lastSideStepSide = 1;
|
||||
self->NPC->sideStepHoldTime = level.time + 2000;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( avoidLeft_dir, movedir );
|
||||
self->NPC->lastSideStepSide = -1;
|
||||
self->NPC->sideStepHoldTime = level.time + 2000;
|
||||
}
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//if neither are enough, we probably can't get around him
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_Bypass
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NAVNEW_Bypass( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir, float blocked_dist, vec3_t movedir, qboolean setBlockedInfo )
|
||||
{
|
||||
vec3_t moveangles, right;
|
||||
|
||||
//Draw debug info if requested
|
||||
if ( NAVDEBUG_showCollision )
|
||||
{
|
||||
G_DrawEdge( self->r.currentOrigin, blocker->r.currentOrigin, EDGE_NORMAL );
|
||||
}
|
||||
|
||||
vectoangles( movedir, moveangles );
|
||||
moveangles[2] = 0;
|
||||
AngleVectors( moveangles, NULL, right, NULL );
|
||||
|
||||
//Check to see what dir the other guy is moving in (if any) and pick the opposite dir
|
||||
if ( NAVNEW_DanceWithBlocker( self, blocker, movedir, right ) )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//Okay, so he's not moving to my side, see which side of him is most clear
|
||||
if ( NAVNEW_SidestepBlocker( self, blocker, blocked_dir, blocked_dist, movedir, right ) )
|
||||
{
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//Neither side is clear, tell him to step aside
|
||||
NAVNEW_PushBlocker( self, blocker, right, setBlockedInfo );
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_CheckDoubleBlock
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NAVNEW_CheckDoubleBlock( gentity_t *self, gentity_t *blocker, vec3_t blocked_dir )
|
||||
{
|
||||
//Stop double waiting
|
||||
if ( ( blocker->NPC ) && ( blocker->NPC->blockingEntNum == self->s.number ) )
|
||||
return qtrue;
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_ResolveEntityCollision
|
||||
-------------------------
|
||||
*/
|
||||
extern void CalcTeamDoorCenter ( gentity_t *ent, vec3_t center );
|
||||
qboolean NAVNEW_ResolveEntityCollision( gentity_t *self, gentity_t *blocker, vec3_t movedir, vec3_t pathDir, qboolean setBlockedInfo )
|
||||
{
|
||||
vec3_t blocked_dir;
|
||||
float blocked_dist;
|
||||
|
||||
//Doors are ignored
|
||||
if ( Q_stricmp( blocker->classname, "func_door" ) == 0 )
|
||||
{
|
||||
vec3_t center;
|
||||
CalcTeamDoorCenter ( blocker, center );
|
||||
if ( DistanceSquared( self->r.currentOrigin, center ) > MIN_DOOR_BLOCK_DIST_SQR )
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
VectorSubtract( blocker->r.currentOrigin, self->r.currentOrigin, blocked_dir );
|
||||
blocked_dist = VectorNormalize( blocked_dir );
|
||||
|
||||
//Make sure an actual collision is going to happen
|
||||
// if ( NAVNEW_PredictCollision( self, blocker, movedir, blocked_dir ) == qfalse )
|
||||
// return qtrue;
|
||||
|
||||
//First, attempt to walk around the blocker or shove him out of the way
|
||||
if ( NAVNEW_Bypass( self, blocker, blocked_dir, blocked_dist, movedir, setBlockedInfo ) )
|
||||
return qtrue;
|
||||
|
||||
//Can't get around him... see if I'm blocking him too... if so, I need to just keep moving?
|
||||
if ( NAVNEW_CheckDoubleBlock( self, blocker, blocked_dir ) )
|
||||
return qtrue;
|
||||
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
//Complain about it if we can
|
||||
NPC_SetBlocked( self, blocker );
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_AvoidCollision
|
||||
-------------------------
|
||||
*/
|
||||
qboolean NAVNEW_AvoidCollision( gentity_t *self, gentity_t *goal, navInfo_t *info, qboolean setBlockedInfo, int blockedMovesLimit )
|
||||
{
|
||||
vec3_t movedir;
|
||||
vec3_t movepos;
|
||||
|
||||
//Cap our distance
|
||||
if ( info->distance > MAX_COLL_AVOID_DIST )
|
||||
{
|
||||
info->distance = MAX_COLL_AVOID_DIST;
|
||||
}
|
||||
|
||||
//Get an end position
|
||||
VectorMA( self->r.currentOrigin, info->distance, info->direction, movepos );
|
||||
VectorCopy( info->direction, movedir );
|
||||
|
||||
//Now test against entities
|
||||
if ( NAV_CheckAhead( self, movepos, &info->trace, CONTENTS_BODY ) == qfalse )
|
||||
{
|
||||
//Get the blocker
|
||||
info->blocker = &g_entities[ info->trace.entityNum ];
|
||||
info->flags |= NIF_COLLISION;
|
||||
|
||||
//Ok to hit our goal entity
|
||||
if ( goal == info->blocker )
|
||||
return qtrue;
|
||||
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
if ( self->NPC->consecutiveBlockedMoves > blockedMovesLimit )
|
||||
{
|
||||
if ( d_patched.integer )
|
||||
{//use patch-style navigation
|
||||
self->NPC->consecutiveBlockedMoves++;
|
||||
}
|
||||
NPC_SetBlocked( self, info->blocker );
|
||||
return qfalse;
|
||||
}
|
||||
self->NPC->consecutiveBlockedMoves++;
|
||||
}
|
||||
//See if we're moving along with them
|
||||
//if ( NAVNEW_TrueCollision( self, info->blocker, movedir, info->direction ) == qfalse )
|
||||
// return qtrue;
|
||||
|
||||
//Test for blocking by standing on goal
|
||||
if ( NAV_TestForBlocked( self, goal, info->blocker, info->distance, &info->flags ) == qtrue )
|
||||
return qfalse;
|
||||
|
||||
//If the above function said we're blocked, don't do the extra checks
|
||||
/*
|
||||
if ( info->flags & NIF_BLOCKED )
|
||||
return qtrue;
|
||||
*/
|
||||
|
||||
//See if we can get that entity to move out of our way
|
||||
if ( NAVNEW_ResolveEntityCollision( self, info->blocker, movedir, info->pathDirection, setBlockedInfo ) == qfalse )
|
||||
return qfalse;
|
||||
|
||||
VectorCopy( movedir, info->direction );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
self->NPC->consecutiveBlockedMoves = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Our path is clear, just move there
|
||||
if ( NAVDEBUG_showCollision )
|
||||
{
|
||||
G_DrawEdge( self->r.currentOrigin, movepos, EDGE_MOVEDIR );
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
qboolean NAVNEW_TestNodeConnectionBlocked( int wp1, int wp2, gentity_t *ignoreEnt, int goalEntNum, qboolean checkWorld, qboolean checkEnts )
|
||||
{//see if the direct path between 2 nodes is blocked by architecture or an ent
|
||||
vec3_t pos1, pos2, mins, maxs;
|
||||
trace_t trace;
|
||||
int clipmask = MASK_NPCSOLID|CONTENTS_BOTCLIP;
|
||||
int ignoreEntNum;
|
||||
vec3_t playerMins, playerMaxs;
|
||||
|
||||
if ( !checkWorld && !checkEnts )
|
||||
{//duh, nothing to trace against
|
||||
return qfalse;
|
||||
}
|
||||
VectorSet(playerMins, -15, -15, DEFAULT_MINS_2);
|
||||
VectorSet(playerMaxs, 15, 15, DEFAULT_MAXS_2);
|
||||
|
||||
trap_Nav_GetNodePosition( wp1, pos1 );
|
||||
trap_Nav_GetNodePosition( wp2, pos2 );
|
||||
|
||||
if ( !checkWorld )
|
||||
{
|
||||
clipmask &= ~(CONTENTS_SOLID|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
|
||||
}
|
||||
if ( !checkEnts )
|
||||
{
|
||||
clipmask &= ~CONTENTS_BODY;
|
||||
}
|
||||
if ( ignoreEnt )
|
||||
{
|
||||
VectorCopy( ignoreEnt->r.mins, mins );
|
||||
VectorCopy( ignoreEnt->r.maxs, maxs );
|
||||
ignoreEntNum = ignoreEnt->s.number;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( playerMins, mins );
|
||||
VectorCopy( playerMaxs, mins );
|
||||
ignoreEntNum = ENTITYNUM_NONE;
|
||||
}
|
||||
mins[2] += STEPSIZE;
|
||||
//don't let box get inverted
|
||||
if ( mins[2] > maxs[2] )
|
||||
{
|
||||
mins[2] = maxs[2];
|
||||
}
|
||||
|
||||
trap_Trace( &trace, pos1, mins, maxs, pos2, ignoreEntNum, clipmask );
|
||||
if ( trace.fraction >= 1.0f || trace.entityNum == goalEntNum )
|
||||
{//clear or hit goal
|
||||
return qfalse;
|
||||
}
|
||||
//hit something we weren't supposed to
|
||||
return qtrue;
|
||||
}
|
||||
/*
|
||||
-------------------------
|
||||
NAVNEW_MoveToGoal
|
||||
-------------------------
|
||||
*/
|
||||
int NAVNEW_MoveToGoal( gentity_t *self, navInfo_t *info )
|
||||
{
|
||||
int bestNode = WAYPOINT_NONE;
|
||||
qboolean foundClearPath = qfalse;
|
||||
vec3_t origin;
|
||||
navInfo_t tempInfo;
|
||||
qboolean setBlockedInfo = qtrue;
|
||||
qboolean inBestWP, inGoalWP, goalWPFailed = qfalse;
|
||||
int numTries = 0;
|
||||
|
||||
memcpy( &tempInfo, info, sizeof( tempInfo ) );
|
||||
|
||||
//Must have a goal entity to move there
|
||||
if( self->NPC->goalEntity == NULL )
|
||||
return WAYPOINT_NONE;
|
||||
|
||||
if ( self->waypoint == WAYPOINT_NONE && self->noWaypointTime > level.time )
|
||||
{//didn't have a valid one in about the past second, don't look again just yet
|
||||
return WAYPOINT_NONE;
|
||||
}
|
||||
if ( self->NPC->goalEntity->waypoint == WAYPOINT_NONE && self->NPC->goalEntity->noWaypointTime > level.time )
|
||||
{//didn't have a valid one in about the past second, don't look again just yet
|
||||
return WAYPOINT_NONE;
|
||||
}
|
||||
if ( self->noWaypointTime > level.time &&
|
||||
self->NPC->goalEntity->noWaypointTime > level.time )
|
||||
{//just use current waypoints
|
||||
bestNode = trap_Nav_GetBestNodeAltRoute2( self->waypoint, self->NPC->goalEntity->waypoint, bestNode );
|
||||
}
|
||||
//FIXME!!!!: this is making them wiggle back and forth between waypoints
|
||||
else if ( (bestNode = trap_Nav_GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qtrue ) )
|
||||
{//one of us didn't have a valid waypoint!
|
||||
if ( self->waypoint == NODE_NONE )
|
||||
{//don't even try to find one again for a bit
|
||||
self->noWaypointTime = level.time + Q_irand( 500, 1500 );
|
||||
}
|
||||
if ( self->NPC->goalEntity->waypoint == NODE_NONE )
|
||||
{//don't even try to find one again for a bit
|
||||
self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 );
|
||||
}
|
||||
return WAYPOINT_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self->NPC->goalEntity->noWaypointTime < level.time )
|
||||
{
|
||||
self->NPC->goalEntity->noWaypointTime = level.time + Q_irand( 500, 1500 );
|
||||
}
|
||||
}
|
||||
|
||||
while( !foundClearPath )
|
||||
{
|
||||
inBestWP = inGoalWP = qfalse;
|
||||
/*
|
||||
bestNode = trap_Nav_GetBestNodeAltRoute( self->waypoint, self->NPC->goalEntity->waypoint, bestNode );
|
||||
*/
|
||||
|
||||
if ( bestNode == WAYPOINT_NONE )
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
//see if we can get directly to the next node off bestNode en route to goal's node...
|
||||
//NOTE: shouldn't be necc. now
|
||||
/*
|
||||
int oldBestNode = bestNode;
|
||||
bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue );//, self->NPC->goalEntity->waypoint );//
|
||||
//NOTE: Guaranteed to return something
|
||||
if ( bestNode != oldBestNode )
|
||||
{//we were blocked somehow
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
self->NPC->aiFlags |= NPCAI_BLOCKED;
|
||||
trap_Nav_GetNodePosition( oldBestNode, NPCInfo->blockedDest );
|
||||
}
|
||||
}
|
||||
*/
|
||||
trap_Nav_GetNodePosition( bestNode, origin );
|
||||
/*
|
||||
if ( !goalWPFailed )
|
||||
{//we haven't already tried to go straight to goal or goal's wp
|
||||
if ( bestNode == self->NPC->goalEntity->waypoint )
|
||||
{//our bestNode is the goal's wp
|
||||
if ( NAV_HitNavGoal( self->r.currentOrigin, self->r.mins, self->r.maxs, origin, trap_Nav_GetNodeRadius( bestNode ), FlyingCreature( self ) ) )
|
||||
{//we're in the goal's wp
|
||||
inGoalWP = qtrue;
|
||||
//we're in the goalEntity's waypoint already
|
||||
//so head for the goalEntity since we know it's clear of architecture
|
||||
//FIXME: this is pretty stupid because the NPCs try to go straight
|
||||
// towards their goal before then even try macro_nav...
|
||||
VectorCopy( self->NPC->goalEntity->r.currentOrigin, origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
if ( !inGoalWP )
|
||||
{//not heading straight for goal
|
||||
if ( bestNode == self->waypoint )
|
||||
{//we know it's clear or architecture
|
||||
//trap_Nav_GetNodePosition( self->waypoint, origin );
|
||||
/*
|
||||
if ( NAV_HitNavGoal( self->r.currentOrigin, self->r.mins, self->r.maxs, origin, trap_Nav_GetNodeRadius( bestNode ), FlyingCreature( self ) ) )
|
||||
{//we're in the wp we're heading for already
|
||||
inBestWP = qtrue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
else
|
||||
{//heading to an edge off our confirmed clear waypoint... make sure it's clear
|
||||
//it it's not, bestNode will fall back to our waypoint
|
||||
int oldBestNode = bestNode;
|
||||
bestNode = NAV_TestBestNode( self, self->waypoint, bestNode, qtrue );
|
||||
if ( bestNode == self->waypoint )
|
||||
{//we fell back to our waypoint, reset the origin
|
||||
self->NPC->aiFlags |= NPCAI_BLOCKED;
|
||||
trap_Nav_GetNodePosition( oldBestNode, NPCInfo->blockedDest );
|
||||
trap_Nav_GetNodePosition( bestNode, origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
//Com_Printf( "goalwp = %d, mywp = %d, node = %d, origin = %s\n", self->NPC->goalEntity->waypoint, self->waypoint, bestNode, vtos(origin) );
|
||||
|
||||
memcpy( &tempInfo, info, sizeof( tempInfo ) );
|
||||
VectorSubtract( origin, self->r.currentOrigin, tempInfo.direction );
|
||||
VectorNormalize( tempInfo.direction );
|
||||
|
||||
//NOTE: One very important thing NAVNEW_AvoidCollision does is
|
||||
// it actually CHANGES the value of "direction" - it changes it to
|
||||
// whatever dir you need to go in to avoid the obstacle...
|
||||
foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, &tempInfo, setBlockedInfo, 5 );
|
||||
|
||||
if ( !foundClearPath )
|
||||
{//blocked by an ent
|
||||
if ( inGoalWP )
|
||||
{//we were heading straight for the goal, head for the goal's wp instead
|
||||
trap_Nav_GetNodePosition( bestNode, origin );
|
||||
foundClearPath = NAVNEW_AvoidCollision( self, self->NPC->goalEntity, &tempInfo, setBlockedInfo, 5 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( foundClearPath )
|
||||
{//clear!
|
||||
//If we got set to blocked, clear it
|
||||
NPC_ClearBlocked( self );
|
||||
//Take the dir
|
||||
memcpy( info, &tempInfo, sizeof( *info ) );
|
||||
if ( self->s.weapon == WP_SABER )
|
||||
{//jedi
|
||||
if ( info->direction[2] * info->distance > 64 )
|
||||
{
|
||||
self->NPC->aiFlags |= NPCAI_BLOCKED;
|
||||
VectorCopy( origin, NPCInfo->blockedDest );
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{//blocked by ent!
|
||||
if ( setBlockedInfo )
|
||||
{
|
||||
self->NPC->aiFlags |= NPCAI_BLOCKED;
|
||||
trap_Nav_GetNodePosition( bestNode, NPCInfo->blockedDest );
|
||||
}
|
||||
//Only set blocked info first time
|
||||
setBlockedInfo = qfalse;
|
||||
|
||||
if ( inGoalWP )
|
||||
{//we headed for our goal and failed and our goal's WP and failed
|
||||
if ( self->waypoint == self->NPC->goalEntity->waypoint )
|
||||
{//our waypoint is our goal's waypoint, nothing we can do
|
||||
//remember that this node is blocked
|
||||
trap_Nav_AddFailedNode( self, self->waypoint );
|
||||
goto failed;
|
||||
}
|
||||
else
|
||||
{//try going for our waypoint this time
|
||||
goalWPFailed = qtrue;
|
||||
inGoalWP = qfalse;
|
||||
}
|
||||
}
|
||||
else if ( bestNode != self->waypoint )
|
||||
{//we headed toward our next waypoint (instead of our waypoint) and failed
|
||||
if ( d_altRoutes.integer )
|
||||
{//mark this edge failed and try our waypoint
|
||||
//NOTE: don't assume there is something blocking the direct path
|
||||
// between my waypoint and the bestNode... I could be off
|
||||
// that path because of collision avoidance...
|
||||
if ( d_patched.integer &&//use patch-style navigation
|
||||
( !trap_Nav_NodesAreNeighbors( self->waypoint, bestNode )
|
||||
|| NAVNEW_TestNodeConnectionBlocked( self->waypoint, bestNode, self, self->NPC->goalEntity->s.number, qfalse, qtrue ) ) )
|
||||
{//the direct path between these 2 nodes is blocked by an ent
|
||||
trap_Nav_AddFailedEdge( self->s.number, self->waypoint, bestNode );
|
||||
}
|
||||
bestNode = self->waypoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
//we should stop
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{//we headed for *our* waypoint and couldn't get to it
|
||||
if ( d_altRoutes.integer )
|
||||
{
|
||||
//remember that this node is blocked
|
||||
trap_Nav_AddFailedNode( self, self->waypoint );
|
||||
//Now we should get our waypoints again
|
||||
//FIXME: cache the trace-data for subsequent calls as only the route info would have changed
|
||||
//if ( (bestNode = trap_Nav_GetBestPathBetweenEnts( self, self->NPC->goalEntity, NF_CLEAR_PATH )) == NODE_NONE )//!NAVNEW_GetWaypoints( self, qfalse ) )
|
||||
{//one of our waypoints is WAYPOINT_NONE now
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//we should stop
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ++numTries >= 10 )
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//finish:
|
||||
//Draw any debug info, if requested
|
||||
if ( NAVDEBUG_showEnemyPath )
|
||||
{
|
||||
vec3_t dest, start;
|
||||
|
||||
//Get the positions
|
||||
trap_Nav_GetNodePosition( self->NPC->goalEntity->waypoint, dest );
|
||||
trap_Nav_GetNodePosition( bestNode, start );
|
||||
|
||||
//Draw the route
|
||||
G_DrawNode( start, NODE_START );
|
||||
if ( bestNode != self->waypoint )
|
||||
{
|
||||
vec3_t wpPos;
|
||||
trap_Nav_GetNodePosition( self->waypoint, wpPos );
|
||||
G_DrawNode( wpPos, NODE_NAVGOAL );
|
||||
}
|
||||
G_DrawNode( dest, NODE_GOAL );
|
||||
G_DrawEdge( dest, self->NPC->goalEntity->r.currentOrigin, EDGE_PATH );
|
||||
G_DrawNode( self->NPC->goalEntity->r.currentOrigin, NODE_GOAL );
|
||||
trap_Nav_ShowPath( bestNode, self->NPC->goalEntity->waypoint );
|
||||
}
|
||||
|
||||
self->NPC->shoveCount = 0;
|
||||
|
||||
//let me keep this waypoint for a while
|
||||
if ( self->noWaypointTime < level.time )
|
||||
{
|
||||
self->noWaypointTime = level.time + Q_irand( 500, 1500 );
|
||||
}
|
||||
return bestNode;
|
||||
|
||||
failed:
|
||||
//FIXME: What we should really do here is have a list of the goal's and our
|
||||
// closest clearpath waypoints, ranked. If the first set fails, try the rest
|
||||
// until there are no alternatives.
|
||||
|
||||
trap_Nav_GetNodePosition( self->waypoint, origin );
|
||||
|
||||
//do this to avoid ping-ponging?
|
||||
return WAYPOINT_NONE;
|
||||
/*
|
||||
//this was causing ping-ponging
|
||||
if ( DistanceSquared( origin, self->r.currentOrigin ) < 16 )//woo, magic number
|
||||
{//We're right up on our waypoint, so that won't help, return none
|
||||
//Or maybe find the nextbest here?
|
||||
return WAYPOINT_NONE;
|
||||
}
|
||||
else
|
||||
{//Try going to our waypoint
|
||||
bestNode = self->waypoint;
|
||||
|
||||
VectorSubtract( origin, self->r.currentOrigin, info.direction );
|
||||
VectorNormalize( info.direction );
|
||||
}
|
||||
|
||||
goto finish;
|
||||
*/
|
||||
}
|
||||
287
codemp/game/g_object.c
Normal file
287
codemp/game/g_object.c
Normal file
@@ -0,0 +1,287 @@
|
||||
#include "g_local.h"
|
||||
|
||||
extern void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg );
|
||||
void G_StopObjectMoving( gentity_t *object );
|
||||
|
||||
void pitch_roll_for_slope( gentity_t *forwhom, vec3_t pass_slope );
|
||||
|
||||
/*
|
||||
================
|
||||
G_BounceObject
|
||||
|
||||
================
|
||||
*/
|
||||
void G_BounceObject( gentity_t *ent, trace_t *trace )
|
||||
{
|
||||
vec3_t velocity;
|
||||
float dot, bounceFactor;
|
||||
int hitTime;
|
||||
|
||||
// reflect the velocity on the trace plane
|
||||
hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
|
||||
BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
|
||||
dot = DotProduct( velocity, trace->plane.normal );
|
||||
// bounceFactor = 60/ent->mass; // NOTENOTE Mass is not yet implemented
|
||||
bounceFactor = 1.0f;
|
||||
if ( bounceFactor > 1.0f )
|
||||
{
|
||||
bounceFactor = 1.0f;
|
||||
}
|
||||
VectorMA( velocity, -2*dot*bounceFactor, trace->plane.normal, ent->s.pos.trDelta );
|
||||
|
||||
//FIXME: customized or material-based impact/bounce sounds
|
||||
if ( ent->flags & FL_BOUNCE_HALF )
|
||||
{
|
||||
VectorScale( ent->s.pos.trDelta, 0.5, ent->s.pos.trDelta );
|
||||
|
||||
// check for stop
|
||||
if ( ((trace->plane.normal[2] > 0.7&&g_gravity.value>0) || (trace->plane.normal[2]<-0.7&&g_gravity.value<0)) && ((ent->s.pos.trDelta[2]<40&&g_gravity.value>0)||(ent->s.pos.trDelta[2]>-40&&g_gravity.value<0)) ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7
|
||||
{
|
||||
//G_SetOrigin( ent, trace->endpos );
|
||||
//ent->nextthink = level.time + 500;
|
||||
ent->s.apos.trType = TR_STATIONARY;
|
||||
VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
|
||||
VectorCopy( trace->endpos, ent->r.currentOrigin );
|
||||
VectorCopy( trace->endpos, ent->s.pos.trBase );
|
||||
ent->s.pos.trTime = level.time;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW--It would seem that we want to set our trBase to the trace endpos
|
||||
// and set the trTime to the actual time of impact....
|
||||
// FIXME: Should we still consider adding the normal though??
|
||||
VectorCopy( trace->endpos, ent->r.currentOrigin );
|
||||
ent->s.pos.trTime = hitTime;
|
||||
|
||||
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
||||
VectorCopy( trace->plane.normal, ent->pos1 );//???
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_RunObject
|
||||
|
||||
TODO: When transition to 0 grav, push away from surface you were resting on
|
||||
TODO: When free-floating in air, apply some friction to your trDelta (based on mass?)
|
||||
================
|
||||
*/
|
||||
extern void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf );
|
||||
extern void pitch_roll_for_slope( gentity_t *forwhom, vec3_t pass_slope );
|
||||
void G_RunObject( gentity_t *ent )
|
||||
{
|
||||
vec3_t origin, oldOrg;
|
||||
trace_t tr;
|
||||
gentity_t *traceEnt = NULL;
|
||||
|
||||
//FIXME: floaters need to stop floating up after a while, even if gravity stays negative?
|
||||
if ( ent->s.pos.trType == TR_STATIONARY )//g_gravity.value <= 0 &&
|
||||
{
|
||||
ent->s.pos.trType = TR_GRAVITY;
|
||||
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
||||
ent->s.pos.trTime = level.previousTime;//?necc?
|
||||
if ( !g_gravity.value )
|
||||
{
|
||||
ent->s.pos.trDelta[2] += 100;
|
||||
}
|
||||
}
|
||||
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
|
||||
VectorCopy( ent->r.currentOrigin, oldOrg );
|
||||
// get current position
|
||||
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
||||
//Get current angles?
|
||||
BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
|
||||
|
||||
if ( VectorCompare( ent->r.currentOrigin, origin ) )
|
||||
{//error - didn't move at all!
|
||||
return;
|
||||
}
|
||||
// trace a line from the previous position to the current position,
|
||||
// ignoring interactions with the missile owner
|
||||
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
|
||||
ent->parent ? ent->parent->s.number : ent->s.number, ent->clipmask );
|
||||
|
||||
if ( !tr.startsolid && !tr.allsolid && tr.fraction )
|
||||
{
|
||||
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
||||
trap_LinkEntity( ent );
|
||||
}
|
||||
else
|
||||
//if ( tr.startsolid )
|
||||
{
|
||||
tr.fraction = 0;
|
||||
}
|
||||
|
||||
G_MoverTouchPushTriggers( ent, oldOrg );
|
||||
/*
|
||||
if ( !(ent->s.eFlags & EF_TELEPORT_BIT) && !(ent->svFlags & SVF_NO_TELEPORT) )
|
||||
{
|
||||
G_MoverTouchTeleportTriggers( ent, oldOrg );
|
||||
if ( ent->s.eFlags & EF_TELEPORT_BIT )
|
||||
{//was teleported
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.eFlags &= ~EF_TELEPORT_BIT;
|
||||
}
|
||||
*/
|
||||
|
||||
if ( tr.fraction == 1 )
|
||||
{
|
||||
if ( g_gravity.value <= 0 )
|
||||
{
|
||||
if ( ent->s.apos.trType == TR_STATIONARY )
|
||||
{
|
||||
VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
|
||||
ent->s.apos.trType = TR_LINEAR;
|
||||
ent->s.apos.trDelta[1] = flrand( -300, 300 );
|
||||
ent->s.apos.trDelta[0] = flrand( -10, 10 );
|
||||
ent->s.apos.trDelta[2] = flrand( -10, 10 );
|
||||
ent->s.apos.trTime = level.time;
|
||||
}
|
||||
}
|
||||
//friction in zero-G
|
||||
if ( !g_gravity.value )
|
||||
{
|
||||
float friction = 0.975f;
|
||||
//friction -= ent->mass/1000.0f;
|
||||
if ( friction < 0.1 )
|
||||
{
|
||||
friction = 0.1f;
|
||||
}
|
||||
|
||||
VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta );
|
||||
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//hit something
|
||||
|
||||
//Do impact damage
|
||||
traceEnt = &g_entities[tr.entityNum];
|
||||
if ( tr.fraction || (traceEnt && traceEnt->takedamage) )
|
||||
{
|
||||
if ( !VectorCompare( ent->r.currentOrigin, oldOrg ) )
|
||||
{//moved and impacted
|
||||
if ( (traceEnt && traceEnt->takedamage) )
|
||||
{//hurt someone
|
||||
// G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHurt.wav" ) );
|
||||
}
|
||||
// G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHit.wav" ) );
|
||||
}
|
||||
|
||||
if (ent->s.weapon != WP_SABER)
|
||||
{
|
||||
DoImpact( ent, traceEnt, qtrue );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !ent || (ent->takedamage&&ent->health <= 0) )
|
||||
{//been destroyed by impact
|
||||
//chunks?
|
||||
// G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectBreak.wav" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
//do impact physics
|
||||
if ( ent->s.pos.trType == TR_GRAVITY )//tr.fraction < 1.0 &&
|
||||
{//FIXME: only do this if no trDelta
|
||||
if ( g_gravity.value <= 0 || tr.plane.normal[2] < 0.7 )
|
||||
{
|
||||
if ( ent->flags&(FL_BOUNCE|FL_BOUNCE_HALF) )
|
||||
{
|
||||
if ( tr.fraction <= 0.0f )
|
||||
{
|
||||
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
||||
VectorCopy( tr.endpos, ent->s.pos.trBase );
|
||||
VectorClear( ent->s.pos.trDelta );
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_BounceObject( ent, &tr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{//slide down?
|
||||
//FIXME: slide off the slope
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.apos.trType = TR_STATIONARY;
|
||||
pitch_roll_for_slope( ent, tr.plane.normal );
|
||||
//ent->r.currentAngles[0] = 0;//FIXME: match to slope
|
||||
//ent->r.currentAngles[2] = 0;//FIXME: match to slope
|
||||
VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
|
||||
//okay, we hit the floor, might as well stop or prediction will
|
||||
//make us go through the floor!
|
||||
//FIXME: this means we can't fall if something is pulled out from under us...
|
||||
G_StopObjectMoving( ent );
|
||||
}
|
||||
}
|
||||
else if (ent->s.weapon != WP_SABER)
|
||||
{
|
||||
ent->s.apos.trType = TR_STATIONARY;
|
||||
pitch_roll_for_slope( ent, tr.plane.normal );
|
||||
//ent->r.currentAngles[0] = 0;//FIXME: match to slope
|
||||
//ent->r.currentAngles[2] = 0;//FIXME: match to slope
|
||||
VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
|
||||
}
|
||||
|
||||
//call touch func
|
||||
ent->touch( ent, &g_entities[tr.entityNum], &tr );
|
||||
}
|
||||
|
||||
|
||||
void G_StopObjectMoving( gentity_t *object )
|
||||
{
|
||||
object->s.pos.trType = TR_STATIONARY;
|
||||
VectorCopy( object->r.currentOrigin, object->s.origin );
|
||||
VectorCopy( object->r.currentOrigin, object->s.pos.trBase );
|
||||
VectorClear( object->s.pos.trDelta );
|
||||
|
||||
/*
|
||||
//Stop spinning
|
||||
VectorClear( self->s.apos.trDelta );
|
||||
vectoangles(trace->plane.normal, self->s.angles);
|
||||
VectorCopy(self->s.angles, self->r.currentAngles );
|
||||
VectorCopy(self->s.angles, self->s.apos.trBase);
|
||||
*/
|
||||
}
|
||||
|
||||
void G_StartObjectMoving( gentity_t *object, vec3_t dir, float speed, trType_t trType )
|
||||
{
|
||||
VectorNormalize (dir);
|
||||
|
||||
//object->s.eType = ET_GENERAL;
|
||||
object->s.pos.trType = trType;
|
||||
VectorCopy( object->r.currentOrigin, object->s.pos.trBase );
|
||||
VectorScale(dir, speed, object->s.pos.trDelta );
|
||||
object->s.pos.trTime = level.time;
|
||||
|
||||
/*
|
||||
//FIXME: incorporate spin?
|
||||
vectoangles(dir, object->s.angles);
|
||||
VectorCopy(object->s.angles, object->s.apos.trBase);
|
||||
VectorSet(object->s.apos.trDelta, 300, 0, 0 );
|
||||
object->s.apos.trTime = level.time;
|
||||
*/
|
||||
|
||||
//FIXME: make these objects go through G_RunObject automatically, like missiles do
|
||||
if ( object->think == NULL )
|
||||
{
|
||||
object->nextthink = level.time + FRAMETIME;
|
||||
object->think = G_RunObject;
|
||||
}
|
||||
else
|
||||
{//You're responsible for calling RunObject
|
||||
}
|
||||
}
|
||||
920
codemp/game/g_public.h
Normal file
920
codemp/game/g_public.h
Normal file
@@ -0,0 +1,920 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
#ifndef G_PUBLIC_H
|
||||
|
||||
// g_public.h -- game module information visible to server
|
||||
|
||||
#define G_PUBLIC_H
|
||||
|
||||
#define Q3_INFINITE 16777216
|
||||
|
||||
#define GAME_API_VERSION 8
|
||||
|
||||
// entity->svFlags
|
||||
// the server does not know how to interpret most of the values
|
||||
// in entityStates (level eType), so the game must explicitly flag
|
||||
// special server behaviors
|
||||
#define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects
|
||||
#define SVF_BOT 0x00000008 // set if the entity is a bot
|
||||
#define SVF_PLAYER_USABLE 0x00000010 // player can use this with the use button
|
||||
#define SVF_BROADCAST 0x00000020 // send to all connected clients
|
||||
#define SVF_PORTAL 0x00000040 // merge a second pvs at origin2 into snapshots
|
||||
#define SVF_USE_CURRENT_ORIGIN 0x00000080 // entity->r.currentOrigin instead of entity->s.origin
|
||||
// for link position (missiles and movers)
|
||||
#define SVF_SINGLECLIENT 0x00000100 // only send to a single client (entityShared_t->singleClient)
|
||||
#define SVF_NOSERVERINFO 0x00000200 // don't send CS_SERVERINFO updates to this client
|
||||
// so that it can be updated for ping tools without
|
||||
// lagging clients
|
||||
#define SVF_CAPSULE 0x00000400 // use capsule for collision detection instead of bbox
|
||||
#define SVF_NOTSINGLECLIENT 0x00000800 // send entity to everyone but one client
|
||||
// (entityShared_t->singleClient)
|
||||
|
||||
#define SVF_OWNERNOTSHARED 0x00001000 // If it's owned by something and another thing owned by that something
|
||||
// hits it, it will still touch
|
||||
|
||||
#define SVF_ICARUS_FREEZE 0x00008000 // NPCs are frozen, ents don't execute ICARUS commands
|
||||
|
||||
#define SVF_GLASS_BRUSH 0x08000000 // Ent is a glass brush
|
||||
|
||||
//rww - ghoul2 trace flags
|
||||
#define G2TRFLAG_DOGHOULTRACE 0x00000001 //do the ghoul2 trace
|
||||
#define G2TRFLAG_HITCORPSES 0x00000002 //will try g2 collision on the ent even if it's EF_DEAD
|
||||
#define G2TRFLAG_GETSURFINDEX 0x00000004 //will replace surfaceFlags with the ghoul2 surface index that was hit, if any.
|
||||
#define G2TRFLAG_THICK 0x00000008 //assures that the trace radius will be significantly large regardless of the trace box size.
|
||||
|
||||
//===============================================================
|
||||
|
||||
//this structure is shared by gameside and in-engine NPC nav routines.
|
||||
typedef struct failedEdge_e
|
||||
{
|
||||
int startID;
|
||||
int endID;
|
||||
int checkTime;
|
||||
int entID;
|
||||
} failedEdge_t;
|
||||
|
||||
typedef struct {
|
||||
qboolean linked; // qfalse if not in any good cluster
|
||||
int linkcount;
|
||||
|
||||
int svFlags; // SVF_NOCLIENT, SVF_BROADCAST, etc
|
||||
int singleClient; // only send to this client when SVF_SINGLECLIENT is set
|
||||
|
||||
qboolean bmodel; // if false, assume an explicit mins / maxs bounding box
|
||||
// only set by trap_SetBrushModel
|
||||
vec3_t mins, maxs;
|
||||
int contents; // CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc
|
||||
// a non-solid entity should set to 0
|
||||
|
||||
vec3_t absmin, absmax; // derived from mins/maxs and origin + rotation
|
||||
|
||||
// currentOrigin will be used for all collision detection and world linking.
|
||||
// it will not necessarily be the same as the trajectory evaluation for the current
|
||||
// time, because each entity must be moved one at a time after time is advanced
|
||||
// to avoid simultanious collision issues
|
||||
vec3_t currentOrigin;
|
||||
vec3_t currentAngles;
|
||||
qboolean mIsRoffing; // set to qtrue when the entity is being roffed
|
||||
|
||||
// when a trace call is made and passEntityNum != ENTITYNUM_NONE,
|
||||
// an ent will be excluded from testing if:
|
||||
// ent->s.number == passEntityNum (don't interact with self)
|
||||
// ent->s.ownerNum = passEntityNum (don't interact with your own missiles)
|
||||
// entity[ent->s.ownerNum].ownerNum = passEntityNum (don't interact with other missiles from owner)
|
||||
int ownerNum;
|
||||
|
||||
// mask of clients that this entity should be broadcast too. The first 32 clients
|
||||
// are represented by the first array index and the latter 32 clients are represented
|
||||
// by the second array index.
|
||||
int broadcastClients[2];
|
||||
|
||||
} entityShared_t;
|
||||
|
||||
//===============================================================
|
||||
|
||||
//
|
||||
// system traps provided by the main engine
|
||||
//
|
||||
typedef enum {
|
||||
//============== general Quake services ==================
|
||||
|
||||
G_PRINT, // ( const char *string );
|
||||
// print message on the local console
|
||||
|
||||
G_ERROR, // ( const char *string );
|
||||
// abort the game
|
||||
|
||||
G_MILLISECONDS, // ( void );
|
||||
// get current time for profiling reasons
|
||||
// this should NOT be used for any game related tasks,
|
||||
// because it is not journaled
|
||||
|
||||
//Also for profiling.. do not use for game related tasks.
|
||||
G_PRECISIONTIMER_START,
|
||||
G_PRECISIONTIMER_END,
|
||||
|
||||
// console variable interaction
|
||||
G_CVAR_REGISTER, // ( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );
|
||||
G_CVAR_UPDATE, // ( vmCvar_t *vmCvar );
|
||||
G_CVAR_SET, // ( const char *var_name, const char *value );
|
||||
G_CVAR_VARIABLE_INTEGER_VALUE, // ( const char *var_name );
|
||||
|
||||
G_CVAR_VARIABLE_STRING_BUFFER, // ( const char *var_name, char *buffer, int bufsize );
|
||||
|
||||
G_ARGC, // ( void );
|
||||
// ClientCommand and ServerCommand parameter access
|
||||
|
||||
G_ARGV, // ( int n, char *buffer, int bufferLength );
|
||||
|
||||
G_FS_FOPEN_FILE, // ( const char *qpath, fileHandle_t *file, fsMode_t mode );
|
||||
G_FS_READ, // ( void *buffer, int len, fileHandle_t f );
|
||||
G_FS_WRITE, // ( const void *buffer, int len, fileHandle_t f );
|
||||
G_FS_FCLOSE_FILE, // ( fileHandle_t f );
|
||||
|
||||
G_SEND_CONSOLE_COMMAND, // ( const char *text );
|
||||
// add commands to the console as if they were typed in
|
||||
// for map changing, etc
|
||||
|
||||
|
||||
//=========== server specific functionality =============
|
||||
|
||||
G_LOCATE_GAME_DATA, // ( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,
|
||||
// playerState_t *clients, int sizeofGameClient );
|
||||
// the game needs to let the server system know where and how big the gentities
|
||||
// are, so it can look at them directly without going through an interface
|
||||
|
||||
G_DROP_CLIENT, // ( int clientNum, const char *reason );
|
||||
// kick a client off the server with a message
|
||||
|
||||
G_SEND_SERVER_COMMAND, // ( int clientNum, const char *fmt, ... );
|
||||
// reliably sends a command string to be interpreted by the given
|
||||
// client. If clientNum is -1, it will be sent to all clients
|
||||
|
||||
G_SET_CONFIGSTRING, // ( int num, const char *string );
|
||||
// config strings hold all the index strings, and various other information
|
||||
// that is reliably communicated to all clients
|
||||
// All of the current configstrings are sent to clients when
|
||||
// they connect, and changes are sent to all connected clients.
|
||||
// All confgstrings are cleared at each level start.
|
||||
|
||||
G_GET_CONFIGSTRING, // ( int num, char *buffer, int bufferSize );
|
||||
|
||||
G_GET_USERINFO, // ( int num, char *buffer, int bufferSize );
|
||||
// userinfo strings are maintained by the server system, so they
|
||||
// are persistant across level loads, while all other game visible
|
||||
// data is completely reset
|
||||
|
||||
G_SET_USERINFO, // ( int num, const char *buffer );
|
||||
|
||||
G_GET_SERVERINFO, // ( char *buffer, int bufferSize );
|
||||
// the serverinfo info string has all the cvars visible to server browsers
|
||||
|
||||
G_SET_SERVER_CULL,
|
||||
//server culling to reduce traffic on open maps -rww
|
||||
|
||||
G_SET_BRUSH_MODEL, // ( gentity_t *ent, const char *name );
|
||||
// sets mins and maxs based on the brushmodel name
|
||||
|
||||
G_TRACE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
// collision detection against all linked entities
|
||||
|
||||
G_G2TRACE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
// collision detection against all linked entities with ghoul2 check
|
||||
|
||||
G_POINT_CONTENTS, // ( const vec3_t point, int passEntityNum );
|
||||
// point contents against all linked entities
|
||||
|
||||
G_IN_PVS, // ( const vec3_t p1, const vec3_t p2 );
|
||||
|
||||
G_IN_PVS_IGNORE_PORTALS, // ( const vec3_t p1, const vec3_t p2 );
|
||||
|
||||
G_ADJUST_AREA_PORTAL_STATE, // ( gentity_t *ent, qboolean open );
|
||||
|
||||
G_AREAS_CONNECTED, // ( int area1, int area2 );
|
||||
|
||||
G_LINKENTITY, // ( gentity_t *ent );
|
||||
// an entity will never be sent to a client or used for collision
|
||||
// if it is not passed to linkentity. If the size, position, or
|
||||
// solidity changes, it must be relinked.
|
||||
|
||||
G_UNLINKENTITY, // ( gentity_t *ent );
|
||||
// call before removing an interactive entity
|
||||
|
||||
G_ENTITIES_IN_BOX, // ( const vec3_t mins, const vec3_t maxs, gentity_t **list, int maxcount );
|
||||
// EntitiesInBox will return brush models based on their bounding box,
|
||||
// so exact determination must still be done with EntityContact
|
||||
|
||||
G_ENTITY_CONTACT, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );
|
||||
// perform an exact check against inline brush models of non-square shape
|
||||
|
||||
// access for bots to get and free a server client (FIXME?)
|
||||
G_BOT_ALLOCATE_CLIENT, // ( void );
|
||||
|
||||
G_BOT_FREE_CLIENT, // ( int clientNum );
|
||||
|
||||
G_GET_USERCMD, // ( int clientNum, usercmd_t *cmd )
|
||||
|
||||
G_GET_ENTITY_TOKEN, // qboolean ( char *buffer, int bufferSize )
|
||||
// Retrieves the next string token from the entity spawn text, returning
|
||||
// false when all tokens have been parsed.
|
||||
// This should only be done at GAME_INIT time.
|
||||
|
||||
G_SIEGEPERSSET,
|
||||
G_SIEGEPERSGET,
|
||||
|
||||
G_FS_GETFILELIST,
|
||||
G_DEBUG_POLYGON_CREATE,
|
||||
G_DEBUG_POLYGON_DELETE,
|
||||
G_REAL_TIME,
|
||||
G_SNAPVECTOR,
|
||||
|
||||
G_TRACECAPSULE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
G_ENTITY_CONTACTCAPSULE, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );
|
||||
|
||||
// SP_REGISTER_SERVER_CMD,
|
||||
SP_GETSTRINGTEXTSTRING,
|
||||
|
||||
G_ROFF_CLEAN, // qboolean ROFF_Clean(void);
|
||||
G_ROFF_UPDATE_ENTITIES, // void ROFF_UpdateEntities(void);
|
||||
G_ROFF_CACHE, // int ROFF_Cache(char *file);
|
||||
G_ROFF_PLAY, // qboolean ROFF_Play(int entID, int roffID, qboolean doTranslation);
|
||||
G_ROFF_PURGE_ENT, // qboolean ROFF_PurgeEnt( int entID )
|
||||
|
||||
//rww - dynamic vm memory allocation!
|
||||
G_TRUEMALLOC,
|
||||
G_TRUEFREE,
|
||||
|
||||
//rww - icarus traps
|
||||
G_ICARUS_RUNSCRIPT,
|
||||
G_ICARUS_REGISTERSCRIPT,
|
||||
|
||||
G_ICARUS_INIT,
|
||||
G_ICARUS_VALIDENT,
|
||||
G_ICARUS_ISINITIALIZED,
|
||||
G_ICARUS_MAINTAINTASKMANAGER,
|
||||
G_ICARUS_ISRUNNING,
|
||||
G_ICARUS_TASKIDPENDING,
|
||||
G_ICARUS_INITENT,
|
||||
G_ICARUS_FREEENT,
|
||||
G_ICARUS_ASSOCIATEENT,
|
||||
G_ICARUS_SHUTDOWN,
|
||||
G_ICARUS_TASKIDSET,
|
||||
G_ICARUS_TASKIDCOMPLETE,
|
||||
G_ICARUS_SETVAR,
|
||||
G_ICARUS_VARIABLEDECLARED,
|
||||
G_ICARUS_GETFLOATVARIABLE,
|
||||
G_ICARUS_GETSTRINGVARIABLE,
|
||||
G_ICARUS_GETVECTORVARIABLE,
|
||||
|
||||
G_SET_SHARED_BUFFER,
|
||||
|
||||
//BEGIN VM STUFF
|
||||
G_MEMSET = 100,
|
||||
G_MEMCPY,
|
||||
G_STRNCPY,
|
||||
G_SIN,
|
||||
G_COS,
|
||||
G_ATAN2,
|
||||
G_SQRT,
|
||||
G_MATRIXMULTIPLY,
|
||||
G_ANGLEVECTORS,
|
||||
G_PERPENDICULARVECTOR,
|
||||
G_FLOOR,
|
||||
G_CEIL,
|
||||
|
||||
G_TESTPRINTINT,
|
||||
G_TESTPRINTFLOAT,
|
||||
|
||||
G_ACOS,
|
||||
G_ASIN,
|
||||
|
||||
//END VM STUFF
|
||||
|
||||
//rww - BEGIN NPC NAV TRAPS
|
||||
G_NAV_INIT = 200,
|
||||
G_NAV_FREE,
|
||||
G_NAV_LOAD,
|
||||
G_NAV_SAVE,
|
||||
G_NAV_ADDRAWPOINT,
|
||||
G_NAV_CALCULATEPATHS,
|
||||
G_NAV_HARDCONNECT,
|
||||
G_NAV_SHOWNODES,
|
||||
G_NAV_SHOWEDGES,
|
||||
G_NAV_SHOWPATH,
|
||||
G_NAV_GETNEARESTNODE,
|
||||
G_NAV_GETBESTNODE,
|
||||
G_NAV_GETNODEPOSITION,
|
||||
G_NAV_GETNODENUMEDGES,
|
||||
G_NAV_GETNODEEDGE,
|
||||
G_NAV_GETNUMNODES,
|
||||
G_NAV_CONNECTED,
|
||||
G_NAV_GETPATHCOST,
|
||||
G_NAV_GETEDGECOST,
|
||||
G_NAV_GETPROJECTEDNODE,
|
||||
G_NAV_CHECKFAILEDNODES,
|
||||
G_NAV_ADDFAILEDNODE,
|
||||
G_NAV_NODEFAILED,
|
||||
G_NAV_NODESARENEIGHBORS,
|
||||
G_NAV_CLEARFAILEDEDGE,
|
||||
G_NAV_CLEARALLFAILEDEDGES,
|
||||
G_NAV_EDGEFAILED,
|
||||
G_NAV_ADDFAILEDEDGE,
|
||||
G_NAV_CHECKFAILEDEDGE,
|
||||
G_NAV_CHECKALLFAILEDEDGES,
|
||||
G_NAV_ROUTEBLOCKED,
|
||||
G_NAV_GETBESTNODEALTROUTE,
|
||||
G_NAV_GETBESTNODEALT2,
|
||||
G_NAV_GETBESTPATHBETWEENENTS,
|
||||
G_NAV_GETNODERADIUS,
|
||||
G_NAV_CHECKBLOCKEDEDGES,
|
||||
G_NAV_CLEARCHECKEDNODES,
|
||||
G_NAV_CHECKEDNODE,
|
||||
G_NAV_SETCHECKEDNODE,
|
||||
G_NAV_FLAGALLNODES,
|
||||
G_NAV_GETPATHSCALCULATED,
|
||||
G_NAV_SETPATHSCALCULATED,
|
||||
//rww - END NPC NAV TRAPS
|
||||
|
||||
BOTLIB_SETUP = 250, // ( void );
|
||||
BOTLIB_SHUTDOWN, // ( void );
|
||||
BOTLIB_LIBVAR_SET,
|
||||
BOTLIB_LIBVAR_GET,
|
||||
BOTLIB_PC_ADD_GLOBAL_DEFINE,
|
||||
BOTLIB_START_FRAME,
|
||||
BOTLIB_LOAD_MAP,
|
||||
BOTLIB_UPDATENTITY,
|
||||
BOTLIB_TEST,
|
||||
|
||||
BOTLIB_GET_SNAPSHOT_ENTITY, // ( int client, int ent );
|
||||
BOTLIB_GET_CONSOLE_MESSAGE, // ( int client, char *message, int size );
|
||||
BOTLIB_USER_COMMAND, // ( int client, usercmd_t *ucmd );
|
||||
|
||||
BOTLIB_AAS_ENABLE_ROUTING_AREA = 300,
|
||||
BOTLIB_AAS_BBOX_AREAS,
|
||||
BOTLIB_AAS_AREA_INFO,
|
||||
BOTLIB_AAS_ENTITY_INFO,
|
||||
|
||||
BOTLIB_AAS_INITIALIZED,
|
||||
BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX,
|
||||
BOTLIB_AAS_TIME,
|
||||
|
||||
BOTLIB_AAS_POINT_AREA_NUM,
|
||||
BOTLIB_AAS_TRACE_AREAS,
|
||||
|
||||
BOTLIB_AAS_POINT_CONTENTS,
|
||||
BOTLIB_AAS_NEXT_BSP_ENTITY,
|
||||
BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY,
|
||||
BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY,
|
||||
BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY,
|
||||
BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY,
|
||||
|
||||
BOTLIB_AAS_AREA_REACHABILITY,
|
||||
|
||||
BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA,
|
||||
|
||||
BOTLIB_AAS_SWIMMING,
|
||||
BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT,
|
||||
|
||||
BOTLIB_EA_SAY = 400,
|
||||
BOTLIB_EA_SAY_TEAM,
|
||||
BOTLIB_EA_COMMAND,
|
||||
|
||||
BOTLIB_EA_ACTION,
|
||||
BOTLIB_EA_GESTURE,
|
||||
BOTLIB_EA_TALK,
|
||||
BOTLIB_EA_ATTACK,
|
||||
BOTLIB_EA_ALT_ATTACK,
|
||||
BOTLIB_EA_FORCEPOWER,
|
||||
BOTLIB_EA_USE,
|
||||
BOTLIB_EA_RESPAWN,
|
||||
BOTLIB_EA_CROUCH,
|
||||
BOTLIB_EA_MOVE_UP,
|
||||
BOTLIB_EA_MOVE_DOWN,
|
||||
BOTLIB_EA_MOVE_FORWARD,
|
||||
BOTLIB_EA_MOVE_BACK,
|
||||
BOTLIB_EA_MOVE_LEFT,
|
||||
BOTLIB_EA_MOVE_RIGHT,
|
||||
|
||||
BOTLIB_EA_SELECT_WEAPON,
|
||||
BOTLIB_EA_JUMP,
|
||||
BOTLIB_EA_DELAYED_JUMP,
|
||||
BOTLIB_EA_MOVE,
|
||||
BOTLIB_EA_VIEW,
|
||||
|
||||
BOTLIB_EA_END_REGULAR,
|
||||
BOTLIB_EA_GET_INPUT,
|
||||
BOTLIB_EA_RESET_INPUT,
|
||||
|
||||
|
||||
BOTLIB_AI_LOAD_CHARACTER = 500,
|
||||
BOTLIB_AI_FREE_CHARACTER,
|
||||
BOTLIB_AI_CHARACTERISTIC_FLOAT,
|
||||
BOTLIB_AI_CHARACTERISTIC_BFLOAT,
|
||||
BOTLIB_AI_CHARACTERISTIC_INTEGER,
|
||||
BOTLIB_AI_CHARACTERISTIC_BINTEGER,
|
||||
BOTLIB_AI_CHARACTERISTIC_STRING,
|
||||
|
||||
// BOTLIB_AI_ALLOC_CHAT_STATE,
|
||||
// BOTLIB_AI_FREE_CHAT_STATE,
|
||||
// BOTLIB_AI_QUEUE_CONSOLE_MESSAGE,
|
||||
// BOTLIB_AI_REMOVE_CONSOLE_MESSAGE,
|
||||
// BOTLIB_AI_NEXT_CONSOLE_MESSAGE,
|
||||
// BOTLIB_AI_NUM_CONSOLE_MESSAGE,
|
||||
// BOTLIB_AI_INITIAL_CHAT,
|
||||
// BOTLIB_AI_REPLY_CHAT,
|
||||
// BOTLIB_AI_CHAT_LENGTH,
|
||||
// BOTLIB_AI_ENTER_CHAT,
|
||||
// BOTLIB_AI_STRING_CONTAINS,
|
||||
// BOTLIB_AI_FIND_MATCH,
|
||||
// BOTLIB_AI_MATCH_VARIABLE,
|
||||
// BOTLIB_AI_UNIFY_WHITE_SPACES,
|
||||
// BOTLIB_AI_REPLACE_SYNONYMS,
|
||||
// BOTLIB_AI_LOAD_CHAT_FILE,
|
||||
// BOTLIB_AI_SET_CHAT_GENDER,
|
||||
// BOTLIB_AI_SET_CHAT_NAME,
|
||||
|
||||
BOTLIB_AI_RESET_GOAL_STATE,
|
||||
BOTLIB_AI_RESET_AVOID_GOALS,
|
||||
BOTLIB_AI_PUSH_GOAL,
|
||||
BOTLIB_AI_POP_GOAL,
|
||||
BOTLIB_AI_EMPTY_GOAL_STACK,
|
||||
BOTLIB_AI_DUMP_AVOID_GOALS,
|
||||
BOTLIB_AI_DUMP_GOAL_STACK,
|
||||
BOTLIB_AI_GOAL_NAME,
|
||||
BOTLIB_AI_GET_TOP_GOAL,
|
||||
BOTLIB_AI_GET_SECOND_GOAL,
|
||||
BOTLIB_AI_CHOOSE_LTG_ITEM,
|
||||
BOTLIB_AI_CHOOSE_NBG_ITEM,
|
||||
BOTLIB_AI_TOUCHING_GOAL,
|
||||
BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE,
|
||||
BOTLIB_AI_GET_LEVEL_ITEM_GOAL,
|
||||
BOTLIB_AI_AVOID_GOAL_TIME,
|
||||
BOTLIB_AI_INIT_LEVEL_ITEMS,
|
||||
BOTLIB_AI_UPDATE_ENTITY_ITEMS,
|
||||
BOTLIB_AI_LOAD_ITEM_WEIGHTS,
|
||||
BOTLIB_AI_FREE_ITEM_WEIGHTS,
|
||||
BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC,
|
||||
BOTLIB_AI_ALLOC_GOAL_STATE,
|
||||
BOTLIB_AI_FREE_GOAL_STATE,
|
||||
|
||||
BOTLIB_AI_RESET_MOVE_STATE,
|
||||
BOTLIB_AI_MOVE_TO_GOAL,
|
||||
BOTLIB_AI_MOVE_IN_DIRECTION,
|
||||
BOTLIB_AI_RESET_AVOID_REACH,
|
||||
BOTLIB_AI_RESET_LAST_AVOID_REACH,
|
||||
BOTLIB_AI_REACHABILITY_AREA,
|
||||
BOTLIB_AI_MOVEMENT_VIEW_TARGET,
|
||||
BOTLIB_AI_ALLOC_MOVE_STATE,
|
||||
BOTLIB_AI_FREE_MOVE_STATE,
|
||||
BOTLIB_AI_INIT_MOVE_STATE,
|
||||
|
||||
BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON,
|
||||
BOTLIB_AI_GET_WEAPON_INFO,
|
||||
BOTLIB_AI_LOAD_WEAPON_WEIGHTS,
|
||||
BOTLIB_AI_ALLOC_WEAPON_STATE,
|
||||
BOTLIB_AI_FREE_WEAPON_STATE,
|
||||
BOTLIB_AI_RESET_WEAPON_STATE,
|
||||
|
||||
BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION,
|
||||
BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC,
|
||||
BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC,
|
||||
BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL,
|
||||
BOTLIB_AI_GET_MAP_LOCATION_GOAL,
|
||||
BOTLIB_AI_NUM_INITIAL_CHATS,
|
||||
BOTLIB_AI_GET_CHAT_MESSAGE,
|
||||
BOTLIB_AI_REMOVE_FROM_AVOID_GOALS,
|
||||
BOTLIB_AI_PREDICT_VISIBLE_POSITION,
|
||||
|
||||
BOTLIB_AI_SET_AVOID_GOAL_TIME,
|
||||
BOTLIB_AI_ADD_AVOID_SPOT,
|
||||
BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL,
|
||||
BOTLIB_AAS_PREDICT_ROUTE,
|
||||
BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX,
|
||||
|
||||
BOTLIB_PC_LOAD_SOURCE,
|
||||
BOTLIB_PC_FREE_SOURCE,
|
||||
BOTLIB_PC_READ_TOKEN,
|
||||
BOTLIB_PC_SOURCE_FILE_AND_LINE,
|
||||
|
||||
/*
|
||||
Ghoul2 Insert Start
|
||||
*/
|
||||
G_R_REGISTERSKIN,
|
||||
G_G2_LISTBONES,
|
||||
G_G2_LISTSURFACES,
|
||||
G_G2_HAVEWEGHOULMODELS,
|
||||
G_G2_SETMODELS,
|
||||
G_G2_GETBOLT,
|
||||
G_G2_GETBOLT_NOREC,
|
||||
G_G2_GETBOLT_NOREC_NOROT,
|
||||
G_G2_INITGHOUL2MODEL,
|
||||
G_G2_SETSKIN,
|
||||
G_G2_SIZE,
|
||||
G_G2_ADDBOLT,
|
||||
G_G2_SETBOLTINFO,
|
||||
G_G2_ANGLEOVERRIDE,
|
||||
G_G2_PLAYANIM,
|
||||
G_G2_GETBONEANIM,
|
||||
G_G2_GETGLANAME,
|
||||
G_G2_COPYGHOUL2INSTANCE,
|
||||
G_G2_COPYSPECIFICGHOUL2MODEL,
|
||||
G_G2_DUPLICATEGHOUL2INSTANCE,
|
||||
G_G2_HASGHOUL2MODELONINDEX,
|
||||
G_G2_REMOVEGHOUL2MODEL,
|
||||
G_G2_REMOVEGHOUL2MODELS,
|
||||
G_G2_CLEANMODELS,
|
||||
G_G2_COLLISIONDETECT,
|
||||
|
||||
G_G2_SETROOTSURFACE,
|
||||
G_G2_SETSURFACEONOFF,
|
||||
G_G2_SETNEWORIGIN,
|
||||
G_G2_DOESBONEEXIST,
|
||||
G_G2_GETSURFACERENDERSTATUS,
|
||||
|
||||
G_G2_ABSURDSMOOTHING,
|
||||
|
||||
/*
|
||||
//rww - RAGDOLL_BEGIN
|
||||
*/
|
||||
G_G2_SETRAGDOLL,
|
||||
G_G2_ANIMATEG2MODELS,
|
||||
/*
|
||||
//rww - RAGDOLL_END
|
||||
*/
|
||||
//additional ragdoll options -rww
|
||||
G_G2_RAGPCJCONSTRAINT,
|
||||
G_G2_RAGPCJGRADIENTSPEED,
|
||||
G_G2_RAGEFFECTORGOAL,
|
||||
G_G2_GETRAGBONEPOS,
|
||||
G_G2_RAGEFFECTORKICK,
|
||||
G_G2_RAGFORCESOLVE,
|
||||
|
||||
//rww - ik move method, allows you to specify a bone and move it to a world point (within joint constraints)
|
||||
//by using the majority of gil's existing bone angling stuff from the ragdoll code.
|
||||
G_G2_SETBONEIKSTATE,
|
||||
G_G2_IKMOVE,
|
||||
|
||||
G_G2_REMOVEBONE,
|
||||
|
||||
G_G2_ATTACHINSTANCETOENTNUM,
|
||||
G_G2_CLEARATTACHEDINSTANCE,
|
||||
G_G2_CLEANENTATTACHMENTS,
|
||||
G_G2_OVERRIDESERVER,
|
||||
|
||||
G_G2_GETSURFACENAME,
|
||||
|
||||
G_SET_ACTIVE_SUBBSP,
|
||||
G_CM_REGISTER_TERRAIN,
|
||||
G_RMG_INIT,
|
||||
|
||||
G_BOT_UPDATEWAYPOINTS,
|
||||
G_BOT_CALCULATEPATHS
|
||||
/*
|
||||
Ghoul2 Insert End
|
||||
*/
|
||||
|
||||
} gameImport_t;
|
||||
|
||||
//bstate.h
|
||||
typedef enum //# bState_e
|
||||
{//These take over only if script allows them to be autonomous
|
||||
BS_DEFAULT = 0,//# default behavior for that NPC
|
||||
BS_ADVANCE_FIGHT,//# Advance to captureGoal and shoot enemies if you can
|
||||
BS_SLEEP,//# Play awake script when startled by sound
|
||||
BS_FOLLOW_LEADER,//# Follow your leader and shoot any enemies you come across
|
||||
BS_JUMP,//# Face navgoal and jump to it.
|
||||
BS_SEARCH,//# Using current waypoint as a base, search the immediate branches of waypoints for enemies
|
||||
BS_WANDER,//# Wander down random waypoint paths
|
||||
BS_NOCLIP,//# Moves through walls, etc.
|
||||
BS_REMOVE,//# Waits for player to leave PVS then removes itself
|
||||
BS_CINEMATIC,//# Does nothing but face it's angles and move to a goal if it has one
|
||||
//# #eol
|
||||
//internal bStates only
|
||||
BS_WAIT,//# Does nothing but face it's angles
|
||||
BS_STAND_GUARD,
|
||||
BS_PATROL,
|
||||
BS_INVESTIGATE,//# head towards temp goal and look for enemies and listen for sounds
|
||||
BS_STAND_AND_SHOOT,
|
||||
BS_HUNT_AND_KILL,
|
||||
BS_FLEE,//# Run away!
|
||||
NUM_BSTATES
|
||||
} bState_t;
|
||||
|
||||
enum
|
||||
{
|
||||
EDGE_NORMAL,
|
||||
EDGE_PATH,
|
||||
EDGE_BLOCKED,
|
||||
EDGE_FAILED,
|
||||
EDGE_MOVEDIR
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NODE_NORMAL,
|
||||
NODE_START,
|
||||
NODE_GOAL,
|
||||
NODE_NAVGOAL,
|
||||
};
|
||||
|
||||
typedef enum //# taskID_e
|
||||
{
|
||||
TID_CHAN_VOICE = 0, // Waiting for a voice sound to complete
|
||||
TID_ANIM_UPPER, // Waiting to finish a lower anim holdtime
|
||||
TID_ANIM_LOWER, // Waiting to finish a lower anim holdtime
|
||||
TID_ANIM_BOTH, // Waiting to finish lower and upper anim holdtimes or normal md3 animating
|
||||
TID_MOVE_NAV, // Trying to get to a navgoal or For ET_MOVERS
|
||||
TID_ANGLE_FACE, // Turning to an angle or facing
|
||||
TID_BSTATE, // Waiting for a certain bState to finish
|
||||
TID_LOCATION, // Waiting for ent to enter a specific trigger_location
|
||||
// TID_MISSIONSTATUS, // Waiting for player to finish reading MISSION STATUS SCREEN
|
||||
TID_RESIZE, // Waiting for clear bbox to inflate size
|
||||
TID_SHOOT, // Waiting for fire event
|
||||
NUM_TIDS, // for def of taskID array
|
||||
} taskID_t;
|
||||
|
||||
typedef enum //# bSet_e
|
||||
{//This should check to matching a behavior state name first, then look for a script
|
||||
BSET_INVALID = -1,
|
||||
BSET_FIRST = 0,
|
||||
BSET_SPAWN = 0,//# script to use when first spawned
|
||||
BSET_USE,//# script to use when used
|
||||
BSET_AWAKE,//# script to use when awoken/startled
|
||||
BSET_ANGER,//# script to use when aquire an enemy
|
||||
BSET_ATTACK,//# script to run when you attack
|
||||
BSET_VICTORY,//# script to run when you kill someone
|
||||
BSET_LOSTENEMY,//# script to run when you can't find your enemy
|
||||
BSET_PAIN,//# script to use when take pain
|
||||
BSET_FLEE,//# script to use when take pain below 50% of health
|
||||
BSET_DEATH,//# script to use when killed
|
||||
BSET_DELAYED,//# script to run when self->delayScriptTime is reached
|
||||
BSET_BLOCKED,//# script to run when blocked by a friendly NPC or player
|
||||
BSET_BUMPED,//# script to run when bumped into a friendly NPC or player (can set bumpRadius)
|
||||
BSET_STUCK,//# script to run when blocked by a wall
|
||||
BSET_FFIRE,//# script to run when player shoots their own teammates
|
||||
BSET_FFDEATH,//# script to run when player kills a teammate
|
||||
BSET_MINDTRICK,//# script to run when player does a mind trick on this NPC
|
||||
|
||||
NUM_BSETS
|
||||
} bSet_t;
|
||||
|
||||
#define MAX_PARMS 16
|
||||
#define MAX_PARM_STRING_LENGTH MAX_QPATH//was 16, had to lengthen it so they could take a valid file path
|
||||
typedef struct
|
||||
{
|
||||
char parm[MAX_PARMS][MAX_PARM_STRING_LENGTH];
|
||||
} parms_t;
|
||||
|
||||
#define MAX_FAILED_NODES 8
|
||||
|
||||
typedef struct Vehicle_s Vehicle_t;
|
||||
|
||||
// the server looks at a sharedEntity, which is the start of the game's gentity_t structure
|
||||
//mod authors should not touch this struct
|
||||
typedef struct {
|
||||
entityState_t s; // communicated by server to clients
|
||||
playerState_t *playerState; //needs to be in the gentity for bg entity access
|
||||
//if you want to actually see the contents I guess
|
||||
//you will have to be sure to VMA it first.
|
||||
Vehicle_t *m_pVehicle; //vehicle data
|
||||
void *ghoul2; //g2 instance
|
||||
int localAnimIndex; //index locally (game/cgame) to anim data for this skel
|
||||
vec3_t modelScale; //needed for g2 collision
|
||||
|
||||
//from here up must also be unified with bgEntity/centity
|
||||
|
||||
entityShared_t r; // shared by both the server system and game
|
||||
|
||||
//Script/ICARUS-related fields
|
||||
int taskID[NUM_TIDS];
|
||||
parms_t *parms;
|
||||
char *behaviorSet[NUM_BSETS];
|
||||
char *script_targetname;
|
||||
int delayScriptTime;
|
||||
char *fullName;
|
||||
|
||||
//rww - targetname and classname are now shared as well. ICARUS needs access to them.
|
||||
char *targetname;
|
||||
char *classname; // set in QuakeEd
|
||||
|
||||
//rww - and yet more things to share. This is because the nav code is in the exe because it's all C++.
|
||||
int waypoint; //Set once per frame, if you've moved, and if someone asks
|
||||
int lastWaypoint; //To make sure you don't double-back
|
||||
int lastValidWaypoint; //ALWAYS valid -used for tracking someone you lost
|
||||
int noWaypointTime; //Debouncer - so don't keep checking every waypoint in existance every frame that you can't find one
|
||||
int combatPoint;
|
||||
int failedWaypoints[MAX_FAILED_NODES];
|
||||
int failedWaypointCheckTime;
|
||||
|
||||
int next_roff_time; //rww - npc's need to know when they're getting roff'd
|
||||
} sharedEntity_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
class CSequencer;
|
||||
class CTaskManager;
|
||||
|
||||
//I suppose this could be in another in-engine header or something. But we never want to
|
||||
//include an icarus file before sharedentity_t is declared.
|
||||
extern CSequencer *gSequencers[MAX_GENTITIES];
|
||||
extern CTaskManager *gTaskManagers[MAX_GENTITIES];
|
||||
|
||||
#include "../icarus/icarus.h"
|
||||
#include "../icarus/sequencer.h"
|
||||
#include "../icarus/taskmanager.h"
|
||||
#endif
|
||||
|
||||
//
|
||||
// functions exported by the game subsystem
|
||||
//
|
||||
typedef enum {
|
||||
GAME_INIT, // ( int levelTime, int randomSeed, int restart );
|
||||
// init and shutdown will be called every single level
|
||||
// The game should call G_GET_ENTITY_TOKEN to parse through all the
|
||||
// entity configuration text and spawn gentities.
|
||||
|
||||
GAME_SHUTDOWN, // (void);
|
||||
|
||||
GAME_CLIENT_CONNECT, // ( int clientNum, qboolean firstTime, qboolean isBot );
|
||||
// return NULL if the client is allowed to connect, otherwise return
|
||||
// a text string with the reason for denial
|
||||
|
||||
GAME_CLIENT_BEGIN, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_DISCONNECT, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_COMMAND, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_THINK, // ( int clientNum );
|
||||
|
||||
GAME_RUN_FRAME, // ( int levelTime );
|
||||
|
||||
GAME_CONSOLE_COMMAND, // ( void );
|
||||
// ConsoleCommand will be called when a command has been issued
|
||||
// that is not recognized as a builtin function.
|
||||
// The game can issue trap_argc() / trap_argv() commands to get the command
|
||||
// and parameters. Return qfalse if the game doesn't recognize it as a command.
|
||||
|
||||
BOTAI_START_FRAME, // ( int time );
|
||||
|
||||
GAME_ROFF_NOTETRACK_CALLBACK, // int entnum, char *notetrack
|
||||
|
||||
GAME_SPAWN_RMG_ENTITY, //rwwRMG - added
|
||||
|
||||
//rww - icarus callbacks
|
||||
GAME_ICARUS_PLAYSOUND,
|
||||
GAME_ICARUS_SET,
|
||||
GAME_ICARUS_LERP2POS,
|
||||
GAME_ICARUS_LERP2ORIGIN,
|
||||
GAME_ICARUS_LERP2ANGLES,
|
||||
GAME_ICARUS_GETTAG,
|
||||
GAME_ICARUS_LERP2START,
|
||||
GAME_ICARUS_LERP2END,
|
||||
GAME_ICARUS_USE,
|
||||
GAME_ICARUS_KILL,
|
||||
GAME_ICARUS_REMOVE,
|
||||
GAME_ICARUS_PLAY,
|
||||
GAME_ICARUS_GETFLOAT,
|
||||
GAME_ICARUS_GETVECTOR,
|
||||
GAME_ICARUS_GETSTRING,
|
||||
GAME_ICARUS_SOUNDINDEX,
|
||||
GAME_ICARUS_GETSETIDFORSTRING,
|
||||
GAME_NAV_CLEARPATHTOPOINT,
|
||||
GAME_NAV_CLEARLOS,
|
||||
GAME_NAV_CLEARPATHBETWEENPOINTS,
|
||||
GAME_NAV_CHECKNODEFAILEDFORENT,
|
||||
GAME_NAV_ENTISUNLOCKEDDOOR,
|
||||
GAME_NAV_ENTISDOOR,
|
||||
GAME_NAV_ENTISBREAKABLE,
|
||||
GAME_NAV_ENTISREMOVABLEUSABLE,
|
||||
GAME_NAV_FINDCOMBATPOINTWAYPOINTS,
|
||||
|
||||
GAME_GETITEMINDEXBYTAG
|
||||
} gameExport_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
char name[2048];
|
||||
char channel[2048];
|
||||
} T_G_ICARUS_PLAYSOUND;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
char type_name[2048];
|
||||
char data[2048];
|
||||
} T_G_ICARUS_SET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
vec3_t origin;
|
||||
vec3_t angles;
|
||||
float duration;
|
||||
qboolean nullAngles; //special case
|
||||
} T_G_ICARUS_LERP2POS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
vec3_t origin;
|
||||
float duration;
|
||||
} T_G_ICARUS_LERP2ORIGIN;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
vec3_t angles;
|
||||
float duration;
|
||||
} T_G_ICARUS_LERP2ANGLES;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
char name[2048];
|
||||
int lookup;
|
||||
vec3_t info;
|
||||
} T_G_ICARUS_GETTAG;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
int taskID;
|
||||
float duration;
|
||||
} T_G_ICARUS_LERP2START;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
int taskID;
|
||||
float duration;
|
||||
} T_G_ICARUS_LERP2END;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
char target[2048];
|
||||
} T_G_ICARUS_USE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
char name[2048];
|
||||
} T_G_ICARUS_KILL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
char name[2048];
|
||||
} T_G_ICARUS_REMOVE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int taskID;
|
||||
int entID;
|
||||
char type[2048];
|
||||
char name[2048];
|
||||
} T_G_ICARUS_PLAY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
int type;
|
||||
char name[2048];
|
||||
float value;
|
||||
} T_G_ICARUS_GETFLOAT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
int type;
|
||||
char name[2048];
|
||||
vec3_t value;
|
||||
} T_G_ICARUS_GETVECTOR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int entID;
|
||||
int type;
|
||||
char name[2048];
|
||||
char value[2048];
|
||||
} T_G_ICARUS_GETSTRING;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char filename[2048];
|
||||
} T_G_ICARUS_SOUNDINDEX;
|
||||
typedef struct
|
||||
{
|
||||
char string[2048];
|
||||
} T_G_ICARUS_GETSETIDFORSTRING;
|
||||
|
||||
#endif //G_PUBLIC_H
|
||||
1900
codemp/game/g_saga.c
Normal file
1900
codemp/game/g_saga.c
Normal file
File diff suppressed because it is too large
Load Diff
322
codemp/game/g_session.c
Normal file
322
codemp/game/g_session.c
Normal file
@@ -0,0 +1,322 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
SESSION DATA
|
||||
|
||||
Session data is the only data that stays persistant across level loads
|
||||
and tournament restarts.
|
||||
=======================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
G_WriteClientSessionData
|
||||
|
||||
Called on game shutdown
|
||||
================
|
||||
*/
|
||||
void G_WriteClientSessionData( gclient_t *client ) {
|
||||
const char *s;
|
||||
const char *var;
|
||||
int i = 0;
|
||||
char siegeClass[64];
|
||||
char saberType[64];
|
||||
char saber2Type[64];
|
||||
|
||||
strcpy(siegeClass, client->sess.siegeClass);
|
||||
|
||||
while (siegeClass[i])
|
||||
{ //sort of a hack.. we don't want spaces by siege class names have spaces so convert them all to unused chars
|
||||
if (siegeClass[i] == ' ')
|
||||
{
|
||||
siegeClass[i] = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!siegeClass[0])
|
||||
{ //make sure there's at least something
|
||||
strcpy(siegeClass, "none");
|
||||
}
|
||||
|
||||
//Do the same for the saber
|
||||
strcpy(saberType, client->sess.saberType);
|
||||
|
||||
i = 0;
|
||||
while (saberType[i])
|
||||
{
|
||||
if (saberType[i] == ' ')
|
||||
{
|
||||
saberType[i] = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
strcpy(saber2Type, client->sess.saber2Type);
|
||||
|
||||
i = 0;
|
||||
while (saber2Type[i])
|
||||
{
|
||||
if (saber2Type[i] == ' ')
|
||||
{
|
||||
saber2Type[i] = 1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
s = va("%i %i %i %i %i %i %i %i %i %i %i %i %s %s %s",
|
||||
client->sess.sessionTeam,
|
||||
client->sess.spectatorTime,
|
||||
client->sess.spectatorState,
|
||||
client->sess.spectatorClient,
|
||||
client->sess.wins,
|
||||
client->sess.losses,
|
||||
client->sess.teamLeader,
|
||||
client->sess.setForce,
|
||||
client->sess.saberLevel,
|
||||
client->sess.selectedFP,
|
||||
client->sess.duelTeam,
|
||||
client->sess.siegeDesiredTeam,
|
||||
siegeClass,
|
||||
saberType,
|
||||
saber2Type
|
||||
);
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
|
||||
trap_Cvar_Set( var, s );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_ReadSessionData
|
||||
|
||||
Called on a reconnect
|
||||
================
|
||||
*/
|
||||
void G_ReadSessionData( gclient_t *client ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
const char *var;
|
||||
int i = 0;
|
||||
|
||||
// bk001205 - format
|
||||
int teamLeader;
|
||||
int spectatorState;
|
||||
int sessionTeam;
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
|
||||
|
||||
sscanf( s, "%i %i %i %i %i %i %i %i %i %i %i %i %s %s %s",
|
||||
&sessionTeam, // bk010221 - format
|
||||
&client->sess.spectatorTime,
|
||||
&spectatorState, // bk010221 - format
|
||||
&client->sess.spectatorClient,
|
||||
&client->sess.wins,
|
||||
&client->sess.losses,
|
||||
&teamLeader, // bk010221 - format
|
||||
&client->sess.setForce,
|
||||
&client->sess.saberLevel,
|
||||
&client->sess.selectedFP,
|
||||
&client->sess.duelTeam,
|
||||
&client->sess.siegeDesiredTeam,
|
||||
&client->sess.siegeClass,
|
||||
&client->sess.saberType,
|
||||
&client->sess.saber2Type
|
||||
);
|
||||
|
||||
while (client->sess.siegeClass[i])
|
||||
{ //convert back to spaces from unused chars, as session data is written that way.
|
||||
if (client->sess.siegeClass[i] == 1)
|
||||
{
|
||||
client->sess.siegeClass[i] = ' ';
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
//And do the same for the saber type
|
||||
while (client->sess.saberType[i])
|
||||
{
|
||||
if (client->sess.saberType[i] == 1)
|
||||
{
|
||||
client->sess.saberType[i] = ' ';
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (client->sess.saber2Type[i])
|
||||
{
|
||||
if (client->sess.saber2Type[i] == 1)
|
||||
{
|
||||
client->sess.saber2Type[i] = ' ';
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// bk001205 - format issues
|
||||
client->sess.sessionTeam = (team_t)sessionTeam;
|
||||
client->sess.spectatorState = (spectatorState_t)spectatorState;
|
||||
client->sess.teamLeader = (qboolean)teamLeader;
|
||||
|
||||
client->ps.fd.saberAnimLevel = client->sess.saberLevel;
|
||||
client->ps.fd.saberDrawAnimLevel = client->sess.saberLevel;
|
||||
client->ps.fd.forcePowerSelected = client->sess.selectedFP;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_InitSessionData
|
||||
|
||||
Called on a first-time connect
|
||||
================
|
||||
*/
|
||||
void G_InitSessionData( gclient_t *client, char *userinfo, qboolean isBot ) {
|
||||
clientSession_t *sess;
|
||||
const char *value;
|
||||
|
||||
sess = &client->sess;
|
||||
|
||||
client->sess.siegeDesiredTeam = TEAM_FREE;
|
||||
|
||||
// initial team determination
|
||||
if ( g_gametype.integer >= GT_TEAM ) {
|
||||
if ( g_teamAutoJoin.integer ) {
|
||||
sess->sessionTeam = PickTeam( -1 );
|
||||
BroadcastTeamChange( client, -1 );
|
||||
} else {
|
||||
// always spawn as spectator in team games
|
||||
if (!isBot)
|
||||
{
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
}
|
||||
else
|
||||
{ //Bots choose their team on creation
|
||||
value = Info_ValueForKey( userinfo, "team" );
|
||||
if (value[0] == 'r' || value[0] == 'R')
|
||||
{
|
||||
sess->sessionTeam = TEAM_RED;
|
||||
}
|
||||
else if (value[0] == 'b' || value[0] == 'B')
|
||||
{
|
||||
sess->sessionTeam = TEAM_BLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
sess->sessionTeam = PickTeam( -1 );
|
||||
}
|
||||
BroadcastTeamChange( client, -1 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = Info_ValueForKey( userinfo, "team" );
|
||||
if ( value[0] == 's' ) {
|
||||
// a willing spectator, not a waiting-in-line
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
switch ( g_gametype.integer ) {
|
||||
default:
|
||||
case GT_FFA:
|
||||
case GT_HOLOCRON:
|
||||
case GT_JEDIMASTER:
|
||||
case GT_SINGLE_PLAYER:
|
||||
if ( g_maxGameClients.integer > 0 &&
|
||||
level.numNonSpectatorClients >= g_maxGameClients.integer ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
case GT_DUEL:
|
||||
// if the game is full, go into a waiting mode
|
||||
if ( level.numNonSpectatorClients >= 2 ) {
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
} else {
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
break;
|
||||
case GT_POWERDUEL:
|
||||
//sess->duelTeam = DUELTEAM_LONE; //default
|
||||
{
|
||||
int loners = 0;
|
||||
int doubles = 0;
|
||||
|
||||
G_PowerDuelCount(&loners, &doubles, qtrue);
|
||||
|
||||
if (!doubles || loners > (doubles/2))
|
||||
{
|
||||
sess->duelTeam = DUELTEAM_DOUBLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
sess->duelTeam = DUELTEAM_LONE;
|
||||
}
|
||||
}
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sess->spectatorState = SPECTATOR_FREE;
|
||||
sess->spectatorTime = level.time;
|
||||
|
||||
sess->siegeClass[0] = 0;
|
||||
sess->saberType[0] = 0;
|
||||
sess->saber2Type[0] = 0;
|
||||
|
||||
G_WriteClientSessionData( client );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
G_InitWorldSession
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_InitWorldSession( void ) {
|
||||
char s[MAX_STRING_CHARS];
|
||||
int gt;
|
||||
|
||||
trap_Cvar_VariableStringBuffer( "session", s, sizeof(s) );
|
||||
gt = atoi( s );
|
||||
|
||||
// if the gametype changed since the last session, don't use any
|
||||
// client sessions
|
||||
if ( g_gametype.integer != gt ) {
|
||||
level.newSession = qtrue;
|
||||
G_Printf( "Gametype changed, clearing session data.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
G_WriteSessionData
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_WriteSessionData( void ) {
|
||||
int i;
|
||||
|
||||
trap_Cvar_Set( "session", va("%i", g_gametype.integer) );
|
||||
|
||||
for ( i = 0 ; i < level.maxclients ; i++ ) {
|
||||
if ( level.clients[i].pers.connected == CON_CONNECTED ) {
|
||||
G_WriteClientSessionData( &level.clients[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
1474
codemp/game/g_spawn.c
Normal file
1474
codemp/game/g_spawn.c
Normal file
File diff suppressed because it is too large
Load Diff
73
codemp/game/g_strap.c
Normal file
73
codemp/game/g_strap.c
Normal file
@@ -0,0 +1,73 @@
|
||||
//rww - shared trap call system
|
||||
#include "g_local.h"
|
||||
|
||||
#include "../namespace_begin.h"
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale)
|
||||
{
|
||||
return trap_G2API_GetBoltMatrix(ghoul2, modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix_NoReconstruct(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale)
|
||||
{
|
||||
return trap_G2API_GetBoltMatrix_NoReconstruct(ghoul2, modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_GetBoltMatrix_NoRecNoRot(void *ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix,
|
||||
const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t *modelList, vec3_t scale)
|
||||
{
|
||||
return trap_G2API_GetBoltMatrix_NoRecNoRot(ghoul2, modelIndex, boltIndex, matrix, angles, position, frameNum, modelList, scale);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_SetBoneAngles(void *ghoul2, int modelIndex, const char *boneName, const vec3_t angles, const int flags,
|
||||
const int up, const int right, const int forward, qhandle_t *modelList,
|
||||
int blendTime , int currentTime )
|
||||
{
|
||||
return trap_G2API_SetBoneAngles(ghoul2, modelIndex, boneName, angles, flags, up, right, forward, modelList, blendTime, currentTime);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_SetBoneAnim(void *ghoul2, const int modelIndex, const char *boneName, const int startFrame, const int endFrame,
|
||||
const int flags, const float animSpeed, const int currentTime, const float setFrame , const int blendTime )
|
||||
{
|
||||
return trap_G2API_SetBoneAnim(ghoul2, modelIndex, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_GetBoneAnim(void *ghoul2, const char *boneName, const int currentTime, float *currentFrame,
|
||||
int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList, const int modelIndex)
|
||||
{
|
||||
return trap_G2API_GetBoneAnim(ghoul2, boneName, currentTime, currentFrame, startFrame, endFrame, flags, animSpeed, modelList, modelIndex);
|
||||
}
|
||||
|
||||
void strap_G2API_SetRagDoll(void *ghoul2, sharedRagDollParams_t *params)
|
||||
{
|
||||
trap_G2API_SetRagDoll(ghoul2, params);
|
||||
}
|
||||
|
||||
void strap_G2API_AnimateG2Models(void *ghoul2, int time, sharedRagDollUpdateParams_t *params)
|
||||
{
|
||||
trap_G2API_AnimateG2Models(ghoul2, time, params);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_SetBoneIKState(void *ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params)
|
||||
{
|
||||
return trap_G2API_SetBoneIKState(ghoul2, time, boneName, ikState, params);
|
||||
}
|
||||
|
||||
qboolean strap_G2API_IKMove(void *ghoul2, int time, sharedIKMoveParams_t *params)
|
||||
{
|
||||
return trap_G2API_IKMove(ghoul2, time, params);
|
||||
}
|
||||
|
||||
void strap_TrueMalloc(void **ptr, int size)
|
||||
{
|
||||
trap_TrueMalloc(ptr, size);
|
||||
}
|
||||
|
||||
void strap_TrueFree(void **ptr)
|
||||
{
|
||||
trap_TrueFree(ptr);
|
||||
}
|
||||
|
||||
#include "../namespace_end.h"
|
||||
470
codemp/game/g_svcmds.c
Normal file
470
codemp/game/g_svcmds.c
Normal file
@@ -0,0 +1,470 @@
|
||||
// Copyright (C) 1999-2000 Id Software, Inc.
|
||||
//
|
||||
|
||||
// this file holds commands that can be executed by the server console, but not remote clients
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PACKET FILTERING
|
||||
|
||||
|
||||
You can add or remove addresses from the filter list with:
|
||||
|
||||
addip <ip>
|
||||
removeip <ip>
|
||||
|
||||
The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
|
||||
|
||||
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
|
||||
|
||||
listip
|
||||
Prints the current list of filters.
|
||||
|
||||
g_filterban <0 or 1>
|
||||
|
||||
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
|
||||
|
||||
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
|
||||
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
// extern vmCvar_t g_banIPs;
|
||||
// extern vmCvar_t g_filterBan;
|
||||
|
||||
|
||||
typedef struct ipFilter_s
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned compare;
|
||||
} ipFilter_t;
|
||||
|
||||
// VVFIXME - We don't need this at all, but this is the quick way.
|
||||
#ifdef _XBOX
|
||||
#define MAX_IPFILTERS 1
|
||||
#else
|
||||
#define MAX_IPFILTERS 1024
|
||||
#endif
|
||||
|
||||
static ipFilter_t ipFilters[MAX_IPFILTERS];
|
||||
static int numIPFilters;
|
||||
|
||||
/*
|
||||
=================
|
||||
StringToFilter
|
||||
=================
|
||||
*/
|
||||
static qboolean StringToFilter (char *s, ipFilter_t *f)
|
||||
{
|
||||
char num[128];
|
||||
int i, j;
|
||||
byte b[4];
|
||||
byte m[4];
|
||||
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
b[i] = 0;
|
||||
m[i] = 0;
|
||||
}
|
||||
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
if (*s < '0' || *s > '9')
|
||||
{
|
||||
G_Printf( "Bad filter address: %s\n", s );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
{
|
||||
num[j++] = *s++;
|
||||
}
|
||||
num[j] = 0;
|
||||
b[i] = atoi(num);
|
||||
if (b[i] != 0)
|
||||
m[i] = 255;
|
||||
|
||||
if (!*s)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
|
||||
f->mask = *(unsigned *)m;
|
||||
f->compare = *(unsigned *)b;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
UpdateIPBans
|
||||
=================
|
||||
*/
|
||||
static void UpdateIPBans (void)
|
||||
{
|
||||
byte b[4];
|
||||
int i;
|
||||
char iplist[MAX_INFO_STRING];
|
||||
|
||||
*iplist = 0;
|
||||
for (i = 0 ; i < numIPFilters ; i++)
|
||||
{
|
||||
if (ipFilters[i].compare == 0xffffffff)
|
||||
continue;
|
||||
|
||||
*(unsigned *)b = ipFilters[i].compare;
|
||||
Com_sprintf( iplist + strlen(iplist), sizeof(iplist) - strlen(iplist),
|
||||
"%i.%i.%i.%i ", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
trap_Cvar_Set( "g_banIPs", iplist );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_FilterPacket
|
||||
=================
|
||||
*/
|
||||
qboolean G_FilterPacket (char *from)
|
||||
{
|
||||
byte m[4];// = {'\0','\0','\0','\0'};
|
||||
int i = 0;
|
||||
unsigned int in;
|
||||
char *p;
|
||||
|
||||
while (i < 4)
|
||||
{
|
||||
m[i] = 0;
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
p = from;
|
||||
while (*p && i < 4) {
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
m[i] = m[i]*10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (!*p || *p == ':')
|
||||
break;
|
||||
i++, p++;
|
||||
}
|
||||
|
||||
in = *(unsigned int *)m;
|
||||
|
||||
for (i=0 ; i<numIPFilters ; i++)
|
||||
if ( (in & ipFilters[i].mask) == ipFilters[i].compare)
|
||||
return g_filterBan.integer != 0;
|
||||
|
||||
return g_filterBan.integer == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
AddIP
|
||||
=================
|
||||
*/
|
||||
static void AddIP( char *str )
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < numIPFilters ; i++)
|
||||
if (ipFilters[i].compare == 0xffffffff)
|
||||
break; // free spot
|
||||
if (i == numIPFilters)
|
||||
{
|
||||
if (numIPFilters == MAX_IPFILTERS)
|
||||
{
|
||||
G_Printf ("IP filter list is full\n");
|
||||
return;
|
||||
}
|
||||
numIPFilters++;
|
||||
}
|
||||
|
||||
if (!StringToFilter (str, &ipFilters[i]))
|
||||
ipFilters[i].compare = 0xffffffffu;
|
||||
|
||||
UpdateIPBans();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_ProcessIPBans
|
||||
=================
|
||||
*/
|
||||
void G_ProcessIPBans(void)
|
||||
{
|
||||
char *s, *t;
|
||||
char str[MAX_TOKEN_CHARS];
|
||||
|
||||
Q_strncpyz( str, g_banIPs.string, sizeof(str) );
|
||||
|
||||
for (t = s = g_banIPs.string; *t; /* */ ) {
|
||||
s = strchr(s, ' ');
|
||||
if (!s)
|
||||
break;
|
||||
while (*s == ' ')
|
||||
*s++ = 0;
|
||||
if (*t)
|
||||
AddIP( t );
|
||||
t = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Svcmd_AddIP_f
|
||||
=================
|
||||
*/
|
||||
void Svcmd_AddIP_f (void)
|
||||
{
|
||||
char str[MAX_TOKEN_CHARS];
|
||||
|
||||
if ( trap_Argc() < 2 ) {
|
||||
G_Printf("Usage: addip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
|
||||
AddIP( str );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Svcmd_RemoveIP_f
|
||||
=================
|
||||
*/
|
||||
void Svcmd_RemoveIP_f (void)
|
||||
{
|
||||
ipFilter_t f;
|
||||
int i;
|
||||
char str[MAX_TOKEN_CHARS];
|
||||
|
||||
if ( trap_Argc() < 2 ) {
|
||||
G_Printf("Usage: sv removeip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
|
||||
if (!StringToFilter (str, &f))
|
||||
return;
|
||||
|
||||
for (i=0 ; i<numIPFilters ; i++) {
|
||||
if (ipFilters[i].mask == f.mask &&
|
||||
ipFilters[i].compare == f.compare) {
|
||||
ipFilters[i].compare = 0xffffffffu;
|
||||
G_Printf ("Removed.\n");
|
||||
|
||||
UpdateIPBans();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
G_Printf ( "Didn't find %s.\n", str );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_EntityList_f
|
||||
===================
|
||||
*/
|
||||
void Svcmd_EntityList_f (void) {
|
||||
int e;
|
||||
gentity_t *check;
|
||||
|
||||
check = g_entities+1;
|
||||
for (e = 1; e < level.num_entities ; e++, check++) {
|
||||
if ( !check->inuse ) {
|
||||
continue;
|
||||
}
|
||||
G_Printf("%3i:", e);
|
||||
switch ( check->s.eType ) {
|
||||
case ET_GENERAL:
|
||||
G_Printf("ET_GENERAL ");
|
||||
break;
|
||||
case ET_PLAYER:
|
||||
G_Printf("ET_PLAYER ");
|
||||
break;
|
||||
case ET_ITEM:
|
||||
G_Printf("ET_ITEM ");
|
||||
break;
|
||||
case ET_MISSILE:
|
||||
G_Printf("ET_MISSILE ");
|
||||
break;
|
||||
case ET_MOVER:
|
||||
G_Printf("ET_MOVER ");
|
||||
break;
|
||||
case ET_BEAM:
|
||||
G_Printf("ET_BEAM ");
|
||||
break;
|
||||
case ET_PORTAL:
|
||||
G_Printf("ET_PORTAL ");
|
||||
break;
|
||||
case ET_SPEAKER:
|
||||
G_Printf("ET_SPEAKER ");
|
||||
break;
|
||||
case ET_PUSH_TRIGGER:
|
||||
G_Printf("ET_PUSH_TRIGGER ");
|
||||
break;
|
||||
case ET_TELEPORT_TRIGGER:
|
||||
G_Printf("ET_TELEPORT_TRIGGER ");
|
||||
break;
|
||||
case ET_INVISIBLE:
|
||||
G_Printf("ET_INVISIBLE ");
|
||||
break;
|
||||
case ET_NPC:
|
||||
G_Printf("ET_NPC ");
|
||||
break;
|
||||
default:
|
||||
G_Printf("%3i ", check->s.eType);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( check->classname ) {
|
||||
G_Printf("%s", check->classname);
|
||||
}
|
||||
G_Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
gclient_t *ClientForString( const char *s ) {
|
||||
gclient_t *cl;
|
||||
int i;
|
||||
int idnum;
|
||||
|
||||
// numeric values are just slot numbers
|
||||
if ( s[0] >= '0' && s[0] <= '9' ) {
|
||||
idnum = atoi( s );
|
||||
if ( idnum < 0 || idnum >= level.maxclients ) {
|
||||
Com_Printf( "Bad client slot: %i\n", idnum );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cl = &level.clients[idnum];
|
||||
if ( cl->pers.connected == CON_DISCONNECTED ) {
|
||||
G_Printf( "Client %i is not connected\n", idnum );
|
||||
return NULL;
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
// check for a name match
|
||||
for ( i=0 ; i < level.maxclients ; i++ ) {
|
||||
cl = &level.clients[i];
|
||||
if ( cl->pers.connected == CON_DISCONNECTED ) {
|
||||
continue;
|
||||
}
|
||||
if ( !Q_stricmp( cl->pers.netname, s ) ) {
|
||||
return cl;
|
||||
}
|
||||
}
|
||||
|
||||
G_Printf( "User %s is not on the server\n", s );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_ForceTeam_f
|
||||
|
||||
forceteam <player> <team>
|
||||
===================
|
||||
*/
|
||||
void Svcmd_ForceTeam_f( void ) {
|
||||
gclient_t *cl;
|
||||
char str[MAX_TOKEN_CHARS];
|
||||
|
||||
// find the player
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
cl = ClientForString( str );
|
||||
if ( !cl ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set the team
|
||||
trap_Argv( 2, str, sizeof( str ) );
|
||||
SetTeam( &g_entities[cl - level.clients], str );
|
||||
}
|
||||
|
||||
char *ConcatArgs( int start );
|
||||
|
||||
/*
|
||||
=================
|
||||
ConsoleCommand
|
||||
|
||||
=================
|
||||
*/
|
||||
qboolean ConsoleCommand( void ) {
|
||||
char cmd[MAX_TOKEN_CHARS];
|
||||
|
||||
trap_Argv( 0, cmd, sizeof( cmd ) );
|
||||
|
||||
if ( Q_stricmp (cmd, "entitylist") == 0 ) {
|
||||
Svcmd_EntityList_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if ( Q_stricmp (cmd, "forceteam") == 0 ) {
|
||||
Svcmd_ForceTeam_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (Q_stricmp (cmd, "game_memory") == 0) {
|
||||
Svcmd_GameMem_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (Q_stricmp (cmd, "addbot") == 0) {
|
||||
Svcmd_AddBot_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (Q_stricmp (cmd, "botlist") == 0) {
|
||||
Svcmd_BotList_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/* if (Q_stricmp (cmd, "abort_podium") == 0) {
|
||||
Svcmd_AbortPodium_f();
|
||||
return qtrue;
|
||||
}
|
||||
*/
|
||||
if (Q_stricmp (cmd, "addip") == 0) {
|
||||
Svcmd_AddIP_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (Q_stricmp (cmd, "removeip") == 0) {
|
||||
Svcmd_RemoveIP_f();
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (Q_stricmp (cmd, "listip") == 0) {
|
||||
trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if (g_dedicated.integer) {
|
||||
if (Q_stricmp (cmd, "say") == 0) {
|
||||
trap_SendServerCommand( -1, va("print \"server: %s\n\"", ConcatArgs(1) ) );
|
||||
return qtrue;
|
||||
}
|
||||
// everything else will also be printed as a say command
|
||||
trap_SendServerCommand( -1, va("print \"server: %s\n\"", ConcatArgs(0) ) );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
313
codemp/game/g_syscalls.asm
Normal file
313
codemp/game/g_syscalls.asm
Normal file
@@ -0,0 +1,313 @@
|
||||
code
|
||||
|
||||
equ trap_Printf -1 ; G_PRINT
|
||||
equ trap_Error -2 ; G_ERROR
|
||||
equ trap_Milliseconds -3 ; G_MILLISECONDS
|
||||
equ trap_PrecisionTimer_Start -4 ; G_PRECISIONTIMER_START
|
||||
equ trap_PrecisionTimer_End -5 ; G_PRECISIONTIMER_END
|
||||
equ trap_Cvar_Register -6 ; G_CVAR_REGISTER
|
||||
equ trap_Cvar_Update -7 ; G_CVAR_UPDATE
|
||||
equ trap_Cvar_Set -8 ; G_CVAR_SET
|
||||
equ trap_Cvar_VariableIntegerValue -9 ; G_CVAR_VARIABLE_INTEGER_VALUE
|
||||
equ trap_Cvar_VariableStringBuffer -10 ; G_CVAR_VARIABLE_STRING_BUFFER
|
||||
equ trap_Argc -11 ; G_ARGC
|
||||
equ trap_Argv -12 ; G_ARGV
|
||||
equ trap_FS_FOpenFile -13 ; G_FS_FOPEN_FILE
|
||||
equ trap_FS_Read -14 ; G_FS_READ
|
||||
equ trap_FS_Write -15 ; G_FS_WRITE
|
||||
equ trap_FS_FCloseFile -16 ; G_FS_FCLOSE_FILE
|
||||
equ trap_SendConsoleCommand -17 ; G_SEND_CONSOLE_COMMAND
|
||||
equ trap_LocateGameData -18 ; G_LOCATE_GAME_DATA
|
||||
equ trap_DropClient -19 ; G_DROP_CLIENT
|
||||
equ trap_SendServerCommand -20 ; G_SEND_SERVER_COMMAND
|
||||
equ trap_SetConfigstring -21 ; G_SET_CONFIGSTRING
|
||||
equ trap_GetConfigstring -22 ; G_GET_CONFIGSTRING
|
||||
equ trap_GetUserinfo -23 ; G_GET_USERINFO
|
||||
equ trap_SetUserinfo -24 ; G_SET_USERINFO
|
||||
equ trap_GetServerinfo -25 ; G_GET_SERVERINFO
|
||||
equ trap_SetBrushModel -26 ; G_SET_BRUSH_MODEL
|
||||
equ trap_Trace -27 ; G_TRACE
|
||||
equ trap_G2Trace -28 ; G_G2TRACE
|
||||
equ trap_PointContents -29 ; G_POINT_CONTENTS
|
||||
equ trap_InPVS -30 ; G_IN_PVS
|
||||
equ trap_InPVSIgnorePortals -31 ; G_IN_PVS_IGNORE_PORTALS
|
||||
equ trap_AdjustAreaPortalState -32 ; G_ADJUST_AREA_PORTAL_STATE
|
||||
equ trap_AreasConnected -33 ; G_AREAS_CONNECTED
|
||||
equ trap_LinkEntity -34 ; G_LINKENTITY
|
||||
equ trap_UnlinkEntity -35 ; G_UNLINKENTITY
|
||||
equ trap_EntitiesInBox -36 ; G_ENTITIES_IN_BOX
|
||||
equ trap_EntityContact -37 ; G_ENTITY_CONTACT
|
||||
equ trap_BotAllocateClient -38 ; G_BOT_ALLOCATE_CLIENT
|
||||
equ trap_BotFreeClient -39 ; G_BOT_FREE_CLIENT
|
||||
equ trap_GetUsercmd -40 ; G_GET_USERCMD
|
||||
equ trap_GetEntityToken -41 ; G_GET_ENTITY_TOKEN
|
||||
equ trap_FS_GetFileList -42 ; G_FS_GETFILELIST
|
||||
equ trap_DebugPolygonCreate -43 ; G_DEBUG_POLYGON_CREATE
|
||||
equ trap_DebugPolygonDelete -44 ; G_DEBUG_POLYGON_DELETE
|
||||
equ trap_RealTime -45 ; G_REAL_TIME
|
||||
equ trap_SnapVector -46 ; G_SNAPVECTOR
|
||||
equ trap_TraceCapsule -47 ; G_TRACECAPSULE
|
||||
equ trap_EntityContactCapsule -48 ; G_ENTITY_CONTACTCAPSULE
|
||||
equ trap_SP_GetStringTextString -49 ; SP_GETSTRINGTEXTSTRING
|
||||
equ trap_ROFF_Clean -50 ; G_ROFF_CLEAN
|
||||
equ trap_ROFF_UpdateEntities -51 ; G_ROFF_UPDATE_ENTITIES
|
||||
equ trap_ROFF_Cache -52 ; G_ROFF_CACHE
|
||||
equ trap_ROFF_Play -53 ; G_ROFF_PLAY
|
||||
equ trap_ROFF_Purge_Ent -54 ; G_ROFF_PURGE_ENT
|
||||
equ trap_TrueMalloc -55 ; G_TRUEMALLOC
|
||||
equ trap_TrueFree -56 ; G_TRUEFREE
|
||||
equ trap_ICARUS_RunScript -57 ; G_ICARUS_RUNSCRIPT
|
||||
equ trap_ICARUS_RegisterScript -58 ; G_ICARUS_REGISTERSCRIPT
|
||||
equ trap_ICARUS_Init -59 ; G_ICARUS_INIT
|
||||
equ trap_ICARUS_ValidEnt -60 ; G_ICARUS_VALIDENT
|
||||
equ trap_ICARUS_IsInitialized -61 ; G_ICARUS_ISINITIALIZED
|
||||
equ trap_ICARUS_MaintainTaskManager -62 ; G_ICARUS_MAINTAINTASKMANAGER
|
||||
equ trap_ICARUS_IsRunning -63 ; G_ICARUS_ISRUNNING
|
||||
equ trap_ICARUS_TaskIDPending -64 ; G_ICARUS_TASKIDPENDING
|
||||
equ trap_ICARUS_InitEnt -65 ; G_ICARUS_INITENT
|
||||
equ trap_ICARUS_FreeEnt -66 ; G_ICARUS_FREEENT
|
||||
equ trap_ICARUS_AssociateEnt -67 ; G_ICARUS_ASSOCIATEENT
|
||||
equ trap_ICARUS_Shutdown -68 ; G_ICARUS_SHUTDOWN
|
||||
equ trap_ICARUS_TaskIDSet -69 ; G_ICARUS_TASKIDSET
|
||||
equ trap_ICARUS_TaskIDComplete -70 ; G_ICARUS_TASKIDCOMPLETE
|
||||
equ trap_ICARUS_SetVar -71 ; G_ICARUS_SETVAR
|
||||
equ trap_ICARUS_VariableDeclared -72 ; G_ICARUS_VARIABLEDECLARED
|
||||
equ trap_ICARUS_GetFloatVariable -73 ; G_ICARUS_GETFLOATVARIABLE
|
||||
equ trap_ICARUS_GetStringVariable -74 ; G_ICARUS_GETSTRINGVARIABLE
|
||||
equ trap_ICARUS_GetVectorVariable -75 ; G_ICARUS_GETVECTORVARIABLE
|
||||
equ trap_Nav_Init -201 ; G_NAV_INIT
|
||||
equ trap_Nav_Free -202 ; G_NAV_FREE
|
||||
equ trap_Nav_Load -203 ; G_NAV_LOAD
|
||||
equ trap_Nav_Save -204 ; G_NAV_SAVE
|
||||
equ trap_Nav_AddRawPoint -205 ; G_NAV_ADDRAWPOINT
|
||||
equ trap_Nav_CalculatePaths -206 ; G_NAV_CALCULATEPATHS
|
||||
equ trap_Nav_HardConnect -207 ; G_NAV_HARDCONNECT
|
||||
equ trap_Nav_ShowNodes -208 ; G_NAV_SHOWNODES
|
||||
equ trap_Nav_ShowEdges -209 ; G_NAV_SHOWEDGES
|
||||
equ trap_Nav_ShowPath -210 ; G_NAV_SHOWPATH
|
||||
equ trap_Nav_GetNearestNode -211 ; G_NAV_GETNEARESTNODE
|
||||
equ trap_Nav_GetBestNode -212 ; G_NAV_GETBESTNODE
|
||||
equ trap_Nav_GetNodePosition -213 ; G_NAV_GETNODEPOSITION
|
||||
equ trap_Nav_GetNodeNumEdges -214 ; G_NAV_GETNODENUMEDGES
|
||||
equ trap_Nav_GetNodeEdge -215 ; G_NAV_GETNODEEDGE
|
||||
equ trap_Nav_GetNumNodes -216 ; G_NAV_GETNUMNODES
|
||||
equ trap_Nav_Connected -217 ; G_NAV_CONNECTED
|
||||
equ trap_Nav_GetPathCost -218 ; G_NAV_GETPATHCOST
|
||||
equ trap_Nav_GetEdgeCost -219 ; G_NAV_GETEDGECOST
|
||||
equ trap_Nav_GetProjectedNode -220 ; G_NAV_GETPROJECTEDNODE
|
||||
equ trap_Nav_CheckFailedNodes -221 ; G_NAV_CHECKFAILEDNODES
|
||||
equ trap_Nav_AddFailedNode -222 ; G_NAV_ADDFAILEDNODE
|
||||
equ trap_Nav_NodeFailed -223 ; G_NAV_NODEFAILED
|
||||
equ trap_Nav_NodesAreNeighbors -224 ; G_NAV_NODESARENEIGHBORS
|
||||
equ trap_Nav_ClearFailedEdge -225 ; G_NAV_CLEARFAILEDEDGE
|
||||
equ trap_Nav_ClearAllFailedEdges -226 ; G_NAV_CLEARALLFAILEDEDGES
|
||||
equ trap_Nav_EdgeFailed -227 ; G_NAV_EDGEFAILED
|
||||
equ trap_Nav_AddFailedEdge -228 ; G_NAV_ADDFAILEDEDGE
|
||||
equ trap_Nav_CheckFailedEdge -229 ; G_NAV_CHECKFAILEDEDGE
|
||||
equ trap_Nav_CheckAllFailedEdges -230 ; G_NAV_CHECKALLFAILEDEDGES
|
||||
equ trap_Nav_RouteBlocked -231 ; G_NAV_ROUTEBLOCKED
|
||||
equ trap_Nav_GetBestNodeAltRoute -232 ; G_NAV_GETBESTNODEALTROUTE
|
||||
equ trap_Nav_GetBestNodeAltRoute2 -233 ; G_NAV_GETBESTNODEALT2
|
||||
equ trap_Nav_GetBestPathBetweenEnts -234 ; G_NAV_GETBESTPATHBETWEENENTS
|
||||
equ trap_Nav_GetNodeRadius -235 ; G_NAV_GETNODERADIUS
|
||||
equ trap_Nav_CheckBlockedEdges -236 ; G_NAV_CHECKBLOCKEDEDGES
|
||||
equ trap_Nav_ClearCheckedNodes -237 ; G_NAV_CLEARCHECKEDNODES
|
||||
equ trap_Nav_CheckedNode -238 ; G_NAV_CHECKEDNODE
|
||||
equ trap_Nav_SetCheckedNode -239 ; G_NAV_SETCHECKEDNODE
|
||||
equ trap_Nav_FlagAllNodes -240 ; G_NAV_FLAGALLNODES
|
||||
equ trap_Nav_GetPathsCalculated -241 ; G_NAV_GETPATHSCALCULATED
|
||||
equ trap_Nav_SetPathsCalculated -242 ; G_NAV_SETPATHSCALCULATED
|
||||
equ trap_SV_RegisterSharedMemory -76 ; G_SET_SHARED_BUFFER
|
||||
equ trap_BotLibSetup -251 ; BOTLIB_SETUP
|
||||
equ trap_BotLibShutdown -252 ; BOTLIB_SHUTDOWN
|
||||
equ trap_BotLibVarSet -253 ; BOTLIB_LIBVAR_SET
|
||||
equ trap_BotLibVarGet -254 ; BOTLIB_LIBVAR_GET
|
||||
equ trap_BotLibDefine -255 ; BOTLIB_PC_ADD_GLOBAL_DEFINE
|
||||
equ trap_BotLibStartFrame -256 ; BOTLIB_START_FRAME
|
||||
equ trap_BotLibLoadMap -257 ; BOTLIB_LOAD_MAP
|
||||
equ trap_BotLibUpdateEntity -258 ; BOTLIB_UPDATENTITY
|
||||
equ trap_BotLibTest -259 ; BOTLIB_TEST
|
||||
equ trap_BotGetSnapshotEntity -260 ; BOTLIB_GET_SNAPSHOT_ENTITY
|
||||
equ trap_BotGetServerCommand -261 ; BOTLIB_GET_CONSOLE_MESSAGE
|
||||
equ trap_BotUserCommand -262 ; BOTLIB_USER_COMMAND
|
||||
equ trap_AAS_EntityInfo -304 ; BOTLIB_AAS_ENTITY_INFO
|
||||
equ trap_AAS_Initialized -305 ; BOTLIB_AAS_INITIALIZED
|
||||
equ trap_AAS_PresenceTypeBoundingBox -306 ; BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX
|
||||
equ trap_AAS_Time -307 ; BOTLIB_AAS_TIME
|
||||
equ trap_AAS_PointAreaNum -308 ; BOTLIB_AAS_POINT_AREA_NUM
|
||||
equ trap_AAS_PointReachabilityAreaIndex -578 ; BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX
|
||||
equ trap_AAS_TraceAreas -309 ; BOTLIB_AAS_TRACE_AREAS
|
||||
equ trap_AAS_BBoxAreas -302 ; BOTLIB_AAS_BBOX_AREAS
|
||||
equ trap_AAS_AreaInfo -303 ; BOTLIB_AAS_AREA_INFO
|
||||
equ trap_AAS_PointContents -310 ; BOTLIB_AAS_POINT_CONTENTS
|
||||
equ trap_AAS_NextBSPEntity -311 ; BOTLIB_AAS_NEXT_BSP_ENTITY
|
||||
equ trap_AAS_ValueForBSPEpairKey -312 ; BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY
|
||||
equ trap_AAS_VectorForBSPEpairKey -313 ; BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY
|
||||
equ trap_AAS_FloatForBSPEpairKey -314 ; BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY
|
||||
equ trap_AAS_IntForBSPEpairKey -315 ; BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY
|
||||
equ trap_AAS_AreaReachability -316 ; BOTLIB_AAS_AREA_REACHABILITY
|
||||
equ trap_AAS_AreaTravelTimeToGoalArea -317 ; BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA
|
||||
equ trap_AAS_EnableRoutingArea -301 ; BOTLIB_AAS_ENABLE_ROUTING_AREA
|
||||
equ trap_AAS_PredictRoute -577 ; BOTLIB_AAS_PREDICT_ROUTE
|
||||
equ trap_AAS_AlternativeRouteGoals -576 ; BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL
|
||||
equ trap_AAS_Swimming -318 ; BOTLIB_AAS_SWIMMING
|
||||
equ trap_AAS_PredictClientMovement -319 ; BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT
|
||||
equ trap_EA_Say -401 ; BOTLIB_EA_SAY
|
||||
equ trap_EA_SayTeam -402 ; BOTLIB_EA_SAY_TEAM
|
||||
equ trap_EA_Command -403 ; BOTLIB_EA_COMMAND
|
||||
equ trap_EA_Action -404 ; BOTLIB_EA_ACTION
|
||||
equ trap_EA_Gesture -405 ; BOTLIB_EA_GESTURE
|
||||
equ trap_EA_Talk -406 ; BOTLIB_EA_TALK
|
||||
equ trap_EA_Attack -407 ; BOTLIB_EA_ATTACK
|
||||
equ trap_EA_Alt_Attack -408 ; BOTLIB_EA_ALT_ATTACK
|
||||
equ trap_EA_ForcePower -409 ; BOTLIB_EA_FORCEPOWER
|
||||
equ trap_EA_Use -410 ; BOTLIB_EA_USE
|
||||
equ trap_EA_Respawn -411 ; BOTLIB_EA_RESPAWN
|
||||
equ trap_EA_Crouch -412 ; BOTLIB_EA_CROUCH
|
||||
equ trap_EA_MoveUp -413 ; BOTLIB_EA_MOVE_UP
|
||||
equ trap_EA_MoveDown -414 ; BOTLIB_EA_MOVE_DOWN
|
||||
equ trap_EA_MoveForward -415 ; BOTLIB_EA_MOVE_FORWARD
|
||||
equ trap_EA_MoveBack -416 ; BOTLIB_EA_MOVE_BACK
|
||||
equ trap_EA_MoveLeft -417 ; BOTLIB_EA_MOVE_LEFT
|
||||
equ trap_EA_MoveRight -418 ; BOTLIB_EA_MOVE_RIGHT
|
||||
equ trap_EA_SelectWeapon -419 ; BOTLIB_EA_SELECT_WEAPON
|
||||
equ trap_EA_Jump -420 ; BOTLIB_EA_JUMP
|
||||
equ trap_EA_DelayedJump -421 ; BOTLIB_EA_DELAYED_JUMP
|
||||
equ trap_EA_Move -422 ; BOTLIB_EA_MOVE
|
||||
equ trap_EA_View -423 ; BOTLIB_EA_VIEW
|
||||
equ trap_EA_EndRegular -424 ; BOTLIB_EA_END_REGULAR
|
||||
equ trap_EA_GetInput -425 ; BOTLIB_EA_GET_INPUT
|
||||
equ trap_EA_ResetInput -426 ; BOTLIB_EA_RESET_INPUT
|
||||
equ trap_BotLoadCharacter -501 ; BOTLIB_AI_LOAD_CHARACTER
|
||||
equ trap_BotFreeCharacter -502 ; BOTLIB_AI_FREE_CHARACTER
|
||||
equ trap_Characteristic_Float -503 ; BOTLIB_AI_CHARACTERISTIC_FLOAT
|
||||
equ trap_Characteristic_BFloat -504 ; BOTLIB_AI_CHARACTERISTIC_BFLOAT
|
||||
equ trap_Characteristic_Integer -505 ; BOTLIB_AI_CHARACTERISTIC_INTEGER
|
||||
equ trap_Characteristic_BInteger -506 ; BOTLIB_AI_CHARACTERISTIC_BINTEGER
|
||||
equ trap_Characteristic_String -507 ; BOTLIB_AI_CHARACTERISTIC_STRING
|
||||
equ trap_BotAllocChatState -508 ; BOTLIB_AI_ALLOC_CHAT_STATE
|
||||
equ trap_BotFreeChatState -509 ; BOTLIB_AI_FREE_CHAT_STATE
|
||||
equ trap_BotQueueConsoleMessage -510 ; BOTLIB_AI_QUEUE_CONSOLE_MESSAGE
|
||||
equ trap_BotRemoveConsoleMessage -511 ; BOTLIB_AI_REMOVE_CONSOLE_MESSAGE
|
||||
equ trap_BotNextConsoleMessage -512 ; BOTLIB_AI_NEXT_CONSOLE_MESSAGE
|
||||
equ trap_BotNumConsoleMessages -513 ; BOTLIB_AI_NUM_CONSOLE_MESSAGE
|
||||
equ trap_BotInitialChat -514 ; BOTLIB_AI_INITIAL_CHAT
|
||||
equ trap_BotNumInitialChats -570 ; BOTLIB_AI_NUM_INITIAL_CHATS
|
||||
equ trap_BotReplyChat -515 ; BOTLIB_AI_REPLY_CHAT
|
||||
equ trap_BotChatLength -516 ; BOTLIB_AI_CHAT_LENGTH
|
||||
equ trap_BotEnterChat -517 ; BOTLIB_AI_ENTER_CHAT
|
||||
equ trap_BotGetChatMessage -571 ; BOTLIB_AI_GET_CHAT_MESSAGE
|
||||
equ trap_StringContains -518 ; BOTLIB_AI_STRING_CONTAINS
|
||||
equ trap_BotFindMatch -519 ; BOTLIB_AI_FIND_MATCH
|
||||
equ trap_BotMatchVariable -520 ; BOTLIB_AI_MATCH_VARIABLE
|
||||
equ trap_UnifyWhiteSpaces -521 ; BOTLIB_AI_UNIFY_WHITE_SPACES
|
||||
equ trap_BotReplaceSynonyms -522 ; BOTLIB_AI_REPLACE_SYNONYMS
|
||||
equ trap_BotLoadChatFile -523 ; BOTLIB_AI_LOAD_CHAT_FILE
|
||||
equ trap_BotSetChatGender -524 ; BOTLIB_AI_SET_CHAT_GENDER
|
||||
equ trap_BotSetChatName -525 ; BOTLIB_AI_SET_CHAT_NAME
|
||||
equ trap_BotResetGoalState -526 ; BOTLIB_AI_RESET_GOAL_STATE
|
||||
equ trap_BotResetAvoidGoals -527 ; BOTLIB_AI_RESET_AVOID_GOALS
|
||||
equ trap_BotRemoveFromAvoidGoals -572 ; BOTLIB_AI_REMOVE_FROM_AVOID_GOALS
|
||||
equ trap_BotPushGoal -528 ; BOTLIB_AI_PUSH_GOAL
|
||||
equ trap_BotPopGoal -529 ; BOTLIB_AI_POP_GOAL
|
||||
equ trap_BotEmptyGoalStack -530 ; BOTLIB_AI_EMPTY_GOAL_STACK
|
||||
equ trap_BotDumpAvoidGoals -531 ; BOTLIB_AI_DUMP_AVOID_GOALS
|
||||
equ trap_BotDumpGoalStack -532 ; BOTLIB_AI_DUMP_GOAL_STACK
|
||||
equ trap_BotGoalName -533 ; BOTLIB_AI_GOAL_NAME
|
||||
equ trap_BotGetTopGoal -534 ; BOTLIB_AI_GET_TOP_GOAL
|
||||
equ trap_BotGetSecondGoal -535 ; BOTLIB_AI_GET_SECOND_GOAL
|
||||
equ trap_BotChooseLTGItem -536 ; BOTLIB_AI_CHOOSE_LTG_ITEM
|
||||
equ trap_BotChooseNBGItem -537 ; BOTLIB_AI_CHOOSE_NBG_ITEM
|
||||
equ trap_BotTouchingGoal -538 ; BOTLIB_AI_TOUCHING_GOAL
|
||||
equ trap_BotItemGoalInVisButNotVisible -539 ; BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE
|
||||
equ trap_BotGetLevelItemGoal -540 ; BOTLIB_AI_GET_LEVEL_ITEM_GOAL
|
||||
equ trap_BotGetNextCampSpotGoal -568 ; BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL
|
||||
equ trap_BotGetMapLocationGoal -569 ; BOTLIB_AI_GET_MAP_LOCATION_GOAL
|
||||
equ trap_BotAvoidGoalTime -541 ; BOTLIB_AI_AVOID_GOAL_TIME
|
||||
equ trap_BotSetAvoidGoalTime -574 ; BOTLIB_AI_SET_AVOID_GOAL_TIME
|
||||
equ trap_BotInitLevelItems -542 ; BOTLIB_AI_INIT_LEVEL_ITEMS
|
||||
equ trap_BotUpdateEntityItems -543 ; BOTLIB_AI_UPDATE_ENTITY_ITEMS
|
||||
equ trap_BotLoadItemWeights -544 ; BOTLIB_AI_LOAD_ITEM_WEIGHTS
|
||||
equ trap_BotFreeItemWeights -545 ; BOTLIB_AI_FREE_ITEM_WEIGHTS
|
||||
equ trap_BotInterbreedGoalFuzzyLogic -566 ; BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC
|
||||
equ trap_BotSaveGoalFuzzyLogic -546 ; BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC
|
||||
equ trap_BotMutateGoalFuzzyLogic -567 ; BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC
|
||||
equ trap_BotAllocGoalState -547 ; BOTLIB_AI_ALLOC_GOAL_STATE
|
||||
equ trap_BotFreeGoalState -548 ; BOTLIB_AI_FREE_GOAL_STATE
|
||||
equ trap_BotResetMoveState -549 ; BOTLIB_AI_RESET_MOVE_STATE
|
||||
equ trap_BotAddAvoidSpot -575 ; BOTLIB_AI_ADD_AVOID_SPOT
|
||||
equ trap_BotMoveToGoal -550 ; BOTLIB_AI_MOVE_TO_GOAL
|
||||
equ trap_BotMoveInDirection -551 ; BOTLIB_AI_MOVE_IN_DIRECTION
|
||||
equ trap_BotResetAvoidReach -552 ; BOTLIB_AI_RESET_AVOID_REACH
|
||||
equ trap_BotResetLastAvoidReach -553 ; BOTLIB_AI_RESET_LAST_AVOID_REACH
|
||||
equ trap_BotReachabilityArea -554 ; BOTLIB_AI_REACHABILITY_AREA
|
||||
equ trap_BotMovementViewTarget -555 ; BOTLIB_AI_MOVEMENT_VIEW_TARGET
|
||||
equ trap_BotPredictVisiblePosition -573 ; BOTLIB_AI_PREDICT_VISIBLE_POSITION
|
||||
equ trap_BotAllocMoveState -556 ; BOTLIB_AI_ALLOC_MOVE_STATE
|
||||
equ trap_BotFreeMoveState -557 ; BOTLIB_AI_FREE_MOVE_STATE
|
||||
equ trap_BotInitMoveState -558 ; BOTLIB_AI_INIT_MOVE_STATE
|
||||
equ trap_BotChooseBestFightWeapon -559 ; BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON
|
||||
equ trap_BotGetWeaponInfo -560 ; BOTLIB_AI_GET_WEAPON_INFO
|
||||
equ trap_BotLoadWeaponWeights -561 ; BOTLIB_AI_LOAD_WEAPON_WEIGHTS
|
||||
equ trap_BotAllocWeaponState -562 ; BOTLIB_AI_ALLOC_WEAPON_STATE
|
||||
equ trap_BotFreeWeaponState -563 ; BOTLIB_AI_FREE_WEAPON_STATE
|
||||
equ trap_BotResetWeaponState -564 ; BOTLIB_AI_RESET_WEAPON_STATE
|
||||
equ trap_GeneticParentsAndChildSelection -565 ; BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION
|
||||
equ trap_PC_LoadSource -579 ; BOTLIB_PC_LOAD_SOURCE
|
||||
equ trap_PC_FreeSource -580 ; BOTLIB_PC_FREE_SOURCE
|
||||
equ trap_PC_ReadToken -581 ; BOTLIB_PC_READ_TOKEN
|
||||
equ trap_PC_SourceFileAndLine -582 ; BOTLIB_PC_SOURCE_FILE_AND_LINE
|
||||
equ trap_R_RegisterSkin -583 ; G_R_REGISTERSKIN
|
||||
equ trap_G2_ListModelBones -584 ; G_G2_LISTBONES
|
||||
equ trap_G2_ListModelSurfaces -585 ; G_G2_LISTSURFACES
|
||||
equ trap_G2_HaveWeGhoul2Models -586 ; G_G2_HAVEWEGHOULMODELS
|
||||
equ trap_G2_SetGhoul2ModelIndexes -587 ; G_G2_SETMODELS
|
||||
equ trap_G2API_GetBoltMatrix -588 ; G_G2_GETBOLT
|
||||
equ trap_G2API_GetBoltMatrix_NoReconstruct -589 ; G_G2_GETBOLT_NOREC
|
||||
equ trap_G2API_GetBoltMatrix_NoRecNoRot -590 ; G_G2_GETBOLT_NOREC_NOROT
|
||||
equ trap_G2API_InitGhoul2Model -591 ; G_G2_INITGHOUL2MODEL
|
||||
equ trap_G2API_SetSkin -592 ; G_G2_SETSKIN
|
||||
equ trap_G2API_AddBolt -593 ; G_G2_ADDBOLT
|
||||
equ trap_G2API_SetBoltInfo -594 ; G_G2_SETBOLTINFO
|
||||
equ trap_G2API_SetBoneAngles -595 ; G_G2_ANGLEOVERRIDE
|
||||
equ trap_G2API_SetBoneAnim -596 ; G_G2_PLAYANIM
|
||||
equ trap_G2API_GetBoneAnim -597 ; G_G2_GETBONEANIM
|
||||
equ trap_G2API_GetGLAName -598 ; G_G2_GETGLANAME
|
||||
equ trap_G2API_CopyGhoul2Instance -599 ; G_G2_COPYGHOUL2INSTANCE
|
||||
equ trap_G2API_CopySpecificGhoul2Model -600 ; G_G2_COPYSPECIFICGHOUL2MODEL
|
||||
equ trap_G2API_DuplicateGhoul2Instance -601 ; G_G2_DUPLICATEGHOUL2INSTANCE
|
||||
equ trap_G2API_HasGhoul2ModelOnIndex -602 ; G_G2_HASGHOUL2MODELONINDEX
|
||||
equ trap_G2API_RemoveGhoul2Model -603 ; G_G2_REMOVEGHOUL2MODEL
|
||||
equ trap_G2API_CleanGhoul2Models -604 ; G_G2_CLEANMODELS
|
||||
equ trap_G2API_CollisionDetect -605 ; G_G2_COLLISIONDETECT
|
||||
equ trap_G2API_GetSurfaceName -614 ; G_G2_GETSURFACENAME
|
||||
equ trap_G2API_SetRootSurface -606 ; G_G2_SETROOTSURFACE
|
||||
equ trap_G2API_SetSurfaceOnOff -607 ; G_G2_SETSURFACEONOFF
|
||||
equ trap_G2API_SetNewOrigin -608 ; G_G2_SETNEWORIGIN
|
||||
equ trap_G2API_GetSurfaceRenderStatus -609 ; G_G2_GETSURFACERENDERSTATUS
|
||||
equ trap_G2API_SetRagDoll -610 ; G_G2_SETRAGDOLL
|
||||
equ trap_G2API_AnimateG2Models -611 ; G_G2_ANIMATEG2MODELS
|
||||
equ trap_G2API_SetBoneIKState -612 ; G_G2_SETBONEIKSTATE
|
||||
equ trap_G2API_IKMove -613 ; G_G2_IKMOVE
|
||||
equ trap_SetActiveSubBSP -615 ; G_SET_ACTIVE_SUBBSP
|
||||
equ trap_CM_RegisterTerrain -616 ; G_CM_REGISTER_TERRAIN
|
||||
equ trap_RMG_Init -617 ; G_RMG_INIT
|
||||
equ trap_Bot_UpdateWaypoints -618 ; G_BOT_UPDATEWAYPOINTS
|
||||
equ trap_Bot_CalculatePaths -619 ; G_BOT_CALCULATEPATHS
|
||||
|
||||
|
||||
; hardcoded functions
|
||||
equ memset -101 ; G_MEMSET
|
||||
equ memcpy -102 ; G_MEMCPY
|
||||
equ strncpy -103 ; G_STRNCPY
|
||||
equ sin -104 ; G_SIN
|
||||
equ cos -105 ; G_COS
|
||||
equ atan2 -106 ; G_ATAN2
|
||||
equ sqrt -107 ; G_SQRT
|
||||
equ matrixmultiply -108 ; G_MATRIXMULTIPLY
|
||||
equ anglevectors -109 ; G_ANGLEVECTORS
|
||||
equ perpendicularvector -110 ; G_PERPENDICULARVECTOR
|
||||
equ floor -111 ; G_FLOOR
|
||||
equ ceil -112 ; G_CEIL
|
||||
equ acos -115 ; G_ACOS
|
||||
equ asin -116 ; G_ASIN
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user