/*
* $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", 2, 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, "");
}
/*
* 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;
#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);
auto_goto_next_command(autopilot, AUTOPILOT_STARTUP_TICK);
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);
}