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: ai.c,v 1.2 2005/08/03 21:40:53 av1-op Exp $
 *
 * Author: Markus Stenberg <fingon@iki.fi>
 *
 *  Copyright (c) 1998 Markus Stenberg
 *  Copyright (c) 1998-2002 Thomas Wouters
 *  Copyright (c) 2000-2002 Cord Awtry 
 *       All rights reserved
 *
 * Created: Thu Mar 12 18:36:33 1998 fingon
 * Last modified: Thu Dec 10 21:46:30 1998 fingon
 *
 */


/* Point of the excercise : move from point a,b to point c,d while
   eliminating opponents and stuff, avoiding enemies in rear/side arc
   and generally having fun */

#include <math.h>

#include "mech.h"
#include "autopilot.h"
#include "p.autopilot_command.h"
#include "p.mech.utils.h"
#include "p.map.obj.h"
#include "spath.h"
#include "p.mech.combat.h"
#include "p.glue.hcode.h"

#define SCORE_MOD 100
#define SAFE_SCORE SCORE_MOD * 1000
#define MIN_SAFE 8
#define NORM_SAFE 32
#define MAX_SIM_PATHS 40

typedef struct {
    int e, t;
    float s, ds;
    float fx, fy;
    short x, y, lx, ly;
    int h;
    int dh;
} LOC;

enum {
    SP_OPT_NORM, SP_OPT_FASTER, SP_OPT_SLOWER, SP_OPT_C
};
int sp_opt[SP_OPT_C] = { 0, 1, -1 };    /* We prefer to keep at same speed, however */

#define MNORM_COUNT 37

int move_norm_opt[MNORM_COUNT][2] = {
    {0, SP_OPT_FASTER},
    {0, SP_OPT_NORM},
    {0, SP_OPT_SLOWER},
    {-1, SP_OPT_NORM},
    {1, SP_OPT_NORM},
    {-2, SP_OPT_NORM},
    {2, SP_OPT_NORM},
    {-3, SP_OPT_NORM},
    {3, SP_OPT_NORM},
    {-5, SP_OPT_NORM},
    {5, SP_OPT_NORM},
    {-10, SP_OPT_NORM},
    {10, SP_OPT_NORM},
    {-15, SP_OPT_NORM},
    {15, SP_OPT_NORM},
    {-20, SP_OPT_NORM},
    {20, SP_OPT_NORM},
    {-30, SP_OPT_NORM},
    {30, SP_OPT_NORM},
    {-40, SP_OPT_NORM},
    {40, SP_OPT_NORM},
    {-60, SP_OPT_NORM},
    {60, SP_OPT_NORM},
    {-60, SP_OPT_SLOWER},
    {60, SP_OPT_SLOWER},
    {-60, SP_OPT_FASTER},
    {60, SP_OPT_FASTER},
    {-80, SP_OPT_NORM},
    {80, SP_OPT_NORM},
    {-120, SP_OPT_NORM},
    {120, SP_OPT_NORM},
    {-160, SP_OPT_NORM},
    {160, SP_OPT_NORM},
    {-160, SP_OPT_SLOWER},
    {160, SP_OPT_SLOWER},
    {-160, SP_OPT_FASTER},
    {160, SP_OPT_FASTER}
};

#define CFAST_COUNT 9

/* Update: Just do subset if we're in silly mood */
int combat_fast_opt[CFAST_COUNT][2] = {
    {0, SP_OPT_FASTER},
    {0, SP_OPT_NORM},
    {0, SP_OPT_SLOWER},
    {-10, SP_OPT_NORM},
    {10, SP_OPT_NORM},
    {-30, SP_OPT_NORM},
    {30, SP_OPT_NORM},
    {-60, SP_OPT_NORM},
    {60, SP_OPT_NORM}
};

void sendAIM(AUTO * a, MECH * m, char *msg)
{
    auto_reply(m, msg);
    SendAI(msg);
}

char *AI_Info(MECH * m, AUTO * a)
{
    static char buf[MBUF_SIZE];

    sprintf(buf, "Unit#%d on #%d [A#%d]:", m->mynum, m->mapindex,
	a->mynum);
    return buf;
}

extern float terrain_speed(MECH * mech, float tempspeed, float maxspeed,
    int terrain, int elev);

static int ai_crash(MAP * map, MECH * m, LOC * l)
{
    float newx = 0.0, newy = 0.0;
    float tempspeed, maxspeed, acc;
    int offset;
    int normangle;
    int mw_mod = 1;
    int ch = 0;

    if (!map)
	return 0;

    maxspeed = MMaxSpeed(m);

    /* UpdateHeading */
    if (l->h != l->dh && !(MechCritStatus(m) & GYRO_DESTROYED)) {
	ch = 1;
	if (is_aero(m))
	    maxspeed = maxspeed * ACCEL_MOD;
	normangle = l->h - l->dh;
	if (MechType(m) == CLASS_MW || MechType(m) == CLASS_BSUIT)
	    mw_mod = 60;
	/* XXX Jumping */
	if (fabs(l->s) < 1.0)
	    offset = 3 * maxspeed * MP_PER_KPH * mw_mod;
	else {
	    offset = 2 * maxspeed * MP_PER_KPH * mw_mod;
	    if ((abs(normangle) > offset) && (l->s > (maxspeed * 2 / 3)))
		offset -= offset / 2 * (3.0 * l->s / maxspeed - 2.0);
	}
	offset = offset * MOVE_MOD;
	if (normangle < 0)
	    normangle += 360;
	if (IsDS(m) && offset >= 10)
	    offset = 10;
	if (normangle > 180) {
	    l->h += offset;
	    if (l->h >= 360)
		l->h -= 360;
	    normangle += offset;
	    if (normangle >= 360)
		l->h = l->dh;
	} else {
	    l->h -= offset;
	    if (l->h < 0)
		l->h += 360;
	    normangle -= offset;
	    if (normangle < 0)
		l->h = l->dh;
	}

    }
    /* UpdateSpeed */
    /* XXX MASC */
    /* XXX heat (_hard_ to track) */
    tempspeed = l->ds;
    if (MechType(m) != CLASS_MW && MechMove(m) != MOVE_VTOL &&
	(MechMove(m) != MOVE_FLY || Landed(m)))
	tempspeed = terrain_speed(m, tempspeed, maxspeed, l->t, l->e);
    if (ch) {
	if (mudconf.btech_slowdown == 2) {
	    int dif = MechFacing(m) - MechDesiredFacing(m);

	    if (dif < 0)
		dif = -dif;
	    if (dif > 180)
		dif = 360 - dif;
	    if (dif) {
		dif = (dif - 1) / 30 + 2;
		tempspeed = tempspeed * (10 - dif) / 10;
	    }
	} else if (mudconf.btech_slowdown == 1) {
	    if (l->h != l->dh)
		tempspeed = tempspeed * 2.0 / 3.0;
	    else
		tempspeed = tempspeed * 3.0 / 4.0;
	}
    }
    if (MechIsQuad(m) && MechLateral(m))
	tempspeed -= MP1;
    if (tempspeed <= 0.0) {
	tempspeed = 0.0;
    }
    if (l->ds < 0.)
	tempspeed = -tempspeed;
    if (tempspeed != l->s) {
	if (MechIsQuad(m))
	    acc = maxspeed / 10.;
	else
	    acc = maxspeed / 20.;
	if (tempspeed < l->s) {
	    /* Decelerating */
	    l->s -= acc;
	    if (tempspeed > l->s)
		l->s = tempspeed;
	} else {
	    /* Accelerating */
	    l->s += acc;
	    if (tempspeed < l->s)
		l->s = tempspeed;
	}
    }
    /* move_mech + NewHexEntered */
    /* XXX Jumping mechs [aeros,VTOLs] */
    /* XXX Non-mechs */
    /* XXX Quads */
    FindComponents(l->s * MOVE_MOD, l->h, &newx, &newy);
    l->fx += newx;
    l->fy += newy;
    l->lx = l->x;
    l->ly = l->y;
    RealCoordToMapCoord(&l->x, &l->y, l->fx, l->fy);
    /* Ensure we don't run off map */
    if (BOUNDED(0, l->x, map->map_width - 1) != l->x)
	return 1;
    if (BOUNDED(0, l->y, map->map_height - 1) != l->y)
	return 1;
    if (l->lx != l->x || l->ly != l->y) {
	int oz = l->e;

	/* Ensure if mech m can do transition auto_lx,auto_ly => auto_x,auto_y */

	/* XXX Decent handling of terrain choosing and stuff */
	switch (GetRTerrain(map, l->x, l->y)) {
	case HEAVY_FOREST:
	    if (MechType(m) != CLASS_MECH)
		return 1;
	    break;
	case WATER:
	    if (MechMove(m) == MOVE_TRACK || MechMove(m) == MOVE_WHEEL)
		return 1;
	    else {
		/* Check floodability for mechs */
		if (MechType(m) == CLASS_MECH) {
		    int t = Elevation(map, l->x, l->y);

		    if (t < 0) {
			if (t == -1) {
			    /* Check feet */
#define Floodable(loc) (!GetSectArmor(m,loc) || (!GetSectRArmor(m,loc) && GetSectORArmor(m,loc)))
#define FloodCheck(loc) if (GetSectInt(m,loc)) if (Floodable(loc)) return 1;
			    FloodCheck(LLEG);
			    FloodCheck(RLEG);
			    if (MechIsQuad(m)) {
				FloodCheck(LARM);
				FloodCheck(RARM);
			    }
			} else {
			    int i;

			    for (i = 0; i < NUM_SECTIONS; i++)
				FloodCheck(i);
			}
		    }
		}
	    }
	    break;
	case HIGHWATER:
	    return 1;
	    break;
	}
	l->e = Elevation(map, l->x, l->y);
	if (MechMove(m) == MOVE_HOVER)
	    l->e = MAX(0, l->e);
	l->t = GetRTerrain(map, l->x, l->y);
	if (MechType(m) == CLASS_MECH)
	    if ((l->e - oz) > 2 || (oz - l->e) > 2)
		return 1;
	/* XXX Check flooding etc, should avoid _that_ */
	if (MechType(m) == CLASS_VEH_GROUND)
	    if ((l->e - oz) > 1 || (oz - l->e) > 1)
		return 1;

    }
    return 0;
}


static void LOCInit(LOC * l, MECH * m)
{
    l->fx = MechFX(m);
    l->fy = MechFY(m);
    l->e = MechElevation(m);
    l->h = MechFacing(m);
    l->dh = MechDesiredFacing(m);
    l->s = MechSpeed(m);
    l->t = MechRTerrain(m);
    l->ds = MechDesiredSpeed(m);
    l->x = MechX(m);
    l->y = MechY(m);
    l->lx = MechX(m);
    l->ly = MechY(m);
}

static LOC enemy_l[MAX_MECHS_PER_MAP];
static MECH *enemy_m[MAX_MECHS_PER_MAP];	/* Hopefully no more mechs on map */
static int enemy_o[MAX_MECHS_PER_MAP];	/* 'out' = not moving anymore (crashed?) */
static int enemy_i[MAX_MECHS_PER_MAP];	/* dbref */
static int enemy_c = 0;		/* Number of enemies */

static LOC friend_l[MAX_MECHS_PER_MAP];
static MECH *friend_m[MAX_MECHS_PER_MAP];	/* Hopefully no more mechs on map */
static int friend_o[MAX_MECHS_PER_MAP];	/* 'out' = not moving anymore (crashed?) */
static int friend_i[MAX_MECHS_PER_MAP];	/* dbref */
static int friend_c = 0;	/* Number of enemies */

int getEnemies(MECH * mech, MAP * map, int reset)
{
    MECH *tempMech;
    int i;

    if (reset) {
	for (i = 0; i < enemy_c; i++) {
	    LOCInit(&enemy_l[i], enemy_m[i]);	/* Just reset location */
	    enemy_o[i] = 0;
	}
	return 0;
    }
    enemy_c = 0;
    for (i = 0; i < map->first_free; i++) {
	tempMech = FindObjectsData(map->mechsOnMap[i]);
	if (!tempMech)
	    continue;
	if (Destroyed(tempMech))
	    continue;
	if (MechStatus(tempMech) & COMBAT_SAFE)
	    continue;
	if (MechTeam(tempMech) == MechTeam(mech))
	    continue;
	if (FlMechRange(map, mech, tempMech) > 50.0)
	    continue;		/* Inconsequential */
	/* Something that is _possibly_ unnerving */
	LOCInit(&enemy_l[enemy_c], tempMech);	/* Location */
	enemy_m[enemy_c] = tempMech;	/* Mech data */
	enemy_i[enemy_c++] = i;
    }
    return enemy_c;
}

int getFriends(MECH * mech, MAP * map, int reset)
{
    MECH *tempMech;
    int i;

    if (reset) {
	for (i = 0; i < friend_c; i++) {
	    LOCInit(&friend_l[i], friend_m[i]);	/* Just reset location */
	    friend_o[i] = 0;
	}
	return 0;
    }
    friend_c = 0;
    for (i = 0; i < map->first_free; i++) {
	tempMech = FindObjectsData(map->mechsOnMap[i]);
	if (!tempMech)
	    continue;
	if (Destroyed(tempMech))
	    continue;
	if (MechTeam(tempMech) != MechTeam(mech))
	    continue;
	if (MechType(tempMech) != CLASS_MECH)
	    continue;
	if (FlMechRange(map, mech, tempMech) > 50.0)
	    continue;		/* Inconsequential */
	LOCInit(&friend_l[friend_c], tempMech);	/* Location */
	friend_m[friend_c] = tempMech;	/* Mech data */
	friend_i[friend_c++] = i;
    }
    return friend_c;
}

/* This has been made .. slightly more complicated.
   Basic idea (now): Calculate all the [n] states _simultaneously_ ;
   our movement isn't affecting enemy/friend movement after all, just
   our own, therefore this is _almost_ [n] times faster than the old code
   (approx. 1/30 P60 per 'sheep outside combat, perhaps 1/10 P60 inside) */

#define MAGIC_NUM -123456

#define U1(a,b) if (a < b || a == MAGIC_NUM) a = b
#define U2(a,b) if (a > b || a == MAGIC_NUM) a = b
#define FU(a,b,c,s) ((b) == (c) ? 0 : (s) * ((a) - (b)) / ((c) - (b)))

void ai_path_score(MECH * m, MAP * map, AUTO * a, int opts[][2], int num_o,
    int gotenemy, float dx, float dy, float delx, float dely, int *rl,
    int *bd, int *bscore)
{
    int i, j, k, l, bearing;
    int sd, sc;
    LOC lo[MAX_SIM_PATHS];
    int dan[MAX_SIM_PATHS], tdan[MAX_SIM_PATHS], msc[MAX_SIM_PATHS],
	bsc[MAX_SIM_PATHS];
    int out[MAX_SIM_PATHS], stack[MAX_SIM_PATHS], br[MAX_SIM_PATHS];


    for (i = 0; i < num_o; i++) {
	msc[i] = 0;
	bsc[i] = 0;
	dan[i] = 0;
	tdan[i] = 0;
	out[i] = 0;
	stack[i] = 0;
	br[i] = 9999;
	LOCInit(&lo[i], m);
	lo[i].dh = AcceptableDegree(lo[i].dh + opts[i][0]);
	sd = sp_opt[opts[i][1]];
	if (sd) {
	    if (sd < 0) {
		lo[i].ds = lo[i].ds * 2.0 / 3.0;
	    } else if (sd == 1) {
		float ms = MMaxSpeed(m);

		lo[i].ds = (lo[i].ds < MP1 ? MP1 : lo[i].ds) * 4.0 / 3.0;
		if (lo[i].ds > ms)
		    lo[i].ds = ms;
	    } else {
		float ms = MMaxSpeed(m);

		lo[i].ds = ms;
	    }
	}
    }
    for (i = 0; i < NORM_SAFE; i++) {
	dx += delx;
	dy += dely;
	for (k = 0; k < num_o; k++) {
	    if (out[k])
		continue;
	    if (ai_crash(map, m, &lo[k])) {	/* Simulate _one_ step */
		out[k] = i + 1;
		continue;
	    }
	    /* Base target-acquisition stuff */
	    if ((l = FindXYRange(lo[k].fx, lo[k].fy, dx, dy)) < br[k])
		br[k] = l;

	    /* Generally speaking we're going to the point spesified */
	    msc[k] += 4 * (2 * (50 - br[k]) + (100 - l));

	    /* Heading change's inherently [slightly] evil */
	    if (lo[k].h != lo[k].dh)
		msc[k] -= 1;

	    /* Moving is a good thing */
	    if (lo[k].x != lo[k].lx || lo[k].y != lo[k].ly) {
		if (lo[k].t == WATER)
		    msc[k] -= 5;
		msc[k] += 10;
	    }
	    /* Punish for not utilizing full speed (this is .. hm, flaky) */
	    if (opts[k][1] != SP_OPT_FASTER && MMaxSpeed(m) > 0.1) {
		sc = BOUNDED(0, 100 * lo[k].ds / MMaxSpeed(m), 100);
		msc[k] -= (100 - sc) / 30;	/* Basically, unused speed is bad */
	    }
	}
	if (MechType(m) == CLASS_MECH) {
	    /* Simulate friends */
	    for (j = 0; j < friend_c; j++) {
		if (friend_o[j])
		    continue;
		if (ai_crash(map, friend_m[j], &friend_l[j]))
		    friend_o[j] = 1;
	    }
	    for (k = 0; k < num_o; k++) {
		int sc = 0;

		if (out[k] || stack[k])
		    continue;
		/* Meaning of stack: Someone moves _into_ the hex */
		for (j = 0; j < friend_c; j++)
		    if (!friend_o[j])
			if (lo[k].x == friend_l[j].x &&
			    lo[k].y == friend_l[j].y)
			    sc++;
		if (sc > 1) {	/* Possible stackage */
		    int osc = sc;

		    for (j = 0; j < friend_c; j++)
			if (!friend_o[j])
			    if (lo[k].x == friend_l[j].x &&
				lo[k].y == friend_l[j].y)
				if ((lo[k].lx != lo[k].x ||
					lo[k].ly != lo[k].y) ||
				    (friend_l[j].lx != friend_l[j].x ||
					friend_l[j].ly != friend_l[j].y))
				    osc--;
		    if (osc != sc)
			stack[k] = i + 1;
		}
		if (gotenemy)
		    tdan[k] = 0;
	    }
	}
	if (gotenemy) {
	    /* Update enemy locations as well */
	    for (j = 0; j < enemy_c; j++) {
		if (enemy_o[j])
		    continue;
		if (ai_crash(map, enemy_m[j], &enemy_l[j]))
		    enemy_o[j] = 1;
		for (k = 0; k < num_o; k++) {
		    if (out[k])
			continue;
		    if ((l = MyHexDist(lo[k].x, lo[k].y, enemy_l[j].x,
				enemy_l[j].y, 0)) >= 100)
			continue;
		    switch (a->auto_cmode) {
		    case 0:	/* Withdraw */
			if (l > a->auto_cdist)
			    bsc[k] +=
				5 * a->auto_cdist + l - a->auto_cdist;
			else
			    bsc[k] += 5 * l;
			break;
		    case 1:	/* Score  = fulfilling goal (=> distance from cdist) */
			if (l < a->auto_cdist)
			    bsc[k] -= 10 * (a->auto_cdist - l);	/* Not too close */
			else
			    bsc[k] -= 2 * (l - a->auto_cdist);
			break;
		    case 2:
			if (l < a->auto_cdist)
			    bsc[k] -= 2 * (a->auto_cdist - l);
			else
			    bsc[k] -= 10 * (l - a->auto_cdist);
		    }
		    if (l > 28)
			continue;
		    /* Danger modifier ; it's _always_ dangerous to be close */
		    tdan[k] += (40 - MIN(40, l));
		    /* Arcs can be .. dangerous */
		    if (MechType(m) == CLASS_MECH) {
			bearing =
			    FindBearing(lo[k].fx, lo[k].fy, enemy_l[j].fx,
			    enemy_l[j].fy);
			bearing = lo[k].h - bearing;
			if (bearing < 0)
			    bearing += 360;
			if (bearing >= 90 && bearing <= 270) {
			    /* Sides are moderately dangerous [potential rear arcs] */
			    tdan[k] += 5 * (29 - MIN(29, l));
			    if (bearing >= 120 && bearing <= 240) {
				/* Rear arc is VERY dangerous */
				tdan[k] += 20 * (29 - MIN(29, l));
			    }
			}
		    } else if (MechType(m) == CLASS_VEH_GROUND) {
			bearing =
			    FindBearing(lo[k].fx, lo[k].fy, enemy_l[j].fx,
			    enemy_l[j].fy);
			bearing = lo[k].h - bearing;
			if (bearing < 0)
			    bearing += 360;
			if (bearing >= 45 && bearing <= 315) {
			    if (bearing >= 135 && bearing <= 225) {
				/* Rear arc is VERY dangerous */
				tdan[k] +=
				    10 * (29 - MIN(29,
					l)) * (100 - 100 * GetSectArmor(m,
					BSIDE) / MAX(1, GetSectOArmor(m,
					    BSIDE))) / 100;
			    } else if (bearing < 135) {
				/* right side */
				tdan[k] +=
				    7 * (29 - MIN(29,
					l)) * (100 - 100 * GetSectArmor(m,
					RSIDE) / MAX(1, GetSectOArmor(m,
					    RSIDE))) / 100;
			    } else {
				tdan[k] +=
				    7 * (29 - MIN(29,
					l)) * (100 - 100 * GetSectArmor(m,
					LSIDE) / MAX(1, GetSectOArmor(m,
					    LSIDE))) / 100;
			    }
			} else
			    tdan[k] +=
				5 * (29 - MIN(29,
				    l)) * (100 - 100 * GetSectArmor(m,
				    FSIDE) / MAX(1, GetSectOArmor(m,
					FSIDE))) / 100;
		    }
		}
		for (k = 0; k < num_o; k++) {
		    if (out[k])
			continue;
		    /* Dangerous to be far from buddy in fight */
		    l = FindXYRange(lo[k].fx, lo[k].fy, dx, dy);
		    if (gotenemy && (delx != 0.0 || dely != 0.0))
			tdan[k] += MIN(100, l * l);
		    if (enemy_c)
			tdan[k] = tdan[k] / enemy_c;
		    /* It's inherently dangerous to move slowly: */
		    if (lo[k].s <= MP2)
			tdan[k] += 400;
		    else if (lo[k].s <= MP4)
			tdan[k] += 300;
		    else if (lo[k].s <= MP6)
			tdan[k] += 200;
		    else if (lo[k].s <= MP9)
			tdan[k] += 100;
		    dan[k] += tdan[k] * (NORM_SAFE - i) / (NORM_SAFE / 2);
		}
	    }
	}
    }
    for (i = 0; i < num_o; i++) {
	U2(a->w_msc, msc[i]);
	U1(a->b_msc, msc[i]);
	if (gotenemy) {
	    U2(a->w_bsc, bsc[i]);
	    U1(a->b_bsc, bsc[i]);
	    U2(a->w_dan, dan[i]);
	    U1(a->b_dan, dan[i]);
	}
    }
    /* Now we have been.. calibrated */
    *bscore = 0;
    *bd = -1;
    /* Find best overall score */
    for (i = 0; i < num_o; i++) {
	if (!out[i])
	    out[i] = NORM_SAFE + 1;
	if (!stack[i])
	    stack[i] = out[i];
	sc = (out[i] - (out[i] - stack[i]) / 2) * SAFE_SCORE + FU(msc[i],
	    a->w_msc, a->b_msc, SCORE_MOD * a->auto_goweight);
	if (gotenemy)
	    sc +=
		FU(bsc[i], a->w_bsc, a->b_bsc,
		SCORE_MOD * a->auto_fweight) - FU(dan[i], a->w_dan,
		a->b_dan,
		SCORE_MOD * (a->auto_fweight + a->auto_goweight));
	if (sc > *bscore || (*bd >= 0 && sc == *bscore &&
		sp_opt[opts[i][1]] > sp_opt[opts[*bd][1]])) {
	    *bscore = sc;
	    *bd = i;
	    *rl = out[i] - 1;
	}
    }
}

int ai_max_speed(MECH * m, AUTO * a)
{
    float ms;

    if (MechDesiredSpeed(m) != (ms = MMaxSpeed(m)))
	return 0;
    return 1;
}

int ai_opponents(AUTO * a, MECH * m)
{
    if (a->auto_nervous) {
	a->auto_nervous--;
	return 1;
    }
    if (MechNumSeen(m))
	a->auto_nervous = 30;	/* We'll stay frisky for awhile even if cons are lost for one reason or another */
    return MechNumSeen(m);
}

void ai_run_speed(MECH * mech, AUTO * a)
{
    /* Warp speed, cap'n! */
    char buf[128];

    if (!ai_max_speed(mech, a)) {
	strcpy(buf, "run");
	mech_speed(a->mynum, mech, buf);
    }
}

void ai_stop(MECH * mech, AUTO * a)
{
    char buf[128];

    if (MechDesiredSpeed(mech) > 0.1) {
	strcpy(buf, "stop");
	mech_speed(a->mynum, mech, buf);
    }
}

#if 0
void ai_set_speed(MECH * mech, AUTO * a, int s)
{
    float ms;
    char buf[SBUF_SIZE];

    if (s >= 99) {		/* To cover rounding errors */
	ai_run_speed(mech, a);
	return;
    }
    if (!s) {
	ai_stop(mech, a);
	return;
    }
    /* Send in percentage-based speed */
    ms = MMaxSpeed(mech);
    ms = ms * s / 100.0;
    if (MechDesiredSpeed(mech) != ms) {
	sprintf(buf, "%f", ms);
	mech_speed(a->mynum, mech, buf);
    }
}
#endif

void ai_set_speed(MECH * mech, AUTO *a, float spd)
{
    char buf[SBUF_SIZE];
    float newspeed;

    if (!mech || !a)
        return;

    newspeed = FBOUNDED(0, spd, ((MMaxSpeed(mech) * a->speed) / 100.0));

    if (MechDesiredSpeed(mech) != newspeed) {
        sprintf(buf, "%f", newspeed);
        mech_speed(a->mynum, mech, buf);
    }
}

void ai_set_heading(MECH * mech, AUTO * a, int dir)
{
    char buf[128];

    if (dir == MechDesiredFacing(mech))
	return;
    sprintf(buf, "%d", dir);
    mech_heading(a->mynum, mech, buf);
}

#define UNREF(a,b,mod) if (a != MAGIC_NUM && b != MAGIC_NUM) { int c = a, d = b; a = (c * (mod - 1) + d) / mod; b = (c + d * (mod - 1)) / mod; }

void ai_adjust_move(AUTO * a, MECH * m, char *text, int hmod, int smod,
    int b_score)
{
    float ms;

    ai_set_heading(m, a, MechDesiredFacing(m) + hmod);
    switch (smod) {
    default:
	SendAI("%s state: %s (hmod:%d) sc:%d", AI_Info(m, a), text, hmod,
	    b_score);
	break;
    case SP_OPT_FASTER:
	SendAI("%s state: %s+accelerating (hmod:%d) sc:%d", AI_Info(m, a),
	    text, hmod, b_score);
	ms = MMaxSpeed(m);
	ai_set_speed(m, a,
	    (float) ((MechDesiredSpeed(m) <
		    MP1 ? MP1 : MechDesiredSpeed(m)) * 4.0 / 3.0));
	break;
    case SP_OPT_SLOWER:
	SendAI("%s state: %s+decelerating (hmod:%d) sc:%d", AI_Info(m, a),
	    text, hmod, b_score);
	ms = MMaxSpeed(m);
	ai_set_speed(m, a,
	    (int) (MechDesiredSpeed(m) * 2.0 / 3.0));
	break;
    }
}

int ai_check_path(MECH * m, AUTO * a, float dx, float dy, float delx,
    float dely)
{
    int o;
    int b_len, bl, b, b_score;
    MAP *map = getMap(m->mapindex);

    o = ai_opponents(a, m);
    if (a->last_upd > mudstate.now || (mudstate.now - a->last_upd) > AUTO_GOET) {
        if ((muxevent_tick - a->last_upd) > AUTO_GOTT) {
            a->b_msc = MAGIC_NUM;
            a->w_msc = MAGIC_NUM;
            a->b_bsc = MAGIC_NUM;
            a->w_bsc = MAGIC_NUM;
            a->b_dan = MAGIC_NUM;
            a->b_dan = (40 + 20 * 29 + 100) * 30;	/* To stay focused */
        } else {
            /* Slight update ; Un-refine the goals somewhat */
            UNREF(a->w_msc, a->b_msc, 3);
            UNREF(a->w_bsc, a->b_bsc, 5);
            UNREF(a->w_dan, a->b_dan, 8);
            a->b_dan = MAX(a->b_dan, (40 + 20 * 29 + 100) * 30);	/* To stay focused */
        }
        a->last_upd = mudstate.now;
    }
    /* Got either opponents (nasty) or [possibly] blocked path (slightly nasty), i.e. 12sec */
    if (MechType(m) == CLASS_MECH)
	getFriends(m, map, 0);
    if (o) {
	getEnemies(m, map, 0);
	if (!((muxevent_tick / AUTOPILOT_GOTO_TICK) % 4)) {	/* Just every fourth tick, i.e. 12sec */
	    /* Thorough check */
	    ai_path_score(m, map, a, move_norm_opt, MNORM_COUNT, 1, dx, dy,
		delx, dely, &bl, &b, &b_score);
	    b_len = b_score / SAFE_SCORE;
	    if (b_len >= MIN_SAFE)
		ai_adjust_move(a, m, "combat(/twitchy)",
		    move_norm_opt[b][0], move_norm_opt[b][1], b_score);
	} else {
	    ai_path_score(m, map, a, combat_fast_opt, CFAST_COUNT, 1, dx,
		dy, delx, dely, &bl, &b, &b_score);
	    b_len = b_score / SAFE_SCORE;
	    if (b_len >= MIN_SAFE)
		ai_adjust_move(a, m, "[f]combat(/twitchy)",
		    combat_fast_opt[b][0], combat_fast_opt[b][1], b_score);
	}
	return 1;		/* We want to keep fighting near foes */
    }
    if (!((muxevent_tick / AUTOPILOT_GOTO_TICK) % 4)) {	/* Just every fourth tick, i.e. 12sec */
	/* Thorough check */
	ai_path_score(m, map, a, move_norm_opt, MNORM_COUNT, 0, dx, dy,
	    delx, dely, &bl, &b, &b_score);
	b_len = b_score / SAFE_SCORE;
	if (b_len >= MIN_SAFE)
	    ai_adjust_move(a, m, "moving", move_norm_opt[b][0],
		move_norm_opt[b][1], b_score);
    } else {
	ai_path_score(m, map, a, combat_fast_opt, CFAST_COUNT, 0, dx, dy,
	    delx, dely, &bl, &b, &b_score);
	b_len = b_score / SAFE_SCORE;
	if (b_len >= MIN_SAFE)
	    ai_adjust_move(a, m, "[f]moving", combat_fast_opt[b][0],
		combat_fast_opt[b][1], b_score);
    }

    if (b_len >= MIN_SAFE)
	return 1;

    /* Slow down + stop - no sense in dying needlessly */
    ai_stop(m, a);
    SendAI("%s state: panic", AI_Info(m, a));
    sendAIM(a, m, "PANIC! Unable to comply with order.");
    return 0;
}

void ai_init(AUTO * a, MECH * m) {

    /* XXX Analyze our unit type ; set basic combat tactic */
    a->auto_cmode = 1;      /* CHARGE! */
    a->auto_cdist = 2;      /* Attempt to avoid kicking distance */
    a->auto_nervous = 0;
    a->auto_goweight = 44;  /* We're mainly concentrating on fighting */
    a->auto_fweight = 55;
    a->speed = 100;         /* Reset to full speed */
    a->flags = 0;
    a->target = -1; 
}

static MECH *target_mech;

int artillery_round_flight_time(float fx, float fy, float tx, float ty);

static int mech_snipe_func(MECH * mech, dbref player, int index, int high)
{
    /* Simulate mech movements until flight_time <= now */
    int now = 0, crashed = 0;
    int flt_time;
    LOC t;
    MAP *map = getMap(mech->mapindex);

    LOCInit(&t, target_mech);
    while ((flt_time =
	    artillery_round_flight_time(MechFX(mech), MechFY(mech), t.fx,
		t.fy)) > now) {
	if (!crashed)
	    if (ai_crash(map, target_mech, &t))
		crashed = 1;
	now++;
    }
    /* Fire at t.x, t.y */
    if (MechTargX(mech) != t.x || MechTargY(mech) != t.y)
	mech_settarget(player, mech, tprintf("%d %d", t.x, t.y));
    mech_fireweapon(player, mech, tprintf("%d", index));
    return 0;
}

void mech_snipe(dbref player, MECH * mech, char *buffer)
{
    char *args[3];
    dbref d;

    DOCHECK(!WizRoy(player), "Permission denied.");
    DOCHECK(mech_parseattributes(buffer, args, 3) != 2,
	"Please supply target ID _and_ weapon(s) to use");
    DOCHECK((d =
	    FindTargetDBREFFromMapNumber(mech, args[0])) <= 0,
	"Invalid target!");
    target_mech = getMech(d);
    multi_weap_sel(mech, player, args[1], 1, mech_snipe_func);

}

/* Experimental (highly) path finding system based on the A* 'a-star'
 * system used in many typical games.
 *
 * Dany - 08/2005 */

/*
 * Create an astar node and return a pointer to it
 */
static astar_node *auto_create_astar_node(short x, short y, 
        short x_parent, short y_parent,
        short g_score, short h_score) {

    astar_node *temp;
    temp = malloc(sizeof(astar_node));
    if (temp == NULL)
        return NULL;

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

    temp->x = x;
    temp->y = y;
    temp->x_parent = x_parent;
    temp->y_parent = y_parent;
    temp->g_score = g_score;
    temp->h_score = h_score;
    temp->f_score = g_score + h_score;
    temp->hexoffset = x * MAPY + y;

    return temp;

}

/*
 * Destroy the astar node
 */
static void auto_destroy_astar_node(astar_node *victim) {

    free(victim);
    return;

}

/*
 * The compare function for the astar pathfinding
 */
static int auto_astar_compare(void *a, void *b, void *token) {

    int *one, *two;

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

    return (*one - *two);
}

/*
 * Function we use to destroy the astar lists using
 * the rbtree walk function
 */
static int auto_astar_callback(void *key, void *data, int depth, void *arg) {

    astar_node *temp;

    temp = (astar_node *) data;
    auto_destroy_astar_node(temp);

    return 1;

}

/*
 * The A* (A-Star) path finding function for the AI
 *
 * Returns 1 if it found a path and 0 if it doesn't
 */
int auto_astar_generate_path(AUTO *autopilot, MECH *mech, short end_x, short end_y) {

    MAP * map = getMap(autopilot->mapindex);
    int found_path = 0;

    /* Our bit arrays */
    unsigned char closed_list_bitfield[MAPX*MAPY/8];
    unsigned char open_list_bitfield[MAPX*MAPY/8];

    float x1, y1, x2, y2;                       /* Floating point vars for real cords */
    short map_x1, map_y1, map_x2, map_y2;       /* The actual map 'hexes' */
    int i;
    int child_g_score, child_h_score;           /* the score values for the child hexes */
    int hexoffset;                              /* temp int to pass around as the hexoffset */

    /* Our lists using Hag's rbtree */
    /* Using two rbtree's to store the open_list so we can sort two
     * different ways */
    rbtree *open_list_by_score;                    /* open list sorted by score */
    rbtree *open_list_by_xy;                       /* open list sorted by hexoffset */
    rbtree *closed_list;                           /* closed list sorted by hexoffset */

    /* Helper node for the final path */
    dllist_node *astar_path_node;

    /* Our astar_node helpers */
    astar_node *temp_astar_node;
    astar_node *parent_astar_node;

#ifdef DEBUG_ASTAR
    /* Log File */
    FILE *logfile;
    char log_msg[MBUF_SIZE];

    /* Open the logfile */
    logfile = fopen("astar.log", "a");

    /* Write first message */
    snprintf(log_msg, MBUF_SIZE, "\nStarting ASTAR Path finding for AI #%d from "
            "%d, %d to %d, %d\n",
            autopilot->mynum, MechX(mech), MechY(mech), end_x, end_y);
    fprintf(logfile, "%s", log_msg);
#endif

    /* Zero the bitfields */
    memset(closed_list_bitfield, 0, sizeof(closed_list_bitfield));
    memset(open_list_bitfield, 0, sizeof(open_list_bitfield));

    /* Setup the trees */
    open_list_by_score = rb_init(&auto_astar_compare, NULL);
    open_list_by_xy = rb_init(&auto_astar_compare, NULL);
    closed_list = rb_init(&auto_astar_compare, NULL);

    /* Setup the path */
    /* Destroy any existing path first */
    auto_destroy_astar_path(autopilot);
    autopilot->astar_path = dllist_create_list();

    /* Setup the start hex */
    temp_astar_node = auto_create_astar_node(MechX(mech), MechY(mech), -1, -1, 0, 0);

    if (temp_astar_node == NULL) {
        /*! \todo {Add code here to break if we can't alloc memory} */

#ifdef DEBUG_ASTAR
        /* Write Log Message */
        snprintf(log_msg, MBUF_SIZE, "AI ERROR - Unable to malloc astar node for "
                "hex %d, %d\n",
                MechX(mech), MechY(mech));
        fprintf(logfile, "%s", log_msg);
#endif

    }

    /* Add start hex to open list */
    rb_insert(open_list_by_score, &temp_astar_node->f_score, temp_astar_node);
    rb_insert(open_list_by_xy, &temp_astar_node->hexoffset, temp_astar_node);
    SetHexBit(open_list_bitfield, temp_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
    /* Log it */
    snprintf(log_msg, MBUF_SIZE, "Added hex %d, %d (%d %d) to open list\n",
            temp_astar_node->x, temp_astar_node->y, temp_astar_node->g_score,
            temp_astar_node->h_score); 
    fprintf(logfile, "%s", log_msg);
#endif

    /* Now loop till we find path */
    while (!found_path) {

        /* Check to make sure there is still stuff in the open list
         * if not, means we couldn't find a path so quit */
        if (rb_size(open_list_by_score) == 0) {
            break;
        }

        /* Get lowest cost node, then remove it from the open list */
        parent_astar_node = (astar_node *) rb_search(open_list_by_score, 
                SEARCH_FIRST, NULL); 

        rb_delete(open_list_by_score, &parent_astar_node->f_score);
        rb_delete(open_list_by_xy, &parent_astar_node->hexoffset);
        ClearHexBit(open_list_bitfield, parent_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
        /* Log it */
        snprintf(log_msg, MBUF_SIZE, "Removed hex %d, %d (%d %d) from open "
                "list - lowest cost node\n",
                parent_astar_node->x, parent_astar_node->y,
                parent_astar_node->g_score, parent_astar_node->h_score); 
        fprintf(logfile, "%s", log_msg);
#endif

        /* Add it to the closed list */
        rb_insert(closed_list, &parent_astar_node->hexoffset, parent_astar_node);
        SetHexBit(closed_list_bitfield, parent_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
        /* Log it */
        snprintf(log_msg, MBUF_SIZE, "Added hex %d, %d (%d %d) to closed list"
                " - lowest cost node\n",
                parent_astar_node->x, parent_astar_node->y,
                parent_astar_node->g_score, parent_astar_node->h_score); 
        fprintf(logfile, "%s", log_msg);
#endif

        /* Now we check to see if we added the end hex to the closed list.
         * When this happens it means we are done */
        if (CheckHexBit(closed_list_bitfield, HexOffSet(end_x, end_y))) {
            found_path = 1;

#ifdef DEBUG_ASTAR
            fprintf(logfile, "Found path for the AI\n");
#endif

            break;
        }

        /* Update open list */
        /* Loop through the hexes around current hex and see if we can add
         * them to the open list */

        /* Set the parent hex of the new nodes */
        map_x1 = parent_astar_node->x;
        map_y1 = parent_astar_node->y;

        /* Going around clockwise direction */
        for (i = 0; i < 360; i += 60) {

            /* Map coord to Real */
            MapCoordToRealCoord(map_x1, map_y1, &x1, &y1);

            /* Calc new hex */
            FindXY(x1, y1, i, 1.0, &x2, &y2);

            /* Real coord to Map */
            RealCoordToMapCoord(&map_x2, &map_y2, x2, y2);

            /* Make sure the hex is sane */
            if (map_x2 < 0 || map_y2 < 0 ||
                    map_x2 >= map->map_width ||
                    map_y2 >= map->map_height)
                continue;

            /* Generate hexoffset for the child node */
            hexoffset = HexOffSet(map_x2, map_y2);

            /* Check to see if its in the closed list 
             * if so just ignore it */
            if (CheckHexBit(closed_list_bitfield, hexoffset))
                continue;

            /* Check to see if we can enter it */
            if ((MechType(mech) == CLASS_MECH) &&
                    (abs(Elevation(map, map_x1, map_y1)
                        - Elevation(map, map_x2, map_y2)) > 2))
                continue;

            if ((MechType(mech) == CLASS_VEH_GROUND) &&
                    (abs(Elevation(map, map_x1, map_y1)
                        - Elevation(map, map_x2, map_y2)) > 1))
                continue;

            /* Score the hex */
            /* Right now just assume movement cost from parent to child hex is
             * the same (so 100) no matter which dir we go*/
            /*! \todo {Possibly add in code to make turning less desirable} */
            child_g_score = 100;

            /* Now add the g score from the parent */
            child_g_score += parent_astar_node->g_score;

            /* Next get range */
            /* Using a varient of the Manhattan method since its perfectly
             * logical for us to go diagonally
             *
             * Basicly just going to get the range,
             * and multiply by 100 */
            /*! \todo {Add in something for elevation cost} */

            /* Get the end hex in real coords, using the old variables 
             * to store the values */
            MapCoordToRealCoord(end_x, end_y, &x1, &y1);
            
            /* Re-using the x2 and y2 values we calc'd for the child hex 
             * to find the range between the child hex and end hex */
            child_h_score = 100 * FindHexRange(x2, y2, x1, y1);

            /* Now add in some modifiers for terrain */
            switch(GetTerrain(map, map_x2, map_y2)) {
                case LIGHT_FOREST:

                    /* Don't bother trying to enter a light forest 
                     * hex unless we can */
                    if ((MechType(mech) == CLASS_VEH_GROUND) &&
                            (MechMove(mech) != MOVE_TRACK))
                        continue;

                    child_g_score += 50;
                    break;
                case ROUGH:
                    child_g_score += 50;
                    break;
                case HEAVY_FOREST:
                    
                    /* Don't bother trying to enter a heavy forest
                     * hex unless we can */
                    if (MechType(mech) == CLASS_VEH_GROUND)
                        continue;

                    child_g_score += 100;
                    break;
                case MOUNTAINS:
                    child_g_score += 100;
                    break;
                case WATER:

                    /* Don't bother trying to enter a water hex
                     * unless we can */
                    if ((MechType(mech) == CLASS_VEH_GROUND) &&
                            (MechMove(mech) != MOVE_HOVER))
                        continue;

                    /* We really don't want them trying to enter water */ 
                    child_g_score += 200;
                    break;
                case HIGHWATER:

                    /* Don't bother trying to enter a water hex
                     * unless we can */
                    if ((MechType(mech) == CLASS_VEH_GROUND) &&
                            (MechMove(mech) != MOVE_HOVER))
                        continue;

                    /* We really don't want them trying to enter water */ 
                    child_g_score += 200;
                    break;
                default:
                    break;
            }

            /* Is it already on the openlist */
            if (CheckHexBit(open_list_bitfield, hexoffset)) {

                /* Ok need to compare the scores and if necessary recalc
                 * and change stuff */
                
                /* Get the node off the open_list */
                temp_astar_node = (astar_node *) rb_find(open_list_by_xy,
                        &hexoffset);

                /* Now compare the 'g_scores' to determine shortest path */
                /* If g_score is lower, this means better path 
                 * from the current parent node */
                if (child_g_score < temp_astar_node->g_score) {

                    /* Remove from open list */
                    rb_delete(open_list_by_score, &temp_astar_node->f_score);
                    rb_delete(open_list_by_xy, &temp_astar_node->hexoffset);
                    ClearHexBit(open_list_bitfield, temp_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
                    /* Log it */
                    snprintf(log_msg, MBUF_SIZE, "Removed hex %d, %d (%d %d) from "
                            "open list - score recal\n",
                            temp_astar_node->x, temp_astar_node->y,
                            temp_astar_node->g_score, temp_astar_node->h_score); 
                    fprintf(logfile, "%s", log_msg);
#endif

                    /* Recalc score */
                    /* H-Score should be the same since the hex doesn't move */
                    temp_astar_node->g_score = child_g_score;
                    temp_astar_node->f_score = temp_astar_node->g_score +
                        temp_astar_node->h_score;

                    /* Change parent hex */
                    temp_astar_node->x_parent = map_x1;
                    temp_astar_node->y_parent = map_y1;

                    /* Will re-add the node below */

                } else {

                    /* Don't need to do anything so we can skip 
                     * to the next node */
                    continue;

                }

            } else {

                /* Node isn't on the open list so we have to create it */
                temp_astar_node = auto_create_astar_node(map_x2, map_y2, map_x1, map_y1,
                        child_g_score, child_h_score);
            
                if (temp_astar_node == NULL) {
                    /*! \todo {Add code here to break if we can't alloc memory} */

#ifdef DEBUG_ASTAR
                    /* Log it */
                    snprintf(log_msg, MBUF_SIZE, "AI ERROR - Unable to malloc astar"
                            " node for hex %d, %d\n",
                            map_x2, map_y2);
                    fprintf(logfile, "%s", log_msg);
#endif

                }

            }

            /* Now add (or re-add) the node to the open list */

            /* Hack to check to make sure its score is not already on the open
             * list. This slightly skews the results towards nodes found earlier
             * then those found later */
            while (1) {

                if (rb_exists(open_list_by_score, &temp_astar_node->f_score)) {
                    temp_astar_node->f_score++;

#ifdef DEBUG_ASTAR
                    fprintf(logfile, "Adjusting score for hex %d, %d - same"
                            " fscore already exists\n",
                            temp_astar_node->x, temp_astar_node->y);
#endif

                } else {
                    break;
                }

            }
            rb_insert(open_list_by_score, &temp_astar_node->f_score, temp_astar_node);
            rb_insert(open_list_by_xy, &temp_astar_node->hexoffset, temp_astar_node);
            SetHexBit(open_list_bitfield, temp_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
            /* Log it */
            snprintf(log_msg, MBUF_SIZE, "Added hex %d, %d (%d %d) to open list\n",
                    temp_astar_node->x, temp_astar_node->y,
                    temp_astar_node->g_score, temp_astar_node->h_score);
            fprintf(logfile, "%s", log_msg);
#endif

        } /* End of looking for hexes next to us */
       
    } /* End of looking for path */

    /* We Done lets go */

    /* Lets first see if we found a path */
    if (found_path) {

#ifdef DEBUG_ASTAR
        /* Log Message */
        fprintf(logfile, "Building Path from closed list for AI\n");
#endif

        /* Found a path so we need to go through the closed list
         * and generate it */

        /* Get the end hex, find its parent hex and work back to
         * start hex while building list */
       
        /* Get end hex from closed list */
        hexoffset = HexOffSet(end_x, end_y);
        temp_astar_node = rb_find(closed_list, &hexoffset);

        /* Add end hex to path list */
        astar_path_node = dllist_create_node(temp_astar_node);
        dllist_insert_beginning(autopilot->astar_path, astar_path_node);

#ifdef DEBUG_ASTAR
        /* Log it */
        fprintf(logfile, "Added hex %d, %d to path list\n",
                temp_astar_node->x, temp_astar_node->y);
#endif

        /* Remove it from closed list */
        rb_delete(closed_list, &temp_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
        /* Log it */
        fprintf(logfile, "Removed hex %d, %d from closed list - path list work\n",
                temp_astar_node->x, temp_astar_node->y);
#endif

        /* Check if the end hex is the start hex */
        if (!(temp_astar_node->x == MechX(mech) &&
                temp_astar_node->y == MechY(mech))) {

            /* Its not so lets loop through the closed list
             * building the path */

            /* Loop */
            while (1) {

                /* Get Parent Node Offset*/
                hexoffset = HexOffSet(temp_astar_node->x_parent, 
                        temp_astar_node->y_parent);

                /*! \todo {Possibly add check here incase the node we're
                 * looking for some how did not end up on the list} */

                /* Get Parent Node from closed list */
                parent_astar_node = rb_find(closed_list, &hexoffset);

                /* Check if start hex */
                /* If start hex quit */
                if (parent_astar_node->x == MechX(mech) &&
                        parent_astar_node->y == MechY(mech)) {
                    break;
                }

                /* Add to path list */
                astar_path_node = dllist_create_node(parent_astar_node);
                dllist_insert_beginning(autopilot->astar_path, astar_path_node);

#ifdef DEBUG_ASTAR
                /* Log it */
                fprintf(logfile, "Added hex %d, %d to path list\n",
                        parent_astar_node->x, parent_astar_node->y);
#endif

                /* Remove from closed list */
                rb_delete(closed_list, &parent_astar_node->hexoffset);

#ifdef DEBUG_ASTAR
                /* Log it */
                fprintf(logfile, "Removed hex %d, %d from closed list - path list work\n",
                        parent_astar_node->x, parent_astar_node->y);
#endif

                /* Make parent new child */
                temp_astar_node = parent_astar_node;

            } /* End of while loop */

        }

        /* Done with the path its cleanup time */

    }

    /* Make sure we destroy all the objects we dont need any more */

#ifdef DEBUG_ASTAR
    /* Log Message */
    fprintf(logfile, "Destorying the AI lists\n");
#endif

    /* Destroy the open lists */
    rb_walk(open_list_by_score, WALK_INORDER, &auto_astar_callback, NULL);
    rb_destroy(open_list_by_score);
    rb_destroy(open_list_by_xy);

    /* Destroy the closed list */
    rb_walk(closed_list, WALK_INORDER, &auto_astar_callback, NULL);
    rb_destroy(closed_list);

#ifdef DEBUG_ASTAR
    /* Close Log file */
    fclose(logfile);
#endif

    /* End */
    if (found_path) {
        return 1;
    } else {
        return 0;
    }

}

/* Function to Smooth out the AI path and remove
 * nodes we don't need */
/* Not even close to being finished yet */
void astar_smooth_path(AUTO *autopilot) {

    dllist_node *current, *next, *prev;

    float x1, y1, x2, y2;
    int degrees;

    /* Get the n node off the list */

    /* Get the n+1 node off the list */

    /* Get bearing from n to n+1 */

    /* Get n+2 node off list */

    /* Get bearing from n to n+2 node */

    /* Compare bearings */
        /* If in same direction as previous
         * don't need n+1 node */

    /* Keep looping till bearing doesn't match */
    /* Then reset n node to final node and continue */

    return;

}

void auto_destroy_astar_path(AUTO *autopilot) {

    astar_node *temp_astar_node;

    /* Make sure there is a path if not quit */
    if (!(autopilot->astar_path))
        return;

    /* There is a path lets kill it */
    if (dllist_size(autopilot->astar_path) > 0) {

        while (dllist_size(autopilot->astar_path)) {
            temp_astar_node = dllist_remove_node_at_pos(autopilot->astar_path, 1);
            auto_destroy_astar_node(temp_astar_node);
        }

    }

    /* Finally destroying the path */
    dllist_destroy_list(autopilot->astar_path);
    autopilot->astar_path = NULL;

}