*** 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:
@@ -19,7 +19,6 @@ CLIENT_OBJS= \
|
||||
renderextras.o \
|
||||
rendergl.o \
|
||||
rendermd2.o \
|
||||
rendermd3_2.o \
|
||||
renderparticles.o \
|
||||
rendertext.o \
|
||||
rndmap.o \
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user