//
// C++ Implementation: ac_bot_ai
//
// Description: The AI part of the bot for ac is here(navigation, shooting etc)
//
//
// Author: <rickhelmus@gmail.com>
//
#include "cube.h"
#include "bot.h"
#ifdef AC_CUBE
weaponinfo_s WeaponInfoTable[NUMGUNS] =
{
// DD: desired distance
// iD: ideal distance
// FD: fire distance
// mA: min desired Ammo
// ---- Type : minDD, maxDD, iD, minFD, maxFD, mA
{ TYPE_MELEE, 0.0f, 5.0f, 4.0f, 0.0f, 5.0f, 1 }, // KNIFE
{ TYPE_NORMAL, 5.0f, 90.0f, 24.0f, 0.0f, 100.0f, 3 }, // PISTOL
{ TYPE_SHOTGUN, 4.0f, 16.0f, 6.0f, 0.0f, 32.0f, 2 }, // SHOTGUN
{ TYPE_AUTO, 5.0f, 64.0f, 20.0f, 0.0f, 65.0f, 5 }, // SUBGUN
{ TYPE_SNIPER, 25.0f, 110.0f, 70.0f, 0.0f, 175.0f, 3 }, // SNIPER
{ TYPE_AUTO, 10.0f, 92.0f, 45.0f, 0.0f, 135.0f, 5 }, // ASSAULT
{ TYPE_GRENADE, 50.0f, 85.0f, 60.0f, 15.0f, 999.0f, 1 }, // GRENADE
{ TYPE_NORMAL, 5.0f, 90.0f, 30.0f, 0.0f, 110.0f, 3 }, // AKIMBO
{ TYPE_SNIPER, 40.0f, 130.0f, 80.0f, 0.0f, 200.0f, 2 }, // BOLT
{ TYPE_AUTO, 30.0f, 80.0f, 0.0f, 0.0f, 150.0f, 3 }, // HEAL
{ TYPE_MELEE, 2.0f, 7.0f, 7.0f, 0.0f, 9.0f, 1 }, // SWORD
{ TYPE_ROCKET, 30.0f, 120.0f, 60.0f, 10.0f, 200.0f, 1 }, // RPG
{ TYPE_AUTO, 10.0f, 120.0f, 48.0f, 0.0f, 125.0f, 5 }, // ASSAULT2
{ TYPE_SNIPER, 30.0f, 120.0f, 75.0f, 0.0f, 200.0f, 2 }, // SNIPER2
{ TYPE_SNIPER, 25.0f, 110.0f, 70.0f, 0.0f, 175.0f, 3 }, // SNIPER3
{ TYPE_NORMAL, 5.0f, 90.0f, 24.0f, 0.0f, 100.0f, 3 }, // PISTOL2
{ TYPE_AUTO, 10.0f, 92.0f, 45.0f, 0.0f, 135.0f, 5 }, // ASSAULT_PRO
{ TYPE_SHOTGUN, 4.0f, 16.0f, 6.0f, 0.0f, 32.0f, 2 }, // SHOTGUN_PRO
{ TYPE_AUTO, 10.0f, 120.0f, 48.0f, 0.0f, 125.0f, 5 }, // ACR_PRO
};
// Code of CACBot - Start
bool CACBot::ChoosePreferredWeapon()
{
if(lastmillis < m_iChangeWeaponDelay) return false;
short bestWeapon = m_pMyEnt->gunselect;
short bestWeaponScore = SHRT_MIN;
short sWeaponScore;
float flDist = GetDistance(m_pMyEnt->enemy->o);
char bestWeap[NUMGUNS];
loopi(NUMGUNS) bestWeap[i] = 0;
// Choose a weapon
for(int i=0; i<NUMGUNS; i++)
{
sWeaponScore = primary_weap(i) ? 5 : 0; // Primary are usually better
if (!m_pMyEnt->mag[i] && WeaponInfoTable[i].eWeaponType != TYPE_MELEE) continue;
sWeaponScore += 3*m_pMyEnt->weapstats[i].kills/(m_pMyEnt->weapstats[i].deaths ? m_pMyEnt->weapstats[i].deaths : 0.5f);
sWeaponScore -= 2*m_pMyEnt->weapstats[i].deaths/(m_pMyEnt->weapstats[i].kills ? m_pMyEnt->weapstats[i].kills : 0.5f);
if((flDist >= WeaponInfoTable[i].flMinDesiredDistance) &&
(flDist <= WeaponInfoTable[i].flMaxDesiredDistance))
{ // In desired range for this weapon
sWeaponScore += 5; // Increase score much
if(i == GUN_PISTOL || WeaponInfoTable[i].eWeaponType == TYPE_MELEE)
{
if(WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SNIPER) sWeaponScore += 10; // At a close range, knife & pistol are strong with a sniper like primary weapon
if(WeaponInfoTable[m_pMyEnt->primary].eWeaponType == TYPE_SHOTGUN) sWeaponScore -= 2; // Penalize a bit knife and pistol on close range with shotgun
}
if(flDist < 50 && WeaponInfoTable[i].eWeaponType == TYPE_SNIPER) sWeaponScore -= 5;
}
else if (((flDist < WeaponInfoTable[i].flMinFireDistance) ||
(flDist > WeaponInfoTable[i].flMaxFireDistance)) && i != GUN_GRENADE && i != GUN_RPG)
continue; // Wrong distance for this weapon
if(i == GUN_GRENADE) sWeaponScore += 30; // Nades have high priority
// Score on the distance to the ideal distance
float flIdealDiff = fabs(flDist - WeaponInfoTable[i].flIdealDistance);
if(flIdealDiff < 1.0f) sWeaponScore += 10;
else if(flIdealDiff <= 5.0f) sWeaponScore += 4;
else if(flIdealDiff <= 7.5f) sWeaponScore += 2;
else if(flIdealDiff <= 10.0f) ++sWeaponScore;
// Now rate the weapon on available ammo in magazine...
if (WeaponInfoTable[i].sMinDesiredAmmo > 0)
{
// Calculate how much percent of the min desired ammo in mag the bot has
float flDesiredPercent = (float(m_pMyEnt->mag[i])/float(WeaponInfoTable[i].sMinDesiredAmmo))*100.0f;
if (flDesiredPercent >= 400.0f)
sWeaponScore += 10;
else if (flDesiredPercent >= 200.0f)
sWeaponScore += 8;
else if (flDesiredPercent >= 100.0f)
sWeaponScore += 3;
else if (flDesiredPercent >= 50.0f)
sWeaponScore -= 2;
else
sWeaponScore -= 5;
}
else sWeaponScore += 15; // Not needing ammo is an advantage...
if(sWeaponScore > bestWeaponScore)
{
bestWeaponScore = sWeaponScore;
bestWeapon = i;
loopi(bestWeapon) bestWeap[i] = 0;
bestWeap[bestWeapon] = 1;
}
else if(sWeaponScore == bestWeaponScore) bestWeap[i] = 1;
}
int tie = 0;
loopi(NUMGUNS) tie += bestWeap[i];
if (tie)
{
if (tie > 1)
{
int select = rnd(tie), i = 0;
while (select >= 0)
{
bestWeapon = i;
select -= bestWeap[i++];
}
}
}
else bestWeapon = GUN_KNIFE;
return SelectGun(bestWeapon);
};
void CACBot::Reload(int Gun)
{
};
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 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 = 0;
switch(e.type)
{
case I_CLIPS:
sMaxAmmo = ammostats[m_pMyEnt->secondary].max;
bInteresting = (m_pMyEnt->ammo[m_pMyEnt->secondary]<sMaxAmmo);
sAmmo = m_pMyEnt->ammo[m_pMyEnt->secondary];
break;
case I_AMMO:
sMaxAmmo = ammostats[m_pMyEnt->primary].max;
bInteresting = (m_pMyEnt->ammo[m_pMyEnt->primary]<sMaxAmmo);
sAmmo = m_pMyEnt->ammo[m_pMyEnt->primary];
break;
case I_GRENADE:
sMaxAmmo = ammostats[GUN_GRENADE].max;
bInteresting = (m_pMyEnt->mag[GUN_GRENADE]<sMaxAmmo);
sAmmo = -1;
break;
case I_HEALTH:
sMaxAmmo = MAXHEALTH;
bInteresting = (m_pMyEnt->health < sMaxAmmo);
sAmmo = m_pMyEnt->health;
break;
case I_HELMET:
case I_ARMOUR:
sMaxAmmo = MAXARMOUR;
bInteresting = (m_pMyEnt->armour < sMaxAmmo);
sAmmo = m_pMyEnt->armour;
break;
// bots are not allowed to pickup alimbo
/*
case I_AKIMBO:
bInteresting = !m_pMyEnt->akimbo;
sAmmo = -1;
break;
*/
};
if (!bInteresting)
continue; // Not an interesting item, skip
// Score on ammo and need
// Akimbo & nade
if (sAmmo == -1)
{
sScore += 75; // Bonus
}
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;
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;
}
entity *CACBot::SearchForFlags(bool bUseWPs, float flRange, float flMaxHeight)
{
/*
Flags are scored on the following:
- Distance
*/
float flDist;
entity *pNewTargetFlag = NULL;
waypoint_s *pWptNearBot = NULL, *pBestWpt = NULL;
short sScore, sHighestScore = 0;
vec vNewGoal = g_vecZero;
if ((WaypointClass.m_iWaypointCount >= 1) && bUseWPs)
pWptNearBot = GetNearestWaypoint(200.0f);
#ifdef WP_FLOOD
if (!pWptNearBot && bUseWPs)
pWptNearBot = GetNearestFloodWP(64.0f);
#endif
loopv(ents)
{
sScore = 0;
entity &e = ents[i];
if(!CanTakeFlag(e)) continue;
//vec o = g_vecZero;
vec o = vec(e.x, e.y, S(e.x, e.y)->floor + PLAYERHEIGHT + PLAYERABOVEEYE);
if(!m_secure(gamemode) && /*e.attr2 >= 0 &&*/ e.attr2 < 2)
{
flaginfo &f = flaginfos[e.attr2];
// flaginfo &of = flaginfos[team_opposite(i)];
if(f.state == CTFF_DROPPED)
{
o = f.pos;
o.z += PLAYERHEIGHT + PLAYERABOVEEYE;
}
}
if(OUTBORD((int)o.x, (int)o.y)) continue;
flDist = GetDistance(o);
if (flDist > flRange) continue;
// Score on distance
float ff = flDist;
if (ff > 100.0f) ff = 100.0f;
sScore += ((100 - short(ff)) / 2);
waypoint_s *pWptNearEnt = NULL;
// If this flag 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, 100.0f);
else
#endif
pWptNearEnt = GetNearestWaypoint(o, 200.f);
if (!pWptNearEnt) continue;
}
// Score on visibility
if (pWptNearEnt == NULL) // Ent is visible
sScore += 6;
else
sScore += 3;
if(sScore > sHighestScore)
{
if (pWptNearEnt) pBestWpt = pWptNearEnt;
else pBestWpt = NULL; // best flag doesn't need any waypoints
vNewGoal = o;
pNewTargetFlag = &e;
}
}
if (pNewTargetFlag)
{
// Need waypoints to reach it?
if (pBestWpt)
{
ResetWaypointVars();
SetCurrentWaypoint(pWptNearBot);
SetCurrentGoalWaypoint(pBestWpt);
}
m_vGoal = vNewGoal;
}
return pNewTargetFlag;
}
bool CACBot::CanTakeFlag(const entity &e)
{
if(!m_flags(gamemode)) return false;
if(m_secure(gamemode))
{
if(e.type != CTF_FLAG || e.attr2 < 2 || e.attr2 > 2 + TEAM_SPECT) return false;
return (e.attr2 - 2) != m_pMyEnt->team || e.attr4;
}
else
{
if(e.type != CTF_FLAG || (e.attr2 != 0 && e.attr2 != 1)) return false;
const int f_team = e.attr2;
flaginfo &f = flaginfos[f_team];
flaginfo &of = flaginfos[team_opposite(f_team)];
switch(f.state)
{
case CTFF_INBASE: // go to this base
// in CTF to capture the enemy flag and to return our flag
if (m_capture(gamemode))
return f_team != m_pMyEnt->team || (of.state == CTFF_STOLEN && of.actor == m_pMyEnt);
// in HTF to take our own flag
else if (m_hunt(gamemode))
return f_team == m_pMyEnt->team;
// in BTF to take own flag, and to score it on the enemy base
else if (m_bomber(gamemode))
return f_team == m_pMyEnt->team || (of.state == CTFF_STOLEN && of.actor == m_pMyEnt);
// in overload
else if (m_overload(gamemode))
return f_team != m_pMyEnt->team && (/*!IsVisible(f.pos) ||*/ f.pos.dist(m_pMyEnt->o) > 30);
// if KTF, always
return true;
case CTFF_STOLEN: // go to our stolen flag's base
// if rCTF and we have our flag
return m_return(gamemode, mutators) && f.actor == m_pMyEnt && f_team == m_pMyEnt->team;
case CTFF_IDLE: // not active
// never
return false;
case CTFF_DROPPED: // take every dropped flag, regardless of anything!
default:
// always
return true;
}
}
}
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::HeadToTargetFlag()
{
if(m_pTargetFlag)
{
const vec o = m_vGoal;
if(CanTakeFlag(*m_pTargetFlag) && (!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 flag");
return true;
}
}
else
bIsVisible = IsVisible(o);
if (bIsVisible)
{
if (m_pCurrentWaypoint || m_pCurrentGoalWaypoint)
{
condebug("flag 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, "Flag 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
Advertisement
153
pages
Ac bot ai.cpp
Advertisement