ICT290 / src / scene / AIController / EntityBrain / AttackSystem.cpp
AttackSystem.cpp
Raw
//
// 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;
        }
    }
}
 */