btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * $Id: autogun.c,v 1.5 2005/08/03 21:40:54 av1-op Exp $
 *
 * Author: Markus Stenberg <fingon@iki.fi>
 *
 *  Copyright (c) 1996 Markus Stenberg
 *  Copyright (c) 1998-2002 Thomas Wouters
 *  Copyright (c) 2000-2002 Cord Awtry
 *       All rights reserved
 *
 * Created: Sun Nov 17 13:23:20 1996 fingon
 * Last modified: Sun Jun 14 16:29:44 1998 fingon
 *
 */

/*
 * AI's sensor and targeting system
 *
 * Gives the AI the ability to choose what to shoot as well as
 * what sensor to use when it has found a target
 */

/*! \todo { Add more code to the sensor system so the AI can be
 * more aware of its terrain } */

#include "mech.h"
#include "mech.events.h"
#include "autopilot.h"
#include "failures.h"
#include "mech.sensor.h"
#include "p.autogun.h"
#include "p.bsuit.h"
#include "p.glue.h"
#include "p.mech.sensor.h"
#include "p.mech.utils.h"
#include "p.mech.physical.h"
#include "p.mech.combat.h"
#include "p.mech.advanced.h"
#include "p.mech.bth.h"

#if 0
static char *my2string(const char * old)
{
    static char new[64];

    strncpy(new, old, 63);
    new[63] = '\0';
    return new;
}
#endif

/* Function to determine if there are any slites affecting the AI */
int SearchLightInRange(MECH *mech, MAP *map) {
    
    MECH *target;
    int i;

    /* Make sure theres a valid mech or map */
    if (!mech || !map)
        return 0;

    /* Loop through all the units on the map */
    for (i = 0; i < map->first_free; i++) {
        
        /* No units on the map */
        if (!(target = FindObjectsData(map->mechsOnMap[i])))
            continue;

        /* The unit doesn't have slite on */
        if (!(MechSpecials(target) & SLITE_TECH) || MechCritStatus(mech) & SLITE_DEST)
            continue;
        
        /* Is the mech close enough to be affected by the slite */
        if (FaMechRange(target, mech) < LITE_RANGE) {

            /* Returning true, but let's differentiate also between being in-arc. */
            if ((MechStatus(target) & SLITE_ON) && 
                    InWeaponArc(target, MechFX(mech), MechFY(mech)) & FORWARDARC) {

                /* Make sure its in los */
                if (!(map->LOSinfo[target->mapnumber][mech->mapnumber] & MECHLOSFLAG_BLOCK))

                    /* Slite on and, arced, and LoS to you */
                    return 3;
                else

                    /* Slite on, arced, but LoS blocked */
                    return 4;

            } else if (!MechStatus(target) & SLITE_ON && 
                    InWeaponArc(target, MechFX(mech), MechFY(mech)) & FORWARDARC) {
                
                if (!(map->LOSinfo[target->mapnumber][mech->mapnumber] & MECHLOSFLAG_BLOCK))
                    
                    /* Slite off, arced, and LoS to you */
                    return 5;

                else

                    /* Slite off, arced, and LoS blocked */
                    return 6;
            }

            /* Slite is in range of you, but apparently not arced on you. 
             * Return tells wether on or off */
            return (MechStatus(target) & SLITE_ON ? 1 : 2);
        }

    }
    return 0;
}

/* Function to determine if the AI should use V or L sensor */
int PrefVisSens(MECH *mech, MAP *map, int slite, MECH *target) {

    /* No map or mech so use default till we get put somewhere */
    if (!mech || !map)
        return SENSOR_VIS;

    /* Ok the AI is lit or using slite so use V */
    if (MechStatus(mech) & SLITE_ON || MechCritStatus(mech) & SLITE_LIT)
        return SENSOR_VIS;

    /* The target is lit so use V */
    if (target && MechCritStatus(target) & SLITE_LIT)
        return SENSOR_VIS;

    /* Ok if its night/dawn/dusk and theres no slite use L */
    if (map->maplight <= 1 && slite != 3 && slite != 5)
        return SENSOR_LA;

    /* Default sensor */
    return SENSOR_VIS;
}

/*
 * AI event to let the AI decide what sensors to use for a given
 * target and situation
 */
/*! \todo {Improve this so it knows more about the terrain} */
void auto_sensor_event(MUXEVENT *muxevent) {

    AUTO *autopilot = (AUTO *) muxevent->data;
    MECH *mech = (MECH *) autopilot->mymech;
    MECH *target = NULL;
    MAP *map;
    int flag = (int) muxevent->data2;
    char buf[16];
    int wanted_s[2];
    int rvis;
    int slite, prefvis;
    float trng;
    int set = 0;

    /* Make sure its a MECH Xcode Object and the AI is
     * an AUTOPILOT Xcode Object */
    if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum))
        return;

    /* Mech is dead so stop trying to shoot things */
    if (Destroyed(mech)) {
        DoStopGun(autopilot);
        return;
    }

    /* Mech isn't started */
    if (!Started(mech)) {
        Zombify(autopilot);
        return;
    }

    /* The mech is using user defined sensors so don't try
     * and change them */
    if (autopilot->flags & AUTOPILOT_LSENS)
        return;

    /* Get the map */
    if(!(map = getMap(mech->mapindex))) {

        /* Bad Map */
        Zombify(autopilot);
        return;
    }

    /* Get the target if there is one */
    if (MechTarget(mech) > 0)
        target = getMech(MechTarget(mech));

    /* Checks to see if there is slite, and what types of vis
     * and which visual sensor (V or L) to use */ 
    slite = (map->mapvis != 2 ? SearchLightInRange(mech, map) : 0);
    rvis = (map->maplight ? (map->mapvis) : (map->mapvis * (slite ? 1 : 3)));
    prefvis = PrefVisSens(mech, map, slite, target);

    /* Is there a target */
    if (target) {

        /* Range to target */
        trng = FaMechRange(mech, target);

        /* Actually not gonna bother with this */
        /* If the target is running hot and is close switch to IR */
        if (!set && HeatFactor(target) > 35 && (int) trng < 15) {
            //wanted_s[0] = SENSOR_IR;
            //wanted_s[1] = ((MechTons(target) >= 60) ? SENSOR_EM : prefvis);
            //set++;
        }

        /* If the target is BIG and close enough, use EM */
        if (!set && MechTons(target) >= 60 && (int) trng <= 20) {
            wanted_s[0] = SENSOR_EM;
            wanted_s[1] = SENSOR_IR;
            set++;
        }

        /* If the target is flying switch to Radar */
        if (!set && !Landed(target) && FlyingT(target)) {
            wanted_s[0] = SENSOR_RA;
            wanted_s[1] = prefvis;
            set++;
        }

        /* If the target is really close and the unit has BAP, use it */
        if (!set && (int) trng <= 4 && MechSpecials(mech) & BEAGLE_PROBE_TECH 
                && !(MechCritStatus(mech) & BEAGLE_DESTROYED)) {
            wanted_s[0] = SENSOR_BAP;
            wanted_s[1] = SENSOR_BAP;
            set++;
        }

        /* If the target is really close and the unit has Bloodhound, use it */
        if (!set && (int) trng <= 8 && MechSpecials2(mech) & BLOODHOUND_PROBE_TECH 
                && !(MechCritStatus(mech) & BLOODHOUND_DESTROYED)) {
            wanted_s[0] = SENSOR_BHAP;
            wanted_s[1] = SENSOR_BHAP;
            set++;
        }

        /* Didn't stop at any of the others so use selected visual sensors */
        if (!set) {
            wanted_s[0] = prefvis;
            wanted_s[1] = (rvis <= 15 ? SENSOR_EM : prefvis);
            set++;
        }
    
    }

    /* Ok no target and no sensors set yet so lets go for defaults */
    if (!set) {
        if (rvis <= 15) {
            /* Vis is less then or equal to 15 so go to E I for longer range */
            wanted_s[0] = SENSOR_EM;
            wanted_s[1] = SENSOR_IR;
        } else {
            /* Ok lets go with default visual sensors */
            wanted_s[0] = prefvis;
            wanted_s[1] = prefvis;
        }
    }

    /* Check to make sure valid sensors are selected and then set them */
    if (wanted_s[0] >= SENSOR_VIS && wanted_s[0] <= SENSOR_BHAP &&
            wanted_s[1] >= SENSOR_VIS && wanted_s[1] <= SENSOR_BHAP &&
            (MechSensor(mech)[0] != wanted_s[0] || MechSensor(mech)[1] != wanted_s[1])) {

        wanted_s[0] = BOUNDED(SENSOR_VIS, wanted_s[0], SENSOR_BHAP);
        wanted_s[1] = BOUNDED(SENSOR_VIS, wanted_s[1], SENSOR_BHAP);

        memset(buf, '\0', sizeof(char) * 16);
        sprintf(buf, "%c  %c", 
                sensors[wanted_s[0]].matchletter[0],
                sensors[wanted_s[1]].matchletter[0]);

        mech_sensor(autopilot->mynum, mech, buf);

    }

    if (!flag)
        AUTOEVENT(autopilot, EVENT_AUTO_SENSOR, auto_sensor_event, AUTO_SENSOR_TICK, 0);

}

/*
 * Create a weapon_list node
 */
weapon_node *auto_create_weapon_node(short weapon_number, short weapon_db_number,
        short section, short critical) {

    weapon_node *temp;

    temp = malloc(sizeof(weapon_node));

    if (temp == NULL) {
        return NULL;
    }

    memset(temp, 0, sizeof(weapon_node));

    temp->weapon_number = weapon_number;
    temp->weapon_db_number = weapon_db_number;
    temp->section = section;
    temp->critical = critical;

    return temp;

}

/*
 * Destroy weapon node
 */
void auto_destroy_weapon_node(weapon_node *victim) {

    free(victim);
    return;
}

/*
 * Create a target node for the target list
 */
target_node *auto_create_target_node(int target_score, dbref target_dbref) {

    target_node *temp;

    temp = malloc(sizeof(target_node));

    if (temp == NULL) {
        return NULL;
    }

    memset(temp, 0, sizeof(target_node));

    temp->target_score = target_score;
    temp->target_dbref = target_dbref;

    return temp;

}

/*
 * Destroy a target node
 */
void auto_destroy_target_node(target_node *victim) {

    free(victim);
    return;

}

/*
 * Destroy autopilot's weaplist
 */
void auto_destroy_weaplist(AUTO *autopilot) {

    weapon_node *temp_weapon_node;

    /* Check to make sure there is a weapon list */ 
    if (!(autopilot->weaplist))
        return;

    /* There is a weapon list - lets kill it */
    if (dllist_size(autopilot->weaplist) > 0) {

        while (dllist_size(autopilot->weaplist)) {
            temp_weapon_node = (weapon_node *) dllist_remove_node_at_pos(autopilot->weaplist, 1);
            auto_destroy_weapon_node(temp_weapon_node);
        }

    }

    /* Finally destroying the list */
    dllist_destroy_list(autopilot->weaplist);
    autopilot->weaplist = NULL;

}

/*
 * Callback function to destroy target list
 */
static int auto_targets_callback(void *key, void *data, int depth, void *arg) {

    target_node *temp;

    temp = (target_node *) data;
    auto_destroy_target_node(temp);

    return 1;

}

/*
 * rbtree generic compare function
 */
static int auto_generic_compare(void *a, void *b, void *token) {

    int *one, *two;

    one = (int *) a;
    two = (int *) b;

    return (*one - *two);
}

/*
 * How we score a given weapon based on range, heat and damage
 */
int auto_calc_weapon_score(int weapon_db_number, int range) {

    int weapon_score;
    int range_score;
    int damage_score;
    int heat_score;
    int minrange_score;

    int weapon_damage;
    int weapon_heat;

    int i;

    /* Simple Calc */

    /* For the modifiers I assumed best was approx 550
     *
     * So for SR, chance of hitting is roughly 92% which is 506 rounded to 500 
     * For MR, its 72%, so 390 and LR its 41% its 225 */

    /* Assume default values */
    weapon_score = 0;
    weapon_damage = 0;
    range_score = 500;      /* Since by default we assume its SR */
    damage_score = 0;
    heat_score = 0;
    minrange_score = 0;

    /* Don't bother trying to set a value if its outside its range */
    if (range >= MechWeapons[weapon_db_number].longrange) {
        return weapon_score; 
    }

    /* Are we at LR ? */
    if (range >= MechWeapons[weapon_db_number].medrange) {
        range_score = 215;
    }

    /* Are we at MR ? */
    if (range >= MechWeapons[weapon_db_number].shortrange &&
            range < MechWeapons[weapon_db_number].medrange) {
        range_score = 390;
    }

    /* Check min range */
    /* Use a polynomial equation here because at 2 under min its equiv to MR, at
     * 4 under its equiv to LR, so we want it to balance out the range score */
    /* score = -12.5(min - range)^2 - 25 * (min - range) */
    if (range < MechWeapons[weapon_db_number].min) {
        minrange_score = -12.5 * (float) ((MechWeapons[weapon_db_number].min - range) *
                (MechWeapons[weapon_db_number].min - range)) - 25.0 *
            (float) (MechWeapons[weapon_db_number].min - range);
    }

    /* Get the damage for the weapon */
    if (IsMissile(weapon_db_number)) {

        /* Its a missile weapon so lookup in the Missile table get the max
         * number of missiles it can hit with, and multiply by the damage
         * per missile */
        /* To make it more fair going to use the avg # of missile hits
         * which is when they would roll a 7, which becomes slot #
         * 5 */
        for (i = 0; MissileHitTable[i].key != -1; i++) {
            if (MissileHitTable[i].key == weapon_db_number) {
                weapon_damage = MissileHitTable[i].num_missiles[5] *
                    MechWeapons[weapon_db_number].damage;
                break;
            }
        }

    } else {
        weapon_damage = MechWeapons[weapon_db_number].damage;
    }

    /* Get the damage score */
    /* Straight linear plot */
    damage_score = 50 * weapon_damage;

    /* Get the heat */
    weapon_heat = MechWeapons[weapon_db_number].heat;

    /* Get the heat score */
    /* Straight inverse linear plot - more heat bad... */
    heat_score = -25 * weapon_heat + 250;

    /* Final calc */
    weapon_score = range_score + damage_score + heat_score + minrange_score;

    return weapon_score;

}

/*
 * AI profiling event
 *
 * Every so often updates the profile for the AI's weapons
 */
void auto_update_profile_event(MUXEVENT *muxevent) {

    AUTO *autopilot = (AUTO *) muxevent->data;
    MECH *mech = (MECH *) autopilot->mymech;

    weapon_node *temp_weapon_node;
    dllist_node *temp_dllist_node;

    int section;
    int weapon_count_section;
    unsigned char weaparray[MAX_WEAPS_SECTION];
    unsigned char weapdata[MAX_WEAPS_SECTION];
    int critical[MAX_WEAPS_SECTION];

    int range;

    int weapon_count;
    int weapon_number;

    /* Basic checks */
    if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum))
        return;

    /* Ok our mech is dead we're done */
    if (Destroyed(mech)) {
        return;
    }

    /* Log Message */
    print_autogun_log(autopilot, "Profiling Unit #%d", mech->mynum);

    /* Destroy the arrays first, don't worry about the weap
     * structures because we can clear them with the ddlist
     * weaplist */

    /* Zero the array of rbtree stuff */
    for (range = 0; range < AUTO_PROFILE_MAX_SIZE; range++) {

        if (autopilot->profile[range]) {
            
            /* Destroy rbtree */
            rb_destroy(autopilot->profile[range]);
        }
        autopilot->profile[range] = NULL;
    }

    /* Check to see if the weaplist exists */
    if (autopilot->weaplist != NULL) {
        
        /* Destroy the list */
        auto_destroy_weaplist(autopilot);
    }

    /* List doesn't exist so lets build it */
    autopilot->weaplist = dllist_create_list();

    /* Reset the AI's max range value for its mech */
    autopilot->mech_max_range = 0;

    /* Set our counter */
    weapon_count = -1;

    /* Now loop through the weapons building a list */
    for (section = 0; section < NUM_SECTIONS; section++) {
        
        /* Find all the weapons for a given section */
        weapon_count_section = FindWeapons(mech, section, 
                weaparray, weapdata, critical);

        /* No weapons here */
        if (weapon_count_section <= 0)
            continue;

        /* loop through the possible weapons */
        for (weapon_number = 0; weapon_number < weapon_count_section; weapon_number++) {

            /* Count it even if its not a valid weapon like AMS */
            /* This is so when we go to fire the weapon we know
             * which one to send in the command */
            weapon_count++;

            if (IsAMS(weaparray[weapon_number]))
                continue;

            /* Does it work? */
            if (WeaponIsNonfunctional(mech, section, critical[weapon_number], 
                    GetWeaponCrits(mech, Weapon2I(weaparray[weapon_number]))) > 0)
                continue;

            /* Ok made it this far, lets add it to our list */
            temp_weapon_node = auto_create_weapon_node(weapon_count,
                    weaparray[weapon_number], section, critical[weapon_number]);
            
            temp_dllist_node = dllist_create_node(temp_weapon_node);
            dllist_insert_end(autopilot->weaplist, temp_dllist_node);

            /* Check the max range */
            if (autopilot->mech_max_range < 
                    MechWeapons[weaparray[weapon_number]].longrange) {
                autopilot->mech_max_range = MechWeapons[weaparray[weapon_number]].longrange;
            }

        }

    }

    /* Now build the profile array, basicly loop through 
     * all the current avail weapons, get its max range,
     * then loop through ranges and for each range add it
     * to profile */

    /* Our counter */
    weapon_number = 1;

    while (weapon_number <= dllist_size(autopilot->weaplist)) {

        /* Get the weapon */
        temp_weapon_node = (weapon_node *) dllist_get_node(autopilot->weaplist, weapon_number);

        for (range = 0; range < 
                MechWeapons[temp_weapon_node->weapon_db_number].longrange; range++) {

            /* Out side the the range of AI's profile system */
            if (range >= AUTO_PROFILE_MAX_SIZE) {
                break;
            }

            /* Score the weapon */
            temp_weapon_node->range_scores[range] = 
                auto_calc_weapon_score(temp_weapon_node->weapon_db_number, range);

            /* If rbtree for this range doesn't exist, create it */
            if (autopilot->profile[range] == NULL) {
                autopilot->profile[range] = 
                    rb_init(&auto_generic_compare, NULL);
            }

            /* Check to see if the score exists in the tree
             * if so alter it slightly so we don't have
             * overlaping keys */
            while (1) {

                if (rb_exists(autopilot->profile[range], 
                            &temp_weapon_node->range_scores[range])) {
                    temp_weapon_node->range_scores[range]++;
                } else {
                    break;
                }

            }

            /* Add it to tree */
            rb_insert(autopilot->profile[range], &temp_weapon_node->range_scores[range],
                    temp_weapon_node);

        }
        
        /* Increment */
        weapon_number++;

    }

    /* Is this loop running somewhere else */
    if (muxevent_count_type_data(EVENT_AUTO_PROFILE, (void *) autopilot)) {
        muxevent_remove_type_data(EVENT_AUTO_PROFILE, (void *) autopilot);
    }

    /* Run this loop some time later incase we suffer damage */
    AUTOEVENT(autopilot, EVENT_AUTO_PROFILE, 
            auto_update_profile_event, AUTO_PROFILE_TICK, 0);

    /* Log Message */
    print_autogun_log(autopilot, "Finished Profiling");

}

/*
 * Function to calculate a score based on a target and
 * its range to the AI
 */
int auto_calc_target_score(AUTO *autopilot, MECH *target) {

    MECH *mech = (MECH *) autopilot->mymech;
    
    int target_score;
    float range;
    float target_speed;
    int target_bv;

    int total_armor_current;
    int total_armor_original;
    int total_internal_current;
    int total_internal_original;

    int section;

    int damage_score;
    int bv_score;
    int speed_score;
    int range_score;
    int status_score;

    /* Default Values */
    target_score = 0;

    total_armor_current = 0;
    total_armor_original = 0;
    total_internal_current = 0;
    total_internal_original = 0;

    damage_score = 0;
    bv_score = 0;
    speed_score = 0;
    range_score = 0;
    status_score = 0;

    /* Here is the meat of the function, basicly I gave each
     * part a maximum score, then fit a linear plot from the
     * max to a min value and score.  Then I just summed
     * all the pieces together, very linear but should
     * give us a good starting point */

    /* Is the target dead? */
    if (Destroyed(target))
        return target_score;

    /* If target is combat safe don't even try to shoot it */
    if (MechStatus(target) & COMBAT_SAFE)
        return target_score;

    /* Compare Teams - for now we won't try to shoot a guy on our team */
    if (MechTeam(target) == MechTeam(mech))
        return target_score;

    /* Are we in los of the target - not sure really what to do about this
     * one, since we want the AI to be smart and all, for now, lets have
     * it be all seeing */
   
    /* Range to target */
    range = FindHexRange(MechFX(mech), MechFY(mech), MechFX(target),
            MechFY(target));

    /* Our we outside the range of the AI's System */
    if ((range >= (float) AUTO_GUN_MAX_RANGE)) {
        return target_score;
    }

    /* Range score calc */
    /* Min range is 0, max range is 30, so score goes from 300 to 0 */
    range_score = - 10 * range + 300;

    /* Get the Speed of the target */
    target_speed = MechSpeed(target);

    /* Speed score calc */
    /* Min speed is 0, max is 150 (can go higher tho), and score goes from
     * 300 to 0 (can go negative if the target is faster then 150) */
    speed_score = - 2 * target_speed + 300;

    /* Get the BV of the target */
    target_bv = MechBV(target);

    /* BV score calc */
    /* Min bv is 0, max is around 2000 (can go higher), and score goes from
     * 0 to 100 (can go higher but we don't care much about bv) */
    bv_score = 0.05 * target_bv;

    /* Get the damage of the target by cycling through all the sections
     * and adding up the current and original values */
    for (section = 0; section < NUM_SECTIONS; section++) {

        /* Total the current armor and original armor */
        total_armor_current += GetSectArmor(target, section) +
            GetSectRArmor(target, section);
        total_armor_original += GetSectOArmor(target, section) +
            GetSectORArmor(target, section);

        /* Total the current internal and original internal */
        total_internal_current += GetSectInt(target, section);
        total_internal_original += GetSectOInt(target, section);

    }

    /* Ok like above, we set a min and max, for armor was 100% to 0%
     * and scored from 0 to 300.  For internal was 100% to 0% and
     * scored from 0 to 200. But we have to take care not to divide
     * by zero. */

    /* Check the totals before we divide so no Divide by zeros */
    if (total_internal_original == 0 &&
            total_armor_original == 0) {

        /* Both values are zero, not going to try and shoot it */
        return target_score;

    } else if (total_internal_original == 0) {

        /* Just use armor part of the calc */
        damage_score = - 3 * ((float) total_armor_current / 
                (float) total_armor_original) + 300;

    } else if (total_armor_original == 0) {
    
        /* Just use internal part of the calc */
        damage_score = - 2 * ((float) total_internal_current /
                (float) total_internal_original) + 200;

    } else {

        /* Use the whole thing */
        damage_score = - 3 * ((float) total_armor_current /
                (float) total_armor_original) + 300
                - 2 * ((float) total_internal_current /
                (float) total_internal_original) + 200;

    }

    /* Get the 'state' ie: shutdown, prone whatever */
    if (!Started(target))
        status_score += 100;

    if (Uncon(target))
        status_score += 100;

    /* Add the individual scores and return the value */
    target_score = range_score + speed_score + bv_score +
        damage_score + status_score;

    return target_score;

}

/*
 * The main targeting/firing event for the AI
 *
 * Loops through all the cons around it, scoring them and deciding
 * what to shoot and what weapons to shoot at it
 */
void auto_gun_event(MUXEVENT *muxevent) {

    AUTO *autopilot = (AUTO *) muxevent->data;          /* The autopilot */
    MECH *mech = (MECH *) autopilot->mymech;            /* Its Mech */
    MAP *map;                                           /* The current Map */
    MECH *target;                                       /* Our current target */
    MECH *physical_target;                              /* Our physical target */
    rbtree *targets;                                    /* all the targets we're looking at */
    target_node *temp_target_node;                      /* temp target node struct */
    weapon_node *temp_weapon_node;                      /* temp weapon node struct */

    char buffer[LBUF_SIZE];                             /* General use buffer */

    int target_score;                                   /* variable to store temp score */
    int threshold_score;                                /* The score to beat to switch targets */

    /* Stuff for Physical attacks */
    int elevation_diff;                                 /* Whats the elevation difference between
                                                           the target and the mech */
    int what_arc;                                       /* What arc is the target in */
    int new_arc;                                        /* What arc now that we've twisted
                                                           our torso */
    int relative_bearing;                               /* Int to figure out which part of
                                                           the rear arc hes in */
    int is_section_destroyed[4];                        /* Array of the different possible
                                                           sections that could be destroyed */
    int section_hasbusyweap[4];                         /* Array to show if a section has a
                                                           cycling weapon in it */
    int rleg_bth, lleg_bth;                             /* To help us decide which leg to kick
                                                           with */
    int is_rarm_ready, is_larm_ready;                   /* To help us decide which arms to
                                                           punch with */

    /* Stuff for Weapon Attacks */
    int accumulate_heat;                                /* How much heat we're building up */
    int i, j;

    float range;                                        /* General variable for range */
    float maxspeed;                                     /* So we know how fast our guy is going */

    /* For use with chase targ */
    short x, y;
    float fx, fy;
    char do_chasetarget;                                /* Flag should we do chasetarget
                                                           based on certain conditions */

    /* Basic checks */
    if (!mech || !autopilot)
        return;

    if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum))
        return;

    /* Ok our mech is dead we're done */
    if (Destroyed(mech)) {
        DoStopGun(autopilot);
        return;
    }

    /*! \todo {Need to change this incase the AI shuts down while fighting} */
    if (!Started(mech)) {
        Zombify(autopilot);
        return;
    }

    /* Not on map - so lets calm down */
    if (!(map = getMap(mech->mapindex))) {
        Zombify(autopilot);
        return;
    }

    /* Log it */
    print_autogun_log(autopilot, "Autogun Event Started");

    /* Check the profile - if it doesn't exist, calc it, then re-run autogun */
    if (autopilot->weaplist == NULL) {
        AUTOEVENT(autopilot, EVENT_AUTO_PROFILE, auto_update_profile_event, 1, 0);
    
        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);
        
        /* Log It */
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;
    }

    /* OODing so don't shoot any guns */
    if (OODing(mech)) {
        
        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);

        /* Log It */
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;
    }

    /* First check to make sure we have a valid current target */
    if (autopilot->target > -1) {

        if (!(target = getMech(autopilot->target))) {

            /* ok its not a valid target reset */
            autopilot->target = -1;
            autopilot->target_score = 0;

        } else if (Destroyed(target) || (target->mapindex != mech->mapindex)) {

            /* Target is either dead or not on the map anymore */
            autopilot->target = -1;
            autopilot->target_score = 0;

        } else {

            /* Will keep on an assigned target even if its to far
             * away */

            /* Get range from mech to current target */
            range = FindHexRange(MechFX(mech), MechFY(mech), 
                    MechFX(target), MechFY(target));

            if ((range >= (float) AUTO_GUN_MAX_RANGE) &&
                    !AssignedTarget(autopilot)) {

                /* Target is to far away */
                autopilot->target = -1;
                autopilot->target_score = 0;

            }

        }

    }

    /* Were we given a target and its no longer there? */
    if (AssignedTarget(autopilot) && autopilot->target == -1) {

        /* Ok we had an assigned target but its gone now */
        UnassignTarget(autopilot);

        /*! \todo {Possibly add a radio message saying target destroyed} */
    }

    /* Do we need to look for a new target */
    if (autopilot->target == -1 || (autopilot->target_update_tick >= 30 &&
            !AssignedTarget(autopilot))) {

        /* Ok looking for a new target */

        /* Log It */
        print_autogun_log(autopilot, "Autogun - Looking for new target");

        /* Reset the update ticker */
        autopilot->target_update_tick = 0;

        /* Setup the rbtree */
        targets = rb_init(&auto_generic_compare, NULL);

        /* Cycle through possible targets and pick something to shoot */
        for (i = 0; i < map->first_free; i++) {

            /* Make sure its on the right map */
            if (i != mech->mapnumber && (j = map->mechsOnMap[i]) > 0) {

                /* Is it a valid unit ? */
                if (!(target = getMech(j)))
                    continue;

                /* Score the target */
                target_score = auto_calc_target_score(autopilot, target);

                /* Log It */
                print_autogun_log(autopilot, "Autogun - Possible target #%d with score %d",
                        target->mynum, target_score);

                /* If target has a score add it to rbtree */
                if (target_score > 0) {

                    /* Create target node and fill with proper values */
                    temp_target_node = auto_create_target_node(target_score,
                            target->mynum);

                    /*! \todo {should add check incase it returns a NULL struct} */

                    /* Add it to list but first make sure it doesn't overlap
                     * with a current score */
                    while (1) {

                        if (rb_exists(targets, &temp_target_node->target_score)) {
                            temp_target_node->target_score++;
                        } else {
                            break;
                        }

                    }

                    /* Add it */
                    rb_insert(targets, &temp_target_node->target_score,
                            temp_target_node);

                }

                /* Check to see if its our current target */
                if (autopilot->target == target->mynum) {

                    /* Save the new score */
                    autopilot->target_score = target_score;

                }

            }

        } /* End of for loop */

        /* Check to see if we couldn't find ANY targets within range,
         * if not, cycle autogun and set the update tick to 20, so we
         * check again in 10 seconds */
        if (!(rb_size(targets) > 0)) {

            /* Have the AI look for a new target 10 seconds from now */
            /*! \todo {Possibly change this since this gives an attacker who
             * appears somehow very quickly 10 seconds to hose the AI} */
            autopilot->target = -1;
            autopilot->target_score = 0;
            autopilot->target_update_tick = 0;
            
            /* Don't need the target list any more so lets destroy it */
            rb_walk(targets, WALK_INORDER, &auto_targets_callback, NULL);
            rb_destroy(targets);

            /* Make sure multiple instances of autogun aren't running */
            if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
                muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
            }
            AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, 10, 0);

            /* Log It */
            print_autogun_log(autopilot, "Autogun in idle mode");
            print_autogun_log(autopilot, "Autogun Event Finished");
            return;
        }

        /* Now if we have a current target, compare it to best target from
         * the new list.  If better then threshold, lock new target, else
         * stay on target */

        /* Best target */
        temp_target_node = (target_node *) rb_search(targets, SEARCH_LAST, NULL);

        /* Log It */
        print_autogun_log(autopilot, "Autogun - Best target #%d with score %d",
                temp_target_node->target_dbref, temp_target_node->target_score);
        print_autogun_log(autopilot, "Autogun - Current target #%d with score %d",
                autopilot->target, autopilot->target_score);

        if (autopilot->target > -1 && autopilot->target_score > 0) {

            /* Check to see if its our current target */
            if (autopilot->target != temp_target_node->target_dbref) {

                /* Calc the threshold score to beat */
                threshold_score = ((100.0 + (float) autopilot->target_threshold) / 100.0) *
                    autopilot->target_score;

                if (temp_target_node->target_score > threshold_score) {

                    /* Change targets */
                    autopilot->target = temp_target_node->target_dbref;
                    autopilot->target_score = temp_target_node->target_score;

                    print_autogun_log(autopilot, "Switching Target to #%d",
                            autopilot->target);

                }

                /* Else: Don't switch targets */

            }

            /* Else: Don't need to swtich targets */

        } else {

            /* Don't have a good current target so lock this one */
            autopilot->target = temp_target_node->target_dbref;
            autopilot->target_score = temp_target_node->target_score;

        } /* End of choosing new target */

        /* Don't need the target list any more so lets destroy it */
        rb_walk(targets, WALK_INORDER, &auto_targets_callback, NULL);
        rb_destroy(targets);

    } else {

        /* Ok didn't need to look for a new target so update the ticker */
        autopilot->target_update_tick++;

    }

    /* End of picking a new target */

    /* Log It */
    print_autogun_log(autopilot, "Autogun - Current target #%d with score %d",
            autopilot->target, autopilot->target_score);

    /* Setup the current target */
    if (!(target = getMech(autopilot->target))) {

        /* There were no valid targets so
         * rerun autogun */

        /* Reset the AI */
        autopilot->target = -1;
        autopilot->target_score = 0;

        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);

        /* Log It */
        print_autogun_log(autopilot, "Autogun - No valid current targets");
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;

    }

    /* Check to see if we need to (re)lock our target */
    if (MechTarget(mech) != autopilot->target) {

        /* Lock Him */
        snprintf(buffer, LBUF_SIZE, "%c%c", MechID(target)[0], MechID(target)[1]);
        mech_settarget(autopilot->mynum, mech, buffer);

        /* Log It */
        print_autogun_log(autopilot, "Autogun - Locking target #%d",
                autopilot->target);

    }

    /* Update autosensor */

    /* Now lets get physical */
    
    /* Log It */
    print_autogun_log(autopilot, "Autogun - Start Physical Attack Stage");

    /* Get range from mech to current target */
    range = FindHexRange(MechFX(mech), MechFY(mech), MechFX(target), MechFY(target));

    /* First check our range to our target, if within range attack it, else
     * check to see if its outside our range threshold and if so pick a target
     * close and attack that */

    /*! \todo {Might need to add in here something incase the target is a bsuit} */
    if (range < 1.0) {

        /* We're beating on our main target */
        physical_target = target;

    } else if (range > AUTO_GUN_PHYSICAL_RANGE_MIN) {

        /* Try and find a target */

        physical_target = NULL;

        /* Cycle through possible targets and pick something to beat on */
        for (i = 0; i < map->first_free; i++) {

            /* Make sure its on the right map */
            if (i != mech->mapnumber && (j = map->mechsOnMap[i]) > 0) {

                /* Is it a valid unit ? */
                if (!(target = getMech(j)))
                    continue;

                if (Destroyed(target))
                    continue;

                if (MechStatus(target) & COMBAT_SAFE)
                    continue;

                if (MechTeam(target) == MechTeam(mech))
                    continue;

                /* Check its range */
                range = FindHexRange(MechFX(mech), MechFY(mech),
                        MechFX(target), MechFY(target));

                /* Just go for first one , can always add scoring later */
                if (range < 1.0) {
                    physical_target = target;
                    break;
                }

            }

        }

    } else {
       
        /* Our target is close so dont try and physically attack anyone */
        physical_target = NULL;

    }

    /* Now nail it with a physical attack but only if we see it */
    if (physical_target && 
            (MechToMech_LOSFlag(map, mech, physical_target) & MECHLOSFLAG_SEEN)) {

        /* Log It */
        print_autogun_log(autopilot, "Autogun - Attempting physical attack against"
                " target #%d", physical_target->mynum);

        /* Calculate elevation difference */
        elevation_diff = MechZ(mech) - MechZ(target);

        /* Are we a biped Mech */
        if ((MechType(mech) == CLASS_MECH) && 
                (MechMove(mech) == MOVE_BIPED)) {

            /* Center the torso */
            MechStatus(mech) &= ~(TORSO_RIGHT | TORSO_LEFT);

            if (MechSpecials(mech) & FLIPABLE_ARMS) {

                /* Center the arms if need be */
                MechStatus(mech) &= ~(FLIPPED_ARMS);
            }

            /* Find direction of bad guy */
            what_arc = InWeaponArc(mech, MechFX(physical_target), MechFY(physical_target));

            /* Rotate if we need to */
            if (what_arc & LSIDEARC) {

                /* Rotate Left */
                MechStatus(mech) |= TORSO_LEFT;

            } else if (what_arc & RSIDEARC) {

                /* Rotate Right */
                MechStatus(mech) |= TORSO_RIGHT;

            } else if (what_arc & REARARC) {

                /* Find out if it would be better to
                 * rotate left or right */
                relative_bearing = MechFacing(mech) - FindBearing(MechFX(mech), 
                        MechFY(mech), MechFX(physical_target), MechFY(physical_target));

                if (relative_bearing > 120 && relative_bearing < 180) {

                    /* Rotate Right */
                    MechStatus(mech) |= TORSO_RIGHT;

                } else if (relative_bearing > 180 && relative_bearing < 240) {

                    /* Rotate Left */
                    MechStatus(mech) |= TORSO_LEFT;

                }

                /* ELSE: Hes directly behind us so we can't do anything */

            }

            /* Calculate the new arc */
            new_arc = InWeaponArc(mech, MechFX(physical_target), MechFY(physical_target));

            /* Check to see what sections are destroyed */
            memset(is_section_destroyed, 0, sizeof(is_section_destroyed));
            is_section_destroyed[0] = SectIsDestroyed(mech, RARM);
            is_section_destroyed[1] = SectIsDestroyed(mech, LARM);
            is_section_destroyed[2] = SectIsDestroyed(mech, RLEG);
            is_section_destroyed[3] = SectIsDestroyed(mech, LLEG);

            /* Check to see if the sections have a busy weapon */
            memset(section_hasbusyweap, 0, sizeof(section_hasbusyweap));
            section_hasbusyweap[0] = SectHasBusyWeap(mech, RARM);
            section_hasbusyweap[1] = SectHasBusyWeap(mech, LARM);
            section_hasbusyweap[2] = SectHasBusyWeap(mech, RLEG);
            section_hasbusyweap[3] = SectHasBusyWeap(mech, LLEG);

            /* Try weapon physical attacks */

            /* Right Arm */
            if (!is_section_destroyed[0] && !section_hasbusyweap[0] &&
                    !AnyLimbsRecycling(mech) && ((new_arc & FORWARDARC) ||
                    (new_arc & RSIDEARC)) &&
                    (elevation_diff == 0 || elevation_diff == -1)) {

                snprintf(buffer, LBUF_SIZE, "r %c%c",
                        MechID(target)[0], MechID(target)[1]);

                if (have_axe(mech, RARM))
                    mech_axe(autopilot->mynum, mech, buffer);
                /* else if (have_mace(mech, RARM)) */
                /*! \todo {Add in mace code here} */
                else if (have_sword(mech, RARM))
                    mech_sword(autopilot->mynum, mech, buffer);

            }

            /* Left Arm */
            if (!is_section_destroyed[1] && !section_hasbusyweap[1] &&
                    !AnyLimbsRecycling(mech) && ((new_arc & FORWARDARC) ||
                    (new_arc & LSIDEARC)) &&
                    (elevation_diff == 0 || elevation_diff == -1)) {

                snprintf(buffer, LBUF_SIZE, "l %c%c",
                        MechID(target)[0], MechID(target)[1]);

                if (have_axe(mech, LARM))
                    mech_axe(autopilot->mynum, mech, buffer);
                /* else if (have_mace(mech, RARM)) */
                /*! \todo {Add in mace code here} */
                else if (have_sword(mech, LARM))
                    mech_sword(autopilot->mynum, mech, buffer);

            }

            /* Try and kick but only if we got two legs, one of them
             * doesn't have a cycling weapon and the target is in the
             * front arc */
            if ((!section_hasbusyweap[2] || !section_hasbusyweap[3]) && 
                    !is_section_destroyed[2] && !is_section_destroyed[3] &&
                    (what_arc & FORWARDARC) && !AnyLimbsRecycling(mech) &&
                    (elevation_diff == 0 || elevation_diff == 1)) {

                rleg_bth = 0;
                lleg_bth = 0;

                /* Check the RLEG for any crits or weaps cycling */
                if (!section_hasbusyweap[2]) {
                    if (!OkayCritSectS(RLEG, 0, SHOULDER_OR_HIP))
                        rleg_bth += 3;
                    if (!OkayCritSectS(RLEG, 1, UPPER_ACTUATOR))
                        rleg_bth++;
                    if (!OkayCritSectS(RLEG, 2, LOWER_ACTUATOR))
                        rleg_bth++;
                    if (!OkayCritSectS(RLEG, 3, HAND_OR_FOOT_ACTUATOR))
                        rleg_bth++;
                } else {
                    rleg_bth = 99;
                }

                /* Check the LLEG for any crits or weaps cycling */
                if (!section_hasbusyweap[3]) {
                    if (!OkayCritSectS(LLEG, 0, SHOULDER_OR_HIP))
                        lleg_bth += 3;
                    if (!OkayCritSectS(LLEG, 1, UPPER_ACTUATOR))
                        lleg_bth++;
                    if (!OkayCritSectS(LLEG, 2, LOWER_ACTUATOR))
                        lleg_bth++;
                    if (!OkayCritSectS(LLEG, 3, HAND_OR_FOOT_ACTUATOR))
                        lleg_bth++;
                } else {
                    rleg_bth = 99;
                }

                /* Now kick depending on which one would be better 
                 * to kick with */
                if (rleg_bth <= lleg_bth) {
                    snprintf(buffer, LBUF_SIZE, "r %c%c", MechID(physical_target)[0],
                            MechID(physical_target)[1]);
                } else {
                    snprintf(buffer, LBUF_SIZE, "l %c%c", MechID(physical_target)[0],
                            MechID(physical_target)[1]);
                }
                mech_kick(autopilot->mynum, mech, buffer);
            }

            /* Finally try to punch */
            if (((!is_section_destroyed[0] && !section_hasbusyweap[0]) ||
                    (!is_section_destroyed[1] && !section_hasbusyweap[1]))
                    && !AnyLimbsRecycling(mech) &&
                    (elevation_diff == 0 || elevation_diff == -1)) {

                is_rarm_ready = 0;
                is_larm_ready = 0;

                if (!is_section_destroyed[0] &&
                        !section_hasbusyweap[0] &&
                        ((new_arc & FORWARDARC) ||
                        (new_arc & RSIDEARC))) {

                    /* We can use the right arm */
                    is_rarm_ready = 1;
                }

                if (!is_section_destroyed[1] &&
                        !section_hasbusyweap[1] &&
                        ((new_arc & FORWARDARC) ||
                        (new_arc & LSIDEARC))) {

                    /* We can use the left arm */
                    is_larm_ready = 1;
                }

                if (is_rarm_ready == 1 && is_larm_ready == 1) {
                    snprintf(buffer, LBUF_SIZE, "b %c%c",
                            MechID(target)[0], MechID(target)[1]);
                } else if (is_rarm_ready == 1) {
                    snprintf(buffer, LBUF_SIZE, "r %c%c",
                            MechID(target)[0], MechID(target)[1]);
                } else {
                    snprintf(buffer, LBUF_SIZE, "l %c%c",
                            MechID(target)[0], MechID(target)[1]);
                }

                /* Now punch */
                mech_punch(autopilot->mynum, mech, buffer);

            }

        } else if ((MechType(mech) == CLASS_MECH) && 
                (MechMove(mech) == MOVE_QUAD)) {
            
            /* Quad Mech - Right now only supporting kicking front style for quad */
            /* Remember, the RARM becomes the Front RLEG and the LARM becomes the
             * Front LLEG */

            /* Find direction of bad guy */
            what_arc = InWeaponArc(mech, MechFX(physical_target), MechFY(physical_target));

            /* Check to see what sections are destroyed */
            memset(is_section_destroyed, 0, sizeof(is_section_destroyed));
            is_section_destroyed[0] = SectIsDestroyed(mech, RARM);
            is_section_destroyed[1] = SectIsDestroyed(mech, LARM);
            is_section_destroyed[2] = SectIsDestroyed(mech, RLEG);
            is_section_destroyed[3] = SectIsDestroyed(mech, LLEG);

            /* Check to see if the sections have a busy weapon */
            memset(section_hasbusyweap, 0, sizeof(section_hasbusyweap));
            section_hasbusyweap[0] = SectHasBusyWeap(mech, RARM);
            section_hasbusyweap[1] = SectHasBusyWeap(mech, LARM);
            //section_hasbusyweap[2] = SectHasBusyWeap(mech, RLEG);
            //section_hasbusyweap[3] = SectHasBusyWeap(mech, LLEG);

            /* Try and kick but only if we got two legs, one of them
             * doesn't have a cycling weapon and the target is in the
             * front arc */
            if ((!section_hasbusyweap[0] || !section_hasbusyweap[0]) && 
                    !is_section_destroyed[0] && !is_section_destroyed[1] &&
                    !is_section_destroyed[2] && !is_section_destroyed[3] &&
                    (what_arc & FORWARDARC) && !AnyLimbsRecycling(mech) &&
                    (elevation_diff == 0 || elevation_diff == 1)) {

                rleg_bth = 0;
                lleg_bth = 0;

                /* Check the Front Right Leg for any crits or weaps cycling */
                if (!section_hasbusyweap[0]) {
                    if (!OkayCritSectS(RARM, 0, SHOULDER_OR_HIP))
                        rleg_bth += 3;
                    if (!OkayCritSectS(RARM, 1, UPPER_ACTUATOR))
                        rleg_bth++;
                    if (!OkayCritSectS(RARM, 2, LOWER_ACTUATOR))
                        rleg_bth++;
                    if (!OkayCritSectS(RARM, 3, HAND_OR_FOOT_ACTUATOR))
                        rleg_bth++;
                } else {
                    rleg_bth = 99;
                }

                /* Check the Front Left Leg for any crits or weaps cycling */
                if (!section_hasbusyweap[1]) {
                    if (!OkayCritSectS(LARM, 0, SHOULDER_OR_HIP))
                        lleg_bth += 3;
                    if (!OkayCritSectS(LARM, 1, UPPER_ACTUATOR))
                        lleg_bth++;
                    if (!OkayCritSectS(LARM, 2, LOWER_ACTUATOR))
                        lleg_bth++;
                    if (!OkayCritSectS(LARM, 3, HAND_OR_FOOT_ACTUATOR))
                        lleg_bth++;
                } else {
                    rleg_bth = 99;
                }

                /* Now kick depending on which one would be better 
                 * to kick with */
                if (rleg_bth <= lleg_bth) {
                    snprintf(buffer, LBUF_SIZE, "r %c%c", MechID(physical_target)[0],
                            MechID(physical_target)[1]);
                } else {
                    snprintf(buffer, LBUF_SIZE, "l %c%c", MechID(physical_target)[0],
                            MechID(physical_target)[1]);
                }
                mech_kick(autopilot->mynum, mech, buffer);
            }

        } else if (MechType(mech) == CLASS_BSUIT) {

            /* Are we a BSuit */

        } else {

            /* Eventually add code here maybe for other physical attacks for
             * tanks perhaps */

        }

    } /* End of physical attack */

    /* Log It */
    print_autogun_log(autopilot, "Autogun - End Physical Attack Stage");

    /* Now we mow down our target */

    /* Get our current target */
    /* Check to make sure we didn't kill it with physical attack
     * or something */
    if (!(target = getMech(autopilot->target))) {

        /* There were no valid targets so
         * rerun autogun */

        /* Reset the AI */
        autopilot->target = -1;
        autopilot->target_score = 0;

        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);

        /* Log It */
        print_autogun_log(autopilot, "Autogun - No valid current target");
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;

    } else if (Destroyed(target) || (target->mapindex != mech->mapindex)) {

        /* Target is either dead or not on the map anymore */
        autopilot->target = -1;
        autopilot->target_score = 0;

        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);
        
        /* Log it */
        print_autogun_log(autopilot, "Autogun - Target Gone");
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;
    }

    /* Log It */
    print_autogun_log(autopilot, "Autogun - Starting Weapon Attack Phase");

    /* Get range from mech to current target */
    range = FindHexRange(MechFX(mech), MechFY(mech), 
            MechFX(target), MechFY(target));

    /* This probably unnecessary but since it doesn't
     * take much to calc range it should be ok for
     * testing for now */
    if ((range >= (float) AUTO_GUN_MAX_RANGE) &&
            !AssignedTarget(autopilot)) {

        /* Target is to far - reset */
        autopilot->target = -1;
        autopilot->target_score = 0;

        /* Make sure multiple instances of autogun aren't running */
        if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
            muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
        }
        AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);
        
        /* Log it */
        print_autogun_log(autopilot, "Autogun - Target out of range");
        print_autogun_log(autopilot, "Autogun Event Finished");
        return;
    }

    /* Cycle through Guns while watching the heat */
    if ((range < (float) AUTO_GUN_MAX_RANGE) && autopilot->profile[(int) range]) {

        /* Ok we got weapons lets use them */

        /* Reset heat counter to current heat */
        accumulate_heat = MechWeapHeat(mech);

        /* If the unit is moving need to account for the heat of that as well */
        if ((MechType(mech) == CLASS_MECH) && (fabs(MechSpeed(mech)) > 0.0)) {

            maxspeed = MMaxSpeed(mech);
            if (IsRunning(MechDesiredSpeed(mech), maxspeed))
                accumulate_heat += 2;
            else
                accumulate_heat += 1;
        }

        /* Get first weapon */
        temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                SEARCH_LAST, NULL);

        while (temp_weapon_node) {

            /* Check to see if the weapon even works */
            if (WeaponIsNonfunctional(mech, temp_weapon_node->section,
                        temp_weapon_node->critical, GetWeaponCrits(mech, 
                        Weapon2I(temp_weapon_node->weapon_db_number))) > 0) {

                /* Weapon Doesn't work so go to next one */
                temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                        SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                continue; 
            }

            /* Check to see if its cycling */
            if (WpnIsRecycling(mech, temp_weapon_node->section, temp_weapon_node->critical)) {

                /* Go to the next one */
                temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                        SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                continue; 
            }

            if (IsAMS(temp_weapon_node->weapon_db_number)) {

                /* Ok its an AMS so go to next weapon */
                temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                        SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);
                continue; 
            }

            /* Check heat levels, since the heat isn't updated untill we're done
             * we have to manage the heat ourselves */
            /*! \todo {Add a check also for aeros} */
            if ((MechType(mech) == CLASS_MECH) && (((float) accumulate_heat + 
                        (float) MechWeapons[temp_weapon_node->weapon_db_number].heat -
                        (float) MechMinusHeat(mech)) > 
                        AUTO_GUN_MAX_HEAT)) {

                /* Would make ourselves to hot to fire this gun */
                temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                        SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                continue; 
            }

            /* Ok passed the checks now setup the arcs and see if we can fire it */
            
            /* Ok the rest depends on what type of unit we driving */
            if ((MechType(mech) == CLASS_MECH) &&
                    (MechMove(mech) == MOVE_BIPED)) {

                /* Center ourself and get target arc */
                MechStatus(mech) &= ~(TORSO_RIGHT | TORSO_LEFT);
                if (MechSpecials(mech) & FLIPABLE_ARMS) {

                    /* Center the arms if need be */
                    MechStatus(mech) &= ~(FLIPPED_ARMS);
                }

                /* Get Target Arc */
                what_arc = InWeaponArc(mech, MechFX(target), MechFY(target));

                /* Now go through the various arcs and see if we
                 * need to flip arm or rotorso or something */
                if (what_arc & REARARC) {

                    if (temp_weapon_node->section == LARM ||
                            temp_weapon_node->section == RARM) {

                        /* First see if we can flip arms */
                        if (MechSpecials(mech) & FLIPABLE_ARMS) {
                       
                            /* Flip the arms */
                            MechStatus(mech) |= FLIPPED_ARMS;

                        } else {

                            /* Now see if we can rotatorso */

                            /* Find out if it would be better to
                             * rotate left or right */
                            relative_bearing = MechFacing(mech) - 
                                FindBearing(MechFX(mech), MechFY(mech), 
                                        MechFX(target), 
                                        MechFY(target));

                            if (relative_bearing > 120 && relative_bearing < 180 &&
                                    temp_weapon_node->section == RARM) {

                                /* Rotate Right */
                                MechStatus(mech) |= TORSO_RIGHT;

                            } else if (relative_bearing > 180 && relative_bearing < 240 &&
                                    temp_weapon_node->section == LARM) {

                                /* Rotate Left */
                                MechStatus(mech) |= TORSO_LEFT;

                            } else {

                                /* Can't do anything so go to next weapon */
                                temp_weapon_node = 
                                    (weapon_node *) rb_search(autopilot->profile[(int) range],
                                    SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                                continue; 

                            }

                        }

                    } else if (!(GetPartFireMode(mech, temp_weapon_node->section, 
                                temp_weapon_node->critical) & REAR_MOUNT)) {

                        /* Weapon is forward torso or leg mounted weapon
                         * so no way to shoot with */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                    /* ELSE: Weapon is rear mounted so don't need to 
                     * do anything */

                } else if (what_arc & LSIDEARC) {

                    if (temp_weapon_node->section == RLEG ||
                            temp_weapon_node->section == LLEG) {

                        /* No way can we hit him with leg mounted
                         * weapons so lets go to next one */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                    /* Rotate torso left */
                    MechStatus(mech) |= TORSO_LEFT;

                } else if (what_arc & RSIDEARC) {

                    if (temp_weapon_node->section == RLEG ||
                            temp_weapon_node->section == LLEG) {

                        /* No way can we hit him with leg mounted
                         * weapons so lets go to next one */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                    /* Rotate torso right */
                    MechStatus(mech) |= TORSO_RIGHT;

                } else {

                    if (GetPartFireMode(mech, temp_weapon_node->section, 
                                temp_weapon_node->critical) & REAR_MOUNT) {

                        /* No way can we hit the guy with a rear
                         * gun so lets go to next one */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                }

            } else if ((MechType(mech) == CLASS_MECH) && 
                    (MechMove(mech) == MOVE_QUAD)) {

                /* Get Target Arc */
                what_arc = InWeaponArc(mech, MechFX(target), MechFY(target));

                if (what_arc & REARARC) {

                    if (!(GetPartFireMode(mech, temp_weapon_node->section, 
                                temp_weapon_node->critical) & REAR_MOUNT)) {

                        /* Weapon is not rear mounted so skip it and
                         * go to the next weapon */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                } else if (what_arc & FORWARDARC) {

                    if (GetPartFireMode(mech, temp_weapon_node->section, 
                                temp_weapon_node->critical) & REAR_MOUNT) {

                        /* Weapon is rear mounted so skip it and
                         * go to the next weapon */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                } else { 

                    /* The attacker is in a zone we can't possibly
                     * shoot into, so just go to next weapon */
                    temp_weapon_node = 
                        (weapon_node *) rb_search(autopilot->profile[(int) range],
                        SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                    continue; 

                }

            } else if ((MechType(mech) == CLASS_VEH_GROUND) ||
                    (MechType(mech) == CLASS_VEH_NAVAL)) {

                /* Get Target Arc */
                what_arc = InWeaponArc(mech, MechFX(target), MechFY(target));

                /* Check if turret exists and weapon is there */
                if (GetSectInt(mech, TURRET) && temp_weapon_node->section == TURRET) {

                    /* Rotate Turret and nail the guy */
                    if (MechTurretFacing(mech) != FindBearing(MechFX(mech),
                        MechFY(mech), MechFX(target), MechFY(target))) {

                        snprintf(buffer, LBUF_SIZE, "%d", FindBearing(MechFX(mech), 
                                    MechFY(mech), MechFX(target), MechFY(target)));
                        mech_turret(autopilot->mynum, mech, buffer);
                    }

                } else {

                    /* Check if in arc of weapon */
                    if (!IsInWeaponArc(mech, MechFX(target), MechFY(target), 
                                temp_weapon_node->section,
                                temp_weapon_node->critical)) {
                    
                        /* Not in the arc so lets go to the next weapon */
                        temp_weapon_node = 
                            (weapon_node *) rb_search(autopilot->profile[(int) range],
                            SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

                        continue; 

                    }

                }

            } else {

                /* We're either an aero, ds, bsuit, mechwarrior or vtol
                 *
                 * Still need to add code for them */

            }

            /* Done moving around, fire the weapon */
            snprintf(buffer, LBUF_SIZE, "%d", temp_weapon_node->weapon_number);
            mech_fireweapon(autopilot->mynum, mech, buffer);

            /* Log It */
            print_autogun_log(autopilot, "Autogun - Fired Weapon #%d "
                    "at target #%d", temp_weapon_node->weapon_number,
                    autopilot->target);

            /* Ok check to see if weapon was fired if so account for the
             * heat */
            if (WpnIsRecycling(mech, temp_weapon_node->section, temp_weapon_node->critical)) {
                accumulate_heat += MechWeapons[temp_weapon_node->weapon_db_number].heat;
            }

            /* Ok go to the next weapon */
            temp_weapon_node = (weapon_node *) rb_search(autopilot->profile[(int) range],
                    SEARCH_PREV, &temp_weapon_node->range_scores[(int) range]);

        } /* End of cycling through weapons */

    }

    /* Log It */
    print_autogun_log(autopilot, "Autogun - End Weapon Attack Phase");

    /* Setup chasetarg 
     * 
     * Since chasetarget uses follow but we don't want follow getting
     * messed up if its following a normal target.  We define our own
     * follow (basicly its follow but with the command name chasetarget */
  
    /* Get the first command, if its chasetarget or there is no command
     * check to see if we need to update chasetarget, otherwise means
     * AI is doing something else important so don't try to chase
     * anything */

    if (ChasingTarget(autopilot)) { 

        /* Reset the flag */
        do_chasetarget = 0;

        /* Get the first command's enum and check it */
        switch (auto_get_command_enum(autopilot, 1)) {

            case GOAL_CHASETARGET:

                /* Ok its our command so we can change it */
                do_chasetarget = 1;
                break;

            case -1:
                
                /* No current commands so we can do our chasetarget */
                do_chasetarget = 1;
                break;

            /* ALl the other stuff we don't want to mess with */
            default:

                /* Reset the chase values */
                autopilot->chase_target = -10;
                autopilot->chasetarg_update_tick = AUTOPILOT_CHASETARG_UPDATE_TICK;
                autopilot->follow_update_tick = AUTOPILOT_FOLLOW_UPDATE_TICK;
                break;

        }
    
        /* Check the flag */
        if (do_chasetarget) {

            /* Ok lets chase the guy */
       
            /* First see if we need to update */
            if ((autopilot->target != autopilot->chase_target) ||
                    (autopilot->chasetarg_update_tick >= AUTOPILOT_CHASETARG_UPDATE_TICK)) {

                /* Tell the AI to follow its target */
                /* Basicly remove all the commands, add in
                 * autogun and follow and engage */

                /* Let the AI know we chasing this guy */
                autopilot->chase_target = autopilot->target;

                /* Reset the tickers */
                autopilot->chasetarg_update_tick = 0;
                autopilot->follow_update_tick = AUTOPILOT_FOLLOW_UPDATE_TICK;

                /* Reset the AI */
                auto_disengage(autopilot->mynum, autopilot, "");
                auto_delcommand(autopilot->mynum, autopilot, "-1");

                /* Add in autogun and follow and engage */
                if (AssignedTarget(autopilot) && autopilot->target != -1) {
                    snprintf(buffer, LBUF_SIZE, "autogun target %d", autopilot->target);
                } else {
                    snprintf(buffer, LBUF_SIZE, "autogun on");
                }

                auto_addcommand(autopilot->mynum, autopilot, buffer);
                snprintf(buffer, LBUF_SIZE, "chasetarget %d", autopilot->target);
                auto_addcommand(autopilot->mynum, autopilot, buffer);
                auto_engage(autopilot->mynum, autopilot, "");

                /* Log it */
                print_autogun_log(autopilot, "Autogun Event Finished");

                return;

            } else {
        
                /* Update the ticker */
                autopilot->chasetarg_update_tick++;

                /* Check to see if we need to turn to face the guy by
                 * generating our target hex and seeing if we are in that
                 * hex then face the bad guy */
                if ((target = getMech(autopilot->target)) &&
                        (!Destroyed(target) && (target->mapindex == mech->mapindex))) {

                    /* Generate the target hex */
                    FindXY(MechFX(target), MechFY(target), MechFacing(target) + autopilot->ofsx,
                            autopilot->ofsy, &fx, &fy);

                    RealCoordToMapCoord(&x, &y, fx, fy);

                    /* Make sure the hex is sane */
                    if (x < 0 || y < 0 || x >= map->map_width || y >= map->map_height) {

                        /* Bad Target Hex */

                        /* Reset the hex to the Target's current hex */
                        x = MechX(target);
                        y = MechY(target);

                    }

                    /* Are we in the target hex and is the target not moving */
                    if ((MechX(mech) == x) && (MechY(mech) == y) && (MechSpeed(target) < 0.5)) {

                        /* Get his bearing and face him */
                        MapCoordToRealCoord(x, y, &fx, &fy);

                        /* If we're not facing him, turn towards him */
                        if (MechDesiredFacing(mech) != FindBearing(MechFX(mech), 
                                    MechFY(mech), fx, fy)) {

                            snprintf(buffer, LBUF_SIZE, "%d", 
                                    FindBearing(MechFX(mech), MechFY(mech), fx, fy));
                            mech_heading(autopilot->mynum, mech, buffer);

                        } /* Turn towards him */

                    } /* Is he moving and are we in target hex */

                } /* Do we need to turn towards him */

            } /* Do we need to update */

        } /* Do we run chasetarget */

    } /* Is chasetarget on */

    /* Make sure multiple instances of autogun aren't running */
    if (muxevent_count_type_data(EVENT_AUTOGUN, (void *) autopilot)) {
        muxevent_remove_type_data(EVENT_AUTOGUN, (void *) autopilot);
    }

    AUTOEVENT(autopilot, EVENT_AUTOGUN, auto_gun_event, AUTO_GUN_TICK, 0);

    /* Log it */
    print_autogun_log(autopilot, "Autogun Event Finished");

    /* The End */

}