*** empty log message ***

git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@27 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
driAn
2005-08-14 14:15:02 +00:00
parent 0374523483
commit a1bba487b4
12 changed files with 9902 additions and 1 deletions
-1
View File
@@ -19,7 +19,6 @@ CLIENT_OBJS= \
renderextras.o \
rendergl.o \
rendermd2.o \
rendermd3_2.o \
renderparticles.o \
rendertext.o \
rndmap.o \
+318
View File
@@ -0,0 +1,318 @@
//
// C++ Implementation: bot
//
// Description: Main bot code for Action Cube
//
// Main bot file
//
// Author: Rick <[email protected]>
//
//
//
#include "../cube.h"
#ifdef AC_CUBE
extern vector<server_entity> sents;
extern int triggertime;
extern itemstat itemstats[];
extern void spawnstate(dynent *d);
//AC Bot class begin
void CACBot::Spawn()
{
// Init all bot variabeles
m_pMyEnt->eyeheight = 4.25f;
m_pMyEnt->aboveeye = 0.7f;
m_pMyEnt->radius = 1.1f;
spawnplayer(m_pMyEnt);
m_pMyEnt->targetyaw = m_pMyEnt->yaw = m_pMyEnt->targetpitch = m_pMyEnt->pitch = 0.0f;
m_pMyEnt->move = 0;
m_pMyEnt->enemy = NULL;
m_pMyEnt->maxspeed = 16.0f;
m_pMyEnt->health = 100;
m_pMyEnt->armour = 50;
m_pMyEnt->pitch = 0;
m_pMyEnt->roll = 0;
m_pMyEnt->state = CS_ALIVE;
m_pMyEnt->anger = 0;
m_pMyEnt->pBot = this;
loopi(NUMGUNS) m_pMyEnt->ammo[i] = 0;
/* UNDONE
m_pMyEnt->ammo[GUN_FIST] = 1;
if(m_noitems)
{
m_pMyEnt->gunselect = GUN_RIFLE;
m_pMyEnt->armour = 0;
if(m_noitemsrail)
{
m_pMyEnt->health = 1;
m_pMyEnt->ammo[GUN_RIFLE] = 100;
}
else
{
if(gamemode==12) // eihrul's secret "instafist" mode
{
m_pMyEnt->gunselect = GUN_FIST;
}
else
{
m_pMyEnt->health = 256;
if(m_tarena)
{
int gun1 = rnd(4)+1;
botbaseammo(m_pMyEnt->gunselect = gun1, m_pMyEnt);
for(;;)
{
int gun2 = rnd(4)+1;
if(gun1!=gun2) { botbaseammo(gun2, m_pMyEnt); break; };
}
}
else if(m_arena) // insta arena
{
m_pMyEnt->ammo[GUN_RIFLE] = 100;
}
else // efficiency
{
loopi(4) botbaseammo(i+1, m_pMyEnt);
m_pMyEnt->gunselect = GUN_CG;
}
m_pMyEnt->ammo[GUN_CG] /= 2;
}
}
}
else
{
m_pMyEnt->ammo[GUN_SEMIPISTOL] = 5;
SelectGun(GUN_SG);
}*/
m_pMyEnt->ammo[GUN_KNIFE] = 1;
m_pMyEnt->ammo[GUN_SEMIPISTOL] = 5;
m_pMyEnt->mag[GUN_SEMIPISTOL] = 5;
SelectGun(GUN_SEMIPISTOL);
m_eCurrentBotState = STATE_NORMAL;
m_iShootDelay = m_iChangeWeaponDelay = 0;
m_iCheckEnvDelay = 0;
m_vPrevOrigin = g_vecZero;
m_iStuckCheckDelay = lastmillis + 250;
m_bStuck = false;
m_iStuckTime = 0;
m_iStrafeTime = m_iStrafeCheckDelay = 0;
m_iMoveDir = DIR_NONE;
m_pPrevEnemy = NULL;
m_iCombatNavTime = 0;
m_iSPMoveTime = 0;
m_iEnemySearchDelay = 0;
m_bCombatJump = false;
m_iCombatJumpDelay = 0;
m_bShootAtFeet = (RandomLong(1, 100) <= m_pBotSkill->sShootAtFeetWithRLPercent);
m_iHuntDelay = 0;
m_vHuntLocation = m_vPrevHuntLocation = g_vecZero;
m_pHuntTarget = NULL;
m_fPrevHuntDist = 0.0f;
m_iHuntLastTurnLessTime = m_iHuntPlayerUpdateTime = m_iHuntPauseTime = 0;
m_iLastJumpPad = 0;
m_pTargetEnt = NULL;
m_iCheckTeleporterDelay = m_iCheckJumppadsDelay = 0;
m_iCheckEntsDelay = 0;
m_iCheckTriggersDelay = 0;
m_iLookForWaypointTime = 0;
m_iAimDelay = 0;
m_fYawToTurn = m_fPitchToTurn = 0.0f;
m_vGoal = m_vWaterGoal = g_vecZero;
ResetWaypointVars();
}
void CACBot::BotPain(int damage, dynent *d)
{
if(m_pMyEnt->state!=CS_ALIVE || editmode || intermission) return;
int ad = damage*(m_pMyEnt->armourtype+1)*20/100; // let armour absorb when possible
if(ad>m_pMyEnt->armour) ad = m_pMyEnt->armour;
m_pMyEnt->armour -= ad;
damage -= ad;
float droll = damage/0.5f;
m_pMyEnt->roll += m_pMyEnt->roll>0 ? droll : (m_pMyEnt->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage
if((m_pMyEnt->health -= damage)<=0)
{
if (player1 == d)
{
int frags;
if(isteam(player1->team, m_pMyEnt->team))
{
frags = -1;
conoutf("you fragged a teammate (%s)", (int)m_pMyEnt->name);
}
else
{
frags = 1;
conoutf("you fragged %s", (int)m_pMyEnt->name);
}
addmsg(1, 2, SV_FRAGS, player1->frags += frags);
addmsg(1, 4, SV_BOTDIED, BotManager.GetBotIndex(m_pMyEnt), -1, false);
}
else if (d->monsterstate)
conoutf("%s got killed by %s", (int)m_pMyEnt->name, (int)d->name);
else
{
int KillerIndex = -1;
if (d == m_pMyEnt)
{
conoutf("%s suicided", (int)d->name);
KillerIndex = BotManager.GetBotIndex(d);
addmsg(1, 3, SV_BOTFRAGS, KillerIndex, --d->frags);
}
else
{
if (d->bIsBot)
{
KillerIndex = BotManager.GetBotIndex(d);
addmsg(1, 3, SV_BOTFRAGS, KillerIndex, ++d->frags);
}
else
loopv(players){ if (players[i] == d) { KillerIndex = i; break;} }
if(isteam(m_pMyEnt->team, d->team))
{
conoutf("%s fragged his teammate (%s)", (int)d->name,
(int)m_pMyEnt->name);
}
else
{
conoutf("%s fragged %s", (int)d->name, (int)m_pMyEnt->name);
}
}
addmsg(1, 4, SV_BOTDIED, BotManager.GetBotIndex(m_pMyEnt), KillerIndex,
d->bIsBot);
}
m_pMyEnt->lifesequence++;
m_pMyEnt->attacking = false;
m_pMyEnt->state = CS_DEAD;
m_pMyEnt->pitch = 0;
m_pMyEnt->roll = 60;
playsound(S_DIE1+rnd(2), &m_pMyEnt->o);
spawnstate(m_pMyEnt);
m_pMyEnt->lastaction = lastmillis;
}
else
{
playsound(S_PAIN6, &m_pMyEnt->o);
}
}
void CACBot::CheckItemPickup()
{
if(editmode) return;
loopv(ents)
{
entity &e = ents[i];
if(e.type==NOTUSED) continue;
if (!e.spawned) continue;
if ((i < sents.length()) && (!sents[i].spawned)) continue;
if(OUTBORD(e.x, e.y)) continue;
vec v = { e.x, e.y, S(e.x, e.y)->floor+m_pMyEnt->eyeheight };
vdist(dist, t, m_pMyEnt->o, v);
if(dist<2.5)
{
PickUp(i);
}
}
}
void CACBot::PickUp(int n)
{
int np = 1;
loopv(players) if(players[i]) np++;
loopv(bots) if(bots[i]) np++;
np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players
int ammo = np*2;
switch(ents[n].type)
{
case I_SEMIPISTOL: AddItem(n, m_pMyEnt->ammo[1], ammo); break;
case I_AUTOPISTOL: AddItem(n, m_pMyEnt->ammo[2], ammo); break;
case I_SHOTGUN: AddItem(n, m_pMyEnt->ammo[3], ammo); break;
case I_SNIPER: AddItem(n, m_pMyEnt->ammo[4], ammo); break;
case I_SUBGUN: AddItem(n, m_pMyEnt->ammo[5], ammo); break;
case I_CARBINE: AddItem(n, m_pMyEnt->ammo[6], ammo); break;
case I_SEMIRIFLE: AddItem(n, m_pMyEnt->ammo[7], ammo); break;
case I_AUTORIFLE: AddItem(n, m_pMyEnt->ammo[8], ammo); break;
case I_GRENADE: AddItem(n, m_pMyEnt->ammo[9], ammo); break;
case I_HELMET:
// (100h/100g only absorbs 166 damage)
if(m_pMyEnt->armourtype==A_YELLOW && m_pMyEnt->armour>66) break;
AddItem(n, m_pMyEnt->armour, 20);
break;
case I_ARMOUR:
AddItem(n, m_pMyEnt->armour, 20);
break;
case OBJ_ITEM:
AddItem(n, m_pMyEnt->quadmillis, 60);
break;
case TRIGGER:
ents[n].spawned = false;
triggertime = lastmillis;
trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer
// HACK: Reset ent goal if bot was looking for this ent
if (m_pTargetEnt == &ents[n])
{
m_pTargetEnt = NULL;
m_vGoal = g_vecZero;
ResetWaypointVars();
m_iCheckEntsDelay = lastmillis + RandomLong(5000, 10000);
}
BotManager.PickNextTrigger();
break;
};
};
void CACBot::AddItem(int i, int &v, int spawnsec)
{
if((i>=sents.length()) || (!sents[i].spawned))
return;
if(v>=itemstats[ents[i].type-I_SEMIPISTOL].max) // don't pick up if not needed
return;
itemstat &is = itemstats[ents[i].type-I_SEMIPISTOL];
ents[i].spawned = false;
v += is.add;
if(v>is.max) v = is.max;
botplaysound(is.sound, m_pMyEnt);
sents[i].spawned = false;
sents[i].spawnsecs = spawnsec;
addmsg(1, 4, SV_ITEMPICKUP, i, m_classicsp ? 100000 : spawnsec, false);
// HACK: Reset ent goal if bot was looking for for this ent
if (m_pTargetEnt == &ents[i])
{
m_pTargetEnt = NULL;
m_vGoal = g_vecZero;
ResetWaypointVars();
m_iCheckEntsDelay = lastmillis + RandomLong(5000, 10000);
}
}
// AC Bot class end
#endif
+46
View File
@@ -0,0 +1,46 @@
//
// C++ Implementation: bot
//
// Description:
//
// Header specific for AC_CUBE
//
// Author: Rick <[email protected]>
//
//
//
#ifndef AC_BOT_H
#define AC_BOT_H
#ifdef AC_CUBE
#define MAX_WEAPONS 7
class CACBot: public CBot
{
public:
friend class CBotManager;
friend class CWaypointClass;
virtual void CheckItemPickup(void);
virtual void PickUp(int n);
virtual void AddItem(int i, int &v, int spawnsec);
// AI Functions
virtual bool ChoosePreferredWeapon(void);
virtual entity *SearchForEnts(bool bUseWPs, float flRange=9999.0f,
float flMaxHeight=JUMP_HEIGHT);
virtual bool HeadToTargetEnt(void);
virtual bool DoSPStuff(void);
virtual void Spawn(void);
virtual void BotPain(int damage, dynent *d);
};
inline void AddScreenText(char *t, ...) {}; // UNDONE
inline void AddDebugText(char *t, ...) {}; // UNDONE
#endif
#endif
+298
View File
@@ -0,0 +1,298 @@
//
// C++ Implementation: ac_bot_ai
//
// Description: The AI part of the bot for ac is here(navigation, shooting etc)
//
//
// Author: <[email protected]>
//
#include "../cube.h"
#ifdef AC_CUBE
extern vector<server_entity> sents;
extern vector<dynent*> monsters;
// UNDONE
weaponinfo_s WeaponInfoTable[MAX_WEAPONS] =
{
// KNIFE
{ TYPE_MELEE, 0.0f, 3.5f, 0.0f, 5.0f, 1 },
// SEMI PISTOL
{ TYPE_NORMAL, 5.0f, 12.0f, 0.0f, 50.0f, 4 },
// AUTO PISTOL
{ TYPE_AUTO, 10.0f, 20.0f, 0.0f, 100.0f, 10 },
// SHOTGUN
{ TYPE_SHOTGUN, 15.0f, 25.0f, 10.0f, 70.0f, 3 },
// SNIPER
{ TYPE_SNIPER, 12.0f, 25.0f, 0.0f, 100.0f, 4 },
// SUBGUN
{ TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 },
/* EDIT: Adrian Henke - those are not used yet
// CARBINE
{ TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 },
// SEMI RIFLE
{ TYPE_SNIPER, 12.0f, 25.0f, 0.0f, 100.0f, 4 },
// AUTO RIFLE
{ TYPE_AUTO, 12.0f, 25.0f, 0.0f, 100.0f, 4 },
*/
// GRENADE
{ TYPE_GRENADE, 12.0f, 25.0f, 0.0f, 100.0f, 4 }
};
// Code of CACBot - Start
bool CACBot::ChoosePreferredWeapon()
{
if (CBot::ChoosePreferredWeapon())
return true;
else
return SelectGun(GUN_KNIFE);
}
entity *CACBot::SearchForEnts(bool bUseWPs, float flRange, float flMaxHeight)
{
/* Entities are scored on the following things:
- Visibility
- For ammo: Need(ie has this bot much of this type or not)
- distance
*/
float flNearestDist = 9999, flDist;
entity *pNewTargetEnt = NULL;
waypoint_s *pWptNearBot = NULL, *pBestWpt = NULL;
short sScore, sHighestScore = 0;
if ((WaypointClass.m_iWaypointCount >= 1) && bUseWPs)
pWptNearBot = GetNearestWaypoint(15.0f);
#ifdef WP_FLOOD
if (!pWptNearBot && bUseWPs)
pWptNearBot = GetNearestFloodWP(5.0f);
#endif
loopv(ents)
{
sScore = 0;
entity &e = ents[i];
vec o = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
if (!ents[i].spawned) continue;
if (OUTBORD(e.x, e.y)) continue;
bool bInteresting = false;
short sAmmo = 0, sMaxAmmo = itemstats[e.type-I_SEMIPISTOL].max;
switch(e.type)
{
case I_SEMIPISTOL:
bInteresting = (m_pMyEnt->ammo[1]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[1];
break;
case I_AUTOPISTOL:
bInteresting = (m_pMyEnt->ammo[2]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[2];
break;
case I_SHOTGUN:
bInteresting = (m_pMyEnt->ammo[3]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[3];
break;
case I_SNIPER:
bInteresting = (m_pMyEnt->ammo[4]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_SUBGUN:
bInteresting = (m_pMyEnt->ammo[5]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_CARBINE:
bInteresting = (m_pMyEnt->ammo[6]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_SEMIRIFLE:
bInteresting = (m_pMyEnt->ammo[7]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_AUTORIFLE:
bInteresting = (m_pMyEnt->ammo[8]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_GRENADE:
bInteresting = (m_pMyEnt->ammo[9]<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->ammo[4];
break;
case I_HELMET:
bInteresting = ((m_pMyEnt->armour<itemstats[e.type-I_SEMIPISTOL].max) &&
(m_pMyEnt->armourtype!=A_YELLOW || m_pMyEnt->armour<=66));
sAmmo = m_pMyEnt->armour;
break;
case I_ARMOUR:
bInteresting = (m_pMyEnt->armour<itemstats[e.type-I_SEMIPISTOL].max);
sAmmo = m_pMyEnt->armour;
break;
}
if (!bInteresting)
continue; // Not an interesting item, skip
// Score on ammo and need
if (sAmmo == -1)
{
// This entity doesn't have/need ammo
// Score on type instead
switch(e.type)
{ // UNDONE
}
}
else
{
// Calculate current percentage of max ammo
float percent = ((float)sAmmo / (float)sMaxAmmo) * 100.0f;
if (percent > 100.0f) percent = 100.0f;
sScore += ((100 - short(percent))/2);
}
flDist = GetDistance(o);
if (flDist > flRange) continue;
// Score on distance
float f = flDist;
if (f > 100.0f) f = 100.0f;
sScore += ((100 - short(f)) / 2);
waypoint_s *pWptNearEnt = NULL;
// If this entity isn't visible check if there is a nearby waypoint
if (!IsReachable(o, flMaxHeight))//(!IsVisible(o))
{
if (!pWptNearBot) continue;
#ifdef WP_FLOOD
if (pWptNearBot->pNode->iFlags & W_FL_FLOOD)
pWptNearEnt = GetNearestFloodWP(o, 8.0f);
else
#endif
pWptNearEnt = GetNearestWaypoint(o, 15.0f);
if (!pWptNearEnt) continue;
}
// Score on visibility
if (pWptNearEnt == NULL) // Ent is visible
sScore += 30;
else
sScore += 15;
if (sScore > sHighestScore)
{
// Found a valid wp near the bot and the ent,so...lets store it :)
if (pWptNearEnt)
pBestWpt = pWptNearEnt;
else
pBestWpt = NULL; // Best ent so far doesn't need any waypoints
sHighestScore = sScore;
flNearestDist = flDist;
pNewTargetEnt = &ents[i];
}
}
if (pNewTargetEnt)
{
// Need waypoints to reach it?
if (pBestWpt)
{
ResetWaypointVars();
SetCurrentWaypoint(pWptNearBot);
SetCurrentGoalWaypoint(pBestWpt);
}
m_vGoal.x = pNewTargetEnt->x;
m_vGoal.y = pNewTargetEnt->y;
m_vGoal.z = S(pNewTargetEnt->x, pNewTargetEnt->y)->floor+player1->eyeheight;
}
return pNewTargetEnt;
}
bool CACBot::HeadToTargetEnt()
{
if (m_pTargetEnt)
{
vec o = { m_pTargetEnt->x, m_pTargetEnt->y,
S(m_pTargetEnt->x, m_pTargetEnt->y)->floor+m_pMyEnt->eyeheight };
if (m_pTargetEnt->spawned && (!UnderWater(m_pMyEnt->o) ||
!UnderWater(o)))
{
bool bIsVisible = false;
if (m_pCurrentGoalWaypoint)
{
if ((GetDistance(o) <= 20.0f) && IsReachable(o, 1.0f))
bIsVisible = true;
else if (HeadToGoal())
{
//debugbeam(m_pMyEnt->o, m_pCurrentWaypoint->pNode->v_origin);
//debugbeam(m_pMyEnt->o,
// m_pCurrentGoalWaypoint->pNode->v_origin);
AddDebugText("Using WPs for ents");
return true;
}
}
else
bIsVisible = IsVisible(o);
if (bIsVisible)
{
if (m_pCurrentWaypoint || m_pCurrentGoalWaypoint)
{
condebug("ent is now visible");
ResetWaypointVars();
}
float flHeightDiff = o.z - m_pMyEnt->o.z;
bool bToHigh = false;
if (Get2DDistance(o) <= 2.0f)
{
if (flHeightDiff >= 1.5f)
{
if (flHeightDiff <= JUMP_HEIGHT)
{
#ifndef RELEASE_BUILD
char sz[64];
sprintf(sz, "Ent z diff: %f", o.z-m_pMyEnt->o.z);
condebug(sz);
#endif
// Jump if close to ent and the ent is high
m_pMyEnt->jumpnext = true;
}
else
bToHigh = true;
}
}
if (!bToHigh)
{
AimToVec(o);
return true;
}
}
}
}
return false;
}
bool CACBot::DoSPStuff()
{
return false;
}
// Code of CACBot - End
#endif
+497
View File
@@ -0,0 +1,497 @@
//
// C++ Implementation: bot
//
// Description:
//
// Contains code of the base bot class
//
// Author: Rick <[email protected]>
//
//
//
#include "../cube.h"
dvector bots;
extern vector<server_entity> sents;
extern int triggertime;
extern itemstat itemstats[];
extern ENetHost *clienthost;
const vec g_vecZero = {0,0,0};
//Bot class begin
CBot::~CBot()
{
// Delete all waypoints
loopi(MAX_MAP_GRIDS)
{
loopj(MAX_MAP_GRIDS)
{
while (m_WaypointList[i][j].Empty() == false)
delete m_WaypointList[i][j].Pop();
}
}
}
void CBot::Spawn()
{
// Init all bot variabeles
m_pMyEnt->eyeheight = 3.2f;
m_pMyEnt->aboveeye = 0.7f;
m_pMyEnt->radius = 1.1f;
spawnplayer(m_pMyEnt);
m_pMyEnt->targetyaw = m_pMyEnt->yaw = m_pMyEnt->targetpitch = m_pMyEnt->pitch = 0.0f;
m_pMyEnt->move = 0;
m_pMyEnt->enemy = NULL;
m_pMyEnt->maxspeed = 22.0f;
m_pMyEnt->health = 100;
m_pMyEnt->armour = 50;
m_pMyEnt->pitch = 0;
m_pMyEnt->roll = 0;
m_pMyEnt->state = CS_ALIVE;
m_pMyEnt->anger = 0;
m_pMyEnt->pBot = this;
m_eCurrentBotState = STATE_NORMAL;
m_iShootDelay = m_iChangeWeaponDelay = 0;
m_iCheckEnvDelay = 0;
m_vPrevOrigin = g_vecZero;
m_iStuckCheckDelay = lastmillis + 250;
m_bStuck = false;
m_iStuckTime = 0;
m_iStrafeTime = m_iStrafeCheckDelay = 0;
m_iMoveDir = DIR_NONE;
m_pPrevEnemy = NULL;
m_iCombatNavTime = 0;
m_iSPMoveTime = 0;
m_iEnemySearchDelay = 0;
m_iSawEnemyTime = 0;
m_bCombatJump = false;
m_iCombatJumpDelay = 0;
m_bShootAtFeet = (RandomLong(1, 100) <= m_pBotSkill->sShootAtFeetWithRLPercent);
m_iHuntDelay = 0;
m_vHuntLocation = m_vPrevHuntLocation = g_vecZero;
m_pHuntTarget = NULL;
m_fPrevHuntDist = 0.0f;
m_iHuntLastTurnLessTime = m_iHuntPlayerUpdateTime = m_iHuntPauseTime = 0;
m_iLookAroundDelay = m_iLookAroundTime = m_iLookAroundUpdateTime = 0;
m_fLookAroundYaw = 0.0f;
m_bLookLeft = false;
m_iLastJumpPad = 0;
m_pTargetEnt = NULL;
while(!m_UnreachableEnts.Empty()) delete m_UnreachableEnts.Pop();
m_iCheckTeleporterDelay = m_iCheckJumppadsDelay = 0;
m_iCheckEntsDelay = 0;
m_iCheckTriggersDelay = 0;
m_iLookForWaypointTime = 0;
m_iAimDelay = 0;
m_fYawToTurn = m_fPitchToTurn = 0.0f;
m_vGoal = m_vWaterGoal = g_vecZero;
ResetWaypointVars();
}
void CBot::Think()
{
if (intermission)
return;
// Bot is dead?
if (m_pMyEnt->state == CS_DEAD)
{
if(lastmillis-m_pMyEnt->lastaction<1200)
{
m_pMyEnt->move = 0;
moveplayer(m_pMyEnt, 1, false);
}
else if (!m_arena)
Spawn();
SendBotInfo();
return;
}
CheckItemPickup();
TLinkedList<unreachable_ent_s*>::node_s *p = m_UnreachableEnts.GetFirst(), *tmp;
while(p)
{
if ((lastmillis - p->Entry->time) > 3500)
{
tmp = p;
p = p->next;
delete tmp->Entry;
m_UnreachableEnts.DeleteNode(tmp);
continue;
}
p = p->next;
}
if (!BotManager.IdleBots())
{
MainAI();
}
else
{
ResetMoveSpeed();
}
// Aim to ideal yaw and pitch
AimToIdeal();
// Store current location, to see if the bot is stuck
m_vPrevOrigin = m_pMyEnt->o;
// Don't check for stuck if the bot doesn't want to move
if (!m_pMyEnt->move && !m_pMyEnt->strafe)
m_iStuckCheckDelay = max(m_iStuckCheckDelay, (lastmillis+100));
// Move the bot
moveplayer(m_pMyEnt, 1, false);
// Update bot info on all clients
SendBotInfo();
}
void CBot::AimToVec(const vec &o)
{
m_pMyEnt->targetpitch = atan2(o.z-m_pMyEnt->o.z, GetDistance(o))*180/PI;
m_pMyEnt->targetyaw = -(float)atan2(o.x - m_pMyEnt->o.x, o.y -
m_pMyEnt->o.y)/PI*180+180;
}
void CBot::AimToIdeal()
{
if (m_iAimDelay > lastmillis)
return;
float MaxXSpeed = RandomFloat(m_pBotSkill->flMinAimXSpeed,
m_pBotSkill->flMaxAimXSpeed);
float MaxYSpeed = RandomFloat(m_pBotSkill->flMinAimYSpeed,
m_pBotSkill->flMaxAimYSpeed);
float XOffset = RandomFloat(m_pBotSkill->flMinAimXOffset,
m_pBotSkill->flMaxAimXOffset);
float YOffset = RandomFloat(m_pBotSkill->flMinAimYOffset,
m_pBotSkill->flMaxAimYOffset);
float RealXOffset, RealYOffset;
float AimXSpeed = MaxXSpeed, AimYSpeed = MaxYSpeed;
float XDiff = fabs(m_pMyEnt->targetpitch - m_pMyEnt->pitch);
float YDiff = fabs(m_pMyEnt->targetyaw - m_pMyEnt->yaw);
// How higher the diff, how higher the offsets and aim speed
if (XOffset)
{
if (RandomLong(0, 1))
RealXOffset = XDiff * (XOffset / 100.0f);
else
RealXOffset = -(XDiff * (XOffset / 100.0f));
}
else
RealXOffset = 0.0f;
if (YOffset)
{
if (RandomLong(0, 1))
RealYOffset = YDiff * (YOffset / 100.0f);
else
RealYOffset = -(YDiff * (YOffset / 100.0f));
}
else
RealYOffset = 0.0f;
if (XDiff >= 1.0f)
AimXSpeed = (AimXSpeed * (XDiff / 80.0f)) + (AimXSpeed * 0.25f);
else
AimXSpeed *= 0.01f;
if (YDiff >= 1.0f)
AimYSpeed = (AimYSpeed * (YDiff / 70.0f)) + (AimYSpeed * 0.25f);
else
AimYSpeed *= 0.015f;
m_fPitchToTurn = fabs((m_pMyEnt->targetpitch + RealXOffset) - m_pMyEnt->pitch);
m_fYawToTurn = fabs((m_pMyEnt->targetyaw + RealYOffset) - m_pMyEnt->yaw);
float flIdealPitch = ChangeAngle(AimXSpeed, m_pMyEnt->targetpitch + RealXOffset, m_pMyEnt->pitch);
float flIdealYaw = ChangeAngle(AimYSpeed, m_pMyEnt->targetyaw + RealYOffset, m_pMyEnt->yaw);
// m_pMyEnt->pitch = WrapXAngle(m_pMyEnt->targetpitch); // Uncomment for instant aiming
// m_pMyEnt->yaw = WrapYZAngle(m_pMyEnt->targetyaw);
m_pMyEnt->pitch = WrapXAngle(flIdealPitch);
m_pMyEnt->yaw = WrapYZAngle(flIdealYaw);
}
// Function code by Botman
float CBot::ChangeAngle(float speed, float ideal, float current)
{
float current_180; // current +/- 180 degrees
float diff;
// find the difference in the current and ideal angle
diff = fabs(current - ideal);
// speed that we can turn during this frame...
speed = speed * (float(BotManager.m_iFrameTime)/1000.0f);
// check if difference is less than the max degrees per turn
if (diff < speed)
speed = diff; // just need to turn a little bit (less than max)
// check if the bot is already facing the idealpitch direction...
if (diff <= 1.0f)
return ideal;
if ((current >= 180) && (ideal >= 180))
{
if (current > ideal)
current -= speed;
else
current += speed;
}
else if ((current >= 180) && (ideal < 180))
{
current_180 = current - 180;
if (current_180 > ideal)
current += speed;
else
current -= speed;
}
else if ((current < 180) && (ideal >= 180))
{
current_180 = current + 180;
if (current_180 > ideal)
current += speed;
else
current -= speed;
}
else // (current < 180) && (ideal < 180)
{
if (current > ideal)
current -= speed;
else
current += speed;
}
return current;
}
void CBot::SendBotInfo()
{
if(lastmillis-m_iLastBotUpdate<40) return; // don't update faster than 25fps
m_iLastBotUpdate = lastmillis;
int x = (int)(m_pMyEnt->o.x*DMF);
int y = (int)(m_pMyEnt->o.y*DMF);
int z = (int)(m_pMyEnt->o.z*DMF);
int yaw = (int)(m_pMyEnt->yaw*DAF);
int pitch = (int)(m_pMyEnt->pitch*DAF);
int roll = (int)(m_pMyEnt->roll*DAF);
int velx = (int)(m_pMyEnt->vel.x*DVF);
int vely = (int)(m_pMyEnt->vel.y*DVF);
int velz = (int)(m_pMyEnt->vel.z*DVF);
// pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3
int moveflags = (m_pMyEnt->strafe&3) | ((m_pMyEnt->move&3)<<2) |
(((int)m_pMyEnt->onfloor)<<4) |
((editmode ? CS_EDITING :
m_pMyEnt->state)<<5);
ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0);
uchar *start = packet->data;
uchar *p = start+2;
putint(p, SV_BOTUPDATE);
putint(p, BotManager.GetBotIndex(m_pMyEnt));
putint(p, x); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes
putint(p, y);
putint(p, z);
putint(p, yaw);
putint(p, pitch);
putint(p, roll);
putint(p, velx); // quantize to 1/100, almost always 1 byte
putint(p, vely);
putint(p, velz);
// pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3
putint(p, moveflags);
if(!m_bSendC2SInit) // tell other clients who I am
{
packet->flags = ENET_PACKET_FLAG_RELIABLE;
m_bSendC2SInit = true;
putint(p, SV_ADDBOT);
putint(p, BotManager.GetBotIndex(m_pMyEnt));
sendstring(m_pMyEnt->name, p);
sendstring(m_pMyEnt->team, p);
putint(p, m_pMyEnt->lifesequence);
};
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
enet_packet_resize(packet, p-start);
incomingdemodata(start, p-start, true);
if(clienthost)
{
enet_host_broadcast(clienthost, 0, packet);
enet_host_flush(clienthost);
}
else
localclienttoserver(packet);
}
float CBot::GetDistance(const vec &o)
{
vdist(flDist, vecto, m_pMyEnt->o, o);
return flDist;
}
float CBot::GetDistance(const vec &v1, const vec &v2)
{
vdist(flDist, vecto, v1, v2);
return flDist;
}
float CBot::GetDistance(entity *e)
{
vec v = { e->x, e->y, e->z };
vdist(flDist, vecto, m_pMyEnt->o, v);
return flDist;
}
bool CBot::SelectGun(int Gun)
{
if (m_pMyEnt->ammo[Gun])
{
//temporary doc out argh
//if (m_pMyEnt->gunselect != Gun)
// botplaysound(S_WEAPLOAD, m_pMyEnt);
m_pMyEnt->gunselect = Gun;
return true;
}
return false;
}
bool CBot::IsVisible(entity *e, bool CheckPlayers)
{
vec v = { e->x, e->y, e->z };
return ::IsVisible(m_pMyEnt->o, v, (CheckPlayers) ? m_pMyEnt : NULL);
}
bool CBot::IsVisible(vec o, int Dir, float flDist, bool CheckPlayers, float *pEndDist)
{
static vec angles, end, forward, right, up;
static traceresult_s tr;
end = o;
angles = GetViewAngles();
angles.x = 0;
if (Dir & UP)
angles.x = WrapXAngle(angles.x + 45.0f);
else if (Dir & DOWN)
angles.x = WrapXAngle(angles.x - 45.0f);
if ((Dir & FORWARD) || (Dir & BACKWARD))
{
if (Dir & BACKWARD)
angles.y = WrapYZAngle(angles.y + 180.0f);
if (Dir & LEFT)
{
if (Dir & FORWARD)
angles.y = WrapYZAngle(angles.y - 45.0f);
else
angles.y = WrapYZAngle(angles.y + 45.0f);
}
else if (Dir & RIGHT)
{
if (Dir & FORWARD)
angles.y = WrapYZAngle(angles.y + 45.0f);
else
angles.y = WrapYZAngle(angles.y - 45.0f);
}
}
else if (Dir & LEFT)
angles.y = WrapYZAngle(angles.y - 90.0f);
else if (Dir & RIGHT)
angles.y = WrapYZAngle(angles.y + 90.0f);
else if (Dir & UP)
angles.x = WrapXAngle(angles.x + 90.0f);
else if (Dir & DOWN)
angles.x = WrapXAngle(angles.x - 90.0f);
AnglesToVectors(angles, forward, right, up);
vmul(forward, flDist);
vadd(end, forward);
TraceLine(o, end, m_pMyEnt, CheckPlayers, &tr);
if (pEndDist)
*pEndDist = GetDistance(o, tr.end);
return !tr.collided;
}
void CBot::SetMoveDir(int iMoveDir, bool add)
{
if (iMoveDir & FORWARD)
m_pMyEnt->move = 1;
else if (m_iMoveDir & BACKWARD)
m_pMyEnt->move = -1;
else if (!add)
m_pMyEnt->move = 0;
if (iMoveDir & LEFT)
m_pMyEnt->strafe = 1;
else if (m_iMoveDir & RIGHT)
m_pMyEnt->strafe = -1;
else if (!add)
m_pMyEnt->strafe = 0;
if (iMoveDir & UP)
m_pMyEnt->jumpnext = true;
}
// Used when switching to another task/state
void CBot::ResetCurrentTask()
{
switch (m_eCurrentBotState)
{
case STATE_ENEMY:
m_pMyEnt->enemy = NULL;
m_pTargetEnt = NULL;
m_iCombatNavTime = m_iMoveDir = 0;
m_bCombatJump = false;
m_vGoal = g_vecZero;
break;
case STATE_ENT:
m_pTargetEnt = NULL;
m_vGoal = g_vecZero;
break;
case STATE_SP:
m_iSPMoveTime = m_iMoveDir = 0;
m_pTargetEnt = NULL;
m_vGoal = g_vecZero;
break;
case STATE_NORMAL:
m_iStrafeTime = m_iMoveDir = 0;
break;
}
}
// Bot class end
+426
View File
@@ -0,0 +1,426 @@
//
// C++ Interface: bot
//
// Description:
//
// Main bot header
//
//
// Author: Rick <[email protected]>
//
//
//
/*
TODO list
-bots that "cover" each other in team matches ?
-If a bot gets ammo it should ignore that kind of ammo for the next 10 seconds. ?
-bots should ignore players that are too far away ?
-bots should dodge/jump when shot at
-If the bot's only weapon is the fist it should make looking for ammo its priority - Done ?
-bots should rocket jump to get quads and the 150 armor if normal jumping doesn't
work (only if health is x so they don't suicide).
-Make all the "UNDONE's" done :)
-Finish waypoint navigation
-Make bot personalities
-More advanced enemy scoring
-Bots should less bump into walls
-(Better) Coop support
-Check for reachable instead of visible in GetNearestWaypoint
-Waypoint experience file
-Bots should avoid team mates
-Handle failed goals better(ie remember them)
-Test multiplayer
-Fix bots getting stuck in each other
-Optimize hunting code
-Check/Fix msg code
*/
#ifndef BOT_H
#define BOT_H
//#define RELEASE_BUILD // Set when you want to make a release build
// Set for which mod the code is
//#define VANILLA_CUBE
#define AC_CUBE
//#define CUBE_X
#include "bot_util.h"
#include "bot_waypoint.h"
#ifdef __linux__
#include <sys/syslog.h>
#else // Windows doesn't have syslog
#define openlog (void)0
#define syslog (void)0
#define closelog (void)0
#endif
extern bool dedserv;
extern itemstat itemstats[];
#ifdef RELEASE_BUILD
inline void condebug(const char *s, int a = 0, int b = 0, int c = 0) {};
inline void debugnav(const char *s, int a = 0, int b = 0, int c = 0) {};
inline void debugbeam(vec &s, vec &e) { };
#else
inline void condebug(const char *s, int a = 0, int b = 0, int c = 0) { /*conoutf(s, a, b, c);*/ };
inline void debugnav(const char *s, int a = 0, int b = 0, int c = 0) { /*conoutf(s, a, b, c);*/ };
inline void debugbeam(vec &s, vec &e) { /*if (!dedserv) particle_trail(1, 500, s, e);*/ };
#endif
#define JUMP_HEIGHT 4.0f // NOT accurate
#define FORWARD (1<<1)
#define BACKWARD (1<<2)
#define LEFT (1<<3)
#define RIGHT (1<<4)
#define UP (1<<5)
#define DOWN (1<<6)
#define DIR_NONE 0
enum EBotCommands // For voting of bot commands
{
COMMAND_ADDBOT=0,
COMMAND_KICKBOT,
COMMAND_BOTSKILL
};
struct bot_skill_s
{
float flMinReactionDelay; // Minimal reaction time
float flMaxReactionDelay; // Maximal reaction time
float flMinAimXSpeed; // Minimal X aim speed in degrees(base value)
float flMaxAimXSpeed; // Maximal X aim speed in degrees(base value)
float flMinAimYSpeed; // Minimal Y aim speed in degrees(base value)
float flMaxAimYSpeed; // Maximal Y aim speed in degrees(base value)
float flMinAimXOffset; // Minimal X aim offset in degrees(base value)
float flMaxAimXOffset; // Maximal X aim offset in degrees(base value)
float flMinAimYOffset; // Minimal Y aim offset in degrees(base value)
float flMaxAimYOffset; // Maximal Y aim offset in degrees(base value)
float flMinAttackDelay; // Minimal delay for when a bot can attack again
float flMaxAttackDelay; // Maximal delay for when a bot can attack again
float flMinEnemySearchDelay; // Minimal delay for when a bot can search for an
// enemy again
float flMaxEnemySearchDelay; // Maximal delay for when a bot can search for an
// enemy again
short sShootAtFeetWithRLPercent; // Percent that a bot shoot with a rocket
// launcher at the enemy feet.
int iMaxHearVolume; // Max volume that bot can hear
bool bCanPredict; // Can this bot predict his enemy position?
bool bCircleStrafe; // Can this bot circle strafe?
bool bCanSearchItemsInCombat;
};
enum EBotWeaponTypes
{
TYPE_MELEE, // Fist, knife etc
TYPE_NORMAL, // Normal weapon, distance doesn't really matter(ie pistol)
TYPE_SHOTGUN,
TYPE_ROCKET,
TYPE_SNIPER, // Rifle, sniper etc
TYPE_GRENADE,
TYPE_AUTO // Chain gun, machine gun etc
};
struct weaponinfo_s
{
EBotWeaponTypes eWeaponType;
float flMinDesiredDistance;
float flMaxDesiredDistance;
float flMinFireDistance;
float flMaxFireDistance;
short sMinDesiredAmmo;
};
enum ECurrentBotState
{
STATE_ENEMY, // Bot has an enemy
STATE_HUNT, // Bot is hunting an enemy
STATE_ENT, // Bot is heading to an entity
STATE_SP, // Bot is doing sp specific stuff
STATE_NORMAL // Bot is doing normal navaigation
};
struct unreachable_ent_s
{
entity *ent;
int time;
unreachable_ent_s(entity *e, int t) : ent(e), time(t) { };
};
class CBot
{
public:
dynent *m_pMyEnt;
int m_iLastBotUpdate;
// Combat variabels
dynent *m_pPrevEnemy;
int m_iShootDelay;
int m_iChangeWeaponDelay;
int m_iCombatNavTime;
int m_iEnemySearchDelay;
int m_iSawEnemyTime;
bool m_bCombatJump;
float m_iCombatJumpDelay;
bool m_bShootAtFeet;
// Hunting variabeles
int m_iHuntDelay;
vec m_vHuntLocation, m_vPrevHuntLocation;
dynent *m_pHuntTarget;
float m_fPrevHuntDist;
int m_iHuntPauseTime, m_iHuntPlayerUpdateTime, m_iHuntLastTurnLessTime;
int m_iLookAroundDelay, m_iLookAroundTime, m_iLookAroundUpdateTime;
float m_fLookAroundYaw;
bool m_bLookLeft;
// Navigation variabeles
int m_iCheckEnvDelay;
int m_iLastJumpPad;
vec m_vPrevOrigin;
float m_iStuckCheckDelay;
bool m_bStuck;
int m_iStuckTime;
int m_iStrafeTime;
int m_iStrafeCheckDelay;
int m_iMoveDir;
int m_iSPMoveTime;
bool m_bCalculatingAStarPath;
TLinkedList<waypoint_s *> m_AStarNodeList;
TPriorList<waypoint_s *, float> m_AStarOpenList[2];
TLinkedList<waypoint_s *> m_AStarClosedList[2];
vec m_vWaterGoal; // Place to go to when underwater
// Waypoint variabeles
TLinkedList<waypoint_s *> m_WaypointList[MAX_MAP_GRIDS][MAX_MAP_GRIDS];
float m_iWaypointTime;
waypoint_s *m_pCurrentWaypoint;
waypoint_s *m_pCurrentGoalWaypoint;
vec m_vGoal;
waypoint_s *m_pPrevWaypoints[5];
int m_iRandomWaypointTime;
float m_fPrevWaypointDistance;
int m_iLookForWaypointTime;
int m_iWaypointHeadLastTurnLessTime; // Last time the didn't had to turn much wile
// heading to a WP
int m_iWaypointHeadPauseTime; // Pause time to give the bot time to aim.
bool m_bGoToDebugGoal;
// Misc stuff
ECurrentBotState m_eCurrentBotState;
entity *m_pTargetEnt;
TLinkedList<unreachable_ent_s *> m_UnreachableEnts;
int m_iCheckTeleporterDelay;
int m_iCheckJumppadsDelay;
int m_iCheckEntsDelay;
int m_iCheckTriggersDelay;
int m_iAimDelay;
float m_fYawToTurn, m_fPitchToTurn;
bot_skill_s *m_pBotSkill; // Pointer to current bot skill
short m_sSkillNr; // Skill number, 0 == best 4 == worst
void AimToVec(const vec &o);
void AimToIdeal(void);
float ChangeAngle(float speed, float ideal, float current);
bool SelectGun(int Gun);
virtual void CheckItemPickup(void) = NULL;
virtual void PickUp(int n) = NULL;
virtual void AddItem(int i, int &v, int spawnsec) = NULL;
virtual void BotPain(int damage, dynent *d) = NULL;
void SendBotInfo(void);
float GetDistance(const vec &o);
float GetDistance(const vec &v1, const vec &v2);
float GetDistance(entity *e);
float Get2DDistance(const vec &v) { return ::Get2DDistance(m_pMyEnt->o, v); };
vec GetViewAngles(void) { vec temp = { m_pMyEnt->pitch, m_pMyEnt->yaw,
m_pMyEnt->roll }; return temp; };
void ResetMoveSpeed(void) { m_pMyEnt->move = m_pMyEnt->strafe = 0; };
void SetMoveDir(int iMoveDir, bool add);
void ResetCurrentTask();
// AI Functions
bool FindEnemy(void);
void ShootEnemy(void);
bool CheckHunt(void);
bool HuntEnemy(void);
void DoCombatNav(void);
void MainAI(void);
bool CheckStuck(void);
bool CheckJump(void);
bool CheckStrafe(void);
void CheckFOV(void);
bool IsVisible(const vec &o, bool CheckPlayers = false) { return ::IsVisible(m_pMyEnt->o, o,
(CheckPlayers) ? m_pMyEnt :
NULL); };
bool IsVisible(dynent *d, bool CheckPlayers = false) { return ::IsVisible(m_pMyEnt->o, d->o,
(CheckPlayers) ?
m_pMyEnt : NULL); };
bool IsVisible(entity *e, bool CheckPlayers = false);
bool IsVisible(vec o, int Dir, float flDist, bool CheckPlayers, float *pEndDist = NULL);
bool IsVisible(int Dir, float flDist, bool CheckPlayers)
{ return IsVisible(m_pMyEnt->o, Dir, flDist, CheckPlayers); };
bool IsInFOV(const vec &o);
bool IsInFOV(dynent *d) { return IsInFOV(d->o); };
int GetShootDelay(void);
virtual bool ChoosePreferredWeapon(void);
virtual entity *SearchForEnts(bool bUseWPs, float flRange=9999.0f,
float flMaxHeight=JUMP_HEIGHT) = NULL;
virtual bool HeadToTargetEnt(void) = NULL;
bool CheckItems(void);
bool InUnreachableList(entity *e);
virtual bool DoSPStuff(void) = NULL;
vec GetEnemyPos(dynent *d);
bool AStar(void);
float AStarCost(waypoint_s *pWP1, waypoint_s *pWP2);
void CleanAStarLists(bool bPathFailed);
bool IsReachable(vec to, float flMaxHeight=JUMP_HEIGHT);
bool WaterNav(void);
void HearSound(int n, vec *o);
// Waypoint functions
bool HeadToWaypoint(void);
bool FindWaypoint(void);
void ResetWaypointVars(void);
void SetCurrentWaypoint(node_s *pWP);
void SetCurrentWaypoint(waypoint_s *pWP);
void SetCurrentGoalWaypoint(node_s *pNode);
void SetCurrentGoalWaypoint(waypoint_s *pWP);
bool CurrentWPIsValid(void);
bool ReachedGoalWP(void);
bool HeadToGoal(void);
waypoint_s *GetWPFromNode(node_s *pNode);
waypoint_s *GetNearestWaypoint(vec v_src, float flRange);
waypoint_s *GetNearestWaypoint(float flRange) { return GetNearestWaypoint(m_pMyEnt->o, flRange); };
waypoint_s *GetNearestTriggerWaypoint(vec v_src, float flRange);
#ifdef WP_FLOOD
waypoint_s *GetNearestFloodWP(vec v_origin, float flRange);
waypoint_s *GetNearestFloodWP(float flRange) { return GetNearestFloodWP(m_pMyEnt->o, flRange); };
waypoint_s *GetNearestTriggerFloodWP(vec v_origin, float flRange);
#endif
void SyncWaypoints(void);
friend class CBotManager;
friend class CWaypointClass;
bool m_bSendC2SInit;
virtual ~CBot(void);
virtual void Spawn(void);
virtual void Think(void);
void GoToDebugGoal(vec o);
};
class CStoredBot;
extern dvector bots;
class CBotManager
{
char m_szBotNames[150][16]; // Max 150 bot names with a length of 16 characters
short m_sBotNameCount;
char m_szBotTeams[20][5]; // Max 100 bot teams co a length of 5 characters
short m_sBotTeamCount;
bool m_bInit;
bool m_bBotsShoot;
bool m_bIdleBots;
bot_skill_s m_BotSkills[5]; // 5 different bot skills, 0 = best 4 = worst
int m_iFrameTime;
int m_iPrevTime;
short m_sBotSkill; // Bad - Worse - Medium - Good - Best
short m_sMaxAStarBots; // Max bots that can use a* at the same time
short m_sUsingAStarBotsCount; // Number of bots that are using a*
short m_sCurrentTriggerNr; // Current waypoint trigger bots should use
TLinkedList<CStoredBot *> m_StoredBots; // List of bots that should be re-added after map change
float m_fReAddBotDelay;
void LoadBotNamesFile(void);
char *GetBotName(void);
void LoadBotTeamsFile(void);
char *GetBotTeam(void);
void CreateSkillData(void);
void InitSkillData(void);
void InitBotItems(void);
friend class CBot;
friend class CCubeBot;
friend class CACBot;
friend class CWaypointClass;
public:
dynent *m_pBotToView;
// Construction
CBotManager(void) { m_bInit = true; m_fReAddBotDelay = -1.0f; };
// Destruction
~CBotManager(void);
void Init(void);
void Think(void);
void RenderBots(void);
void RenderBotScore(void) { loopv(bots) if(bots[i]) renderscore(bots[i]); }
void AddBotTeamScore(void) { loopv(bots) addteamscore(bots[i]); };
void RespawnBots(void) { loopv(bots) if (bots[i] && bots[i]->pBot) bots[i]->pBot->Spawn(); };
void EndMap(void);
void BeginMap(char *szMapName);
int GetBotIndex(dynent *m);
void LetBotsUpdateStats(void);
void LetBotsHear(int n, vec *loc);
void AddWaypoint(node_s *pNode);
void DelWaypoint(node_s *pNode);
bool BotsShoot(void) { return m_bBotsShoot; };
bool IdleBots(void) { return m_bIdleBots; };
void SetBotsShoot(bool bShoot) { m_bBotsShoot = bShoot; };
void SetIdleBots(bool bIdle) { m_bIdleBots = bIdle; };
void EnableBotView(dynent *bot) { m_pBotToView = bot; };
void DisableBotView(void);
void ChangeBotSkill(short Skill, dynent *bot = NULL);
dynent *CreateBot(const char *team, const char *skill, const char *name);
void ViewBot(void);
void CalculateMaxAStarCount(void);
void PickNextTrigger(void);
void MakeBotFileName(const char *szFileName, const char *szDir1, const char *szDir2,
char *szOutput);
};
#if defined VANILLA_CUBE
#include "cube_bot.h"
#elif defined AC_CUBE
#include "ac_bot.h"
#endif
extern CBotManager BotManager;
extern const vec g_vecZero;
#if defined AC_CUBE
extern CACWaypointClass WaypointClass;
#elif defined VANILLA_CUBE
extern CCubeWaypointClass WaypointClass;
#endif
class CStoredBot // Used to store bots after mapchange, so that they can be readded
{
char m_szName[32];
char m_szTeam[32];
short m_sSkillNr;
public:
CStoredBot(const char *name, const char *team, const short skill) : m_sSkillNr(skill)
{ strcpy(m_szName, name); strcpy(m_szTeam, team); };
void ReAddBot(void) { BotManager.CreateBot(m_szTeam, SkillNrToSkillName(m_sSkillNr), m_szName); };
};
#endif
File diff suppressed because it is too large Load Diff
+565
View File
@@ -0,0 +1,565 @@
//
// C++ Implementation: bot_util
//
// Description: Basic util functions, mostly bot related
//
//
// Author: <[email protected]>
//
//
//
//
#include "../cube.h"
#ifndef __linux__
#include <io.h>
#endif
#include <fcntl.h>
#if (defined __linux__)
#include <sys/stat.h>
#include <sys/types.h>
#else
#include <sys\stat.h>
#endif
#include <unistd.h>
extern dvector monsters;
// Function code by PMB - Begin
// Random functions begin
// copied from
// http://forums.bots-united.com/showthread.php?t=244&page=1&pp=10&highlight=random
// So credits to PMB and the person(s) who actually made it :]
// maximum value returned by our number generator (2^31 - 1 = 0x7FFFFFFF)
#define LRAND_MAX 2147483647L
long lseed; // our random number generator's seed
long lrand (void)
{
// this function is the equivalent of the rand() standard C library function,
// except that whereas rand() works only with short integers
// (i.e. not above 32767), this function is able to generate 32-bit random
// numbers. Isn't that nice ?
// credits go to Ray Gardner for his fast implementation of minimal random
// number generators
// http://c.snippets.org/snip_lister.php?fname=rg_rand.c
// compose the two 16-bit parts of the long integer and assemble them
static unsigned long lrand_lo, lrand_hi;
lrand_lo = 16807 * (long) (lseed & 0xFFFF); // low part
lrand_hi = 16807 * (long) ((unsigned long) lseed >> 16);
// high part
lrand_lo += (lrand_hi & 0x7FFF) << 16;
// assemble both in lrand_lo
// is the resulting number greater than LRAND_MAX (half the capacity) ?
if (lrand_lo > LRAND_MAX)
{
lrand_lo &= LRAND_MAX; // then get rid of the disturbing bit
lrand_lo++; // and increase it a bit (to avoid overflow problems, I suppose)
}
lrand_lo += lrand_hi >> 15;
// now do twisted maths to generate the next seed
// is the resulting number greater than LRAND_MAX (half the capacity) ?
if (lrand_lo > LRAND_MAX)
{
lrand_lo &= LRAND_MAX; // then get rid of the disturbing bit
lrand_lo++; // and increase it a bit (to avoid overflow problems, I suppose)
}
// now we've got our (pseudo-)random number.
lseed = (long) lrand_lo; // put it in the seed for next time
return (lseed); // and return it. Yeah, simple as that.
}
void lsrand (unsigned long initial_seed)
{
// this function initializes the random seed based on the initial seed value
// passed in the initial_seed parameter. Since random seeds are
// usually initialized with the time of day, and time is a value that
// changes slowly, we bump the seed twice to have it in a more
// random state for future calls of the random number generator.
// fill in the initial seed of the random number generator
lseed = (long) initial_seed;
lseed = lrand (); // bump it once
lseed = lrand (); // bump it twice
return; // that's all folks
}
long RandomLong (long from, long to)
{
// this function returns a random integer number between (and including)
// the starting and ending values passed by parameters from and to.
if (to <= from)
return (from);
return (from + lrand () / (LRAND_MAX / (to - from + 1)));
}
float RandomFloat (float from, float to)
{
// this function returns a random floating-point number between (and including)
// the starting and ending values passed by parameters from and to.
if (to <= from)
return (from);
return (from + (float) lrand () / (LRAND_MAX / (to - from)));
}
// End random functions
void AnglesToVectors(vec angles, vec &forward, vec &right, vec &up)
{
static float degrees_to_radians = 2 * PI / 360;
float angle;
float sin_pitch;
float sin_yaw;
float sin_roll;
float cos_pitch;
float cos_yaw;
float cos_roll;
// For some reason this has to be done :)
// Me = math n00b
angles.x = -angles.x;
angles.y -= 90;
// compute the sin and cosine of the pitch component
angle = angles.x * degrees_to_radians;
sin_pitch = sinf (angle);
cos_pitch = cosf (angle);
// compute the sin and cosine of the yaw component
angle = angles.y * degrees_to_radians;
sin_yaw = sinf (angle);
cos_yaw = cosf (angle);
// compute the sin and cosine of the roll component
angle = angles.z * degrees_to_radians;
sin_roll = sinf (angle);
cos_roll = cosf (angle);
// build the FORWARD vector
forward.x = cos_pitch * cos_yaw;
forward.y = cos_pitch * sin_yaw;
forward.z = -sin_pitch;
// build the RIGHT vector
right.x = -(-(sin_roll * sin_pitch * cos_yaw) - (cos_roll * -sin_yaw));
right.y = -(-(sin_roll * sin_pitch * sin_yaw) - (cos_roll * cos_yaw));
right.z = -(sin_roll * cos_pitch);
// build the UPWARDS vector
up.x = ((cos_roll * sin_pitch * cos_yaw) - (sin_roll * -sin_yaw));
up.y = ((cos_roll * sin_pitch * sin_yaw) - (sin_roll * cos_yaw));
up.z = cos_roll * cos_pitch;
return;
}
// Function code by PMB - End
float WrapXAngle(float angle)
{
if (angle > 90) angle = 90;
else if (angle < -90) angle = -90;
return angle;
}
float WrapYZAngle(float angle)
{
while (angle >= 360.0f) angle -= 360.0f;
while (angle < 0.0f) angle += 360.0f;
return angle;
}
// Sees how far it can come from 'from' to 'to' and puts the end vector in 'end'
// (1337 description :-)
// UNDONE: Optimize this as much as possible
void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult_s *tr,
bool SkipTags)
{
tr->end = from;
tr->collided = false;
static float flNearestDist, flDist;
static vec v;
static bool solid;
flNearestDist = 9999.0f;
if(OUTBORD((int)from.x, (int)from.y)) return;
// Check if the 'line' collides with entities like mapmodels
loopv(ents)
{
entity &e = ents[i];
if(e.type!=MAPMODEL) continue; // Only check map models for now
mapmodelinfo &mmi = getmminfo(e.attr2);
if(!&mmi || !mmi.h) continue;
float lo = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3);
float hi = lo+mmi.h;
float z = (fabs(from.z-lo) < fabs(from.z-hi)) ? lo : hi;
makevec(&v, e.x, e.y, z);
flDist = GetDistance(from, v);
if ((flDist < flNearestDist) && (intersect(&e, from, to, &tr->end)))
{
flNearestDist = GetDistance(from, tr->end);
tr->collided = true;
}
}
if (CheckPlayers)
{
// Check if the 'line' collides with players
loopv(players)
{
dynent *d = players[i];
if(!d || (d==pTracer) || (d->state != CS_ALIVE)) continue; // Only check valid players
flDist = GetDistance(from, d->o);
if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
{
flNearestDist = flDist;
tr->collided = true;
}
}
// Check if the 'line' collides with bots
loopv(bots)
{
dynent *d = bots[i];
if(!d || (d==pTracer) || (d->state != CS_ALIVE)) continue; // Only check valid bots
flDist = GetDistance(from, d->o);
if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
{
flNearestDist = flDist;
tr->collided = true;
}
}
// Check if the 'line' collides with monsters
/* we don't have monsters anymore, thanks for the bots :) -argh
dvector &mv = getmonsters();
loopv(mv)
{
dynent *d = mv[i];
if(!d || (d==pTracer) || (d->state != CS_ALIVE)) continue; // Only check valid monsters
flDist = GetDistance(from, d->o);
if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
{
flNearestDist = flDist;
tr->collided = true;
}
}
*/
// Check if the 'line' collides with the local player(player1)
dynent *d = player1; // Shortcut
if (d && (d!=pTracer) && !BotManager.m_pBotToView && (d->state == CS_ALIVE))
{
flDist = GetDistance(from, d->o);
if ((flDist < flNearestDist) && (intersect(d, from, to, &tr->end)))
{
flNearestDist = flDist;
tr->collided = true;
}
}
}
float dx = to.x-from.x;
float dy = to.y-from.y;
int steps = (int)(sqrt(dx*dx+dy*dy)/0.9);
if(!steps) // If from and to are on the same cube...
{
if (GetDistance(from, to) < flNearestDist)
{
tr->end = to;
sqr *s = S(fast_f2nat(from.x), fast_f2nat(from.y));
float flr = GetCubeFloor(fast_f2nat(from.x), fast_f2nat(from.y));
solid = ((!SkipTags || !s->tag) && SOLID(s));
if (solid || (to.z < flr) || (to.z > s->ceil))
{
tr->collided = true;
tr->end.z = (fabs(to.z-s->ceil) < fabs(to.z-flr)) ? s->ceil : flr;
}
}
return;
}
float x = from.x;
float y = from.y;
int i = 0;
vec endcube = from;
// Now check if the 'line' is hit by a cube
while(i<steps)
{
if(OUTBORD((int)x, (int)y)) break;
if (GetDistance(from, endcube) >= flNearestDist) break;
sqr *s = S(fast_f2nat(x), fast_f2nat(y));
solid = ((!SkipTags || !s->tag) && SOLID(s));
if(solid) break;
float floor = s->floor;
if(s->type==FHF) floor -= s->vdelta/4.0f;
float ceil = s->ceil;
if(s->type==CHF) ceil += s->vdelta/4.0f;
float rz = from.z-((from.z-to.z)*(i/(float)steps));
if(rz<floor || rz>ceil) break;
endcube.x = x;
endcube.y = y;
endcube.z = rz;
x += dx/(float)steps;
y += dy/(float)steps;
i++;
}
if ((i>=steps) && !tr->collided)
{
tr->end = to;
}
else
{
tr->collided = true;
if (GetDistance(from, endcube) < flNearestDist)
tr->end = endcube;
}
return;
}
float GetDistance(vec v1, vec v2)
{
if ((v1.x == 0) && (v2.x == 0) && (v1.y == 0) && (v2.y == 0) && (v1.z == 0) &&
(v2.z == 0))
return 0.0f;
vdist(flDist, vecto, v1, v2);
return flDist;
}
// As GetDistance(), but doesn take height in account.
float Get2DDistance(vec v1, vec v2)
{
if ((v1.x == 0) && (v2.x == 0) && (v1.y == 0) && (v2.y == 0))
return 0.0f;
v1.z = v2.z = 0.0f;
vdist(flDist, vecto, v1, v2);
return flDist;
}
bool IsVisible(vec v1, vec v2, dynent *tracer, bool SkipTags)
{
traceresult_s tr;
TraceLine(v1, v2, tracer, (tracer!=NULL), &tr, SkipTags);
return !tr.collided;
}
// Prediction:
// - pos: Current position
// - vel: Current velocity
// - Time: In seconds, predict how far it is in Time seconds
vec PredictPos(vec pos, vec vel, float Time)
{
float flVelLength = sqrt(vel.x*vel.x + vel.y*vel.y + vel.z*vel.z);
if (flVelLength <= 1.0)
return pos; // don't bother with low velocities...
float speed = flVelLength * Time;
float flTemp = 1/flVelLength;
vec vecNormalize;
vecNormalize.x = vel.x * flTemp;
vecNormalize.y = vel.y * flTemp;
vecNormalize.z = vel.z * flTemp;
vec v_src = pos;
vmul(vecNormalize, speed);
vec v_end = v_src;
vadd(v_end, vecNormalize);
return v_end;
}
// returns true if the given file is valid
bool IsValidFile(const char *szFileName)
{
FILE *fp = fopen(szFileName, "r");
if (fp)
{
fclose(fp);
return true;
}
return false;
}
bool FileIsOlder(const char *szFileName1, const char *szFileName2)
{
int file1, file2;
struct stat stat1, stat2;
file1 = open(szFileName1, O_RDONLY);
file2 = open(szFileName2, O_RDONLY);
fstat(file1, &stat1);
fstat(file2, &stat2);
close(file1);
close(file2);
return(stat1.st_mtime < stat2.st_mtime);
}
vec Normalize(vec v)
{
float flLen = sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
static vec v_norm = g_vecZero;
if (flLen == 0)
{
v_norm.x=0;v_norm.y=0;v_norm.z=1;
}
else
{
flLen = 1 / flLen;
v_norm.x = v.x * flLen; v_norm.y = v.y * flLen; v_norm.z = v.z * flLen;
}
return v_norm;
}
float GetYawDiff(float curyaw, vec v1, vec v2)
{
float yaw = -(float)atan2(v2.x - v1.x, v2.y - v1.y)/PI*180+180;
return yaw-curyaw;
}
vec CrossProduct(const vec &a, const vec &b)
{
vec cross = { a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x };
return cross;
}
int GetDirection(const vec &angles, const vec &v1, const vec &v2)
{
vec tmp, forward, right, up, cross;
float flDot;
AnglesToVectors(angles, forward, right, up);
tmp = v2;
vsub(tmp, v1);
tmp.z = 0.0f; // Make 2D
tmp = Normalize(tmp);
forward.z = 0; // Make 2D
flDot = dotprod(tmp , forward);
cross = CrossProduct(tmp, forward);
int Dir = DIR_NONE;
if (cross.z > 0.1f) Dir |= LEFT;
else if (cross.z < -0.1f) Dir |= RIGHT;
if (flDot < -0.1f) Dir |= BACKWARD;
else if (flDot > 0.1f) Dir |= FORWARD;
if (v1.z > v2.z) Dir |= DOWN;
if (v1.z < v2.z) Dir |= UP;
return Dir;
}
float GetCubeFloor(int x, int y)
{
sqr *s = S(x, y);
float floor = s->floor;
if (s->type == FHF)
floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f;
return floor;
}
float GetCubeHeight(int x, int y)
{
sqr *s = S(x, y);
float ceil = s->ceil;
if(s->type == CHF)
ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f;
return ceil;
}
char *SkillNrToSkillName(short skillnr)
{
if (skillnr == 0) return "best";
else if (skillnr == 1) return "good";
else if (skillnr == 2) return "medium";
else if (skillnr == 3) return "worse";
else if (skillnr == 4) return "bad";
return NULL;
}
bool IsInGame(dynent *d)
{
if (!d) return false;
/*edit -argh
if (d->monsterstate)
{
loopv(monsters)
{
if (monsters[i] == d)
return true;
}
}
*/
//else
if (d->bIsBot)
{
loopv(bots)
{
if (bots[i] == d)
return true;
}
}
else
{
if (d == player1)
return true;
else
{
loopv(players)
{
#ifdef VANILLA_CUBE
if (!players[i] || (players[i]->state == CS_DEDHOST)) continue;
#elif defined AC_CUBE
if (!players[i]) continue;
#endif
if (players[i] == d)
return true;
}
}
}
return false;
}
+565
View File
@@ -0,0 +1,565 @@
#ifndef BOT_UTIL_H
#define BOT_UTIL_H
// Structure for storing traceresults
struct traceresult_s
{
vec end;
bool collided;
};
long RandomLong(long from, long to);
float RandomFloat(float from, float to);
void lsrand(unsigned long initial_seed);
void AnglesToVectors(vec angles, vec &forward, vec &right, vec &up);
float WrapXAngle(float angle);
float WrapYZAngle(float angle);
void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult_s *tr,
bool SkipTags=false);
float GetDistance(vec v1, vec v2);
float Get2DDistance(vec v1, vec v2);
bool IsVisible(vec v1, vec v2, dynent *tracer = NULL, bool SkipTags=false);
bool IsValidFile(const char *szFileName);
bool FileIsOlder(const char *szFileName1, const char *szFileName2);
vec PredictPos(vec pos, vec vel, float Time);
vec Normalize(vec v);
inline void makevec(vec *v, float x, float y, float z) { v->x=x; v->y=y; v->z=z; };
inline bool UnderWater(const vec &o) { return hdr.waterlevel>o.z-0.5f; };
inline bool InWater(const vec &o) { return hdr.waterlevel>=o.z; };
float GetYawDiff(float curyaw, vec v1, vec v2);
vec CrossProduct(const vec &a, const vec &b);
int GetDirection(const vec &angles, const vec &v1, const vec &v2);
float GetCubeFloor(int x, int y);
float GetCubeHeight(int x, int y);
char *SkillNrToSkillName(short skillnr);
bool IsInGame(dynent *d);
// ==================================================================
// Code of TLinkedList - Start
// ==================================================================
template <class C> class TLinkedList
{
public:
struct node_s
{
C Entry;
node_s *next;
node_s *prev;
node_s(void) : next(NULL), prev(NULL)
{
//memset(&Entry, 0, sizeof(Entry));
};
};
// member functions
void AddNode(C entry)
{
if (!pNodeList)
{
pNodeList = new node_s;
pNodeList->Entry = entry;
pNodeList->next = NULL;
pNodeList->prev = NULL;
pLastNode = pNodeList;
iNodeCount = 1;
}
else
{
pLastNode->next = new node_s;
pLastNode->next->prev = pLastNode;
pLastNode = pLastNode->next;
pLastNode->Entry = entry;
pLastNode->next = NULL;
iNodeCount++;
}
}
void PushNode(C Entry)
{
if (!pNodeList)
{
pNodeList = new node_s;
pNodeList->Entry = Entry;
pNodeList->next = NULL;
pNodeList->prev = NULL;
pLastNode = pNodeList;
iNodeCount = 1;
}
else
{
node_s *pNew = new node_s;
pNew->Entry = Entry;
pNew->prev = NULL;
pNew->next = pNodeList;
pNodeList->prev = pNew;
pNodeList = pNew;
iNodeCount++;
}
}
void DeleteEntry(C Entry)
{
node_s *pNode = pNodeList;
if (!pNode)
return;
if (pNode->Entry == Entry) // first node
{
if (pNodeList == pLastNode)
pLastNode = NULL;
pNodeList = pNodeList->next;
if (pNodeList)
pNodeList->prev = NULL;
pNode->next = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
if (Entry == pLastNode->Entry) // last node
{
pNode = pLastNode;
pLastNode = pLastNode->prev;
pLastNode->next = NULL;
pNode->next = NULL;
pNode->prev = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
// node is somewhere in the middle
pNode = SearchNode(Entry);
if (!pNode)
return;
node_s *pPrevNode = pNode->prev;
if (!pPrevNode)
return;
// unlink pNode
pNode->next->prev = pPrevNode;
pPrevNode->next = pNode->next;
pNode->next = NULL;
pNode->prev = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
}
void DeleteNode(node_s *pNode)
{
if (!pNode)
return;
if (pNodeList == pNode) // first node
{
if (pNodeList == pLastNode)
pLastNode = pNodeList->next;
pNodeList = pNodeList->next;
if (pNodeList)
pNodeList->prev = NULL;
pNode->next = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
if (pNode == pLastNode) // last node
{
pNode = pLastNode;
pLastNode = pLastNode->prev;
pLastNode->next = NULL;
pNode->next = NULL;
pNode->prev = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
// node is somewhere in the middle
node_s *pPrevNode = pNode->prev;
if (!pPrevNode)
return;
// unlink pNode
pNode->next->prev = pPrevNode;
pPrevNode->next = pNode->next;
pNode->next = NULL;
pNode->prev = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
}
void DeleteAllNodes(void)
{
node_s *pNode = pNodeList;
node_s *pTemp;
while (pNode != NULL)
{
pTemp = pNode;
pNode = pNode->next;
pTemp->next = NULL;
pTemp->prev = NULL;
delete pTemp;
}
pNodeList = pLastNode = NULL;
iNodeCount = 0;
}
void Reset(void) // Special case, doesn't delete existing nodes
{
pNodeList = pLastNode = NULL;
iNodeCount = 0;
}
node_s *SearchNode(C Entry)
{
node_s *pNode = pNodeList;
while(pNode)
{
if (pNode->Entry == Entry)
return pNode;
pNode = pNode->next;
}
return NULL;
}
C Pop(void)
{
if (!pNodeList)
return static_cast<C>(NULL);
C Entry = pNodeList->Entry;
DeleteNode(pNodeList);
return Entry;
}
node_s *GetFirst(void) { return pNodeList; };
node_s *GetLast(void) { return pLastNode; };
void EditEntry(C OrigEntry, C NewVal)
{
node_s *pNode = SearchNode(OrigEntry);
if (pNode)
pNode->Entry = NewVal;
}
bool IsInList(C Entry)
{
return (SearchNode(Entry) != NULL);
}
bool Empty(void) { return (pNodeList == NULL); };
int NodeCount(void) { return iNodeCount; };
// construction/destruction
TLinkedList(void)
{
pNodeList = NULL;
pLastNode = NULL;
iNodeCount = 0;
};
~TLinkedList(void)
{
DeleteAllNodes();
};
private:
node_s *pNodeList;
node_s *pLastNode;
int iNodeCount;
};
// ==================================================================
// Code of TLinkedList - End
// ==================================================================
// ==================================================================
// Code of TPriorList - Begin
// ==================================================================
template <class C, class D=int> class TPriorList
{
public:
struct node_s
{
C Entry;
D Priority;
node_s *next;
node_s(C Ent, D Prior) : Entry(Ent), Priority(Prior), next(NULL) {};
};
TPriorList(void) : pHeadNode(NULL), pLastNode(NULL) {};
~TPriorList(void) { Clear(); };
void AddEntry(C Entry, D Prior)
{
if (!pHeadNode)
{
pHeadNode = new node_s(Entry, Prior);
pLastNode = pHeadNode;
iNodeCount=1;
}
else
{
iNodeCount++;
node_s *pNew = new node_s(Entry, Prior);
node_s *pNode = pHeadNode;
node_s *pPrev = NULL;
while(pNode)
{
if (Prior < pNode->Priority)
{
if (!pPrev)
{
pNew->next = pNode;
pHeadNode = pNew;
}
else
{
pPrev->next = pNew;
pNew->next = pNode;
}
break;
}
pPrev = pNode;
pNode = pNode->next;
}
if (!pNode)
{
pLastNode = pNew;
if (pPrev)
pPrev->next = pNew;
}
}
}
C Pop(void)
{
if (!pHeadNode)
return static_cast<C>(NULL);
C Entry = pHeadNode->Entry;
DeleteNode(pHeadNode);
return Entry;
}
void Clear(void)
{
node_s *pTemp;
while(pHeadNode)
{
pTemp = pHeadNode;
pHeadNode = pHeadNode->next;
delete pTemp;
}
pHeadNode = pLastNode = NULL;
}
bool IsInList(C Entry, D Prior)
{
node_s *pNode = pHeadNode;
while(pNode)
{
if (pNode->Entry == Entry)
return true;
if (Prior < pNode->Priority)
return false;
pNode = pNode->next;
}
return false;
}
bool Empty(void) { return (pHeadNode == NULL); }
node_s *GetFirst(void) { return pHeadNode; }
private:
node_s *pHeadNode;
node_s *pLastNode;
int iNodeCount;
node_s *GetPrevNode(node_s *pNode)
{
node_s *pTemp = pHeadNode;
while(pTemp)
{
if (pTemp->next == pNode)
return pTemp;
pTemp = pTemp->next;
}
return NULL;
}
void DeleteNode(node_s *pNode)
{
if (!pNode)
return;
if (pHeadNode == pNode) // first node
{
if (pHeadNode == pLastNode)
pLastNode = pHeadNode->next;
pHeadNode = pHeadNode->next;
pNode->next = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
if (pNode == pLastNode) // last node
{
pNode = pLastNode;
pLastNode->next = NULL;
pNode->next = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
return;
}
// node is somewhere in the middle
node_s *pPrevNode = GetPrevNode(pNode);
if (!pPrevNode)
return;
// unlink pNode
pPrevNode->next = pNode->next;
pNode->next = NULL;
delete pNode;
pNode = NULL;
iNodeCount--;
}
};
// ==================================================================
// Code of TPriorList - End
// ==================================================================
// ==================================================================
// Code of TMutiChoice - Begin
// ==================================================================
template <class C> class TMultiChoice
{
struct SMultiChoice
{
int MinVal;
int MaxVal;
C Choice;
SMultiChoice(void) : MinVal(0), MaxVal(0){};
};
int TotalVal;
TLinkedList<SMultiChoice*> *pChoiceList;
public:
TMultiChoice(void) : TotalVal(0) // Constructor
{
pChoiceList = new TLinkedList<SMultiChoice*>;
};
~TMultiChoice(void) // Destructor
{
while(pChoiceList->Empty() == false)
{
SMultiChoice *pEntry = pChoiceList->Pop();
if (pEntry)
delete pEntry;
else
break;
}
delete pChoiceList;
pChoiceList = NULL;
}
void Insert(C Choice, short Percent = 50)
{
if (Percent == 0)
return;
SMultiChoice *pChoiceEntry = new SMultiChoice;
pChoiceEntry->MinVal = TotalVal;
pChoiceEntry->MaxVal = TotalVal + Percent;
pChoiceEntry->Choice = Choice;
pChoiceList->AddNode(pChoiceEntry);
TotalVal += Percent;
};
bool FindSelection(SMultiChoice *MS, int Choice)
{
if ((Choice >= MS->MinVal) && (Choice < MS->MaxVal))
{
return true;
}
return false;
}
void ClearChoices(void)
{
TotalVal = 0;
while(pChoiceList->Empty() == false)
delete pChoiceList->Pop();
}
bool GetSelection(C &Var)
{
int Choice = RandomLong(0, (TotalVal - 1));
typename TLinkedList<SMultiChoice*>::node_s *pNode = pChoiceList->GetFirst();
while(pNode)
{
if ((Choice >= pNode->Entry->MinVal) && (Choice < pNode->Entry->MaxVal))
{
Var = pNode->Entry->Choice;
return true;
}
pNode = pNode->next;
}
return false;
}
};
// ==================================================================
// Code of TMutiChoice - End
// ==================================================================
#endif
File diff suppressed because it is too large Load Diff
+191
View File
@@ -0,0 +1,191 @@
//
// C++ Implementation: bot_waypoint.h
//
// Description: Containts all defintions for bot waypoint code
//
//
// Author: Rick Helmus <[email protected]>
//
// Enable this for flood support
#define WP_FLOOD
#ifndef BOT_WAYPOINT_H
#define BOT_WAYPOINT_H
#define WAYPOINT_VERSION 2
#define EXP_WP_VERSION 1
#define REACHABLE_RANGE 15.0f
#define MAX_MAP_GRIDS 64
#define SECTOR_SIZE 18
#define W_FL_TELEPORT (1<<1) // used if waypoint is at a teleporter
#define W_FL_TELEPORTDEST (1<<2) // used if waypoint is at a teleporter destination
#define W_FL_FLOOD (1<<3) // used if this waypoint is made by flooding the map
#define W_FL_JUMP (1<<4) // if set, bot jumps when reached this waypoint
#define W_FL_TRIGGER (1<<5) // used if this waypoint is at a trigger
#define W_FL_INTAG (1<<6) // used if this waypoint is in a tagged cube
#define MAX_FLOODWPCONNECTDIST 2
#define MAX_FLOODWPDIST 4.0f
struct waypoint_header_s
{
char szFileType[10];
int iFileVersion;
int iFileFlags; // not currently used
int iWPCount;
char szMapName[32]; // name of map for these waypoints
};
// Old waypoint structure, used for waypoint version 1
struct waypoint_version_1_s
{
int iFlags;
vec v_origin;
TLinkedList<waypoint_version_1_s *> ConnectedWPs;
// A* stuff
float f, g;
waypoint_version_1_s *pParent;
// Construction
waypoint_version_1_s(void) : iFlags(0), f(0.0f), g(0.0f), pParent(NULL) { };
};
struct node_s
{
vec v_origin;
int iFlags;
short sTriggerNr;
short sYaw;
short sCost; // Base and static cost
TLinkedList<node_s *> ConnectedWPs;
TLinkedList<node_s *> ConnectedWPsWithMe;
TLinkedList<node_s *> FailedGoalList;
// Construction
node_s(void) : iFlags(0), sTriggerNr(0), sYaw(-1), sCost(10) { };
node_s(const vec &o, const int &f, const short t=0, const short y=-1) : v_origin(o),
iFlags(f),
sTriggerNr(t),
sYaw(y),
sCost(0) { };
};
struct waypoint_s
{
node_s *pNode;
short g[2];
waypoint_s *pParent[2];
bool bIsOpen[2], bIsClosed[2];
// Construction
waypoint_s(void) : pNode(NULL) { bIsOpen[0] = bIsOpen[1] = bIsClosed[0] =
bIsClosed[1] = false; pParent[0] = pParent[1] = NULL;
g[0] = g[1] = 0; };
};
class CWaypointClass
{
protected:
bool m_bDrawWaypoints;
bool m_bDrawWPPaths;
bool m_bAutoWaypoint;
bool m_bAutoPlacePaths;
bool m_bDrawWPText;
vec m_vLastCreatedWP;
float m_fPathDrawTime;
char m_szMapName[32];
#ifdef WP_FLOOD
bool m_bFlooding; // True if flooding map with waypoints
bool m_bFilteringNodes;
int m_iFloodStartTime;
int m_iCurFloodX, m_iCurFloodY;
int m_iFloodSize;
int m_iFilteredNodes;
#endif
friend class CBot;
public:
TLinkedList<node_s *> m_Waypoints[MAX_MAP_GRIDS][MAX_MAP_GRIDS];
int m_iWaypointCount; // number of waypoints currently in use
CWaypointClass(void);
virtual ~CWaypointClass(void) { Clear(); };
void Think(void);
void Init(void);
void Clear(void);
bool LoadWaypoints(void);
void SaveWaypoints(void);
bool LoadWPExpFile(void);
void SaveWPExpFile(void);
void SetMapName(char *map) { strcpy(m_szMapName, map); };
void SetWaypointsVisible(bool Visible) { m_bDrawWaypoints = Visible; };
void SetPathsVisible(bool Visible) { m_bDrawWPPaths = Visible; };
void SetAutoWaypoint(bool On) { m_bAutoWaypoint = On; };
void SetWPInfo(bool on) { m_bDrawWPText = on; };
void SetWPFlags(node_s *wp, int iFlags) { wp->iFlags |= iFlags; };
void UnsetWPFlags(node_s *wp, int iFlags) { wp->iFlags &= ~iFlags; };
void SetWPTriggerNr(node_s *wp, short sTriggerNr) { wp->sTriggerNr = sTriggerNr; };
void SetWPYaw(node_s *wp, short sYaw) { wp->sYaw = sYaw; };
bool WaypointsAreVisible(void) { return m_bDrawWaypoints; };
node_s *AddWaypoint(vec o, bool connectwp);
void DeleteWaypoint(dynent *d);
void AddPath(node_s *pWP1, node_s *pWP2);
void DeletePath(node_s *pWP);
void DeletePath(node_s *pWP1, node_s *pWP2);
void ManuallyCreatePath(dynent *d, int iCmd, bool TwoWay);
void ManuallyDeletePath(dynent *d, int iCmd, bool TwoWay);
bool WPIsReachable(vec from, vec to);
//node_s *GetNearestWaypoint(vec v_src, float flRange, bool CheckVisible = true, bool SkipFloodWPs = true);
node_s *GetNearestWaypoint(vec v_src, float flRange);
node_s *GetNearestWaypoint(dynent *d, float flRange) { return GetNearestWaypoint(d->o, flRange); };
node_s *GetNearestTriggerWaypoint(vec v_src, float flRange);
node_s *GetWaypointFromVec(const vec &v_src);
void DrawNearWaypoints();
void GetNodeIndexes(const vec &v_origin, short *i, short *j);
void CalcCost(node_s *pNode);
void ReCalcCosts(void);
#ifdef WP_FLOOD
// Flood functions
virtual void StartFlood(void);
void StopFlood(void);
void FloodThink(void);
bool CanPlaceNodeHere(const vec &from);
void ConnectFloodWP(node_s *pWP);
node_s *GetNearestFloodWP(vec v_origin, float flRange, node_s *pIgnore, bool SkipTags=false);
node_s *GetNearestFloodWP(dynent *d, float flRange) { return GetNearestFloodWP(d->o, flRange, NULL); };
node_s *GetNearestTriggerFloodWP(vec v_origin, float flRange);
#endif
};
class CACWaypointClass: public CWaypointClass
{
public:
#ifdef WP_FLOOD
// Flood functions
virtual void StartFlood(void);
#endif
};
class CCubeWaypointClass: public CWaypointClass
{
public:
void CreateWPsAtTeleporters(void);
void CreateWPsAtTriggers(void);
#ifdef WP_FLOOD
// Flood functions
virtual void StartFlood(void);
#endif
};
#endif // BOT_WAYPOINT_H
File diff suppressed because it is too large Load Diff