Cleanup code

Fixed a bug after killing czbot by headshot, angles turn at 180 degrees.
Fixed some bugs with mp_nadedrops
This commit is contained in:
s1lent 2017-11-01 22:01:24 +07:00 committed by Dmitry Novikov
parent 9dcab4bd49
commit 0eed08c133
34 changed files with 1497 additions and 1419 deletions

View File

@ -1,6 +1,6 @@
#include "precompiled.h" #include "precompiled.h"
// BModelOrigin - calculates origin of a bmodel from absmin/size because all bmodel origins are 0 0 0 // Calculates origin of a bmodel from absmin/size because all bmodel origins are 0 0 0
Vector VecBModelOrigin(entvars_t *pevBModel) Vector VecBModelOrigin(entvars_t *pevBModel)
{ {
return pevBModel->absmin + (pevBModel->size * 0.5f); return pevBModel->absmin + (pevBModel->size * 0.5f);
@ -477,7 +477,7 @@ void CFuncRotating::Precache()
} }
} }
// Touch - will hurt others based on how fast the brush is spinning // Will hurt others based on how fast the brush is spinning
void CFuncRotating::HurtTouch(CBaseEntity *pOther) void CFuncRotating::HurtTouch(CBaseEntity *pOther)
{ {
entvars_t *pevOther = pOther->pev; entvars_t *pevOther = pOther->pev;
@ -494,7 +494,7 @@ void CFuncRotating::HurtTouch(CBaseEntity *pOther)
pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev)).Normalize() * pev->dmg; pevOther->velocity = (pevOther->origin - VecBModelOrigin(pev)).Normalize() * pev->dmg;
} }
// RampPitchVol - ramp pitch and volume up to final values, based on difference // Ramp pitch and volume up to final values, based on difference
// between how fast we're going vs how fast we plan to go // between how fast we're going vs how fast we plan to go
void CFuncRotating::RampPitchVol(BOOL fUp) void CFuncRotating::RampPitchVol(BOOL fUp)
{ {
@ -540,7 +540,7 @@ void CFuncRotating::RampPitchVol(BOOL fUp)
EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning), fvol, m_flAttenuation, (SND_CHANGE_PITCH | SND_CHANGE_VOL), pitch); EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noiseRunning), fvol, m_flAttenuation, (SND_CHANGE_PITCH | SND_CHANGE_VOL), pitch);
} }
// SpinUp - accelerates a non-moving func_rotating up to it's speed // Accelerates a non-moving func_rotating up to it's speed
void CFuncRotating::SpinUp() void CFuncRotating::SpinUp()
{ {
// rotational velocity // rotational velocity
@ -616,7 +616,7 @@ void CFuncRotating::Rotate()
pev->nextthink = pev->ltime + 10; pev->nextthink = pev->ltime + 10;
} }
// Rotating Use - when a rotating brush is triggered // When a rotating brush is triggered
void CFuncRotating::RotatingUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) void CFuncRotating::RotatingUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
{ {
// is this a brush that should accelerate and decelerate when turned on/off (fan)? // is this a brush that should accelerate and decelerate when turned on/off (fan)?
@ -662,7 +662,7 @@ void CFuncRotating::RotatingUse(CBaseEntity *pActivator, CBaseEntity *pCaller, U
} }
} }
// RotatingBlocked - An entity has blocked the brush // An entity has blocked the brush
void CFuncRotating::Blocked(CBaseEntity *pOther) void CFuncRotating::Blocked(CBaseEntity *pOther)
{ {
pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH); pOther->TakeDamage(pev, pev, pev->dmg, DMG_CRUSH);

View File

@ -729,7 +729,7 @@ private:
float m_jumpCrouchTimestamp; float m_jumpCrouchTimestamp;
// path navigation data // path navigation data
enum { MAX_PATH_LENGTH = 256 }; enum { MAX_PATH_LENGTH = 256 };
struct ConnectInfo struct ConnectInfo
{ {
CNavArea *area; // the area along the path CNavArea *area; // the area along the path
@ -1643,6 +1643,15 @@ public:
cost += crouchPenalty * dist; cost += crouchPenalty * dist;
} }
// if this is a "walk" area, add penalty
if (area->GetAttributes() & NAV_WALK)
{
// these areas are kinda slow to move through
float_precision walkPenalty = (m_route == FASTEST_ROUTE) ? 2.5f : 1.5f;
cost += walkPenalty * dist;
}
// if this is a "jump" area, add penalty // if this is a "jump" area, add penalty
if (area->GetAttributes() & NAV_JUMP) if (area->GetAttributes() & NAV_JUMP)
{ {

View File

@ -838,24 +838,24 @@ bool BotStatement::IsValid() const
{ {
switch (m_condition[i]) switch (m_condition[i])
{ {
case IS_IN_COMBAT: case IS_IN_COMBAT:
{ {
if (!GetOwner()->IsAttacking()) if (!GetOwner()->IsAttacking())
return false; return false;
break; break;
} }
/*case RADIO_SILENCE: /*case RADIO_SILENCE:
{ {
if (GetOwner()->GetChatter()->GetRadioSilenceDuration() < 10.0f) if (GetOwner()->GetChatter()->GetRadioSilenceDuration() < 10.0f)
return false; return false;
break; break;
}*/ }*/
case ENEMIES_REMAINING: case ENEMIES_REMAINING:
{ {
if (GetOwner()->GetEnemiesRemaining() == 0) if (GetOwner()->GetEnemiesRemaining() == 0)
return false; return false;
break; break;
} }
} }
} }

View File

@ -270,6 +270,7 @@ void CCSBotManager::AddServerCommands()
AddServerCommand("bot_nav_crouch"); AddServerCommand("bot_nav_crouch");
AddServerCommand("bot_nav_jump"); AddServerCommand("bot_nav_jump");
AddServerCommand("bot_nav_precise"); AddServerCommand("bot_nav_precise");
AddServerCommand("bot_nav_walk");
AddServerCommand("bot_nav_no_jump"); AddServerCommand("bot_nav_no_jump");
AddServerCommand("bot_nav_analyze"); AddServerCommand("bot_nav_analyze");
AddServerCommand("bot_nav_strip"); AddServerCommand("bot_nav_strip");
@ -525,6 +526,10 @@ void CCSBotManager::ServerCommand(const char *pcmd)
{ {
m_editCmd = EDIT_ATTRIB_PRECISE; m_editCmd = EDIT_ATTRIB_PRECISE;
} }
else if (FStrEq(pcmd, "bot_nav_walk"))
{
m_editCmd = EDIT_ATTRIB_WALK;
}
else if (FStrEq(pcmd, "bot_nav_no_jump")) else if (FStrEq(pcmd, "bot_nav_no_jump"))
{ {
m_editCmd = EDIT_ATTRIB_NO_JUMP; m_editCmd = EDIT_ATTRIB_NO_JUMP;

View File

@ -288,23 +288,23 @@ bool CCSBot::UpdateLadderMovement()
// check if somehow we totally missed the ladder // check if somehow we totally missed the ladder
switch (m_pathLadderState) switch (m_pathLadderState)
{ {
case MOUNT_ASCENDING_LADDER: case MOUNT_ASCENDING_LADDER:
case MOUNT_DESCENDING_LADDER: case MOUNT_DESCENDING_LADDER:
case ASCEND_LADDER: case ASCEND_LADDER:
case DESCEND_LADDER: case DESCEND_LADDER:
{
const float farAway = 200.0f;
Vector2D d = (m_pathLadder->m_top - pev->origin).Make2D();
if (d.IsLengthGreaterThan(farAway))
{ {
const float farAway = 200.0f; PrintIfWatched("Missed ladder\n");
Vector2D d = (m_pathLadder->m_top - pev->origin).Make2D(); Jump(MUST_JUMP);
if (d.IsLengthGreaterThan(farAway)) DestroyPath();
{ Run();
PrintIfWatched("Missed ladder\n"); return false;
Jump(MUST_JUMP);
DestroyPath();
Run();
return false;
}
break;
} }
break;
}
} }
m_areaEnteredTimestamp = gpGlobals->time; m_areaEnteredTimestamp = gpGlobals->time;
@ -314,12 +314,68 @@ bool CCSBot::UpdateLadderMovement()
switch (m_pathLadderState) switch (m_pathLadderState)
{ {
case APPROACH_ASCENDING_LADDER: case APPROACH_ASCENDING_LADDER:
{
bool approached = false;
Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y);
if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y < 0.0f)
{
Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x);
if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal)
approached = true;
}
// small radius will just slow them down a little for more accuracy in hitting their spot
const float walkRange = 50.0f;
if (d.IsLengthLessThan(walkRange))
{
Walk();
StandUp();
}
// TODO: Check that we are on the ladder we think we are
if (IsOnLadder())
{
m_pathLadderState = ASCEND_LADDER;
PrintIfWatched("ASCEND_LADDER\n");
// find actual top in case m_pathLadder penetrates the ceiling
ComputeLadderEndpoint(true);
}
else if (approached)
{
// face the m_pathLadder
m_pathLadderState = FACE_ASCENDING_LADDER;
PrintIfWatched("FACE_ASCENDING_LADDER\n");
}
else
{
// move toward ladder mount point
MoveTowardsPosition(&m_goalPosition);
}
break;
}
case APPROACH_DESCENDING_LADDER:
{
// fall check
if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight)
{
PrintIfWatched("Fell from ladder.\n");
m_pathLadderState = MOVE_TO_DESTINATION;
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition);
AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth);
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
else
{ {
bool approached = false; bool approached = false;
Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y); Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y);
if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y < 0.0f) if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y > 0.0f)
{ {
Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x); Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x);
@ -327,274 +383,218 @@ bool CCSBot::UpdateLadderMovement()
approached = true; approached = true;
} }
// small radius will just slow them down a little for more accuracy in hitting their spot // if approaching ladder from the side or "ahead", walk
const float walkRange = 50.0f; if (m_pathLadder->m_topBehindArea != m_lastKnownArea)
if (d.IsLengthLessThan(walkRange))
{ {
Walk(); const float walkRange = 150.0f;
StandUp(); if (!IsCrouching() && d.IsLengthLessThan(walkRange))
Walk();
} }
// TODO: Check that we are on the ladder we think we are // TODO: Check that we are on the ladder we think we are
if (IsOnLadder()) if (IsOnLadder())
{ {
m_pathLadderState = ASCEND_LADDER; // we slipped onto the ladder - climb it
PrintIfWatched("ASCEND_LADDER\n"); m_pathLadderState = DESCEND_LADDER;
Run();
PrintIfWatched("DESCEND_LADDER\n");
// find actual top in case m_pathLadder penetrates the ceiling // find actual bottom in case m_pathLadder penetrates the floor
ComputeLadderEndpoint(true); ComputeLadderEndpoint(false);
} }
else if (approached) else if (approached)
{ {
// face the m_pathLadder // face the ladder
m_pathLadderState = FACE_ASCENDING_LADDER; m_pathLadderState = FACE_DESCENDING_LADDER;
PrintIfWatched("FACE_ASCENDING_LADDER\n"); PrintIfWatched("FACE_DESCENDING_LADDER\n");
} }
else else
{ {
// move toward ladder mount point // move toward ladder mount point
MoveTowardsPosition(&m_goalPosition); MoveTowardsPosition(&m_goalPosition);
} }
break;
} }
case APPROACH_DESCENDING_LADDER: break;
}
case FACE_ASCENDING_LADDER:
{
// find yaw to directly aim at ladder
Vector to = m_pathLadder->m_bottom - pev->origin;
Vector idealAngle = UTIL_VecToAngles(to);
const float angleTolerance = 5.0f;
if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance))
{ {
// fall check // move toward ladder until we become "on" it
if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) Run();
{ ResetStuckMonitor();
PrintIfWatched("Fell from ladder.\n"); m_pathLadderState = MOUNT_ASCENDING_LADDER;
PrintIfWatched("MOUNT_ASCENDING_LADDER\n");
m_pathLadderState = MOVE_TO_DESTINATION;
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition);
AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth);
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
else
{
bool approached = false;
Vector2D d(pev->origin.x - m_goalPosition.x, pev->origin.y - m_goalPosition.y);
if (d.x * m_pathLadder->m_dirVector.x + d.y * m_pathLadder->m_dirVector.y > 0.0f)
{
Vector2D perp(-m_pathLadder->m_dirVector.y, m_pathLadder->m_dirVector.x);
if (Q_abs(int64(d.x * perp.x + d.y * perp.y)) < tolerance && d.Length() < closeToGoal)
approached = true;
}
// if approaching ladder from the side or "ahead", walk
if (m_pathLadder->m_topBehindArea != m_lastKnownArea)
{
const float walkRange = 150.0f;
if (!IsCrouching() && d.IsLengthLessThan(walkRange))
Walk();
}
// TODO: Check that we are on the ladder we think we are
if (IsOnLadder())
{
// we slipped onto the ladder - climb it
m_pathLadderState = DESCEND_LADDER;
Run();
PrintIfWatched("DESCEND_LADDER\n");
// find actual bottom in case m_pathLadder penetrates the floor
ComputeLadderEndpoint(false);
}
else if (approached)
{
// face the ladder
m_pathLadderState = FACE_DESCENDING_LADDER;
PrintIfWatched("FACE_DESCENDING_LADDER\n");
}
else
{
// move toward ladder mount point
MoveTowardsPosition(&m_goalPosition);
}
}
break;
} }
case FACE_ASCENDING_LADDER: break;
}
case FACE_DESCENDING_LADDER:
{
// find yaw to directly aim at ladder
Vector to = m_pathLadder->m_top - pev->origin;
Vector idealAngle = UTIL_VecToAngles(to);
const float angleTolerance = 5.0f;
if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance))
{ {
// find yaw to directly aim at ladder // move toward ladder until we become "on" it
Vector to = m_pathLadder->m_bottom - pev->origin; m_pathLadderState = MOUNT_DESCENDING_LADDER;
Vector idealAngle = UTIL_VecToAngles(to); ResetStuckMonitor();
PrintIfWatched("MOUNT_DESCENDING_LADDER\n");
const float angleTolerance = 5.0f;
if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance))
{
// move toward ladder until we become "on" it
Run();
ResetStuckMonitor();
m_pathLadderState = MOUNT_ASCENDING_LADDER;
PrintIfWatched("MOUNT_ASCENDING_LADDER\n");
}
break;
} }
case FACE_DESCENDING_LADDER: break;
}
case MOUNT_ASCENDING_LADDER:
{
if (IsOnLadder())
{ {
// find yaw to directly aim at ladder m_pathLadderState = ASCEND_LADDER;
Vector to = m_pathLadder->m_top - pev->origin; PrintIfWatched("ASCEND_LADDER\n");
Vector idealAngle = UTIL_VecToAngles(to);
const float angleTolerance = 5.0f; // find actual top in case m_pathLadder penetrates the ceiling
if (AnglesAreEqual(pev->v_angle.y, idealAngle.y, angleTolerance)) ComputeLadderEndpoint(true);
{
// move toward ladder until we become "on" it
m_pathLadderState = MOUNT_DESCENDING_LADDER;
ResetStuckMonitor();
PrintIfWatched("MOUNT_DESCENDING_LADDER\n");
}
break;
} }
case MOUNT_ASCENDING_LADDER:
MoveForward();
break;
}
case MOUNT_DESCENDING_LADDER:
{
// fall check
if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight)
{
PrintIfWatched("Fell from ladder.\n");
m_pathLadderState = MOVE_TO_DESTINATION;
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition);
AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth);
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
else
{ {
if (IsOnLadder()) if (IsOnLadder())
{ {
m_pathLadderState = ASCEND_LADDER; m_pathLadderState = DESCEND_LADDER;
PrintIfWatched("ASCEND_LADDER\n"); PrintIfWatched("DESCEND_LADDER\n");
// find actual top in case m_pathLadder penetrates the ceiling // find actual bottom in case m_pathLadder penetrates the floor
ComputeLadderEndpoint(true); ComputeLadderEndpoint(false);
} }
// move toward ladder mount point
MoveForward(); MoveForward();
break;
} }
case MOUNT_DESCENDING_LADDER: break;
}
case ASCEND_LADDER:
{
// run, so we can make our dismount jump to the side, if necessary
Run();
// if our destination area requires us to crouch, do it
if (m_path[m_pathIndex].area->GetAttributes() & NAV_CROUCH)
Crouch();
// did we reach the top?
if (GetFeetZ() >= m_pathLadderEnd)
{ {
// fall check // we reached the top - dismount
if (GetFeetZ() <= m_pathLadder->m_bottom.z + HalfHumanHeight) m_pathLadderState = DISMOUNT_ASCENDING_LADDER;
{ PrintIfWatched("DISMOUNT_ASCENDING_LADDER\n");
PrintIfWatched("Fell from ladder.\n");
m_pathLadderState = MOVE_TO_DESTINATION; if (m_path[m_pathIndex].area == m_pathLadder->m_topForwardArea)
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition); m_pathLadderDismountDir = FORWARD;
else if (m_path[m_pathIndex].area == m_pathLadder->m_topLeftArea)
m_pathLadderDismountDir = LEFT;
else if (m_path[m_pathIndex].area == m_pathLadder->m_topRightArea)
m_pathLadderDismountDir = RIGHT;
AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth); m_pathLadderDismountTimestamp = gpGlobals->time;
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
else
{
if (IsOnLadder())
{
m_pathLadderState = DESCEND_LADDER;
PrintIfWatched("DESCEND_LADDER\n");
// find actual bottom in case m_pathLadder penetrates the floor
ComputeLadderEndpoint(false);
}
// move toward ladder mount point
MoveForward();
}
break;
} }
case ASCEND_LADDER: else if (!IsOnLadder())
{ {
// run, so we can make our dismount jump to the side, if necessary // we fall off the ladder, repath
Run(); DestroyPath();
return false;
// if our destination area requires us to crouch, do it
if (m_path[m_pathIndex].area->GetAttributes() & NAV_CROUCH)
Crouch();
// did we reach the top?
if (GetFeetZ() >= m_pathLadderEnd)
{
// we reached the top - dismount
m_pathLadderState = DISMOUNT_ASCENDING_LADDER;
PrintIfWatched("DISMOUNT_ASCENDING_LADDER\n");
if (m_path[m_pathIndex].area == m_pathLadder->m_topForwardArea)
m_pathLadderDismountDir = FORWARD;
else if (m_path[m_pathIndex].area == m_pathLadder->m_topLeftArea)
m_pathLadderDismountDir = LEFT;
else if (m_path[m_pathIndex].area == m_pathLadder->m_topRightArea)
m_pathLadderDismountDir = RIGHT;
m_pathLadderDismountTimestamp = gpGlobals->time;
}
else if (!IsOnLadder())
{
// we fall off the ladder, repath
DestroyPath();
return false;
}
// move up ladder
MoveForward();
break;
} }
case DESCEND_LADDER:
// move up ladder
MoveForward();
break;
}
case DESCEND_LADDER:
{
Run();
float destHeight = m_pathLadderEnd + HalfHumanHeight;
if (!IsOnLadder() || GetFeetZ() <= destHeight)
{ {
Run(); // we reached the bottom, or we fell off - dismount
m_pathLadderState = MOVE_TO_DESTINATION;
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition);
float destHeight = m_pathLadderEnd + HalfHumanHeight; AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth);
if (!IsOnLadder() || GetFeetZ() <= destHeight) PrintIfWatched("MOVE_TO_DESTINATION\n");
{
// we reached the bottom, or we fell off - dismount
m_pathLadderState = MOVE_TO_DESTINATION;
m_path[m_pathIndex].area->GetClosestPointOnArea(&m_pathLadder->m_bottom, &m_goalPosition);
AddDirectionVector(&m_goalPosition, m_pathLadder->m_dir, HalfHumanWidth);
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
// Move down ladder
MoveForward();
break;
} }
case DISMOUNT_ASCENDING_LADDER:
// Move down ladder
MoveForward();
break;
}
case DISMOUNT_ASCENDING_LADDER:
{
if (gpGlobals->time - m_pathLadderDismountTimestamp >= 0.4f)
{ {
if (gpGlobals->time - m_pathLadderDismountTimestamp >= 0.4f) m_pathLadderState = MOVE_TO_DESTINATION;
{ m_path[m_pathIndex].area->GetClosestPointOnArea(&pev->origin, &m_goalPosition);
m_pathLadderState = MOVE_TO_DESTINATION; PrintIfWatched("MOVE_TO_DESTINATION\n");
m_path[m_pathIndex].area->GetClosestPointOnArea(&pev->origin, &m_goalPosition);
PrintIfWatched("MOVE_TO_DESTINATION\n");
}
// We should already be facing the dismount point
if (m_pathLadderFaceIn)
{
switch (m_pathLadderDismountDir)
{
case LEFT: StrafeLeft(); break;
case RIGHT: StrafeRight(); break;
case FORWARD: MoveForward(); break;
}
}
else
{
switch (m_pathLadderDismountDir)
{
case LEFT: StrafeRight(); break;
case RIGHT: StrafeLeft(); break;
case FORWARD: MoveBackward(); break;
}
}
break;
} }
case MOVE_TO_DESTINATION:
// We should already be facing the dismount point
if (m_pathLadderFaceIn)
{ {
if (m_path[m_pathIndex].area->Contains(&pev->origin)) switch (m_pathLadderDismountDir)
{ {
// successfully traversed ladder and reached destination area case LEFT: StrafeLeft(); break;
// exit ladder state machine case RIGHT: StrafeRight(); break;
PrintIfWatched("Ladder traversed.\n"); case FORWARD: MoveForward(); break;
m_pathLadder = nullptr;
// incrememnt path index to next step beyond this ladder
SetPathIndex(m_pathIndex + 1);
return false;
} }
MoveTowardsPosition(&m_goalPosition);
break;
} }
else
{
switch (m_pathLadderDismountDir)
{
case LEFT: StrafeRight(); break;
case RIGHT: StrafeLeft(); break;
case FORWARD: MoveBackward(); break;
}
}
break;
}
case MOVE_TO_DESTINATION:
{
if (m_path[m_pathIndex].area->Contains(&pev->origin))
{
// successfully traversed ladder and reached destination area
// exit ladder state machine
PrintIfWatched("Ladder traversed.\n");
m_pathLadder = nullptr;
// incrememnt path index to next step beyond this ladder
SetPathIndex(m_pathIndex + 1);
return false;
}
MoveTowardsPosition(&m_goalPosition);
break;
}
} }
return true; return true;
@ -1352,9 +1352,10 @@ CCSBot::PathResult CCSBot::UpdatePathMovement(bool allowSpeedChange)
SetPathIndex(newIndex); SetPathIndex(newIndex);
} }
// Crouching
if (!IsUsingLadder()) if (!IsUsingLadder())
{ {
// Crouching
// if we are approaching a crouch area, crouch // if we are approaching a crouch area, crouch
// if there are no crouch areas coming up, stand // if there are no crouch areas coming up, stand
const float crouchRange = 50.0f; const float crouchRange = 50.0f;
@ -1388,6 +1389,29 @@ CCSBot::PathResult CCSBot::UpdatePathMovement(bool allowSpeedChange)
StandUp(); StandUp();
} }
// end crouching logic // end crouching logic
// Walking
bool didWalk = false;
for (int i = prevIndex; i < m_pathLength; ++i)
{
const CNavArea *to = m_path[i].area;
Vector close;
to->GetClosestPointOnArea(&pev->origin, &close);
if ((close - pev->origin).Make2D().IsLengthGreaterThan(crouchRange))
break;
if (to->GetAttributes() & NAV_WALK)
{
Walk();
didWalk = true;
break;
}
}
if (!didWalk)
Run();
} }
// compute our forward facing angle // compute our forward facing angle

View File

@ -106,98 +106,98 @@ void CCSBot::RespondToRadioCommands()
const float inhibitAutoFollowDuration = 60.0f; const float inhibitAutoFollowDuration = 60.0f;
switch (m_lastRadioCommand) switch (m_lastRadioCommand)
{ {
case EVENT_RADIO_REPORT_IN_TEAM: case EVENT_RADIO_REPORT_IN_TEAM:
{
GetChatter()->ReportingIn();
break;
}
case EVENT_RADIO_FOLLOW_ME:
case EVENT_RADIO_COVER_ME:
case EVENT_RADIO_STICK_TOGETHER_TEAM:
case EVENT_RADIO_REGROUP_TEAM:
{
if (!IsFollowing())
{ {
GetChatter()->ReportingIn(); Follow(m_radioSubject);
break; m_radioSubject->AllowAutoFollow();
}
case EVENT_RADIO_FOLLOW_ME:
case EVENT_RADIO_COVER_ME:
case EVENT_RADIO_STICK_TOGETHER_TEAM:
case EVENT_RADIO_REGROUP_TEAM:
{
if (!IsFollowing())
{
Follow(m_radioSubject);
m_radioSubject->AllowAutoFollow();
canDo = true;
}
break;
}
case EVENT_RADIO_ENEMY_SPOTTED:
case EVENT_RADIO_NEED_BACKUP:
case EVENT_RADIO_TAKING_FIRE:
{
if (!IsFollowing())
{
Follow(m_radioSubject);
GetChatter()->Say("OnMyWay");
m_radioSubject->AllowAutoFollow();
canDo = false;
}
break;
}
case EVENT_RADIO_TEAM_FALL_BACK:
{
if (TryToRetreat())
canDo = true;
break;
}
case EVENT_RADIO_HOLD_THIS_POSITION:
{
// find the leader's area
SetTask(HOLD_POSITION);
StopFollowing();
m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration);
Hide(TheNavAreaGrid.GetNearestNavArea(&m_radioPosition));
canDo = true; canDo = true;
break;
} }
case EVENT_RADIO_GO_GO_GO: break;
case EVENT_RADIO_STORM_THE_FRONT: }
case EVENT_RADIO_ENEMY_SPOTTED:
case EVENT_RADIO_NEED_BACKUP:
case EVENT_RADIO_TAKING_FIRE:
{
if (!IsFollowing())
{ {
StopFollowing(); Follow(m_radioSubject);
Hunt(); GetChatter()->Say("OnMyWay");
m_radioSubject->AllowAutoFollow();
canDo = false;
}
break;
}
case EVENT_RADIO_TEAM_FALL_BACK:
{
if (TryToRetreat())
canDo = true; canDo = true;
break;
}
case EVENT_RADIO_HOLD_THIS_POSITION:
{
// find the leader's area
SetTask(HOLD_POSITION);
StopFollowing();
m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration);
Hide(TheNavAreaGrid.GetNearestNavArea(&m_radioPosition));
canDo = true;
break;
}
case EVENT_RADIO_GO_GO_GO:
case EVENT_RADIO_STORM_THE_FRONT:
{
StopFollowing();
Hunt();
canDo = true;
m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration);
break;
}
case EVENT_RADIO_GET_OUT_OF_THERE:
{
if (TheCSBots()->IsBombPlanted())
{
EscapeFromBomb();
m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration); m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration);
break; canDo = true;
} }
case EVENT_RADIO_GET_OUT_OF_THERE: break;
}
case EVENT_RADIO_SECTOR_CLEAR:
{
// if this is a defusal scenario, and the bomb is planted,
// and a human player cleared a bombsite, check it off our list too
if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB)
{ {
if (TheCSBots()->IsBombPlanted()) if (m_iTeam == CT && TheCSBots()->IsBombPlanted())
{ {
EscapeFromBomb(); const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(m_radioSubject);
m_radioSubject->InhibitAutoFollow(inhibitAutoFollowDuration); if (zone)
canDo = true;
}
break;
}
case EVENT_RADIO_SECTOR_CLEAR:
{
// if this is a defusal scenario, and the bomb is planted,
// and a human player cleared a bombsite, check it off our list too
if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB)
{
if (m_iTeam == CT && TheCSBots()->IsBombPlanted())
{ {
const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone(m_radioSubject); GetGameState()->ClearBombsite(zone->m_index);
if (zone)
{
GetGameState()->ClearBombsite(zone->m_index);
// if we are huting for the planted bomb, re-select bombsite // if we are huting for the planted bomb, re-select bombsite
if (GetTask() == FIND_TICKING_BOMB) if (GetTask() == FIND_TICKING_BOMB)
Idle(); Idle();
canDo = true; canDo = true;
}
} }
} }
break;
} }
default: break;
// ignore all other radio commands for now }
return; default:
// ignore all other radio commands for now
return;
} }
if (canDo) if (canDo)

View File

@ -132,34 +132,34 @@ void CCSBot::Upkeep()
switch (m_lookAtSpotState) switch (m_lookAtSpotState)
{ {
case NOT_LOOKING_AT_SPOT: case NOT_LOOKING_AT_SPOT:
{ {
// look ahead // look ahead
SetLookAngles(m_lookAheadAngle, 0); SetLookAngles(m_lookAheadAngle, 0);
break; break;
} }
case LOOK_TOWARDS_SPOT: case LOOK_TOWARDS_SPOT:
{ {
UpdateLookAt(); UpdateLookAt();
if (IsLookingAtPosition(&m_lookAtSpot, m_lookAtSpotAngleTolerance)) if (IsLookingAtPosition(&m_lookAtSpot, m_lookAtSpotAngleTolerance))
{
m_lookAtSpotState = LOOK_AT_SPOT;
m_lookAtSpotTimestamp = gpGlobals->time;
}
break;
}
case LOOK_AT_SPOT:
{ {
UpdateLookAt(); m_lookAtSpotState = LOOK_AT_SPOT;
m_lookAtSpotTimestamp = gpGlobals->time;
if (m_lookAtSpotDuration >= 0.0f && gpGlobals->time - m_lookAtSpotTimestamp > m_lookAtSpotDuration)
{
m_lookAtSpotState = NOT_LOOKING_AT_SPOT;
m_lookAtSpotDuration = 0.0f;
}
break;
} }
break;
}
case LOOK_AT_SPOT:
{
UpdateLookAt();
if (m_lookAtSpotDuration >= 0.0f && gpGlobals->time - m_lookAtSpotTimestamp > m_lookAtSpotDuration)
{
m_lookAtSpotState = NOT_LOOKING_AT_SPOT;
m_lookAtSpotDuration = 0.0f;
}
break;
}
} }
float driftAmplitude = 2.0f; float driftAmplitude = 2.0f;
@ -189,10 +189,10 @@ void CCSBot::Update()
switch (m_processMode) switch (m_processMode)
{ {
case PROCESS_LEARN: UpdateLearnProcess(); return; case PROCESS_LEARN: UpdateLearnProcess(); return;
case PROCESS_ANALYZE_ALPHA: UpdateAnalyzeAlphaProcess(); return; case PROCESS_ANALYZE_ALPHA: UpdateAnalyzeAlphaProcess(); return;
case PROCESS_ANALYZE_BETA: UpdateAnalyzeBetaProcess(); return; case PROCESS_ANALYZE_BETA: UpdateAnalyzeBetaProcess(); return;
case PROCESS_SAVE: UpdateSaveProcess(); return; case PROCESS_SAVE: UpdateSaveProcess(); return;
} }
// update our radio chatter // update our radio chatter
@ -312,11 +312,11 @@ void CCSBot::Update()
{ {
switch (m_blindMoveDir) switch (m_blindMoveDir)
{ {
case FORWARD: MoveForward(); break; case FORWARD: MoveForward(); break;
case RIGHT: StrafeRight(); break; case RIGHT: StrafeRight(); break;
case BACKWARD: MoveBackward(); break; case BACKWARD: MoveBackward(); break;
case LEFT: StrafeLeft(); break; case LEFT: StrafeLeft(); break;
default: Crouch(); break; default: Crouch(); break;
} }
} }
@ -349,32 +349,32 @@ void CCSBot::Update()
bool doAttack = false; bool doAttack = false;
switch (GetDisposition()) switch (GetDisposition())
{ {
case IGNORE_ENEMIES: case IGNORE_ENEMIES:
{ {
// never attack // never attack
doAttack = false; doAttack = false;
break; break;
} }
case SELF_DEFENSE: case SELF_DEFENSE:
{ {
// attack if fired on // attack if fired on
doAttack = IsPlayerLookingAtMe(threat); doAttack = IsPlayerLookingAtMe(threat);
// attack if enemy very close // attack if enemy very close
if (!doAttack) if (!doAttack)
{
const float selfDefenseRange = 750.0f;
doAttack = (pev->origin - threat->pev->origin).IsLengthLessThan(selfDefenseRange);
}
break;
}
case ENGAGE_AND_INVESTIGATE:
case OPPORTUNITY_FIRE:
{ {
// normal combat range const float selfDefenseRange = 750.0f;
doAttack = true; doAttack = (pev->origin - threat->pev->origin).IsLengthLessThan(selfDefenseRange);
break;
} }
break;
}
case ENGAGE_AND_INVESTIGATE:
case OPPORTUNITY_FIRE:
{
// normal combat range
doAttack = true;
break;
}
} }
if (doAttack) if (doAttack)
@ -596,52 +596,52 @@ void CCSBot::Update()
// Scenario interrupts // Scenario interrupts
switch (TheCSBots()->GetScenario()) switch (TheCSBots()->GetScenario())
{ {
case CCSBotManager::SCENARIO_DEFUSE_BOMB: case CCSBotManager::SCENARIO_DEFUSE_BOMB:
{
// flee if the bomb is ready to blow and we aren't defusing it or attacking and we know where the bomb is
// (aggressive players wait until its almost too late)
float gonnaBlowTime = 8.0f - (2.0f * GetProfile()->GetAggression());
// if we have a defuse kit, can wait longer
if (m_bHasDefuser)
gonnaBlowTime *= 0.66f;
if (!IsEscapingFromBomb() // we aren't already escaping the bomb
&& TheCSBots()->IsBombPlanted() // is the bomb planted
&& GetGameState()->IsPlantedBombLocationKnown() // we know where the bomb is
&& TheCSBots()->GetBombTimeLeft() < gonnaBlowTime // is the bomb about to explode
&& !IsDefusingBomb() // we aren't defusing the bomb
&& !IsAttacking()) // we aren't in the midst of a firefight
{ {
// flee if the bomb is ready to blow and we aren't defusing it or attacking and we know where the bomb is EscapeFromBomb();
// (aggressive players wait until its almost too late)
float gonnaBlowTime = 8.0f - (2.0f * GetProfile()->GetAggression());
// if we have a defuse kit, can wait longer
if (m_bHasDefuser)
gonnaBlowTime *= 0.66f;
if (!IsEscapingFromBomb() // we aren't already escaping the bomb
&& TheCSBots()->IsBombPlanted() // is the bomb planted
&& GetGameState()->IsPlantedBombLocationKnown() // we know where the bomb is
&& TheCSBots()->GetBombTimeLeft() < gonnaBlowTime // is the bomb about to explode
&& !IsDefusingBomb() // we aren't defusing the bomb
&& !IsAttacking()) // we aren't in the midst of a firefight
{
EscapeFromBomb();
break;
}
break; break;
} }
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES: break;
}
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES:
{
if (m_iTeam == CT)
{ {
if (m_iTeam == CT) UpdateHostageEscortCount();
{
UpdateHostageEscortCount();
}
else
{
// Terrorists have imperfect information on status of hostages
CSGameState::ValidateStatusType status = GetGameState()->ValidateHostagePositions();
if (status & CSGameState::HOSTAGES_ALL_GONE)
{
GetChatter()->HostagesTaken();
Idle();
}
else if (status & CSGameState::HOSTAGE_GONE)
{
GetGameState()->HostageWasTaken();
Idle();
}
}
break;
} }
else
{
// Terrorists have imperfect information on status of hostages
CSGameState::ValidateStatusType status = GetGameState()->ValidateHostagePositions();
if (status & CSGameState::HOSTAGES_ALL_GONE)
{
GetChatter()->HostagesTaken();
Idle();
}
else if (status & CSGameState::HOSTAGE_GONE)
{
GetGameState()->HostageWasTaken();
Idle();
}
}
break;
}
} }
// Follow nearby humans if our co-op is high and we have nothing else to do // Follow nearby humans if our co-op is high and we have nothing else to do

View File

@ -103,56 +103,56 @@ void CCSBot::UpdateLookAngles()
// adjust pitch to look up/down ladder as we ascend/descend // adjust pitch to look up/down ladder as we ascend/descend
switch (m_pathLadderState) switch (m_pathLadderState)
{ {
case APPROACH_ASCENDING_LADDER: case APPROACH_ASCENDING_LADDER:
{ {
Vector to = m_goalPosition - pev->origin; Vector to = m_goalPosition - pev->origin;
useYaw = idealYaw; useYaw = idealYaw;
if (to.IsLengthLessThan(lookAlongLadderRange)) if (to.IsLengthLessThan(lookAlongLadderRange))
usePitch = -ladderPitch; usePitch = -ladderPitch;
break; break;
} }
case APPROACH_DESCENDING_LADDER: case APPROACH_DESCENDING_LADDER:
{ {
Vector to = m_goalPosition - pev->origin; Vector to = m_goalPosition - pev->origin;
useYaw = idealYaw; useYaw = idealYaw;
if (to.IsLengthLessThan(lookAlongLadderRange)) if (to.IsLengthLessThan(lookAlongLadderRange))
usePitch = ladderPitch;
break;
}
case FACE_ASCENDING_LADDER:
{
useYaw = idealYaw;
usePitch = -ladderPitch;
break;
}
case FACE_DESCENDING_LADDER:
{
useYaw = idealYaw;
usePitch = ladderPitch; usePitch = ladderPitch;
break; break;
} }
case MOUNT_ASCENDING_LADDER: case FACE_ASCENDING_LADDER:
case ASCEND_LADDER: {
{ useYaw = idealYaw;
useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder); usePitch = -ladderPitch;
usePitch = -ladderPitch; break;
break; }
} case FACE_DESCENDING_LADDER:
case MOUNT_DESCENDING_LADDER: {
case DESCEND_LADDER: useYaw = idealYaw;
{ usePitch = ladderPitch;
useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder); break;
usePitch = ladderPitch; }
break; case MOUNT_ASCENDING_LADDER:
} case ASCEND_LADDER:
case DISMOUNT_ASCENDING_LADDER: {
case DISMOUNT_DESCENDING_LADDER: useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder);
{ usePitch = -ladderPitch;
useYaw = DirectionToAngle(faceDir); break;
break; }
} case MOUNT_DESCENDING_LADDER:
case DESCEND_LADDER:
{
useYaw = DirectionToAngle(faceDir) + StayOnLadderLine(this, m_pathLadder);
usePitch = ladderPitch;
break;
}
case DISMOUNT_ASCENDING_LADDER:
case DISMOUNT_DESCENDING_LADDER:
{
useYaw = DirectionToAngle(faceDir);
break;
}
} }
} }

View File

@ -246,27 +246,27 @@ const Vector *CSGameState::GetBombPosition() const
{ {
switch (m_bombState) switch (m_bombState)
{ {
case MOVING: case MOVING:
{ {
if (!m_lastSawBomber.HasStarted()) if (!m_lastSawBomber.HasStarted())
return nullptr;
return &m_bomberPos;
}
case LOOSE:
{
if (IsLooseBombLocationKnown())
return &m_looseBombPos;
return nullptr; return nullptr;
}
case PLANTED:
{
if (IsPlantedBombLocationKnown())
return &m_plantedBombPos;
return nullptr; return &m_bomberPos;
} }
case LOOSE:
{
if (IsLooseBombLocationKnown())
return &m_looseBombPos;
return nullptr;
}
case PLANTED:
{
if (IsPlantedBombLocationKnown())
return &m_plantedBombPos;
return nullptr;
}
} }
return nullptr; return nullptr;
@ -477,7 +477,7 @@ CSGameState::ValidateStatusType CSGameState::ValidateHostagePositions()
{ {
HostageInfo *info = &m_hostage[i]; HostageInfo *info = &m_hostage[i];
if (!info->hostage) if (!info->hostage)
continue; continue;
// if we can see a hostage, update our knowledge of it // if we can see a hostage, update our knowledge of it

View File

@ -39,7 +39,7 @@ void FetchBombState::OnUpdate(CCSBot *me)
{ {
if (me->IsCarryingBomb()) if (me->IsCarryingBomb())
{ {
me->PrintIfWatched( "I picked up the bomb\n" ); me->PrintIfWatched("I picked up the bomb\n");
me->Idle(); me->Idle();
return; return;
} }

View File

@ -141,106 +141,106 @@ void HideState::OnUpdate(CCSBot *me)
// Scenario logic // Scenario logic
switch (TheCSBots()->GetScenario()) switch (TheCSBots()->GetScenario())
{ {
case CCSBotManager::SCENARIO_DEFUSE_BOMB: case CCSBotManager::SCENARIO_DEFUSE_BOMB:
{
if (me->m_iTeam == CT)
{ {
if (me->m_iTeam == CT) // if we are just holding position (due to a radio order) and the bomb has just planted, go defuse it
if (me->GetTask() == CCSBot::HOLD_POSITION &&
TheCSBots()->IsBombPlanted() &&
TheCSBots()->GetBombPlantTimestamp() > me->GetStateTimestamp())
{ {
// if we are just holding position (due to a radio order) and the bomb has just planted, go defuse it me->Idle();
if (me->GetTask() == CCSBot::HOLD_POSITION && return;
TheCSBots()->IsBombPlanted() &&
TheCSBots()->GetBombPlantTimestamp() > me->GetStateTimestamp())
{
me->Idle();
return;
}
// if we are guarding the defuser and he dies/gives up, stop hiding (to choose another defuser)
if (me->GetTask() == CCSBot::GUARD_BOMB_DEFUSER && !TheCSBots()->GetBombDefuser())
{
me->Idle();
return;
}
// if we are guarding the loose bomb and it is picked up, stop hiding
if (me->GetTask() == CCSBot::GUARD_LOOSE_BOMB && !TheCSBots()->GetLooseBomb())
{
me->GetChatter()->TheyPickedUpTheBomb();
me->Idle();
return;
}
// if we are guarding a bombsite and the bomb is dropped and we hear about it, stop guarding
if (me->GetTask() == CCSBot::GUARD_BOMB_ZONE && me->GetGameState()->IsLooseBombLocationKnown())
{
me->Idle();
return;
}
// if we are guarding (bombsite, initial encounter, etc) and the bomb is planted, go defuse it
if (me->IsDoingScenario() && me->GetTask() == CCSBot::GUARD_BOMB_ZONE && TheCSBots()->IsBombPlanted())
{
me->Idle();
return;
}
} }
// TERRORIST
else // if we are guarding the defuser and he dies/gives up, stop hiding (to choose another defuser)
if (me->GetTask() == CCSBot::GUARD_BOMB_DEFUSER && !TheCSBots()->GetBombDefuser())
{ {
// if we are near the ticking bomb and someone starts defusing it, attack! me->Idle();
if (TheCSBots()->GetBombDefuser()) return;
{
Vector toDefuser = TheCSBots()->GetBombDefuser()->pev->origin;
const float hearDefuseRange = 2000.0f;
if ((toDefuser - me->pev->origin).IsLengthLessThan(hearDefuseRange))
{
// if we are nearby, attack, otherwise move to the bomb (which will cause us to attack when we see defuser)
if (me->CanSeePlantedBomb())
{
me->Attack(TheCSBots()->GetBombDefuser());
}
else
{
me->MoveTo(&toDefuser, FASTEST_ROUTE);
me->InhibitLookAround(10.0f);
}
return;
}
}
} }
break;
// if we are guarding the loose bomb and it is picked up, stop hiding
if (me->GetTask() == CCSBot::GUARD_LOOSE_BOMB && !TheCSBots()->GetLooseBomb())
{
me->GetChatter()->TheyPickedUpTheBomb();
me->Idle();
return;
}
// if we are guarding a bombsite and the bomb is dropped and we hear about it, stop guarding
if (me->GetTask() == CCSBot::GUARD_BOMB_ZONE && me->GetGameState()->IsLooseBombLocationKnown())
{
me->Idle();
return;
}
// if we are guarding (bombsite, initial encounter, etc) and the bomb is planted, go defuse it
if (me->IsDoingScenario() && me->GetTask() == CCSBot::GUARD_BOMB_ZONE && TheCSBots()->IsBombPlanted())
{
me->Idle();
return;
}
} }
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES: // TERRORIST
else
{ {
// if we're guarding the hostages and they all die or are taken, do something else // if we are near the ticking bomb and someone starts defusing it, attack!
if (me->GetTask() == CCSBot::GUARD_HOSTAGES) if (TheCSBots()->GetBombDefuser())
{ {
if (me->GetGameState()->AreAllHostagesBeingRescued() || me->GetGameState()->AreAllHostagesGone()) Vector toDefuser = TheCSBots()->GetBombDefuser()->pev->origin;
const float hearDefuseRange = 2000.0f;
if ((toDefuser - me->pev->origin).IsLengthLessThan(hearDefuseRange))
{ {
me->Idle(); // if we are nearby, attack, otherwise move to the bomb (which will cause us to attack when we see defuser)
return; if (me->CanSeePlantedBomb())
}
}
else if (me->GetTask() == CCSBot::GUARD_HOSTAGE_RESCUE_ZONE)
{
// if we stumble across a hostage, guard it
CHostage *pHostage = me->GetGameState()->GetNearestVisibleFreeHostage();
if (pHostage)
{
// we see a free hostage, guard it
CNavArea *area = TheNavAreaGrid.GetNearestNavArea(&pHostage->pev->origin);
if (area)
{ {
me->SetTask(CCSBot::GUARD_HOSTAGES); me->Attack(TheCSBots()->GetBombDefuser());
me->Hide(area);
me->PrintIfWatched("I'm guarding hostages I found\n");
// don't chatter here - he'll tell us when he's in his hiding spot
return;
} }
else
{
me->MoveTo(&toDefuser, FASTEST_ROUTE);
me->InhibitLookAround(10.0f);
}
return;
} }
} }
} }
break;
}
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES:
{
// if we're guarding the hostages and they all die or are taken, do something else
if (me->GetTask() == CCSBot::GUARD_HOSTAGES)
{
if (me->GetGameState()->AreAllHostagesBeingRescued() || me->GetGameState()->AreAllHostagesGone())
{
me->Idle();
return;
}
}
else if (me->GetTask() == CCSBot::GUARD_HOSTAGE_RESCUE_ZONE)
{
// if we stumble across a hostage, guard it
CHostage *pHostage = me->GetGameState()->GetNearestVisibleFreeHostage();
if (pHostage)
{
// we see a free hostage, guard it
CNavArea *area = TheNavAreaGrid.GetNearestNavArea(&pHostage->pev->origin);
if (area)
{
me->SetTask(CCSBot::GUARD_HOSTAGES);
me->Hide(area);
me->PrintIfWatched("I'm guarding hostages I found\n");
// don't chatter here - he'll tell us when he's in his hiding spot
return;
}
}
}
}
} }
bool isSettledInSniper = (me->IsSniper() && m_isAtSpot) ? true : false; bool isSettledInSniper = (me->IsSniper() && m_isAtSpot) ? true : false;

File diff suppressed because it is too large Load Diff

View File

@ -44,15 +44,15 @@ void MoveToState::OnEnter(CCSBot *me)
RouteType route; RouteType route;
switch (me->GetTask()) switch (me->GetTask())
{ {
case CCSBot::FIND_TICKING_BOMB: case CCSBot::FIND_TICKING_BOMB:
case CCSBot::DEFUSE_BOMB: case CCSBot::DEFUSE_BOMB:
case CCSBot::MOVE_TO_LAST_KNOWN_ENEMY_POSITION: case CCSBot::MOVE_TO_LAST_KNOWN_ENEMY_POSITION:
route = FASTEST_ROUTE; route = FASTEST_ROUTE;
break; break;
default: default:
route = SAFEST_ROUTE; route = SAFEST_ROUTE;
break; break;
} }
// build path to, or nearly to, goal position // build path to, or nearly to, goal position
@ -84,181 +84,180 @@ void MoveToState::OnUpdate(CCSBot *me)
// Scenario logic // Scenario logic
switch (TheCSBots()->GetScenario()) switch (TheCSBots()->GetScenario())
{ {
case CCSBotManager::SCENARIO_DEFUSE_BOMB: case CCSBotManager::SCENARIO_DEFUSE_BOMB:
{
// if the bomb has been planted, find it
// NOTE: This task is used by both CT and T's to find the bomb
if (me->GetTask() == CCSBot::FIND_TICKING_BOMB)
{ {
// if the bomb has been planted, find it if (!me->GetGameState()->IsBombPlanted())
// NOTE: This task is used by both CT and T's to find the bomb
if (me->GetTask() == CCSBot::FIND_TICKING_BOMB)
{ {
if (!me->GetGameState()->IsBombPlanted()) // the bomb is not planted - give up this task
{ me->Idle();
// the bomb is not planted - give up this task return;
me->Idle(); }
return;
}
if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN) if (me->GetGameState()->GetPlantedBombsite() != CSGameState::UNKNOWN)
{ {
// we know where the bomb is planted, stop searching // we know where the bomb is planted, stop searching
me->Idle(); me->Idle();
return; return;
} }
// check off bombsites that we explore or happen to stumble into // check off bombsites that we explore or happen to stumble into
for (int z = 0; z < TheCSBots()->GetZoneCount(); z++) for (int z = 0; z < TheCSBots()->GetZoneCount(); z++)
{ {
// don't re-check zones // don't re-check zones
if (me->GetGameState()->IsBombsiteClear(z)) if (me->GetGameState()->IsBombsiteClear(z))
continue; continue;
if (TheCSBots()->GetZone(z)->m_extent.Contains(&me->pev->origin)) if (TheCSBots()->GetZone(z)->m_extent.Contains(&me->pev->origin))
{
// note this bombsite is clear
me->GetGameState()->ClearBombsite(z);
if (me->m_iTeam == CT)
{ {
// note this bombsite is clear // tell teammates this bombsite is clear
me->GetGameState()->ClearBombsite(z); me->GetChatter()->BombsiteClear(z);
}
if (me->m_iTeam == CT) // find another zone to check
{ me->Idle();
// tell teammates this bombsite is clear return;
me->GetChatter()->BombsiteClear(z); }
} }
// find another zone to check // move to a bombsite
break;
}
else if (me->m_iTeam == CT)
{
if (me->GetGameState()->IsBombPlanted())
{
switch (me->GetTask())
{
case CCSBot::DEFUSE_BOMB:
{
// if we are trying to defuse the bomb, and someone has started defusing, guard them instead
if (me->CanSeePlantedBomb() && TheCSBots()->GetBombDefuser())
{
me->GetChatter()->Say("CoveringFriend");
me->Idle(); me->Idle();
return; return;
} }
break;
} }
default:
// move to a bombsite
break;
}
else if (me->m_iTeam == CT)
{
if (me->GetGameState()->IsBombPlanted())
{ {
switch (me->GetTask()) // we need to find the bomb
{ me->Idle();
case CCSBot::DEFUSE_BOMB: return;
{ }
// if we are trying to defuse the bomb, and someone has started defusing, guard them instead
if (me->CanSeePlantedBomb() && TheCSBots()->GetBombDefuser())
{
me->GetChatter()->Say("CoveringFriend");
me->Idle();
return;
}
break;
}
default:
{
// we need to find the bomb
me->Idle();
return;
}
}
} }
} }
// TERRORIST
else
{
if (me->GetTask() == CCSBot::PLANT_BOMB)
{
if (me->GetFriendsRemaining())
{
// if we are about to plant, radio for cover
if (!m_askedForCover)
{
const float nearPlantSite = 50.0f;
if (me->IsAtBombsite() && me->GetPathDistanceRemaining() < nearPlantSite)
{
// radio to the team
me->GetChatter()->PlantingTheBomb(me->GetPlace());
m_askedForCover = true;
}
// after we have started to move to the bombsite, tell team we're going to plant, and where
// don't do this if we have already radioed that we are starting to plant
if (!m_radioedPlan)
{
const float radioTime = 2.0f;
if (gpGlobals->time - me->GetStateTimestamp() > radioTime)
{
me->GetChatter()->GoingToPlantTheBomb(TheNavAreaGrid.GetPlace(&m_goalPosition));
m_radioedPlan = true;
}
}
}
}
}
}
break;
} }
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES: // TERRORIST
else
{ {
if (me->GetTask() == CCSBot::COLLECT_HOSTAGES) if (me->GetTask() == CCSBot::PLANT_BOMB)
{ {
// Since CT's have a radar, they can directly look at the actual hostage state if (me->GetFriendsRemaining())
// check if someone else collected our hostage, or the hostage died or was rescued
CHostage *pHostage = me->GetGoalEntity<CHostage>();
if (!pHostage || !pHostage->IsAlive() || pHostage->IsFollowingSomeone())
{ {
me->Idle(); // if we are about to plant, radio for cover
return; if (!m_askedForCover)
}
// if our hostage has moved, repath
const float repathToleranceSq = 75.0f * 75.0f;
float error = (pHostage->pev->origin - m_goalPosition).LengthSquared();
if (error > repathToleranceSq)
{
m_goalPosition = pHostage->pev->origin;
me->ComputePath(TheNavAreaGrid.GetNavArea(&m_goalPosition), &m_goalPosition, SAFEST_ROUTE);
}
// TODO: Generalize ladder priorities over other tasks
if (!me->IsUsingLadder())
{
Vector pos = pHostage->pev->origin + Vector(0, 0, HumanHeight * 0.75f);
Vector to = pos - me->pev->origin;
// look at the hostage as we approach
const float watchHostageRange = 100.0f;
if (to.IsLengthLessThan(watchHostageRange))
{ {
me->SetLookAt("Hostage", &pos, PRIORITY_LOW, 0.5f); const float nearPlantSite = 50.0f;
if (me->IsAtBombsite() && me->GetPathDistanceRemaining() < nearPlantSite)
// randomly move just a bit to avoid infinite use loops from bad hostage placement
NavRelativeDirType dir = (NavRelativeDirType)RANDOM_LONG(0, 3);
switch (dir)
{ {
case LEFT: me->StrafeLeft(); break; // radio to the team
case RIGHT: me->StrafeRight(); break; me->GetChatter()->PlantingTheBomb(me->GetPlace());
case FORWARD: me->MoveForward(); break; m_askedForCover = true;
case BACKWARD: me->MoveBackward(); break;
} }
// check if we are close enough to the hostage to talk to him // after we have started to move to the bombsite, tell team we're going to plant, and where
const float useRange = MAX_PLAYER_USE_RADIUS - 14.0f; // shave off a fudge factor to make sure we're within range // don't do this if we have already radioed that we are starting to plant
if (to.IsLengthLessThan(useRange)) if (!m_radioedPlan)
{ {
me->UseEntity(me->GetGoalEntity()); const float radioTime = 2.0f;
return; if (gpGlobals->time - me->GetStateTimestamp() > radioTime)
{
me->GetChatter()->GoingToPlantTheBomb(TheNavAreaGrid.GetPlace(&m_goalPosition));
m_radioedPlan = true;
}
} }
} }
} }
} }
else if (me->GetTask() == CCSBot::RESCUE_HOSTAGES) }
break;
}
case CCSBotManager::SCENARIO_RESCUE_HOSTAGES:
{
if (me->GetTask() == CCSBot::COLLECT_HOSTAGES)
{
// Since CT's have a radar, they can directly look at the actual hostage state
// check if someone else collected our hostage, or the hostage died or was rescued
CHostage *pHostage = me->GetGoalEntity<CHostage>();
if (!pHostage || !pHostage->IsAlive() || pHostage->IsFollowingSomeone())
{ {
// periodically check if we lost all our hostages me->Idle();
if (me->GetHostageEscortCount() == 0) return;
}
// if our hostage has moved, repath
const float repathToleranceSq = 75.0f * 75.0f;
float error = (pHostage->pev->origin - m_goalPosition).LengthSquared();
if (error > repathToleranceSq)
{
m_goalPosition = pHostage->pev->origin;
me->ComputePath(TheNavAreaGrid.GetNavArea(&m_goalPosition), &m_goalPosition, SAFEST_ROUTE);
}
// TODO: Generalize ladder priorities over other tasks
if (!me->IsUsingLadder())
{
Vector pos = pHostage->pev->origin + Vector(0, 0, HumanHeight * 0.75f);
Vector to = pos - me->pev->origin;
// look at the hostage as we approach
const float watchHostageRange = 100.0f;
if (to.IsLengthLessThan(watchHostageRange))
{ {
// lost our hostages - go get 'em me->SetLookAt("Hostage", &pos, PRIORITY_LOW, 0.5f);
me->Idle();
return; // randomly move just a bit to avoid infinite use loops from bad hostage placement
NavRelativeDirType dir = (NavRelativeDirType)RANDOM_LONG(0, 3);
switch (dir)
{
case LEFT: me->StrafeLeft(); break;
case RIGHT: me->StrafeRight(); break;
case FORWARD: me->MoveForward(); break;
case BACKWARD: me->MoveBackward(); break;
}
// check if we are close enough to the hostage to talk to him
const float useRange = MAX_PLAYER_USE_RADIUS - 14.0f; // shave off a fudge factor to make sure we're within range
if (to.IsLengthLessThan(useRange))
{
me->UseEntity(me->GetGoalEntity());
return;
}
} }
} }
break;
} }
else if (me->GetTask() == CCSBot::RESCUE_HOSTAGES)
{
// periodically check if we lost all our hostages
if (me->GetHostageEscortCount() == 0)
{
// lost our hostages - go get 'em
me->Idle();
return;
}
}
break;
}
} }
if (me->UpdatePathMovement() != CCSBot::PROGRESSING) if (me->UpdatePathMovement() != CCSBot::PROGRESSING)
@ -266,47 +265,47 @@ void MoveToState::OnUpdate(CCSBot *me)
// reached destination // reached destination
switch (me->GetTask()) switch (me->GetTask())
{ {
case CCSBot::PLANT_BOMB: case CCSBot::PLANT_BOMB:
{
// if we are at bombsite with the bomb, plant it
if (me->IsAtBombsite() && me->IsCarryingBomb())
{ {
// if we are at bombsite with the bomb, plant it me->PlantBomb();
if (me->IsAtBombsite() && me->IsCarryingBomb()) return;
{
me->PlantBomb();
return;
}
break;
} }
case CCSBot::DEFUSE_BOMB: break;
}
case CCSBot::DEFUSE_BOMB:
{
if (!me->IsActiveWeaponReloading())
{ {
if (!me->IsActiveWeaponReloading()) // if we are near the bomb, defuse it (if we are reloading, don't try to defuse until we finish)
const Vector *bombPos = me->GetGameState()->GetBombPosition();
if (bombPos)
{ {
// if we are near the bomb, defuse it (if we are reloading, don't try to defuse until we finish) const float defuseRange = 100.0f;
const Vector *bombPos = me->GetGameState()->GetBombPosition(); Vector toBomb = *bombPos - me->pev->origin;
if (bombPos) toBomb.z = bombPos->z - me->GetFeetZ();
{
const float defuseRange = 100.0f;
Vector toBomb = *bombPos - me->pev->origin;
toBomb.z = bombPos->z - me->GetFeetZ();
if (toBomb.IsLengthLessThan(defuseRange)) if (toBomb.IsLengthLessThan(defuseRange))
{ {
me->DefuseBomb(); me->DefuseBomb();
return; return;
}
} }
} }
break;
} }
case CCSBot::MOVE_TO_LAST_KNOWN_ENEMY_POSITION: break;
}
case CCSBot::MOVE_TO_LAST_KNOWN_ENEMY_POSITION:
{
CBaseEntity *pVictim = me->GetTaskEntity();
if (pVictim && pVictim->IsAlive())
{ {
CBaseEntity *pVictim = me->GetTaskEntity(); // if we got here and haven't re-acquired the enemy, we lost him
if (pVictim && pVictim->IsAlive()) me->GetChatter()->Say("LostEnemy");
{
// if we got here and haven't re-acquired the enemy, we lost him
me->GetChatter()->Say("LostEnemy");
}
break;
} }
break;
}
} }
// default behavior when destination is reached // default behavior when destination is reached

View File

@ -293,7 +293,7 @@ void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker
if (flAdjustedDamage < 0) if (flAdjustedDamage < 0)
flAdjustedDamage = 0; flAdjustedDamage = 0;
#endif #endif
pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType); pEntity->TakeDamage(pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType);
} }
} }
} }

View File

@ -969,7 +969,7 @@ void CMomentaryDoor::Spawn()
if (pev->dmg == 0) if (pev->dmg == 0)
pev->dmg = 2; pev->dmg = 2;
m_vecPosition1 = pev->origin; m_vecPosition1 = pev->origin;
// Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big // Subtract 2 from size because the engine expands bboxes by 1 in all directions making the size too big
m_vecPosition2 = m_vecPosition1 + (pev->movedir * (Q_fabs(float_precision(pev->movedir.x * (pev->size.x - 2))) + Q_fabs(float_precision(pev->movedir.y * (pev->size.y - 2))) + Q_fabs(float_precision(pev->movedir.z * (pev->size.z - 2))) - m_flLip)); m_vecPosition2 = m_vecPosition1 + (pev->movedir * (Q_fabs(float_precision(pev->movedir.x * (pev->size.x - 2))) + Q_fabs(float_precision(pev->movedir.y * (pev->size.y - 2))) + Q_fabs(float_precision(pev->movedir.z * (pev->size.z - 2))) - m_flLip));

View File

@ -1003,7 +1003,7 @@ void CGlow::Spawn()
if (m_maxFrame > 1.0f && pev->framerate != 0.0f) if (m_maxFrame > 1.0f && pev->framerate != 0.0f)
{ {
pev->nextthink = gpGlobals->time + 0.1f; pev->nextthink = gpGlobals->time + 0.1f;
} }
m_lastTime = gpGlobals->time; m_lastTime = gpGlobals->time;

View File

@ -103,7 +103,7 @@ void CEnvExplosion::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE
vecSpot = pev->origin + Vector(0, 0, 8); vecSpot = pev->origin + Vector(0, 0, 8);
UTIL_TraceLine(vecSpot, vecSpot + Vector (0, 0, -40), ignore_monsters, ENT(pev), & tr); UTIL_TraceLine(vecSpot, vecSpot + Vector(0, 0, -40), ignore_monsters, ENT(pev), &tr);
// Pull out of the wall a bit // Pull out of the wall a bit
if (tr.flFraction != 1.0f) if (tr.flFraction != 1.0f)
@ -112,7 +112,7 @@ void CEnvExplosion::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE
} }
// draw decal // draw decal
if (! (pev->spawnflags & SF_ENVEXPLOSION_NODECAL)) if (!(pev->spawnflags & SF_ENVEXPLOSION_NODECAL))
{ {
if (RANDOM_FLOAT(0, 1) < 0.5f) if (RANDOM_FLOAT(0, 1) < 0.5f)
{ {

View File

@ -94,9 +94,9 @@ void CBreakable::Spawn()
Precache(); Precache();
if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY) if (pev->spawnflags & SF_BREAK_TRIGGER_ONLY)
pev->takedamage = DAMAGE_NO; pev->takedamage = DAMAGE_NO;
else else
pev->takedamage = DAMAGE_YES; pev->takedamage = DAMAGE_YES;
m_flHealth = pev->health; m_flHealth = pev->health;
pev->solid = SOLID_BSP; pev->solid = SOLID_BSP;
@ -541,26 +541,25 @@ void CBreakable::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecD
{ {
switch (m_Material) switch (m_Material)
{ {
case matComputer: case matComputer:
{
UTIL_Sparks(ptr->vecEndPos);
//random volume range
float flVolume = RANDOM_FLOAT(0.7 , 1.0);
switch (RANDOM_LONG(0, 1))
{ {
UTIL_Sparks(ptr->vecEndPos); case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break;
//random volume range
float flVolume = RANDOM_FLOAT(0.7 , 1.0);
switch (RANDOM_LONG(0, 1))
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark5.wav", flVolume, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark6.wav", flVolume, ATTN_NORM); break;
}
break;
} }
case matUnbreakableGlass: break;
{ }
UTIL_Ricochet(ptr->vecEndPos, RANDOM_FLOAT(0.5, 1.5)); case matUnbreakableGlass:
break; {
} UTIL_Ricochet(ptr->vecEndPos, RANDOM_FLOAT(0.5, 1.5));
break;
}
} }
} }

View File

@ -57,7 +57,7 @@ enum HostageChatterType
HOSTAGE_CHATTER_PLEASE_RESCUE_ME, HOSTAGE_CHATTER_PLEASE_RESCUE_ME,
HOSTAGE_CHATTER_SEE_RESCUE_ZONE, HOSTAGE_CHATTER_SEE_RESCUE_ZONE,
HOSTAGE_CHATTER_IMPATIENT_FOR_RESCUE, HOSTAGE_CHATTER_IMPATIENT_FOR_RESCUE,
HOSTAGE_CHATTER_CTS_WIN , HOSTAGE_CHATTER_CTS_WIN,
HOSTAGE_CHATTER_TERRORISTS_WIN, HOSTAGE_CHATTER_TERRORISTS_WIN,
HOSTAGE_CHATTER_RESCUED, HOSTAGE_CHATTER_RESCUED,
HOSTAGE_CHATTER_WARN_NEARBY, HOSTAGE_CHATTER_WARN_NEARBY,

View File

@ -155,7 +155,7 @@ void CHostageImprov::MoveTowards(const Vector &pos, float deltaT)
Vector stepAhead = GetFeet() + farLookAheadRange * aheadRay; Vector stepAhead = GetFeet() + farLookAheadRange * aheadRay;
stepAhead.z += HumanHeight; stepAhead.z += HumanHeight;
if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground, &normal )) if (GetSimpleGroundHeightWithFloor(&stepAhead, &ground, &normal))
{ {
if (normal.z > 0.9f) if (normal.z > 0.9f)
jumped = DiscontinuityJump(ground, HOSTAGE_ONLY_JUMP_DOWN); jumped = DiscontinuityJump(ground, HOSTAGE_ONLY_JUMP_DOWN);

View File

@ -247,7 +247,7 @@ BOOL CItemBattery::MyTouch(CBasePlayer *pPlayer)
pct--; pct--;
char szcharge[64]; char szcharge[64];
Q_sprintf(szcharge,"!HEV_%1dP", pct); Q_sprintf(szcharge, "!HEV_%1dP", pct);
pPlayer->SetSuitUpdate(szcharge, SUIT_SENTENCE, SUIT_NEXT_IN_30SEC); pPlayer->SetSuitUpdate(szcharge, SUIT_SENTENCE, SUIT_NEXT_IN_30SEC);
return TRUE; return TRUE;

View File

@ -262,7 +262,7 @@ CPathTrack *CPathTrack::LookAhead(Vector *origin, float dist, int move)
Vector dir = pcurrent->GetNext()->pev->origin - currentPos; Vector dir = pcurrent->GetNext()->pev->origin - currentPos;
float_precision length = dir.Length(); float_precision length = dir.Length();
if (!length && !ValidPath(pcurrent->GetNext()->GetNext(), move)) if (!length && !ValidPath(pcurrent->GetNext()->GetNext(), move))
{ {
// HACK: up against a dead end // HACK: up against a dead end
if (dist == originalDist) if (dist == originalDist)

View File

@ -474,7 +474,7 @@ void CFuncPlatRot::SetupRotation()
if (m_vecFinalAngle.x != 0) if (m_vecFinalAngle.x != 0)
{ {
CBaseToggle::AxisDir(pev); CBaseToggle::AxisDir(pev);
m_start = pev->angles; m_start = pev->angles;
m_end = pev->angles + pev->movedir * m_vecFinalAngle.x; m_end = pev->angles + pev->movedir * m_vecFinalAngle.x;
} }
else else
@ -1328,9 +1328,15 @@ BOOL CFuncTrackTrain::OnControls(entvars_t *pevTest)
local.y = -DotProduct(offset, gpGlobals->v_right); local.y = -DotProduct(offset, gpGlobals->v_right);
local.z = DotProduct(offset, gpGlobals->v_up); local.z = DotProduct(offset, gpGlobals->v_up);
if (local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z && if (local.x >= m_controlMins.x
local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z) && local.y >= m_controlMins.y
&& local.z >= m_controlMins.z
&& local.x <= m_controlMaxs.x
&& local.y <= m_controlMaxs.y
&& local.z <= m_controlMaxs.z)
{
return TRUE; return TRUE;
}
return FALSE; return FALSE;
} }
@ -1718,8 +1724,9 @@ TRAIN_CODE CFuncTrackChange::EvaluateTrain(CPathTrack *pcurrent)
if (!pcurrent || !m_train) if (!pcurrent || !m_train)
return TRAIN_SAFE; return TRAIN_SAFE;
if (m_train->m_ppath == pcurrent || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious) || if (m_train->m_ppath == pcurrent
(pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext)) || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious)
|| (pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext))
{ {
if (m_train->pev->speed != 0) if (m_train->pev->speed != 0)
return TRAIN_BLOCKING; return TRAIN_BLOCKING;

View File

@ -110,7 +110,7 @@ const char *GetCSModelName(int item_id)
case WEAPON_SCOUT: modelName = "models/w_scout.mdl"; break; case WEAPON_SCOUT: modelName = "models/w_scout.mdl"; break;
case WEAPON_HEGRENADE: modelName = "models/w_hegrenade.mdl"; break; case WEAPON_HEGRENADE: modelName = "models/w_hegrenade.mdl"; break;
case WEAPON_XM1014: modelName = "models/w_xm1014.mdl"; break; case WEAPON_XM1014: modelName = "models/w_xm1014.mdl"; break;
case WEAPON_C4: modelName = "models/w_backpack.mdl"; break; case WEAPON_C4: modelName = "models/w_backpack.mdl"; break;
case WEAPON_MAC10: modelName = "models/w_mac10.mdl"; break; case WEAPON_MAC10: modelName = "models/w_mac10.mdl"; break;
case WEAPON_AUG: modelName = "models/w_aug.mdl"; break; case WEAPON_AUG: modelName = "models/w_aug.mdl"; break;
case WEAPON_SMOKEGRENADE: modelName = "models/w_smokegrenade.mdl"; break; case WEAPON_SMOKEGRENADE: modelName = "models/w_smokegrenade.mdl"; break;
@ -547,7 +547,7 @@ bool CBasePlayer::IsHittingShield(Vector &vecDirection, TraceResult *ptr)
return false; return false;
if (ptr->iHitgroup == HITGROUP_SHIELD) if (ptr->iHitgroup == HITGROUP_SHIELD)
return true; return true;
if (m_bShieldDrawn) if (m_bShieldDrawn)
UTIL_MakeVectors(pev->angles); UTIL_MakeVectors(pev->angles);
@ -1228,7 +1228,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva
return bTookDamage; return bTookDamage;
} }
void packPlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo) void PackPlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo)
{ {
if (!pItem) if (!pItem)
return; return;
@ -1260,7 +1260,7 @@ void packPlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo)
} }
#ifdef REGAMEDLL_ADD #ifdef REGAMEDLL_ADD
void packPlayerNade(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo) void PackPlayerNade(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo)
{ {
if (!pItem) if (!pItem)
return; return;
@ -1282,16 +1282,9 @@ void packPlayerNade(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo)
break; break;
} }
auto& ammoNades = pPlayer->m_rgAmmo[pItem->PrimaryAmmoIndex()]; if ((pPlayer->pev->button & IN_ATTACK) && pPlayer->m_rgAmmo[pItem->PrimaryAmmoIndex()] <= 0) {
if (pItem->m_flStartThrow != 0)
{
if (ammoNades < 2)
return;
ammoNades--;
}
else if (pItem->m_flReleaseThrow > 0 && ammoNades < 1)
return; return;
}
Vector vecAngles = pPlayer->pev->angles; Vector vecAngles = pPlayer->pev->angles;
Vector dir(Q_cos(vecAngles.y) * flOffset, Q_sin(vecAngles.y) * flOffset, 0.0f); Vector dir(Q_cos(vecAngles.y) * flOffset, Q_sin(vecAngles.y) * flOffset, 0.0f);
@ -1367,19 +1360,19 @@ void CBasePlayer::PackDeadPlayerItems()
else if (pPlayerItem->iItemSlot() == GRENADE_SLOT) else if (pPlayerItem->iItemSlot() == GRENADE_SLOT)
{ {
if (AreRunningCZero()) if (AreRunningCZero())
packPlayerItem(this, pPlayerItem, true); PackPlayerItem(this, pPlayerItem, true);
#ifdef REGAMEDLL_ADD #ifdef REGAMEDLL_ADD
else else
{ {
switch ((int)nadedrops.value) switch ((int)nadedrops.value)
{ {
case 1: case 1:
packPlayerNade(this, pPlayerItem, true); PackPlayerNade(this, pPlayerItem, true);
break; break;
case 2: case 2:
{ {
CBasePlayerItem *pNext = pPlayerItem->m_pNext; CBasePlayerItem *pNext = pPlayerItem->m_pNext;
packPlayerNade(this, pPlayerItem, true); PackPlayerNade(this, pPlayerItem, true);
pPlayerItem = pNext; pPlayerItem = pNext;
continue; continue;
} }
@ -1392,7 +1385,7 @@ void CBasePlayer::PackDeadPlayerItems()
} }
} }
packPlayerItem(this, pBestItem, bPackAmmo); PackPlayerItem(this, pBestItem, bPackAmmo);
} }
RemoveAllItems(TRUE); RemoveAllItems(TRUE);
@ -1950,6 +1943,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib)
if ((pev->button & IN_ATTACK) && m_rgAmmo[pHEGrenade->m_iPrimaryAmmoType]) if ((pev->button & IN_ATTACK) && m_rgAmmo[pHEGrenade->m_iPrimaryAmmoType])
{ {
CGrenade::ShootTimed2(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5, m_iTeam, pHEGrenade->m_usCreateExplosion); CGrenade::ShootTimed2(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5, m_iTeam, pHEGrenade->m_usCreateExplosion);
#ifdef REGAMEDLL_FIXES
m_rgAmmo[m_pActiveItem->PrimaryAmmoIndex()]--;
#endif
} }
break; break;
} }
@ -1958,6 +1955,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib)
if ((pev->button & IN_ATTACK) && m_rgAmmo[((CBasePlayerWeapon *)m_pActiveItem)->m_iPrimaryAmmoType]) if ((pev->button & IN_ATTACK) && m_rgAmmo[((CBasePlayerWeapon *)m_pActiveItem)->m_iPrimaryAmmoType])
{ {
CGrenade::ShootTimed(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5); CGrenade::ShootTimed(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5);
#ifdef REGAMEDLL_FIXES
m_rgAmmo[m_pActiveItem->PrimaryAmmoIndex()]--;
#endif
} }
break; break;
} }
@ -1967,6 +1968,10 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib)
if ((pev->button & IN_ATTACK) && m_rgAmmo[pSmoke->m_iPrimaryAmmoType]) if ((pev->button & IN_ATTACK) && m_rgAmmo[pSmoke->m_iPrimaryAmmoType])
{ {
CGrenade::ShootSmokeGrenade(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5, pSmoke->m_usCreateSmoke); CGrenade::ShootSmokeGrenade(pev, (pev->origin + pev->view_ofs), pev->angles, 1.5, pSmoke->m_usCreateSmoke);
#ifdef REGAMEDLL_FIXES
m_rgAmmo[m_pActiveItem->PrimaryAmmoIndex()]--;
#endif
} }
break; break;
} }
@ -1997,7 +2002,9 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib)
MESSAGE_END(); MESSAGE_END();
} }
else else
{
UTIL_ScreenFade(this, Vector(0, 0, 0), 3, 3, 255, (FFADE_OUT | FFADE_STAYOUT)); UTIL_ScreenFade(this, Vector(0, 0, 0), 3, 3, 255, (FFADE_OUT | FFADE_STAYOUT));
}
SetScoreboardAttributes(); SetScoreboardAttributes();
@ -2056,7 +2063,12 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib)
break; break;
} }
#ifdef REGAMEDLL_FIXES
pev->angles.y = UTIL_VecToAngles(pev->velocity).y;
#else
pev->angles.y = UTIL_VecToAngles(-pev->velocity).y; pev->angles.y = UTIL_VecToAngles(-pev->velocity).y;
#endif
pev->v_angle.y = pev->angles.y; pev->v_angle.y = pev->angles.y;
m_iThrowDirection = THROW_NONE; m_iThrowDirection = THROW_NONE;
@ -4728,6 +4740,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(PostThink)()
// NOTE: play on item channel because we play footstep landing on body channel // NOTE: play on item channel because we play footstep landing on body channel
EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", VOL_NORM, ATTN_NORM); EMIT_SOUND(ENT(pev), CHAN_ITEM, "common/bodysplat.wav", VOL_NORM, ATTN_NORM);
} }
#ifdef REGAMEDLL_FIXES #ifdef REGAMEDLL_FIXES
if (flFallDamage >= 1.0f) if (flFallDamage >= 1.0f)
#else #else

View File

@ -939,7 +939,6 @@ Vector VecVelocityForDamage(float flDamage);
int TrainSpeed(int iSpeed, int iMax); int TrainSpeed(int iSpeed, int iMax);
const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller); const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller);
void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name); void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name);
void packPlayerItem(CBasePlayer *pPlayer, CBasePlayerItem *pItem, bool packAmmo);
bool CanSeeUseable(CBasePlayer *me, CBaseEntity *entity); bool CanSeeUseable(CBasePlayer *me, CBaseEntity *entity);
void FixPlayerCrouchStuck(edict_t *pPlayer); void FixPlayerCrouchStuck(edict_t *pPlayer);
BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot); BOOL IsSpawnPointValid(CBaseEntity *pPlayer, CBaseEntity *pSpot);

View File

@ -148,7 +148,7 @@ edict_t *CHalfLifeRules::GetPlayerSpawnSpot(CBasePlayer *pPlayer)
} }
pPlayer->pev->origin = pSpot->pev->origin + Vector(0, 0, 1); pPlayer->pev->origin = pSpot->pev->origin + Vector(0, 0, 1);
pPlayer->pev->v_angle = g_vecZero; pPlayer->pev->v_angle = g_vecZero;
pPlayer->pev->velocity = g_vecZero; pPlayer->pev->velocity = g_vecZero;
pPlayer->pev->angles = pSpot->pev->angles; pPlayer->pev->angles = pSpot->pev->angles;
pPlayer->pev->punchangle = g_vecZero; pPlayer->pev->punchangle = g_vecZero;

View File

@ -15,7 +15,7 @@ NOXREF float GetSkillCvar(char *pName)
if (flValue <= 0.0f) if (flValue <= 0.0f)
{ {
ALERT(at_console, "\n\n** GetSkillCVar Got a zero for %s **\n\n",szBuffer); ALERT(at_console, "\n\n** GetSkillCVar Got a zero for %s **\n\n", szBuffer);
} }
return flValue; return flValue;

View File

@ -1788,7 +1788,7 @@ float TEXTURETYPE_PlaySound(TraceResult *ptr, Vector vecSrc, Vector vecEnd, int
{ {
UTIL_Sparks(ptr->vecEndPos); UTIL_Sparks(ptr->vecEndPos);
//random volume range // random volume range
float flVolume = RANDOM_FLOAT(0.7 , 1.0); float flVolume = RANDOM_FLOAT(0.7 , 1.0);
switch (RANDOM_LONG(0, 1)) switch (RANDOM_LONG(0, 1))

View File

@ -1127,8 +1127,8 @@ public:
void EXPORT Smack(); void EXPORT Smack();
void WeaponAnimation(int iAnimation); void WeaponAnimation(int iAnimation);
int Stab(int fFirst); BOOL Stab(BOOL fFirst);
int Swing(int fFirst); BOOL Swing(BOOL fFirst);
public: public:
bool ShieldSecondaryFire(int iUpAnim, int iDownAnim); bool ShieldSecondaryFire(int iUpAnim, int iDownAnim);

View File

@ -248,9 +248,9 @@ void CKnife::WeaponIdle()
SendWeaponAnim(KNIFE_IDLE, UseDecrement() != FALSE); SendWeaponAnim(KNIFE_IDLE, UseDecrement() != FALSE);
} }
int CKnife::Swing(int fFirst) BOOL CKnife::Swing(BOOL fFirst)
{ {
int fDidHit = FALSE; BOOL fDidHit = FALSE;
TraceResult tr; TraceResult tr;
Vector vecSrc, vecEnd; Vector vecSrc, vecEnd;
@ -422,9 +422,9 @@ int CKnife::Swing(int fFirst)
return fDidHit; return fDidHit;
} }
int CKnife::Stab(int fFirst) BOOL CKnife::Stab(BOOL fFirst)
{ {
int fDidHit = FALSE; BOOL fDidHit = FALSE;
TraceResult tr; TraceResult tr;
Vector vecSrc, vecEnd; Vector vecSrc, vecEnd;

View File

@ -69,6 +69,7 @@ enum NavAttributeType
NAV_JUMP = 0x02, // must jump to traverse this area NAV_JUMP = 0x02, // must jump to traverse this area
NAV_PRECISE = 0x04, // do not adjust for obstacles, just move along area NAV_PRECISE = 0x04, // do not adjust for obstacles, just move along area
NAV_NO_JUMP = 0x08, // inhibit discontinuity jumping NAV_NO_JUMP = 0x08, // inhibit discontinuity jumping
NAV_WALK = 0x10, // must not run through this area
}; };
enum NavDirType enum NavDirType

View File

@ -2338,6 +2338,7 @@ void CNavArea::Draw(byte red, byte green, byte blue, int duration)
UTIL_DrawBeamPoints(nw, se, duration, red, green, blue); UTIL_DrawBeamPoints(nw, se, duration, red, green, blue);
UTIL_DrawBeamPoints(ne, sw, duration, red, green, blue); UTIL_DrawBeamPoints(ne, sw, duration, red, green, blue);
} }
if (GetAttributes() & NAV_PRECISE) if (GetAttributes() & NAV_PRECISE)
{ {
float size = 8.0f; float size = 8.0f;
@ -2349,6 +2350,7 @@ void CNavArea::Draw(byte red, byte green, byte blue, int duration)
Vector right(m_center.x + size, m_center.y, m_center.z + cv_bot_nav_zdraw.value); Vector right(m_center.x + size, m_center.y, m_center.z + cv_bot_nav_zdraw.value);
UTIL_DrawBeamPoints(left, right, duration, red, green, blue); UTIL_DrawBeamPoints(left, right, duration, red, green, blue);
} }
if (GetAttributes() & NAV_NO_JUMP) if (GetAttributes() & NAV_NO_JUMP)
{ {
float size = 8.0f; float size = 8.0f;
@ -2361,6 +2363,19 @@ void CNavArea::Draw(byte red, byte green, byte blue, int duration)
UTIL_DrawBeamPoints(down, left, duration, red, green, blue); UTIL_DrawBeamPoints(down, left, duration, red, green, blue);
UTIL_DrawBeamPoints(left, up, duration, red, green, blue); UTIL_DrawBeamPoints(left, up, duration, red, green, blue);
} }
if (GetAttributes() & NAV_WALK)
{
float size = 8.0f;
Vector up(m_center.x - size, m_center.y - size, m_center.z + cv_bot_nav_zdraw.value);
Vector down(m_center.x + size, m_center.y + size, m_center.z + cv_bot_nav_zdraw.value);
Vector left(m_center.x - size, m_center.y + size, m_center.z + cv_bot_nav_zdraw.value);
Vector right(m_center.x + size, m_center.y - size, m_center.z + cv_bot_nav_zdraw.value);
UTIL_DrawBeamPoints(up, right, duration, red, green, blue);
UTIL_DrawBeamPoints(right, down, duration, red, green, blue);
UTIL_DrawBeamPoints(down, left, duration, red, green, blue);
UTIL_DrawBeamPoints(left, up, duration, red, green, blue);
}
} }
// Draw selected corner for debugging // Draw selected corner for debugging
@ -3826,11 +3841,12 @@ void EditNavAreas(NavEditCmdType cmd)
} }
else else
{ {
Q_sprintf(attrib, "%s%s%s%s", Q_sprintf(attrib, "%s%s%s%s%s",
(area->GetAttributes() & NAV_CROUCH) ? "CROUCH " : "", (area->GetAttributes() & NAV_CROUCH) ? "CROUCH " : "",
(area->GetAttributes() & NAV_JUMP) ? "JUMP " : "", (area->GetAttributes() & NAV_JUMP) ? "JUMP " : "",
(area->GetAttributes() & NAV_PRECISE) ? "PRECISE " : "", (area->GetAttributes() & NAV_PRECISE) ? "PRECISE " : "",
(area->GetAttributes() & NAV_NO_JUMP) ? "NO_JUMP " : ""); (area->GetAttributes() & NAV_NO_JUMP) ? "NO_JUMP " : "",
(area->GetAttributes() & NAV_WALK) ? "WALK " : "");
} }
Q_sprintf(buffer, "Area #%d %s %s\n", area->GetID(), locName, attrib); Q_sprintf(buffer, "Area #%d %s %s\n", area->GetID(), locName, attrib);
@ -3853,36 +3869,36 @@ void EditNavAreas(NavEditCmdType cmd)
switch (cmd) switch (cmd)
{ {
case EDIT_TOGGLE_PLACE_MODE: case EDIT_TOGGLE_PLACE_MODE:
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/blip1.wav", 1, ATTN_NORM, 0, 100); EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/blip1.wav", 1, ATTN_NORM, 0, 100);
isPlaceMode = false; isPlaceMode = false;
return; return;
case EDIT_TOGGLE_PLACE_PAINTING: case EDIT_TOGGLE_PLACE_PAINTING:
{
if (isPlacePainting)
{ {
if (isPlacePainting) isPlacePainting = false;
{ EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/latchunlocked2.wav", 1, ATTN_NORM, 0, 100);
isPlacePainting = false;
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/latchunlocked2.wav", 1, ATTN_NORM, 0, 100);
}
else
{
isPlacePainting = true;
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/lightswitch2.wav", 1, ATTN_NORM, 0, 100);
// paint the initial area
area->SetPlace(TheCSBots()->GetNavPlace());
}
break;
} }
case EDIT_PLACE_PICK: else
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/blip1.wav", 1, ATTN_NORM, 0, 100); {
TheCSBots()->SetNavPlace(area->GetPlace()); isPlacePainting = true;
break; EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/lightswitch2.wav", 1, ATTN_NORM, 0, 100);
case EDIT_PLACE_FLOODFILL:
PlaceFloodFillFunctor pff(area); // paint the initial area
SearchSurroundingAreas(area, area->GetCenter(), pff); area->SetPlace(TheCSBots()->GetNavPlace());
break; }
break;
}
case EDIT_PLACE_PICK:
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/blip1.wav", 1, ATTN_NORM, 0, 100);
TheCSBots()->SetNavPlace(area->GetPlace());
break;
case EDIT_PLACE_FLOODFILL:
PlaceFloodFillFunctor pff(area);
SearchSurroundingAreas(area, area->GetCenter(), pff);
break;
} }
} }
else // normal editing mode else // normal editing mode
@ -3974,6 +3990,10 @@ void EditNavAreas(NavEditCmdType cmd)
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/bell1.wav", 1, ATTN_NORM, 0, 100); EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/bell1.wav", 1, ATTN_NORM, 0, 100);
area->SetAttributes(area->GetAttributes() ^ NAV_PRECISE); area->SetAttributes(area->GetAttributes() ^ NAV_PRECISE);
break; break;
case EDIT_ATTRIB_WALK:
EMIT_SOUND_DYN(ENT(UTIL_GetLocalPlayer()->pev), CHAN_ITEM, "buttons/bell1.wav", 1, ATTN_NORM, 0, 100);
area->SetAttributes(area->GetAttributes() ^ NAV_WALK);
break;
case EDIT_ATTRIB_NO_JUMP: case EDIT_ATTRIB_NO_JUMP:
EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/bell1.wav", 1, ATTN_NORM, 0, 100); EMIT_SOUND_DYN(ENT(pLocalPlayer->pev), CHAN_ITEM, "buttons/bell1.wav", 1, ATTN_NORM, 0, 100);
area->SetAttributes(area->GetAttributes() ^ NAV_NO_JUMP); area->SetAttributes(area->GetAttributes() ^ NAV_NO_JUMP);

View File

@ -50,6 +50,7 @@ enum NavEditCmdType
EDIT_ATTRIB_CROUCH, // toggle crouch attribute on current area EDIT_ATTRIB_CROUCH, // toggle crouch attribute on current area
EDIT_ATTRIB_JUMP, // toggle jump attribute on current area EDIT_ATTRIB_JUMP, // toggle jump attribute on current area
EDIT_ATTRIB_PRECISE, // toggle precise attribute on current area EDIT_ATTRIB_PRECISE, // toggle precise attribute on current area
EDIT_ATTRIB_WALK, // toggle walk attribute on current area
EDIT_ATTRIB_NO_JUMP, // toggle inhibiting discontinuity jumping in current area EDIT_ATTRIB_NO_JUMP, // toggle inhibiting discontinuity jumping in current area
EDIT_BEGIN_AREA, // begin creating a new nav area EDIT_BEGIN_AREA, // begin creating a new nav area
EDIT_END_AREA, // end creation of the new nav area EDIT_END_AREA, // end creation of the new nav area

View File

@ -130,12 +130,13 @@ void CNavArea::Save(FILE *fp) const
fprintf(fp, "v %f %f %f\n", m_extent.lo.x, m_extent.hi.y, m_swZ); fprintf(fp, "v %f %f %f\n", m_extent.lo.x, m_extent.hi.y, m_swZ);
static int base = 1; static int base = 1;
fprintf(fp, "\n\ng %04dArea%s%s%s%s\n", m_id, fprintf(fp, "\n\ng %04dArea%s%s%s%s%s\n", m_id,
(GetAttributes() & NAV_CROUCH) ? "CROUCH" : "", (GetAttributes() & NAV_JUMP) ? "JUMP" : "", (GetAttributes() & NAV_CROUCH) ? "CROUCH" : "", (GetAttributes() & NAV_JUMP) ? "JUMP" : "",
(GetAttributes() & NAV_PRECISE) ? "PRECISE" : "", (GetAttributes() & NAV_NO_JUMP) ? "NO_JUMP" : ""); (GetAttributes() & NAV_PRECISE) ? "PRECISE" : "", (GetAttributes() & NAV_NO_JUMP) ? "NO_JUMP" : "",
(GetAttributes() & NAV_WALK) ? "NAV_WALK" : "");
fprintf(fp, "f %d %d %d %d\n\n", base, base + 1, base + 2, base + 3); fprintf(fp, "f %d %d %d %d %d\n\n", base, base + 1, base + 2, base + 3, base + 4);
base += 4; base += 5;
} }
void CNavArea::Save(int fd, unsigned int version) void CNavArea::Save(int fd, unsigned int version)