/* * $Id: autopilot.c,v 1.2 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: Wed Oct 30 19:35:21 1996 fingon * Last modified: Sun Jun 14 14:13:13 1998 fingon * */ #include <math.h> #include "mech.h" #include "mech.events.h" #include "autopilot.h" #include "create.h" #include "p.mech.startup.h" #include "p.econ.h" #include "p.econ_cmds.h" #include "p.mech.maps.h" #include "p.mech.utils.h" #include "p.eject.h" #include "p.btechstats.h" #include "p.bsuit.h" #include "p.mech.pickup.h" #include "p.mech.los.h" #include "p.ds.bay.h" #include "p.glue.h" /* * List of all the available autopilot commands * that can be given to the AI. These use a * large enum that is located in autopilot.h */ ACOM acom[AUTO_NUM_COMMANDS + 1] = { {"chasetarget", 1, GOAL_CHASETARGET, NULL} , /* Extension of follow, for chasetarget */ {"dumbfollow", 1, GOAL_DUMBFOLLOW, NULL} , /* [dumbly] follow the given target */ {"dumbgoto", 2, GOAL_DUMBGOTO, NULL} , /* [dumbly] goto a given hex */ {"enterbase", 1, GOAL_ENTERBASE, NULL} , /* enterbase via <dir> */ {"follow", 1, GOAL_FOLLOW, NULL} , /* follow the given target */ {"goto", 2, GOAL_GOTO, NULL} , /* goto a given hex */ {"leavebase", 1, GOAL_LEAVEBASE, NULL} , /* leave a hangar */ {"oldgoto", 2, GOAL_OLDGOTO, NULL} , /* Old style goto - will phase out */ {"roam", 1, GOAL_ROAM, NULL} , /* roam around an area - like patroling */ {"wait", 2, GOAL_WAIT, NULL} , /* sit there and don't do anything for a while */ {"attackleg", 1, COMMAND_ATTACKLEG, NULL} , /* ? */ {"autogun", 1, COMMAND_AUTOGUN, NULL} , /* Let the AI decide what to shoot */ {"chasemode", 1, COMMAND_CHASEMODE, NULL} , /* chase after a target or not */ {"cmode", 2, COMMAND_CMODE, NULL} , /* ? */ {"dropoff", 0, COMMAND_DROPOFF, NULL} , /* dropoff whatever the AI is towing */ {"embark", 1, COMMAND_EMBARK, NULL} , /* embark a carrier */ {"enterbay", 0, COMMAND_ENTERBAY, NULL} , /* enter a DS's bay */ {"jump", 1, COMMAND_JUMP, NULL} , /* jump */ {"load", 0, COMMAND_LOAD, NULL} , /* load cargo */ {"pickup", 1, COMMAND_PICKUP, NULL} , /* pickup a given target */ {"report", 0, COMMAND_REPORT, NULL} , /* report current conditions */ {"roammode", 1, COMMAND_ROAMMODE, NULL} , /* more roam stuff */ {"shutdown", 0, COMMAND_SHUTDOWN, NULL} , /* shutdown the AI's unit */ {"speed", 1, COMMAND_SPEED, NULL} , /* set a given speed (% of max) */ {"startup", 0, COMMAND_STARTUP, NULL} , /* startup an AI's unit */ {"stopgun", 0, COMMAND_STOPGUN, NULL} , /* make the AI stop shooting stuff */ {"swarm", 1, COMMAND_SWARM, NULL} , /* ? */ {"swarmmode", 1, COMMAND_SWARMMODE, NULL} , /* ? */ {"udisembark", 0, COMMAND_UDISEMBARK, NULL} , /* disembark from a carrier */ {"unload", 0, COMMAND_UNLOAD, NULL} , /* unload cargo */ {NULL, 0, AUTO_NUM_COMMANDS, NULL} }; /* backwards compat till I can fix all of these */ /* \todo {Get rid of these once we're done redoing the AI} */ #define GSTART AUTO_GSTART #define PSTART AUTO_PSTART #define CCH AUTO_CHECKS #define REDO AUTO_COM /* * AI Startup - force AI to startup if its not */ void auto_command_startup(AUTO *autopilot, MECH *mech) { if (Started(mech)) return; if (!Starting(mech)) { mech_startup(autopilot->mynum, mech, ""); auto_goto_next_command(autopilot, AUTOPILOT_STARTUP_TICK); } } /* * AI Shutdown - force AI to shutdown if its not */ void auto_command_shutdown(AUTO *autopilot, MECH *mech) { if (!Started(mech)) return; mech_shutdown(autopilot->mynum, mech, ""); } #if 0 /*! \todo {Not really sure what this does and don't really care I just know we need to do something about this} */ void gradually_load(MECH * mech, int loc, int percent) { int pile[BRANDCOUNT + 1][NUM_ITEMS]; float spd = (float) MMaxSpeed(mech); float nspd = (float) MechCargoMaxSpeed(mech, (float) spd); int cnt = 0; char *t; int i, j; int i1, i2, i3; int lastid = -1, lastbrand = -1; /* XXX Fix this - was broken when CargoMaxSpeed interface changed */ bzero(pile, sizeof(pile)); t = silly_atr_get(loc, A_ECONPARTS); while (*t) { if (*t == '[') if ((sscanf(t, "[%d,%d,%d]", &i1, &i2, &i3)) == 3) { pile[i2][i1] += i3; cnt++; } t++; } while (nspd > ((float) spd * percent / 100) && cnt) { for (j = 0; j <= BRANDCOUNT; j++) { for (i = 0; i < NUM_ITEMS; i++) if (pile[j][i]) break; if (i != NUM_ITEMS) break; } if (i == NUM_ITEMS) break; lastid = i; lastbrand = j; econ_change_items(loc, i, j, -1); econ_change_items(mech->mynum, i, j, 1); pile[j][i]--; cnt--; SetCargoWeight(mech); nspd = (float) MechCargoMaxSpeed(mech, (float) spd); } if (lastid >= 0) { i = lastid; j = lastbrand; econ_change_items(loc, i, j, 1); econ_change_items(mech->mynum, i, j, -1); } SetCargoWeight(mech); } void autopilot_load_cargo(dbref player, MECH * mech, int percent) { DOCHECK(fabs(MechSpeed(mech)) > MP1, "You're moving too fast!"); DOCHECK(Location(mech->mynum) != mech->mapindex || In_Character(Location(mech->mynum)), "You aren't inside hangar!"); if (loading_bay_whine(player, Location(mech->mynum), mech)) return; gradually_load(mech, mech->mapindex, percent); SetCargoWeight(mech); } #endif /* Recal the AI to the proper map */ /*! \todo{Possibly move this to autopilot_core.c} */ void auto_cal_mapindex(MECH * mech) { AUTO *autopilot; char error_buf[MBUF_SIZE]; if (!mech) { SendError("Null pointer catch in auto_cal_mapindex"); return; } if (MechAuto(mech) > 0) { if (!(autopilot = FindObjectsData(MechAuto(mech))) || !Good_obj(MechAuto(mech)) || Location(MechAuto(mech)) != mech->mynum) { snprintf(error_buf, MBUF_SIZE, "Mech #%d thinks it has the Autopilot #%d on it" " but FindObj breaks", mech->mynum, MechAuto(mech)); SendError(error_buf); MechAuto(mech) = -1; } else { /* Check here if the AI is either entering or leaving a base * so it doesn't reset the mapindex which the specific commands * need */ switch (auto_get_command_enum(autopilot, 1)) { case GOAL_LEAVEBASE: break; case GOAL_ENTERBASE: break; default: autopilot->mapindex = mech->mapindex; } } } return; } /* * Function to turn chasetarget on/off as well as let the AI * remember that it was on. * * Figured this was easier then coding a bunch of blocks of * stuff all over the place. */ void auto_set_chasetarget_mode(AUTO *autopilot, int mode) { /* Depending on the mode we do different things*/ switch (mode) { case AUTO_CHASETARGET_ON: /* Start Chasing */ if (!ChasingTarget(autopilot)) StartChasingTarget(autopilot); /* Reset this flag because we don't need it set */ if (WasChasingTarget(autopilot)) ForgetChasingTarget(autopilot); /* Flags to reset */ autopilot->chase_target = -10; autopilot->chasetarg_update_tick = AUTOPILOT_CHASETARG_UPDATE_TICK; break; case AUTO_CHASETARGET_OFF: /* Stop Chasing */ if (ChasingTarget(autopilot)) StopChasingTarget(autopilot); /* Reset this flag because we don't need it set */ if (WasChasingTarget(autopilot)) ForgetChasingTarget(autopilot); break; case AUTO_CHASETARGET_REMEMBER: /* If we we had chasetarget on - turn it back on */ if (WasChasingTarget(autopilot)) { /* Start chasing */ if (!ChasingTarget(autopilot)) StartChasingTarget(autopilot); /* Reset the values */ autopilot->chase_target = -10; autopilot->chasetarg_update_tick = AUTOPILOT_CHASETARG_UPDATE_TICK; /* Unset the flag because we don't need it now */ ForgetChasingTarget(autopilot); } break; case AUTO_CHASETARGET_SAVE: /* If we are chasing a target turn this off * but save it */ if (ChasingTarget(autopilot)) { StopChasingTarget(autopilot); RememberChasingTarget(autopilot); } break; } } #if 0 void autopilot_cmode(AUTO * a, MECH * mech, int mode, int range) { static char buf[MBUF_SIZE]; if (!a || !mech) return; if (mode < 0 || mode > 2) return; if (range < 0 || range > 40) return; a->auto_cdist = range; a->auto_cmode = mode; return; } void autopilot_swarm(MECH * mech, char *id) { if (MechType(mech) == CLASS_BSUIT) bsuit_swarm(GOD, mech, id); } void autopilot_attackleg(MECH * mech, char *id) { bsuit_attackleg(GOD, mech, id); } #endif /* * Interface to the autogun system * Even tho it takes 1 argument, we will parse that * 1 argument looking for pieces. */ void auto_command_autogun(AUTO *autopilot, MECH *mech) { dbref target_dbref; MECH *target; char *argument; char error_buf[MBUF_SIZE]; char *args[AUTOPILOT_MAX_ARGS - 1]; int argc; int i; /* Read in the argument */ argument = auto_get_command_arg(autopilot, 1, 1); /* Parse the argument */ argc = proper_explodearguments(argument, args, AUTOPILOT_MAX_ARGS - 1); /* Free the argument */ free(argument); /* Now we check to see how many arguments it found */ if (argc == 1) { /* Ok its either going to be on or off */ if (strcmp(args[0], "on") == 0) { /* Reset the AI parameters */ autopilot->target = -1; autopilot->target_score = 0; autopilot->target_update_tick = AUTO_GUN_UPDATE_TICK; /* Check if assigned target flag on */ if (AssignedTarget(autopilot)) { UnassignTarget(autopilot); } /* Get the AI going */ AUTO_GSTART(autopilot, mech); if (Gunning(autopilot)) { DoStopGun(autopilot); } DoStartGun(autopilot); } else if (strcmp(args[0], "off") == 0) { /* Reset the target */ autopilot->target = -2; autopilot->target_score = 0; autopilot->target_update_tick = 0; /* Check if Assigned Target Flag on */ if (AssignedTarget(autopilot)) { UnassignTarget(autopilot); } if (Gunning(autopilot)) { DoStopGun(autopilot); } } else { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for autogun command", autopilot->mynum); SendAI(error_buf); } } else if (argc == 2) { /* Check for 'target' */ if (strcmp(args[0], "target") == 0) { /* Read in the 2nd argument - the target */ if (Readnum(target_dbref, args[1])) { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for autogun command", autopilot->mynum); SendAI(error_buf); /* Free Args */ for (i = 0; i < AUTOPILOT_MAX_ARGS - 1; i++) { if (args[i]) free(args[i]); } return; } /* Now see if its a mech */ if (!(target = getMech(target_dbref))) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " target for autogun command", autopilot->mynum); SendAI(error_buf); /* Free Args */ for (i = 0; i < AUTOPILOT_MAX_ARGS - 1; i++) { if (args[i]) free(args[i]); } return; } /* Ok valid unit so lets lock it and setup parameters */ autopilot->target = target_dbref; autopilot->target_score = 0; autopilot->target_update_tick = 0; /* Set the Assigned Flag */ if (!AssignedTarget(autopilot)) { AssignTarget(autopilot); } /* Get the AI going */ AUTO_GSTART(autopilot, mech); if (Gunning(autopilot)) { DoStopGun(autopilot); } DoStartGun(autopilot); } else { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for autogun command", autopilot->mynum); SendAI(error_buf); } } /* Free Args */ for (i = 0; i < AUTOPILOT_MAX_ARGS - 1; i++) { if (args[i]) free(args[i]); } } /* * Command to interface between chasetarget and follow */ void auto_command_chasetarget(AUTO *autopilot) { /* Fire off follow event */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 1); return; } /* * Command to try to get AI to pickup a target */ void auto_command_pickup(AUTO *autopilot, MECH *mech) { char *argument; int target; char error_buf[MBUF_SIZE]; char buf[SBUF_SIZE]; MECH *tempmech; /*! \todo {Add in more checks for picking up target} */ /* Read in the argument */ argument = auto_get_command_arg(autopilot, 1, 1); if (Readnum(target, argument)) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for pickup command", autopilot->mynum); SendAI(error_buf); free(argument); return; } free(argument); /* Check the target */ if (!(tempmech = getMech(target))) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d unable to pickup" " unit #%d", autopilot->mynum, target); SendAI(error_buf); return; } /* Now try and pick it up */ strcpy(buf, MechIDS(tempmech, 1)); mech_pickup(GOD, mech, buf); /*! \todo {Possibly add in something either here or in autopilot_radio.c * so that when the unit is picked up or not, it radios a message} */ } /* * Tell AI to drop whatever they're carrying */ void auto_command_dropoff(MECH *mech) { mech_dropoff(GOD, mech, NULL); } /* * Tell AI to set its speed (in %) */ void auto_command_speed(AUTO *autopilot) { char *argument; unsigned short speed; char error_buf[MBUF_SIZE]; /* Read in the argument */ argument = auto_get_command_arg(autopilot, 1, 1); if (Readnum(speed, argument)) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for speed command", autopilot->mynum); SendAI(error_buf); free(argument); return; } free(argument); /* Make sure its a valid speed value */ if (speed < 1 || speed > 100) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for speed command - out side of the range", autopilot->mynum); SendAI(error_buf); return; } /* Now set it */ autopilot->speed = speed; } /* * Command to get AI to embark a carrier */ void auto_command_embark(AUTO *autopilot, MECH *mech) { char *argument; int target; char error_buf[MBUF_SIZE]; char buf[SBUF_SIZE]; MECH *tempmech; /* Make sure the mech is on and standing */ AUTO_GSTART(autopilot, mech); /* Read in the argument */ argument = auto_get_command_arg(autopilot, 1, 1); if (Readnum(target, argument)) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for embark command", autopilot->mynum); SendAI(error_buf); free(argument); return; } free(argument); /* Check the target */ if (!(tempmech = getMech(target))) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d unable to embark" " unit #%d", autopilot->mynum, target); SendAI(error_buf); return; } strcpy(buf, MechIDS(tempmech, 1)); mech_embark(GOD, mech, buf); } /* * Function to force AI to disembark a carrier */ void auto_command_udisembark(MECH *mech) { dbref pil = -1; char *buf; buf = silly_atr_get(mech->mynum, A_PILOTNUM); sscanf(buf, "#%d", &pil); mech_udisembark(pil, mech, ""); } #if 0 void autopilot_enterbase(MECH * mech, int dir) { static char strng[2]; switch (dir) { case 0: strcpy(strng, "n"); break; case 1: strcpy(strng, "e"); break; case 2: strcpy(strng, "s"); break; case 3: strcpy(strng, "w"); break; default: sprintf(strng, "%c", dir); break; } mech_enterbase(GOD, mech, strng); } #endif /* * Main Autopilot event, checks to see what command we should * be running and tries to run it */ void auto_com_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; MECH *mech = autopilot->mymech; MECH *tempmech; char buf[SBUF_SIZE]; int i, j, t; command_node *command; /* No mech and/or no AI */ if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Make sure the map exists */ if (!(FindObjectsData(mech->mapindex))) { autopilot->mapindex = mech->mapindex; PilZombify(autopilot); /* if (GVAL(a, 0) != COMMAND_UDISEMBARK && GVAL(a, 0) != GOAL_WAIT) return; */ if (auto_get_command_enum(autopilot, 1) != COMMAND_UDISEMBARK) return; } /* Set the MAP on the AI */ if (autopilot->mapindex < 0) autopilot->mapindex = mech->mapindex; /* Basic Checks */ AUTO_CHECKS(autopilot); /* Get the enum value for the FIRST command */ switch (auto_get_command_enum(autopilot, 1)) { /* First check the various GOALs then the COMMANDs */ case GOAL_CHASETARGET: auto_command_chasetarget(autopilot); return; case GOAL_DUMBGOTO: AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_dumbgoto_event, AUTOPILOT_GOTO_TICK, 0); return; case GOAL_DUMBFOLLOW: AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_dumbfollow_event, AUTOPILOT_FOLLOW_TICK, 0); return; case GOAL_ENTERBASE: AUTOEVENT(autopilot, EVENT_AUTOENTERBASE, auto_enter_event, AUTOPILOT_NC_DELAY, 1); return; case GOAL_FOLLOW: AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 1); return; case GOAL_GOTO: AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_astar_goto_event, AUTOPILOT_GOTO_TICK, 1); return; case GOAL_LEAVEBASE: AUTOEVENT(autopilot, EVENT_AUTOLEAVE, auto_leave_event, AUTOPILOT_LEAVE_TICK, 1); return; case GOAL_OLDGOTO: AUTO_GSTART(autopilot, mech); AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_goto_event, AUTOPILOT_GOTO_TICK, 0); return; case GOAL_ROAM: auto_command_roam(autopilot, mech); return; #if 0 case GOAL_WAIT: i = GVAL(a, 1); j = GVAL(a, 2); if (!i) { PG(a) += CCLEN(a); AUTOEVENT(a, EVENT_AUTOCOM, auto_com_event, MAX(1, j), 0); } else { if (i == 1) { if (MechNumSeen(mech)) { ADVANCE_PG(a); } else { AUTOEVENT(a, EVENT_AUTOCOM, auto_com_event, AUTOPILOT_WAITFOE_TICK, 0); } } else { ADVANCE_PG(a); } } return; #endif #if 0 case COMMAND_ATTACKLEG: if (!(tempmech = getMech(GVAL(a, 1)))) { SendAI(tprintf("AIAttacklegError #%d", GVAL(a,1))); //ADVANCE_PG(a); auto_goto_next_command(a); return; } strcpy(buf, MechIDS(tempmech, 1)); autopilot_attackleg(mech, buf); //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif case COMMAND_AUTOGUN: auto_command_autogun(autopilot, mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); break; #if 0 case COMMAND_CHASEMODE: if (GVAL(a,1)) a->flags |= AUTOPILOT_CHASETARG; else a->flags &= ~AUTOPILOT_CHASETARG; //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif #if 0 case COMMAND_CMODE: i = GVAL(a,1); j = GVAL(a,2); autopilot_cmode(a, mech, i, j); //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif case COMMAND_DROPOFF: auto_command_dropoff(mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; case COMMAND_EMBARK: auto_command_embark(autopilot, mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; #if 0 case COMMAND_ENTERBAY: PSTART(a, mech); mech_enterbay(GOD, mech, my2string("")); //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif #if 0 case COMMAND_JUMP: if (auto_valid_progline(a, GVAL(a, 1))) { PG(a) = GVAL(a, 1); AUTOEVENT(a, EVENT_AUTOCOM, auto_com_event, AUTOPILOT_NC_DELAY, 0); } else { ADVANCE_PG(a); } return; #endif #if 0 case COMMAND_LOAD: /* mech_loadcargo(GOD, mech, "50"); */ autopilot_load_cargo(GOD, mech, 50); //ADVANCE_PG(a); auto_goto_next_command(a); break; #endif case COMMAND_PICKUP: auto_command_pickup(autopilot, mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; #if 0 case COMMAND_ROAMMODE: t = a->flags; if (GVAL(a,1)) { a->flags |= AUTOPILOT_ROAMMODE; if (!(t & AUTOPILOT_ROAMMODE)) { if (MechType(mech) == CLASS_BSUIT) a->flags |= AUTOPILOT_SWARMCHARGE; auto_addcommand(a->mynum, a, tprintf("roam 0 0")); } } else { a->flags &= ~AUTOPILOT_ROAMMODE; } //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif case COMMAND_SHUTDOWN: auto_command_shutdown(autopilot, mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; case COMMAND_SPEED: auto_command_speed(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; case COMMAND_STARTUP: auto_command_startup(autopilot, mech); return; #if 0 case COMMAND_STOPGUN: if (Gunning(a)) DoStopGun(a); //ADVANCE_PG(a); auto_goto_next_command(a); break; #endif #if 0 case COMMAND_SWARM: if (!(tempmech = getMech(GVAL(a, 1)))) { SendAI(tprintf("AISwarmError #%d", GVAL(a,1))); //ADVANCE_PG(a); auto_goto_next_command(a); return; } strcpy(buf, MechIDS(tempmech, 1)); autopilot_swarm(mech, buf); //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif #if 0 case COMMAND_SWARMMODE: if (MechType(mech) != CLASS_BSUIT) { //ADVANCE_PG(a); auto_goto_next_command(a); return; } if (GVAL(a,1)) a->flags |= AUTOPILOT_SWARMCHARGE; else a->flags &= ~AUTOPILOT_SWARMCHARGE; //ADVANCE_PG(a); auto_goto_next_command(a); return; #endif case COMMAND_UDISEMBARK: auto_command_udisembark(mech); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; #if 0 case COMMAND_UNLOAD: mech_unloadcargo(GOD, mech, my2string(" * 9999")); //ADVANCE_PG(a); auto_goto_next_command(a); break; #endif } } /*! \todo {Make the speed up and slow down functions behave a little better} */ /* * Function to force the AI to move if its not near its target */ static void speed_up_if_neccessary(AUTO * a, MECH * mech, int tx, int ty, int bearing) { if (bearing < 0 || abs((int) MechDesiredSpeed(mech)) < 2) if (bearing < 0 || abs(bearing - MechFacing(mech)) <= 30) if (MechX(mech) != tx || MechY(mech) != ty) { ai_set_speed(mech, a, MMaxSpeed(mech)); } } /* * Quick function to change the AI's heading to the current * bearing of its target */ static void update_wanted_heading(AUTO * a, MECH * mech, int bearing) { if (MechDesiredFacing(mech) != bearing) mech_heading(a->mynum, mech, tprintf("%d", bearing)); } /* * Slow down the AI if its close to its target hex */ /*! \todo {Make this more variable perhaps so it wont always slow down?} */ static int slow_down_if_neccessary(AUTO * a, MECH * mech, float range, int bearing, int tx, int ty) { if (range < 0) range = 0; if (range > 2.0) return 0; if (abs(bearing - MechFacing(mech)) > 30) { /* Fix the bearing as well */ ai_set_speed(mech, a, 0); update_wanted_heading(a, mech, bearing); } else if (tx == MechX(mech) && ty == MechY(mech)) { ai_set_speed(mech, a, 0); } else { /* slowdown */ ai_set_speed(mech, a, (float) (0.4 + range / 2.0) * MMaxSpeed(mech)); } return 1; } /* * Quick calcualtion of range and bearing from mech to target * hex */ void figure_out_range_and_bearing(MECH * mech, int tx, int ty, float *range, int *bearing) { float x, y; MapCoordToRealCoord(tx, ty, &x, &y); *bearing = FindBearing(MechFX(mech), MechFY(mech), x, y); *range = FindHexRange(MechFX(mech), MechFY(mech), x, y); } /* Basically, all we need to do is course correction now and then. In case we get disabled, we call for help now and then */ /* * Old goto system - will phase it out */ void auto_goto_event(MUXEVENT * e) { AUTO *autopilot = (AUTO *) e->data; int tx, ty; float dx, dy; MECH *mech = autopilot->mymech; float range; int bearing; char *argument; if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Basic Checks */ AUTO_CHECKS(autopilot); /* Make sure mech is started and standing */ AUTO_GSTART(autopilot, mech); /* Get the first argument - x coord */ argument = auto_get_command_arg(autopilot, 1, 1); if (Readnum(tx, argument)) { /*! \todo {add a thing here incase the argument isn't a number} */ free(argument); } free(argument); /* Get the second argument - y coord */ argument = auto_get_command_arg(autopilot, 1, 2); if (Readnum(ty, argument)) { /*! \todo {add a thing here incase the argument isn't a number} */ free(argument); } free(argument); if (MechX(mech) == tx && MechY(mech) == ty && abs(MechSpeed(mech)) < 0.5) { /* We've reached this goal! Time for next one. */ ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } MapCoordToRealCoord(tx, ty, &dx, &dy); figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); if (!slow_down_if_neccessary(autopilot, mech, range, bearing, tx, ty)) { /* Use the AI */ if (ai_check_path(mech, autopilot, dx, dy, 0.0, 0.0)) AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_goto_event, AUTOPILOT_GOTO_TICK, 0); } else { AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_goto_event, AUTOPILOT_GOTO_TICK, 0); } } #if 0 /* ROAMMODE is a funky beast */ void auto_roam_event(MUXEVENT * e) { AUTO *a = (AUTO *) e->data; int tx, ty; float dx, dy, range; MECH *mech = a->mymech; MAP *map; int bearing, i = 1, t; if (!IsMech(mech->mynum) || !IsAuto(a->mynum)) return; CCH(a); GSTART(a, mech); tx = GVAL(a, 1); ty = GVAL(a, 2); if (!mech || !(map = FindObjectsData(mech->mapindex))) { return; } if (!(a->flags & AUTOPILOT_ROAMMODE) || MechTarget(mech) > 0) { return; } if ((tx == 0 && ty == 0) || e->data2 > 0 || (MechX(mech) == tx && MechY(mech) == ty && abs(MechSpeed(mech)) < 0.5)) { while (i) { tx = BOUNDED(1, Number(20, map->map_width - 21), map->map_width - 1); ty = BOUNDED(1, Number(20, map->map_height - 21), map->map_height - 1); MapCoordToRealCoord(tx, ty, &dx, &dy); t = GetRTerrain(map, tx, ty); range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech), dx, dy, ZSCALE * GetElev(map, tx, ty)); if ((InLineOfSight(mech, NULL, tx, ty, range) && t != WATER && t != HIGHWATER && t != MOUNTAINS ) || i > 5000) { i = 0; } else { i++; } } a->commands[a->program_counter + 1] = tx; a->commands[a->program_counter + 2] = ty; AUTOEVENT(a, EVENT_AUTOGOTO, auto_roam_event, AUTOPILOT_GOTO_TICK, 0); return; } MapCoordToRealCoord(tx, ty, &dx, &dy); figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); if (!slow_down_if_neccessary(a, mech, range, bearing, tx, ty)) { /* Use the AI */ if (ai_check_path(mech, a, dx, dy, 0.0, 0.0)) AUTOEVENT(a, EVENT_AUTOGOTO, auto_roam_event, AUTOPILOT_GOTO_TICK, 0); } else { AUTOEVENT(a, EVENT_AUTOGOTO, auto_roam_event, AUTOPILOT_GOTO_TICK, 0); } } #endif /* * Dumbly[goto] a given a hex */ void auto_dumbgoto_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; int tx, ty; MECH *mech = autopilot->mymech; MAP *map; float range; int bearing; char *argument; char error_buf[MBUF_SIZE]; if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_DUMBGOTO) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto [dumbly] with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_dumbgoto_event, AUTOPILOT_STARTUP_TICK, 0); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_dumbgoto_event, AUTOPILOT_NC_DELAY, 0); return; } /*! \todo {Add something in here for other units} */ /* Get the first argument - x coord */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto [dumbly] with AI #%d but was unable to - bad" " first argument - going to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Read in the argument */ if (Readnum(tx, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto [dumbly] with AI #%d but was unable to - bad" " first argument '%s' - going to next command", autopilot->mynum, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* Get the first argument - y coord */ if (!(argument = auto_get_command_arg(autopilot, 1, 2))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto [dumbly] with AI #%d but was unable to - bad" " second argument - going to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Read in the argument */ if (Readnum(ty, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto [dumbly] with AI #%d but was unable to - bad" " second argument '%s' - going to next command", autopilot->mynum, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* If we're at the target hex - stop */ if (MechX(mech) == tx && MechY(mech) == ty && abs(MechSpeed(mech)) < 0.5) { /* We've reached this goal! Time for next one. */ ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make our way to the goal */ figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); speed_up_if_neccessary(autopilot, mech, tx, ty, bearing); slow_down_if_neccessary(autopilot, mech, range, bearing, tx, ty); update_wanted_heading(autopilot, mech, bearing); AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_dumbgoto_event, AUTOPILOT_GOTO_TICK, 0); } /* * The Astar goto event * Uses the A* (Astar) pathfinding method used * in common games to get the AI from point A * to point B */ void auto_astar_goto_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; int tx, ty; MECH *mech = autopilot->mymech; MAP *map; float range; int bearing; int generate_path = (int) muxevent->data2; char *argument; astar_node *temp_astar_node; char error_buf[MBUF_SIZE]; /* Make sure the mech is a mech and the autopilot is an autopilot */ if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_GOTO) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " goto with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started and standing */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_astar_goto_event, AUTOPILOT_STARTUP_TICK, generate_path); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_astar_goto_event, AUTOPILOT_NC_DELAY, generate_path); return; } /*! \todo {Add stuff for the other types of units} */ /* Do we need to generate the path */ if (generate_path) { /* Get the first argument - x coord */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d but was" " unable to - bad first argument - going to next command", autopilot->mynum, tx, ty); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Now change it into a number and make sure its valid */ if (Readnum(tx, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d but was" " unable to - bad first argument '%s' - going to next command", autopilot->mynum, tx, ty, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* Get the second argument - y coord */ if (!(argument = auto_get_command_arg(autopilot, 1, 2))) { /* Ok bad argument - either means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d but was" " unable to - bad second argument - going to next command", autopilot->mynum, tx, ty); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Read second argument into a number and make sure its ok */ if (Readnum(ty, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d but was" " unable to - bad second argument '%s' - going to next command", autopilot->mynum, tx, ty, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* Look for a path */ if(!(auto_astar_generate_path(autopilot, mech, tx, ty))) { /* Couldn't find a path for some reason */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d but was" " unable to", autopilot->mynum, tx, ty); SendAI(error_buf); /*! \todo {add in some message the AI can give if it can't find a path} */ auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } } /* Make sure list is ok */ if (!(autopilot->astar_path) || (dllist_size(autopilot->astar_path) <= 0)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to follow" " Astar path for AI #%d - but the path is not there", autopilot->mynum); SendAI(error_buf); auto_destroy_astar_path(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Get the current hex target */ temp_astar_node = (astar_node *) dllist_get_node(autopilot->astar_path, 1); if (!(temp_astar_node)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attemping to follow" " Astar path for AI #%d - but the current astar node does not" " exist", autopilot->mynum); SendAI(error_buf); auto_destroy_astar_path(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Are we in the current target hex */ if ((MechX(mech) == temp_astar_node->x) && (MechY(mech) == temp_astar_node->y)) { /* Is this the last hex */ if (dllist_size(autopilot->astar_path) == 1) { /* Done! */ ai_set_speed(mech, autopilot, 0); /* Destroy the path and goto the next command */ auto_destroy_astar_path(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } else { /* Delete the node and goto the next one */ temp_astar_node = (astar_node *) dllist_remove_node_at_pos(autopilot->astar_path, 1); free(temp_astar_node); /* Call this event again */ AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_astar_goto_event, AUTOPILOT_GOTO_TICK, 0); return; } } /* Set our current goal - not the end goal tho - unless this is * the end hex but whatever */ tx = temp_astar_node->x; ty = temp_astar_node->y; /* Move towards our next hex */ figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); speed_up_if_neccessary(autopilot, mech, tx, ty, bearing); slow_down_if_neccessary(autopilot, mech, range, bearing, tx, ty); update_wanted_heading(autopilot, mech, bearing); AUTOEVENT(autopilot, EVENT_AUTOGOTO, auto_astar_goto_event, AUTOPILOT_GOTO_TICK, 0); } /* * New follow system based on astar goto */ void auto_astar_follow_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; MECH *mech = autopilot->mymech; MECH *target; MAP *map; dbref target_dbref; float range; float fx, fy; short x, y; int bearing; int destroy_path = (int) muxevent->data2; char *argument; astar_node *temp_astar_node; char error_buf[MBUF_SIZE]; if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ switch (auto_get_command_enum(autopilot, 1)) { case GOAL_FOLLOW: break; case GOAL_CHASETARGET: break; default: return; } /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " follow with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started and standing */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_STARTUP_TICK, destroy_path); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_NC_DELAY, destroy_path); return; } /*! \todo {Add in stuff for other units if need be} */ /* Get the only argument - dbref of target */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - AI #%d attempting" " to follow target but was unable to - bad argument - going" " to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* See if its a valid number */ if (Readnum(target_dbref, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - AI #%d attempting" " to follow target but was unable to - bad argument '%s' - going" " to next command", autopilot->mynum, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* Get the target */ if (!(target = getMech(target_dbref))) { /* Bad Target */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " follow unit #%d with AI #%d but its not a valid unit.", target_dbref, autopilot->mynum); SendAI(error_buf); ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Is the target destroyed or we not even on the same map */ if (Destroyed(target) || (target->mapindex != mech->mapindex)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " follow unit #%d with AI #%d but it is either dead or" " not on the same map.", target_dbref, autopilot->mynum); SendAI(error_buf); ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Generate the target hex - since this can be altered by position command */ 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 not set the target hex to the target's * hex */ if (x < 0 || y < 0 || x >= map->map_width || y >= map->map_height) { /* Reset the hex to the Target's current hex */ x = MechX(target); y = MechY(target); } /* Are we in the target hex and the target isn't moving ? */ if ((MechX(mech) == x) && (MechY(mech) == y) && (MechSpeed(target) < 0.5)) { /* Ok go into holding pattern */ ai_set_speed(mech, autopilot, 0.0); /* Destroy the path so we can force the path to be generated if the * target moves */ if (autopilot->astar_path) { auto_destroy_astar_path(autopilot); } AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 0); return; } /* Destroy the path if we need to - this typically happens * if its the first run of the event */ if (destroy_path) { auto_destroy_astar_path(autopilot); } /* Do we need to generate the path - only switch paths if we don't have * one or if the ticker has gone high enough */ if (!(autopilot->astar_path) || autopilot->follow_update_tick >= AUTOPILOT_FOLLOW_UPDATE_TICK) { /* Target hex is not target's hex */ if ((x != MechX(mech)) || (y != MechY(mech))) { /* Try and generate path with target hex */ if (!(auto_astar_generate_path(autopilot, mech, x, y))) { /* Didn't work so reset the x,y coords to target's hex * and try again */ x = MechX(target); y = MechY(target); /* This is how we try again - reset the ticker and * it will try again */ autopilot->follow_update_tick = AUTOPILOT_FOLLOW_UPDATE_TICK; } else { /* Reset the ticker - found path */ autopilot->follow_update_tick = 0; } if ((autopilot->follow_update_tick != 0) && !(auto_astar_generate_path(autopilot, mech, x, y))) { /* Major failure - No path found */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d to follow" " unit #%d, but was unable to.", autopilot->mynum, x, y, target_dbref); SendAI(error_buf); /*! \todo {add in some message the AI can give if it can't find a path} */ ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } else { /* Path found */ autopilot->follow_update_tick = 0; } } else { /* Ok same hex so try and generate path */ if (!(auto_astar_generate_path(autopilot, mech, x, y))) { /* Couldn't find a path for some reason */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " generate an astar path for AI #%d to hex %d,%d to follow" " unit #%d, but was unable to.", autopilot->mynum, x, y, target_dbref); SendAI(error_buf); /*! \todo {add in some message the AI can give if it can't find a path} */ ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } else { /* Zero the ticker */ autopilot->follow_update_tick = 0; } } } /* Make sure list is ok */ if (!(autopilot->astar_path) || (dllist_size(autopilot->astar_path) <= 0)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to follow" " Astar path for AI #%d - but the path is not there", autopilot->mynum); SendAI(error_buf); /* Destroy List */ auto_destroy_astar_path(autopilot); ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Get the current hex target */ temp_astar_node = (astar_node *) dllist_get_node(autopilot->astar_path, 1); if (!(temp_astar_node)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attemping to follow" " Astar path for AI #%d - but the current astar node does not" " exist", autopilot->mynum); SendAI(error_buf); /* Destroy List */ auto_destroy_astar_path(autopilot); ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Are we in the current target hex */ if ((MechX(mech) == temp_astar_node->x) && (MechY(mech) == temp_astar_node->y)) { /* Is this the last hex */ if (dllist_size(autopilot->astar_path) == 1) { /* Done! */ ai_set_speed(mech, autopilot, 0); auto_destroy_astar_path(autopilot); /* Re-Run Follow */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 0); return; } else { /* Delete the node and goto the next one */ temp_astar_node = (astar_node *) dllist_remove_node_at_pos(autopilot->astar_path, 1); free(temp_astar_node); /* Call this event again */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 0); return; } } /* Set our current goal - not the end goal tho - unless this is * the end hex but whatever */ x = temp_astar_node->x; y = temp_astar_node->y; /* Move towards our next hex */ figure_out_range_and_bearing(mech, x, y, &range, &bearing); speed_up_if_neccessary(autopilot, mech, x, y, bearing); slow_down_if_neccessary(autopilot, mech, range, bearing, x, y); update_wanted_heading(autopilot, mech, bearing); /* Increase Tick */ autopilot->follow_update_tick++; AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_astar_follow_event, AUTOPILOT_FOLLOW_TICK, 0); } #if 0 /* Old follow system - will phase out */ void auto_follow_event(MUXEVENT * e) { AUTO *a = (AUTO *) e->data; float fx, fy, newx, newy; int h; MECH *leader; MECH *mech = a->mymech; if (!IsMech(mech->mynum) || !IsAuto(a->mynum)) return; CCH(a); GSTART(a, mech); if (!(leader = getMech(GVAL(a, 1)))) { /* For some reason, leader is missing(?) */ ADVANCE_PG(a); return; } h = MechFacing(leader); FindXY(MechFX(leader), MechFY(leader), h + a->ofsx, a->ofsy, &fx, &fy); FindComponents(MechSpeed(leader) * MOVE_MOD, MechFacing(leader), &newx, &newy); if (ai_check_path(mech, a, fx, fy, newx, newy)) AUTOEVENT(a, EVENT_AUTOFOLLOW, auto_follow_event, AUTOPILOT_FOLLOW_TICK, 0); } #endif /* * Make the AI [dumbly]follow the given target */ void auto_dumbfollow_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; int tx, ty, x, y; int h; MECH *leader; MECH *mech = autopilot->mymech; MAP *map; float range; int bearing; char *argument; int target; char error_buf[MBUF_SIZE]; char buffer[SBUF_SIZE]; /* Making sure the mech is a mech and the autopilot is an autopilot */ if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_DUMBFOLLOW) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " follow [dumbly] with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started and standing */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_dumbfollow_event, AUTOPILOT_STARTUP_TICK, 0); return; } /* Make sure the mech is standing before going on */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_dumbfollow_event, AUTOPILOT_NC_DELAY, 0); return; } /*! \todo {Add in stuff for other units if need be} */ /* Get the target */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - AI #%d attempting" " to follow target [dumbly] but was unable to - bad argument - going" " to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Try and read the value */ if (Readnum(target, argument)) { /* Not proper number so skip command goto next */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - AI #%d attempting" " to follow target [dumbly] but was unable to - bad argument '%s' - going" " to next command", autopilot->mynum, argument); SendAI(error_buf); free(argument); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } free(argument); /* Make sure its a valid target */ if (!(leader = getMech(target)) || Destroyed(leader)) { /* For some reason, leader is missing(?) */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - AI #%d attempting" " to follow target [dumbly] but was unable to - bad or dead target -" " going to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } h = MechDesiredFacing(leader); x = autopilot->ofsy * cos(TWOPIOVER360 * (270.0 + (h + autopilot->ofsx))); y = autopilot->ofsy * sin(TWOPIOVER360 * (270.0 + (h + autopilot->ofsx))); tx = MechX(leader) + x; ty = MechY(leader) + y; if (MechX(mech) == tx && MechY(mech) == ty) { /* Do ugly stuff */ /* For now, try to match speed (if any) and heading (if any) of the leader */ if (MechSpeed(leader) > 1 || MechSpeed(leader) < -1 || MechSpeed(mech) > 1 || MechSpeed(mech) < -1) { if (MechDesiredFacing(mech) != MechFacing(leader)) { snprintf(buffer, SBUF_SIZE, "%d", MechFacing(leader)); mech_heading(autopilot->mynum, mech, buffer); } if (MechSpeed(mech) != MechSpeed(leader)) { snprintf(buffer, SBUF_SIZE, "%.2f", MechSpeed(leader)); mech_speed(autopilot->mynum, mech, buffer); } } AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_dumbfollow_event, AUTOPILOT_FOLLOW_TICK, 0); return; } figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); speed_up_if_neccessary(autopilot, mech, tx, ty, -1); if (MechSpeed(leader) < MP1) slow_down_if_neccessary(autopilot, mech, range + 1, bearing, tx, ty); update_wanted_heading(autopilot, mech, bearing); AUTOEVENT(autopilot, EVENT_AUTOFOLLOW, auto_dumbfollow_event, AUTOPILOT_FOLLOW_TICK, 0); } /* * Command the AI to leave a hangar or base */ void auto_leave_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; MECH *mech = autopilot->mymech; MAP *map; int dir; int reset_mapindex = (int) muxevent->data2; char *argument; char error_buf[MBUF_SIZE]; if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_LEAVEBASE) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " leavebase with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOLEAVE, auto_leave_event, AUTOPILOT_STARTUP_TICK, reset_mapindex); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOLEAVE, auto_leave_event, AUTOPILOT_NC_DELAY, reset_mapindex); return; } /*! \todo {Possibly add stuff here for other units} */ /* Do we need to reset the mapindex value ? */ if (reset_mapindex) { autopilot->mapindex = mech->mapindex; } /* Get the argument - direction */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " leavebase with AI #%d but was given bad argument" " defaulting to direction = 0", autopilot->mynum); SendAI(error_buf); dir = 0; } else if (Readnum(dir, argument)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " leavebase with AI #%d but was given bad argument '%s'" " defaulting to direction = 0", autopilot->mynum, argument); SendAI(error_buf); dir = 0; } free(argument); if (mech->mapindex != autopilot->mapindex) { /* We're elsewhere, pal! */ autopilot->mapindex = mech->mapindex; ai_set_speed(mech, autopilot, 0); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Still not out yet so keep trying */ speed_up_if_neccessary(autopilot, mech, -1, -1, dir); update_wanted_heading(autopilot, mech, dir); AUTOEVENT(autopilot, EVENT_AUTOLEAVE, auto_leave_event, AUTOPILOT_LEAVE_TICK, 0); } /* * Function to get the AI to enter a base hex given * a certain direction (n w s e) */ void auto_enter_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; MECH *mech = autopilot->mymech; MAP *map; mapobj *map_object; int num; int reset_mapindex = (int) muxevent->data2; char *argument; char dir[2]; char error_buf[MBUF_SIZE]; if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_ENTERBASE) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " enterbase with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Is there anything even to enter here */ if (!(map_object = find_entrance_by_xy(map, MechX(mech), MechY(mech)))) { /* Nothing in this hex */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " enterbase with AI #%d but there is nothing at %d, %d" " to enter", autopilot->mynum, MechX(mech), MechY(mech)); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Reset the mapindex if this is the first run of the event */ if (reset_mapindex) { autopilot->mapindex = mech->mapindex; } /* Make sure mech is started */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTOENTERBASE, auto_enter_event, AUTOPILOT_STARTUP_TICK, 0); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTOENTERBASE, auto_enter_event, AUTOPILOT_NC_DELAY, 0); return; } /* Get enter direction */ if (!(argument = auto_get_command_arg(autopilot, 1, 1))) { /* Ok bad argument - means the command is messed up * so should go to next one */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " enterbase with AI #%d but was given bad argument -" " going to next command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Check the first letter of the 'only' argument * this tells us what direction to enter */ switch (argument[0]) { case 'n': case 'N': strcpy(dir, "n"); break; case 's': case 'S': strcpy(dir, "s"); break; case 'w': case 'W': strcpy(dir, "w"); break; case 'e': case 'E': strcpy(dir, "e"); break; default: strcpy(dir, ""); } free(argument); /* New map so we're done */ if (mech->mapindex != autopilot->mapindex) { autopilot->mapindex = mech->mapindex; auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } if (MechDesiredSpeed(mech) != 0.0) ai_set_speed(mech, autopilot, 0); if ((MechSpeed(mech) == 0.0) && !EnteringHangar(mech)) { mech_enterbase(GOD, mech, dir); } /* Run this event again if we're not in yet */ AUTOEVENT(autopilot, EVENT_AUTOENTERBASE, auto_enter_event, AUTOPILOT_NC_DELAY, 0); } /* * Roam master command * Works like autogun where it takes 1 argument then looks * for pieces */ void auto_command_roam(AUTO *autopilot, MECH *mech) { char *argument; char error_buf[MBUF_SIZE]; char *args[4]; int argc; int i; int anchor_hex_x; int anchor_hex_y; int anchor_distance; MAP *map; /* Read in the argument */ argument = auto_get_command_arg(autopilot, 1, 1); /* Parse the argument */ argc = proper_explodearguments(argument, args, 4); /* Free the argument */ free(argument); /* Now we check to see how many arguments it found */ if (argc == 1) { /* Wander the map aimlessly */ if (strcmp(args[0], "map") == 0) { /* Set flags */ autopilot->roam_type = AUTO_ROAM_MAP; /* Fire off event */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 1); } else { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); } } else if (argc == 4) { /* Stay within a certain radius */ if (strcmp(args[0], "radius") == 0) { /* Need to grab distance and start hex */ if (Readnum(anchor_hex_x, args[1])) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument (anchor_hex_x) for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } return; } if (Readnum(anchor_hex_y, args[2])) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument (anchor_hex_y) for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } return; } /* Need to grab distance and start hex */ if (Readnum(anchor_distance, args[3])) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument (anchor_distance) for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } return; } /* Make sure values are sane */ /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " roam with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } return; } /* Check to make sure the hexes are inside the map and the distance * is not beyond our limit */ if (anchor_hex_x < 0 || anchor_hex_y < 0 || anchor_hex_x >= map->map_width || anchor_hex_y >= map->map_height || anchor_distance > AUTO_ROAM_MAX_RADIUS) { snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument (bad anchor hex or bad anchor distance)" " %d,%d : %d hexes for roam command", autopilot->mynum, anchor_hex_x, anchor_hex_y, anchor_distance); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } return; } /* Set values */ autopilot->roam_type = AUTO_ROAM_SPOT; autopilot->roam_anchor_hex_x = anchor_hex_x; autopilot->roam_anchor_hex_y = anchor_hex_y; autopilot->roam_anchor_distance = anchor_distance; /* Fire off event */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 1); } else { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); } } else { /* Invalid command */ snprintf(error_buf, MBUF_SIZE, "AI Error - AI #%d given bad" " argument for roam command", autopilot->mynum); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); } /* Free Args */ for (i = 0; i < 4; i++) { if (args[i]) free(args[i]); } } /* * Generate a random hex to roam to */ void auto_roam_generate_target_hex(AUTO *autopilot, MECH *mech, MAP *map, int attempt) { short start_hex_x; short start_hex_y; short target_hex_x; short target_hex_y; float x1, y1, x2, y2; float range; int bearing; int max_range; int counter; /* First tho we pick a hex differently based on which roam mode */ if (autopilot->roam_type == AUTO_ROAM_MAP) { start_hex_x = MechX(mech); start_hex_y = MechY(mech); max_range = AUTO_ROAM_MAX_MAP_DISTANCE; } else if (autopilot->roam_type == AUTO_ROAM_SPOT) { start_hex_x = autopilot->roam_anchor_hex_x; start_hex_y = autopilot->roam_anchor_hex_y; max_range = autopilot->roam_anchor_distance; } else { /*! \todo {Add some more types of roams perhaps} */ } /* Adjust roam distance based on number of times we've called this * function */ max_range = max_range / (2 ^ attempt); counter = 0; while (counter < AUTO_ROAM_MAX_ITERATIONS) { /* So we're not caught in some endless loop */ counter++; /* Generate range */ if (max_range < 1) { range = 1.0; } else { range = (float) Number(1, max_range); } /* Generate random bearing */ bearing = Number(0, 359); /* Map coord to Real */ MapCoordToRealCoord(start_hex_x, start_hex_y, &x1, &y1); /* Calc new hex */ FindXY(x1, y1, bearing, range, &x2, &y2); /* Real coord to Map */ RealCoordToMapCoord(&target_hex_x, &target_hex_y, x2, y2); /* Make sure the hex is sane */ if (target_hex_x < 0 || target_hex_y < 0 || target_hex_x >= map->map_width || target_hex_y >= map->map_height) continue; switch (GetTerrain(map, target_hex_x, target_hex_y)) { case LIGHT_FOREST: if ((MechType(mech) == CLASS_VEH_GROUND) && (MechMove(mech) != MOVE_TRACK)) continue; break; case HEAVY_FOREST: if (MechType(mech) == CLASS_VEH_GROUND) continue; break; case WATER: if (MechMove(mech) != MOVE_HOVER) continue; break; } /* End of switch */ /* Ok the hex is more or less sane so lets return and see if we can * find a path to it */ autopilot->roam_target_hex_x = target_hex_x; autopilot->roam_target_hex_y = target_hex_y; break; } /* End of while loop */ } /* * Event for roaming */ void auto_astar_roam_event(MUXEVENT *muxevent) { AUTO *autopilot = (AUTO *) muxevent->data; int tx, ty; MECH *mech = autopilot->mymech; MAP *map; float range; int bearing; int roam_hex_attempt; int generate_path = (int) muxevent->data2; astar_node *temp_astar_node; char error_buf[MBUF_SIZE]; /* Make sure the mech is a mech and the autopilot is an autopilot */ if (!IsMech(mech->mynum) || !IsAuto(autopilot->mynum)) return; /* Are we in the mech we're supposed to be in */ if (Location(autopilot->mynum) != autopilot->mymechnum) return; /* Our mech is destroyed */ if (Destroyed(mech)) return; /* Check to make sure the first command in the queue is this one */ if (auto_get_command_enum(autopilot, 1) != GOAL_ROAM) return; /* Get the Map */ if (!(map = getMap(autopilot->mapindex))) { /* Bad Map */ snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to" " roam with AI #%d but AI is not on a valid" " Map (#%d).", autopilot->mynum, autopilot->mapindex); SendAI(error_buf); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Make sure mech is started and standing */ if (!Started(mech)) { /* Startup */ if (!Starting(mech)) auto_command_startup(autopilot, mech); /* Run this command after startup */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTOPILOT_STARTUP_TICK, generate_path); return; } /* Ok not standing so lets do that first */ if (MechType(mech) == CLASS_MECH && Fallen(mech) && !(CountDestroyedLegs(mech) > 0)) { if (!Standing(mech)) mech_stand(autopilot->mynum, mech, ""); /* Ok lets run this command again */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTOPILOT_NC_DELAY, generate_path); return; } /*! \todo {Add stuff for the other types of units} */ /* Do we need to generate a target hex */ if (generate_path || autopilot->roam_update_tick >= AUTO_ROAM_NEW_HEX_TICK) { /* Reset counter */ roam_hex_attempt = 0; while (roam_hex_attempt < AUTO_ROAM_MAX_ITERATIONS) { /* Generate Target Hex and then try and generate path to it */ /* Target hex */ auto_roam_generate_target_hex(autopilot, mech, map, roam_hex_attempt); /* Path */ if ((autopilot->roam_target_hex_x != -1) && (autopilot->roam_target_hex_y != -1) && auto_astar_generate_path(autopilot, mech, autopilot->roam_target_hex_x, autopilot->roam_target_hex_y)) { /* Found a path */ break; } roam_hex_attempt++; } /* End of looking for target hex */ /* Check the path */ if (!(autopilot->astar_path) || (dllist_size(autopilot->astar_path) <= 0)) { /* Put Roam to bed and try again */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 1); return; } /* Reset the Roam ticker */ autopilot->roam_update_tick = 0; } /* Make sure list is ok */ if (!(autopilot->astar_path) || (dllist_size(autopilot->astar_path) <= 0)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attempting to roam" " Astar path for AI #%d - but the path is not there", autopilot->mynum); SendAI(error_buf); auto_destroy_astar_path(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Move along the path */ /* Get the current hex target */ temp_astar_node = (astar_node *) dllist_get_node(autopilot->astar_path, 1); if (!(temp_astar_node)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error - Attemping to roam" " Astar path for AI #%d - but the current astar node does not" " exist", autopilot->mynum); SendAI(error_buf); auto_destroy_astar_path(autopilot); auto_goto_next_command(autopilot, AUTOPILOT_NC_DELAY); return; } /* Are we in the current target hex */ if ((MechX(mech) == temp_astar_node->x) && (MechY(mech) == temp_astar_node->y)) { /* Is this the last hex */ if (dllist_size(autopilot->astar_path) == 1) { /* Done! */ ai_set_speed(mech, autopilot, 0); /* Destroy the path and run roam again */ auto_destroy_astar_path(autopilot); AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 1); return; } else { /* Delete the node and goto the next one */ temp_astar_node = (astar_node *) dllist_remove_node_at_pos(autopilot->astar_path, 1); free(temp_astar_node); /* Update the tick counter */ autopilot->roam_update_tick++; /* Call this event again */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 0); return; } } /* Set our current goal - not the end goal tho - unless this is * the end hex but whatever */ tx = temp_astar_node->x; ty = temp_astar_node->y; /* Move towards our next hex */ figure_out_range_and_bearing(mech, tx, ty, &range, &bearing); speed_up_if_neccessary(autopilot, mech, tx, ty, bearing); slow_down_if_neccessary(autopilot, mech, range, bearing, tx, ty); update_wanted_heading(autopilot, mech, bearing); /* Update the tick counter */ autopilot->roam_update_tick++; /* Cycle it again */ AUTOEVENT(autopilot, EVENT_AUTO_ROAM, auto_astar_roam_event, AUTO_ROAM_TICK, 0); }