/* * $Id: autopilot_commands.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 20:42:59 1996 fingon * Last modified: Sat Jun 6 19:32:27 1998 fingon * */ #include "mech.h" #include "mech.events.h" #include "autopilot.h" #include "coolmenu.h" #include "mycool.h" #include "p.mech.utils.h" extern ACOM acom[AUTO_NUM_COMMANDS + 1]; extern char *muxevent_names[]; #define AI_COMMAND_DLLIST_START 51 /* Tag for start of saved command list */ #define AI_COMMAND_DLLIST_END 63 /* Tag for end of saved command list */ #define outbyte(a) tmpb=(a); fwrite(&tmpb, 1, 1, file); #define CHESA(a,b,c,d) if ((tmpb=fwrite(a,b,c,d)) != c) \ { fprintf (stderr, "Error writing dllist\n"); \ fflush(stderr); exit(1); } #define CHELO(a,b,c,d) if ((tmpb=fread(a,b,c,d)) != c) \ { fprintf (stderr, "Error loading dllist\n"); \ fflush(stderr); exit(1); } /* * Creates a new command_node for the AI's * command list */ static command_node *auto_create_command_node() { command_node *temp; temp = malloc(sizeof(command_node)); if (temp == NULL) return NULL; memset(temp, 0, sizeof(command_node)); temp->ai_command_function = NULL; return temp; } /* * Destroys a command_node */ void auto_destroy_command_node(command_node *node) { int i; /* Free the args */ for (i = 0; i < AUTOPILOT_MAX_ARGS; i++) { if (node->args[i]) { free(node->args[i]); node->args[i] = NULL; } } /* Free the node */ free(node); return; } /* * Writes a command_node to the file specified by f. Used to * save AI command list to the hcode.db file */ static void auto_write_command_node(FILE *file, command_node *node) { unsigned char size; /* Number of Arguments to save */ char buf[MBUF_SIZE]; /* Buffer to write the strings */ int i; /* Counter */ unsigned short tmpb; /* Store the number of bytes written */ /* Zero the Buffer */ memset(buf, '\0', sizeof(buf)); /* Write the Number of Arguments we're storing */ size = node->argcount; CHESA(&size, 1, sizeof(size), file); /* Loop through the args and write them */ for (i = 0; i <= size; i++) { strncpy(buf, node->args[i], MBUF_SIZE); CHESA(&buf, 1, sizeof(buf), file); } return; } /* * Reads the data for a command_node from a file. Used * to load the AI command list from hcode.db */ static command_node *auto_read_command_node(FILE *file) { unsigned char size; /* Number of Arguments to read */ char buf[MBUF_SIZE]; /* Buffer to store the strings */ int i; /* Counter */ unsigned short tmpb; /* Store the number of bytes read */ command_node *temp_command_node; /* Allocate a command node */ temp_command_node = auto_create_command_node(); /* Zero the Buffer */ memset(buf, '\0', sizeof(buf)); /* Read the Number of Arguments we're storing */ CHELO(&size, 1, sizeof(size), file); temp_command_node->argcount = size; /* Loop through the arguments and store them */ for (i = 0; i <= size; i++) { CHELO(&buf, 1, sizeof(buf), file); temp_command_node->args[i] = strndup(buf, MBUF_SIZE); } /* Make sure there is a command */ if (!temp_command_node->args[0]) { fprintf(stderr, "Error loading command node from file - " "no command found\n"); exit(1); } /* Get the command_enum and the command_function */ for (i = 0; acom[i].name; i++) { if ((!strncmp(temp_command_node->args[0], acom[i].name, strlen(temp_command_node->args[0]))) && (!strncmp(acom[i].name, temp_command_node->args[0], strlen(acom[i].name)))) break; } if (!acom[i].name) { fprintf(stderr, "Error loading command node from file - " "Invalid Command\n"); exit(1); } temp_command_node->command_enum = acom[i].command_enum; temp_command_node->ai_command_function = acom[i].ai_command_function; return temp_command_node; } /* * Saves the current command list from the AI to * a file. Called by SaveSpecialObjects from * glue.c */ void auto_save_commands(FILE *file, AUTO *autopilot) { int i; /* Our Counter */ unsigned char tmpb; /* Our temp int we use when writing */ unsigned int size; /* The size of our command list */ command_node *temp_command_node; /* Print the Start Code */ outbyte(AI_COMMAND_DLLIST_START); /* Write the size of our list */ size = dllist_size(autopilot->commands); CHESA(&size, sizeof(size), 1, file); /* Check the size of the list, if there are commands save them */ if (dllist_size(autopilot->commands) > 0) { /* Ok there stuff here so lets write it */ for (i = 1; i <= dllist_size(autopilot->commands); i++) { temp_command_node = (command_node *) dllist_get_node(autopilot->commands, i); auto_write_command_node(file, temp_command_node); } } /* Print the Stop Code */ outbyte(AI_COMMAND_DLLIST_END); return; } /* * Loads an AI's saved command list from * a file. Called by load_xcode in glue.c */ void auto_load_commands(FILE *file, AUTO *autopilot) { int i; /* Our Counter */ unsigned char tmpb; /* Our temp int we use when reading */ unsigned int size; /* The size of our command list */ dllist_node *temp_dllist_node; command_node *temp_command_node; /* Alloc a dllist to the AI for commands */ autopilot->commands = dllist_create_list(); /* If we can't even read the file don't bother * with the rest */ if(feof(file)) return; /* Loop for the beginning tag */ fread(&tmpb, 1, 1, file); if (tmpb != AI_COMMAND_DLLIST_START) { fprintf(stderr, "Unable to locate START for reading data" " for AI #%d\n", autopilot->mynum); fflush(stderr); exit(1); } /* Read in size of dllist */ CHELO(&size, sizeof(size), 1, file); /* if size is bigger then zero means we have to read * in some command nodes */ if (size > 0) { /* Loop through the list and add the nodes */ for (i = 1; i <= size; i++) { temp_command_node = auto_read_command_node(file); temp_dllist_node = dllist_create_node(temp_command_node); dllist_insert_end(autopilot->commands, temp_dllist_node); } } /* Look for the end tag */ fread(&tmpb, 1, 1, file); if (tmpb != AI_COMMAND_DLLIST_END) { fprintf(stderr, "Unable to locate END for reading data" " for AI #%d\n", autopilot->mynum); fflush(stderr); exit(1); } return; } /* The Autopilot command interface addcommand <name> [args] delcommand <num> listcommands engage disengage jump */ /* * The commands that are on the XCODE Object along * with some helper commands for modifying the state * of the AI */ /*! \todo {See if we need this function and remove it if not} */ int auto_valid_progline(AUTO * a, int p) { int i; #if 0 for (i = 0; i < a->first_free; i += (acom[a->commands[i]].argcount + 1)) if (i == p) return 1; #endif return 0; } /* * Internal function to return a string that * displays a command from a command_node */ /*! \todo {Maybe re-write this so doesn't use a static buffer} */ static char *auto_show_command(command_node *node) { static char buf[MBUF_SIZE]; int i; snprintf(buf, MBUF_SIZE, "%-10s", node->args[0]); /* Loop through the args and print the commands */ for (i = 1; i < AUTOPILOT_MAX_ARGS; i++) if (node->args[i]) { strncat(buf, " ", MBUF_SIZE); strncat(buf, node->args[i], MBUF_SIZE); } return buf; } /* * Removes a command from the AI's command list */ void auto_delcommand(dbref player, void *data, char *buffer) { int p, i; AUTO *autopilot = (AUTO *) data; int remove_all_commands = 0; command_node *temp_command_node; char error_buf[MBUF_SIZE]; /* Make sure they specified an argument */ if (!*buffer) { notify(player, "No argument used : Usage delcommand [num]\n"); notify_printf(player, "Must be within the range" " 1 to %d or -1 for all\n", dllist_size(autopilot->commands)); return; } /* Make sure its a number */ if (Readnum(p, buffer)) { notify_printf(player, "Invalid Argument : Must be within the range" " 1 to %d or -1 for all\n", dllist_size(autopilot->commands)); return; } /* Check if its a valid command position * If its -1 means remove all */ if (p == -1) { remove_all_commands = 1; } else if ((p > dllist_size(autopilot->commands)) || (p < 1)) { notify_printf(player, "Invalid Argument : Must be within the range" " 1 to %d or -1 for all\n", dllist_size(autopilot->commands)); return; } /*! \todo {Add in check so they don't accidently remove a running command without * disengaging first} */ /* Now remove the node(s) */ if (!remove_all_commands) { /* Remove the node at pos */ temp_command_node = (command_node *) dllist_remove_node_at_pos(autopilot->commands, p); if (!temp_command_node) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to remove" " Command #%d from AI #%d but the command node doesn't exist\n", p, autopilot->mynum); SendAI(error_buf); } /* Destroy the command_node */ auto_destroy_command_node(temp_command_node); notify_printf(player, "Command #%d Successfully Removed\n", p); } else { /* Remove ALL the commands */ while (dllist_size(autopilot->commands)) { /* Remove the first node on the list and get the data * from it */ temp_command_node = (command_node *) dllist_remove(autopilot->commands, dllist_head(autopilot->commands)); /* Make sure the command node exists */ if (!temp_command_node) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to remove" " the first command from AI #%d but the command node doesn't exist\n", autopilot->mynum); SendAI(error_buf); } else { /* Destroy the command node */ auto_destroy_command_node(temp_command_node); } } notify(player, "All the commands have been removed.\n"); } } /* * Jump to a specific command location in the AI's * command list */ void auto_jump(dbref player, void *data, char *buffer) { int p; AUTO *a = (AUTO *) data; notify(player, "jump has been temporarly disabled till I can figure out" " how I want to change it - Dany"); #if 0 skipws(buffer); DOCHECK(!*buffer, "Argument expected!"); DOCHECK(Readnum(p, buffer), "Invalid argument - single number expected."); /* Find out if it's valid position */ DOCHECK(!auto_valid_progline(a, p), "Invalid : Argument out of range, or argument, not command."); PG(a) = p; notify(player, tprintf("Program Counter set to #%d.", p)); #endif } /* * Adds a command to the AI Command List */ void auto_addcommand(dbref player, void *data, char *buffer) { AUTO *autopilot = (AUTO *) data; char *args[AUTOPILOT_MAX_ARGS]; /* args[0] is the command the rest are args for the command */ char *command; /* temp string to get the name of the command */ int argc; int i, j; command_node *temp_command_node; dllist_node *temp_dllist_node; /* Clear the Args */ memset(args, 0, sizeof(char *) * AUTOPILOT_MAX_ARGS); command = first_parseattribute(buffer); /* Look at the buffer and try and get the command */ for (i = 0; acom[i].name; i++) { if ((!strncmp(command, acom[i].name, strlen(command))) && (!strncmp(acom[i].name, command, strlen(acom[i].name)))) break; } /* Free the command string we dont need it anymore */ free(command); /* Make sure its a valid command */ DOCHECK(!acom[i].name, "Invalid Command!"); /* Get the arguments for the command */ if (acom[i].argcount > 0) { /* Parse the buffer for commands * Its argcount + 1 because we are parsing the command + its * arguments */ argc = proper_explodearguments(buffer, args, acom[i].argcount + 1); if (argc != acom[i].argcount + 1) { /* Free the args before we quit */ for(j = 0; j < AUTOPILOT_MAX_ARGS; j++) { if(args[j]) free(args[j]); } notify(player, "Not the proper number of arguments!"); return; } } else { /* Copy the command to the first arg */ args[0] = strdup(acom[i].name); } /* Build the command node */ temp_command_node = auto_create_command_node(); for (j = 0; j < AUTOPILOT_MAX_ARGS; j++) { if (args[j]) temp_command_node->args[j] = args[j]; } temp_command_node->argcount = acom[i].argcount; temp_command_node->command_enum = acom[i].command_enum; temp_command_node->ai_command_function = acom[i].ai_command_function; /* Add the command to the list */ temp_dllist_node = dllist_create_node(temp_command_node); dllist_insert_end(autopilot->commands, temp_dllist_node); /* Let the player know it worked */ notify_printf(player, "Command Added: %s", auto_show_command(temp_command_node)); } /* * Lists the various settings and commands currently on the AI */ void auto_listcommands(dbref player, void *data, char *buffer) { AUTO *autopilot = (AUTO *) data; coolmenu *c = NULL; char buf[MBUF_SIZE]; int i, count = 0; addline(); snprintf(buf, MBUF_SIZE, "Autopilot data for %s", Name(autopilot->mynum)); vsi(buf); snprintf(buf, MBUF_SIZE, "Controling unit %s", Name(Location(autopilot->mynum))); vsi(buf); addline(); snprintf(buf, MBUF_SIZE, "MyRef: #%d MechRef: #%d MapIndex: #%d " "FSpeed: %d %% (Flag:%d)", autopilot->mynum, autopilot->mymechnum, autopilot->mapindex, autopilot->speed, autopilot->flags); vsi(buf); addline(); if (dllist_size(autopilot->commands)) { for (i = 1; i <= dllist_size(autopilot->commands); i++) { snprintf(buf, MBUF_SIZE, "#%-3d %s", i, auto_show_command((command_node *) dllist_get_node(autopilot->commands, i))); vsi(buf); } } else { vsi("No commands have been queued to date."); } addline(); ShowCoolMenu(player, c); KillCoolMenu(c); } void auto_eventstats(dbref player, void *data, char *buffer) { AUTO *autopilot = (AUTO *) data; int i, j, total; notify(player, "Events by type: "); notify(player, "-------------------------------"); total = 0; for (i = FIRST_AUTO_EVENT; i <= LAST_AUTO_EVENT; i++) { if ((j = muxevent_count_type_data(i, (void *) autopilot))) { notify_printf(player, "%-20s%d", muxevent_names[i], j); total += j; } } if (total) { notify(player, "-------------------------------"); notify_printf(player, "%d total", total); } } /* * Turn the autopilot on */ static int auto_pilot_on(AUTO *autopilot) { int i, j, count = 0; for (i = FIRST_AUTO_EVENT; i <= LAST_AUTO_EVENT; i++) if ((j = muxevent_count_type_data(i, (void *) autopilot))) count += j; if (!count) { return autopilot->flags & (AUTOPILOT_AUTOGUN | AUTOPILOT_GUNZOMBIE | AUTOPILOT_PILZOMBIE); } return count; } /* * Stop whatever the autopilot is doing */ static void auto_stop_pilot(AUTO *autopilot) { int i; autopilot->flags &= ~(AUTOPILOT_AUTOGUN | AUTOPILOT_GUNZOMBIE | AUTOPILOT_PILZOMBIE); for (i = FIRST_AUTO_EVENT; i <= LAST_AUTO_EVENT; i++) muxevent_remove_type_data(i, (void *) autopilot); } /* * Set the comtitle for the autopilot's unit */ void auto_set_comtitle(AUTO *autopilot, MECH *mech) { char buf[LBUF_SIZE]; snprintf(buf, LBUF_SIZE, "a=%s/%s", MechType_Ref(mech), MechIDS(mech, 1)); mech_set_channeltitle(autopilot->mynum, mech, buf); } /* * Set default parameters for the AI */ /*! \todo {Make this smarter and check some of these} */ void auto_init(AUTO *autopilot, MECH *mech) { autopilot->ofsx = 0; /* Positional - angle */ autopilot->ofsy = 0; /* Positional - distance */ autopilot->auto_cmode = 1; /* CHARGE! */ autopilot->auto_cdist = 2; /* Attempt to avoid kicking distance */ autopilot->auto_nervous = 0; autopilot->auto_goweight = 44; /* We're mainly concentrating on fighting */ autopilot->auto_fweight = 55; autopilot->speed = 100; /* Reset to full speed */ autopilot->flags = 0; /* Target Stuff */ autopilot->target = -2; autopilot->target_score = 0; autopilot->target_threshold = 50; autopilot->target_update_tick = AUTO_GUN_UPDATE_TICK; /* Follow & Chase target stuff */ autopilot->chase_target = -10; autopilot->chasetarg_update_tick = AUTOPILOT_CHASETARG_UPDATE_TICK; autopilot->follow_update_tick = AUTOPILOT_FOLLOW_UPDATE_TICK; } /* * Setup all the flags and variables to current, then * start the AI's first command. */ void auto_engage(dbref player, void *data, char *buffer) { AUTO *autopilot = (AUTO *) data; MECH *mech; autopilot->mymech = mech = getMech((autopilot->mymechnum = Location(autopilot->mynum))); DOCHECK(!autopilot, "Internal error! - Bad AI object!"); DOCHECK(!mech, "Error: The autopilot isn't inside a 'mech!"); DOCHECK(auto_pilot_on(autopilot), "The autopilot's already online! You have to disengage it first."); if (MechAuto(mech) <= 0) auto_init(autopilot, mech); MechAuto(mech) = autopilot->mynum; if (MechAuto(mech) > 0) auto_set_comtitle(autopilot, mech); autopilot->mapindex = mech->mapindex; notify(player, "Engaging autopilot..."); AUTOEVENT(autopilot, EVENT_AUTOCOM, auto_com_event, AUTOPILOT_NC_DELAY, 0); return; } /* * Turn off the autopilot */ void auto_disengage(dbref player, void *data, char *buffer) { AUTO *autopilot = (AUTO *) data; DOCHECK(!auto_pilot_on(autopilot), "The autopilot's already offline! You have to engage it first."); auto_stop_pilot(autopilot); notify(player, "Autopilot has been disengaged."); return; } /* * Remove the first command_node in the list and go to the next */ void auto_goto_next_command(AUTO *autopilot, int time) { command_node *temp_command_node; char error_buf[MBUF_SIZE]; if (dllist_size(autopilot->commands) < 0) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to remove" " the first command from AI #%d but the command list is empty\n", autopilot->mynum); SendAI(error_buf); return; } temp_command_node = (command_node *) dllist_remove(autopilot->commands, dllist_head(autopilot->commands)); if (!temp_command_node) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to remove" " the first command from AI #%d but the command node doesn't exist\n", autopilot->mynum); SendAI(error_buf); return; } auto_destroy_command_node(temp_command_node); /* Fire off the AUTO_COM event */ AUTO_COM(autopilot, time); } /* * Get the argument for a given command position and argument number * Remember to free the string that this returns after use */ char *auto_get_command_arg(AUTO *autopilot, int command_number, int arg_number) { char *argument; command_node *temp_command_node; char error_buf[MBUF_SIZE]; if (command_number > dllist_size(autopilot->commands)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to " "access Command #%d for AI #%d but it doesn't exist", command_number, autopilot->mynum); SendAI(error_buf); return NULL; } if (arg_number >= AUTOPILOT_MAX_ARGS) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to " "access Arg #%d for AI #%d Command #%d but its greater" " then AUTOPILOT_MAX_ARGS (%d)", arg_number, autopilot->mynum, command_number, AUTOPILOT_MAX_ARGS); SendAI(error_buf); return NULL; } temp_command_node = (command_node *) dllist_get_node(autopilot->commands, command_number); /*! \todo {Add in check incase the command node doesn't exist} */ if (!temp_command_node->args[arg_number]) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to " "access Arg #%d for AI #%d Command #%d but it doesn't exist", autopilot->mynum, arg_number, command_number); SendAI(error_buf); return NULL; } argument = strndup(temp_command_node->args[arg_number], MBUF_SIZE); return argument; } /* * Returns the command_enum value for the given command * from the AI command list */ int auto_get_command_enum(AUTO *autopilot, int command_number) { int command_enum; command_node *temp_command_node; char error_buf[MBUF_SIZE]; /* Make sure there are commands */ if (dllist_size(autopilot->commands) <= 0) { return -1; } if (command_number <= 0) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to " "access a command (%d) for AI #%d that can't be on a list", command_number, autopilot->mynum); SendAI(error_buf); return -1; } /* Make sure the command is on the list */ if (command_number > dllist_size(autopilot->commands)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Trying to " "access Command #%d for AI #%d but it doesn't exist", autopilot->mynum, command_number); SendAI(error_buf); return -1; } temp_command_node = (command_node *) dllist_get_node(autopilot->commands, command_number); /*! \todo {Add in check incase the command node doesn't exist} */ command_enum = temp_command_node->command_enum; /* If its a bad enum value we have a problem */ if ((command_enum >= AUTO_NUM_COMMANDS) || (command_enum < 0)) { snprintf(error_buf, MBUF_SIZE, "Internal AI Error: Command ENUM for" " AI #%d Command Number #%d doesn't exist\n", autopilot->mynum, command_number); SendAI(error_buf); return -1; } return command_enum; } #define SPECIAL_FREE 0 #define SPECIAL_ALLOC 1 /* * Called when either creating a new autopilot - SPECIAL_ALLOC * or when destroying an autopilot - SPECIAL_FREE */ void auto_newautopilot(dbref key, void **data, int selector) { AUTO *autopilot = *data; command_node *temp; int i; switch (selector) { case SPECIAL_ALLOC: /* Allocate the command list */ autopilot->commands = dllist_create_list(); /* Make sure certain things are set NULL */ autopilot->astar_path = NULL; autopilot->weaplist = NULL; for (i = 0; i < AUTO_PROFILE_MAX_SIZE; i++) { autopilot->profile[i] = NULL; } /* And some things not set null */ autopilot->speed = 100; break; case SPECIAL_FREE: /* Go through the list and remove any leftover nodes */ while (dllist_size(autopilot->commands)) { /* Remove the first node on the list and get the data * from it */ temp = (command_node *) dllist_remove(autopilot->commands, dllist_head(autopilot->commands)); /* Destroy the command node */ auto_destroy_command_node(temp); } /* Destroy the list */ dllist_destroy_list(autopilot->commands); autopilot->commands = NULL; /* Destroy any astar path list thats on the AI */ auto_destroy_astar_path(autopilot); /* Destroy profile array */ for (i = 0; i < AUTO_PROFILE_MAX_SIZE; i++) { if (autopilot->profile[i]) { rb_destroy(autopilot->profile[i]); } autopilot->profile[i] = NULL; } /* Destroy weaponlist */ auto_destroy_weaplist(autopilot); break; } }