// // Created by Micha on 27/09/2021. // #include "AttackSystem.h" #include "../Vehicle.h" // need to include attack classes here #include "../Attacks/AttackType.h" #include "../Attacks/Weapon_TestType.h" //#include "../Attacks/Projectile.h" //------------------------- ctor ---------------------------------------------- //----------------------------------------------------------------------------- AttackSystem::AttackSystem(Vehicle* owner, float ReactionTime, float AimAccuracy, float AimPersistance) : m_Owner(owner), m_ReactionTime(ReactionTime), m_AimAccuracy(AimAccuracy), m_AimPersistance(AimPersistance) { initialize(); } //------------------------- dtor ---------------------------------------------- //----------------------------------------------------------------------------- AttackSystem::~AttackSystem() { for (unsigned int w = 0; w < m_AttackMap.size(); ++w) { delete m_AttackMap[w]; } } //------------------------------ Initialize ----------------------------------- // // initializes the weapons //----------------------------------------------------------------------------- void AttackSystem::initialize() { // delete any existing weapons AttackMap::iterator curW; for (curW = m_AttackMap.begin(); curW != m_AttackMap.end(); ++curW) { delete curW->second; } m_AttackMap.clear(); // set up the container // m_CurrentAttack = new Blaster(m_Owner); m_CurrentAttack = new Weapon_TestType(m_Owner); m_AttackMap[0] = m_CurrentAttack; // m_AttackMap[type_shotgun] = 0; // m_AttackMap[type_rail_gun] = 0; // m_AttackMap[type_rocket_launcher] = 0; } //-------------------------------- SelectWeapon ------------------------------- // //----------------------------------------------------------------------------- void AttackSystem::selectAttack() { /* // if a target is present use fuzzy logic to determine the most desirable // weapon. if (m_Owner->getTargetSys()->isTargetPresent()) { // calculate the distance to the target float DistToTarget = Vec2DDistance(m_Owner->getPosition(), m_Owner->getTargetSys() ->GetTarget() ->getPosition()); // for each weapon in the inventory calculate its desirability given the // current situation. The most desirable weapon is selected float BestSoFar = Minfloat; AttackMap::const_iterator curWeap; for (curWeap = m_AttackMap.begin(); curWeap != m_AttackMap.end(); ++curWeap) { // grab the desirability of this weapon (desirability is based upon // distance to target and ammo remaining) if (curWeap->second) { float score = curWeap->second->GetDesirability(DistToTarget); // if it is the most desirable so far select it if (score > BestSoFar) { BestSoFar = score; // place the weapon in the bot's hand. m_CurrentAttack = curWeap->second; } } } } else { m_CurrentAttack = m_AttackMap[type_blaster]; } */ } //-------------------- AddWeapon ------------------------------------------ // // this is called by a weapon affector and will add a weapon of the specified // type to the bot's inventory. // // if the bot already has a weapon of this type then only the ammo is added //----------------------------------------------------------------------------- void AttackSystem::addAttack(unsigned int) { /* // create an instance of this weapon AttackType* w = 0; switch (weapon_type) { case type_rail_gun: w = new RailGun(m_Owner); break; case type_shotgun: w = new ShotGun(m_Owner); break; case type_rocket_launcher: w = new RocketLauncher(m_Owner); break; } // end switch // if the bot already holds a weapon of this type, just add its ammo AttackType* present = getAttackFromInventory(weapon_type); if (present) { present->IncrementRounds(w->NumRoundsRemaining()); delete w; } // if not already holding, add to inventory else { m_AttackMap[weapon_type] = w; } */ } //------------------------- getAttackFromInventory //------------------------------- // // returns a pointer to any matching weapon. // // returns a null pointer if the weapon is not present //----------------------------------------------------------------------------- AttackType* AttackSystem::getAttackFromInventory(int weapon_type) { return m_AttackMap[weapon_type]; } //----------------------- ChangeWeapon ---------------------------------------- void AttackSystem::changeAttack(unsigned int type) { AttackType* w = getAttackFromInventory(type); if (w) m_CurrentAttack = w; } //--------------------------- TakeAimAndShoot --------------------------------- // // this method aims the bots current weapon at the target (if there is a // target) and, if aimed correctly, fires a round //----------------------------------------------------------------------------- void AttackSystem::takeAimAndShoot() const { // aim the weapon only if the current target is shootable or if it has only // very recently gone out of view (this latter condition is to ensure the // weapon is aimed at the target even if it temporarily dodges behind a wall // or other cover) if (m_Owner->getTargetSys()->isTargetShootable() || (m_Owner->getTargetSys()->getTimeTargetHasBeenOutOfView() < m_AimPersistance)) { // the position the weapon will be aimed at // glm::vec2 AimingPos = m_Owner->getTargetBot()->getPosition(); // if the current weapon is not an instant hit type gun the target // position must be adjusted to take into account the predicted movement // of the target // for the arcanist, just check anywas until decided if instahit // if (getCurrentWeapon()->getType() == type_rocket_launcher // || getCurrentWeapon()->getType() == type_blaster) { glm::vec2 AimingPos = predictFuturePositionOfTarget(); // if the weapon is aimed correctly, there is line of sight between // the bot and the aiming position and it has been in view for a // period longer than the bot's reaction time, shoot the weapon if (m_Owner->rotateFacingTowardPosition(AimingPos) && (m_Owner->getTargetSys()->getTimeTargetHasBeenVisible() > m_ReactionTime) && m_Owner->hasLOSto(AimingPos)) { addNoiseToAim(AimingPos); getCurrentWeapon()->shootAt(AimingPos); } // } // no need to predict movement, aim directly at target else { // if the weapon is aimed correctly and it has been in view for a // period longer than the bot's reaction time, shoot the weapon if (m_Owner->rotateFacingTowardPosition(AimingPos) && (m_Owner->getTargetSys()->getTimeTargetHasBeenVisible() > m_ReactionTime)) { addNoiseToAim(AimingPos); getCurrentWeapon()->shootAt(AimingPos); } } } // no target to shoot at so rotate facing to be parallel with the bot's // heading direction else { m_Owner->rotateFacingTowardPosition(m_Owner->getPosition() + m_Owner->getHeadingVec()); } } //---------------------------- addNoiseToAim ---------------------------------- // // adds a random deviation to the firing angle not greater than m_AimAccuracy // rads //----------------------------------------------------------------------------- void AttackSystem::addNoiseToAim(glm::vec2& AimingPos) const { glm::vec2 toPos = AimingPos - m_Owner->getPosition(); std::random_device rdevice{}; std::default_random_engine num{rdevice()}; std::uniform_real_distribution<float> random{-m_AimAccuracy, m_AimAccuracy}; glm::vec2 noisytoPos = toPos * glm::orientate2(random(num) * M_PI / 180); AimingPos = m_Owner->getPosition() + noisytoPos; } //-------------------------- PredictFuturePositionOfTarget -------------------- // // predicts where the target will be located in the time it takes for a // projectile to reach it. This uses a similar logic to the Pursuit steering // behavior. //----------------------------------------------------------------------------- glm::vec2 AttackSystem::predictFuturePositionOfTarget() const { float MaxSpeed = getCurrentWeapon()->getMaxProjectileSpeed(); // if the target is ahead and facing the agent shoot at its current pos glm::vec2 ToEnemy = m_Owner->getTargetBot()->getPosition() - m_Owner->getPosition(); // the lookahead time is proportional to the distance between the enemy // and the pursuer; and is inversely proportional to the sum of the // agent's velocities // float LookAheadTime = ToEnemy.Length() // / (MaxSpeed + // m_Owner->GetTargetBot()->MaxSpeed()); float LookAheadTime = glm::length( ToEnemy / (MaxSpeed + m_Owner->getTargetBot()->getMaxSpeed())); // return the predicted future position of the enemy return m_Owner->getTargetBot()->getPosition() + m_Owner->getTargetBot()->getVelocityVec() * LookAheadTime; } //------------------ GetAmmoRemainingForWeapon -------------------------------- // // returns the amount of ammo remaining for the specified weapon. Return zero // if the weapon is not present //----------------------------------------------------------------------------- int AttackSystem::getAmmoRemainingForWeapon(unsigned int weapon_type) { if (m_AttackMap[weapon_type]) { return m_AttackMap[weapon_type]->numRoundsRemaining(); } return 0; } //---------------------------- shootAt ---------------------------------------- // // shoots the current weapon at the given position //----------------------------------------------------------------------------- void AttackSystem::shootAt(glm::vec2 pos) const { getCurrentWeapon()->shootAt(pos); } /* //-------------------------- RenderCurrentWeapon ------------------------------ //----------------------------------------------------------------------------- void AttackSystem::RenderCurrentWeapon() const { getCurrentWeapon()->Render(); } void AttackSystem::RenderDesirabilities() const { glm::vec2 p = m_Owner->getPosition(); int num = 0; AttackMap::const_iterator curWeap; for (curWeap = m_AttackMap.begin(); curWeap != m_AttackMap.end(); ++curWeap) { if (curWeap->second) num++; } int offset = 15 * num; for (curWeap = m_AttackMap.begin(); curWeap != m_AttackMap.end(); ++curWeap) { if (curWeap->second) { float score = curWeap->second->GetLastDesirabilityScore(); std::string type = GetNameOfType(curWeap->second->getType()); gdi->TextAtPos(p.x + 10.0, p.y - offset, ttos(score) + " " + type); offset += 15; } } } */