Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

946
codemp/game/AnimalNPC.c Normal file
View 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

File diff suppressed because it is too large Load Diff

3
codemp/game/JK2_game.def Normal file
View File

@@ -0,0 +1,3 @@
EXPORTS
dllEntry
vmMain

650
codemp/game/JK2_game.dsp Normal file
View 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
View File

@@ -0,0 +1,744 @@
<?xml version="1.0" encoding = "Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.00"
Name="JK2game"
SccProjectName="&quot;$/jedi/codemp/game&quot;, 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

File diff suppressed because it is too large Load Diff

308
codemp/game/NPC_AI_Atst.c Normal file
View 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();
}
}
*/

View 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
View 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();
}
}

View 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
View 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 );
}

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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
View 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
View 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();
}
}

File diff suppressed because it is too large Load Diff

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
View 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

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

934
codemp/game/NPC_senses.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

1134
codemp/game/SpeederNPC.c Normal file

File diff suppressed because it is too large Load Diff

636
codemp/game/WalkerNPC.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

411
codemp/game/ai_main.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

70
codemp/game/bg_lib.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

116
codemp/game/bg_saga.h Normal file
View 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

File diff suppressed because it is too large Load Diff

38
codemp/game/bg_strap.h Normal file
View 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

File diff suppressed because it is too large Load Diff

628
codemp/game/bg_vehicles.h Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

15
codemp/game/g_ICARUScb.h Normal file
View 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

File diff suppressed because it is too large Load Diff

343
codemp/game/g_arenas.c Normal file
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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
View 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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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
View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

322
codemp/game/g_session.c Normal file
View 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

File diff suppressed because it is too large Load Diff

73
codemp/game/g_strap.c Normal file
View 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
View 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
View 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