/*
 * $Id: autopilot.h,v 1.4 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:43:42 1996 fingon
 * Last modified: Sat Jun  6 19:56:42 1998 fingon
 *
 */
#ifndef AUTOPILOT_H
#define AUTOPILOT_H
#include "mech.events.h"
#include "dllist.h"
#define AUTOPILOT_MEMORY        100     /* Number of command slots available to AI */
#define AUTOPILOT_MAX_ARGS      5       /* Max number of arguments for a given AI Command
                                           Includes the command as the first argument */
/* The various flags for the AI */
#define AUTOPILOT_AUTOGUN           1       /* Is autogun enabled, ie: shoot what AI wants to */
#define AUTOPILOT_GUNZOMBIE         2
#define AUTOPILOT_PILZOMBIE         4
#define AUTOPILOT_ROAMMODE          8       /* Are we roaming around */
#define AUTOPILOT_LSENS             16      /* Should change sensors or user set them */
#define AUTOPILOT_CHASETARG         32      /* Should chase the target */
#define AUTOPILOT_WAS_CHASE_ON      64      /* Was chasetarg on, for use with movement stuff */
#define AUTOPILOT_SWARMCHARGE       128 
#define AUTOPILOT_ASSIGNED_TARGET   256     /* We given a specific target ? */
/* Various delays for the autopilot */
#define AUTOPILOT_NC_DELAY              1       /* Generic command wait time before executing */
#define AUTOPILOT_GOTO_TICK             3       /* How often to check any GOTO event */
#define AUTOPILOT_LEAVE_TICK            5       /* How often to check if we've left 
                                                   the bay/hangar */
#define AUTOPILOT_WAITFOE_TICK          4
#define AUTOPILOT_PURSUE_TICK           3
#define AUTOPILOT_FOLLOW_TICK           3
#define AUTOPILOT_FOLLOW_UPDATE_TICK    30  /* When should we update the target hex */
#define AUTOPILOT_CHASETARG_UPDATE_TICK 30  /* When should we update chasetarg */
#define AUTOPILOT_STARTUP_TICK  STARTUP_TIME + AUTOPILOT_NC_DELAY   /* Delay for startup */
/* Defines for the autogun/autosensor stuff */
#define AUTO_GUN_TICK                   1       /* Every second */
#define AUTO_GUN_MAX_HEAT               6.0     /* Last heat we let heat go to */
#define AUTO_GUN_MAX_TARGETS            100     /* Don't really use this one */
#define AUTO_GUN_MAX_RANGE              30      /* Max range to look for targets */
#define AUTO_GUN_UPDATE_TICK            30      /* When to look for a new target */
#define AUTO_GUN_PHYSICAL_RANGE_MIN     3.0     /* Min range at which to physically attack 
                                                   other targets if our main target is beyond
                                                   this distance */
#define AUTO_PROFILE_TICK               180     /* How often to update the weapon profile 
                                                   of the AI */
#define AUTO_PROFILE_MAX_SIZE           30      /* Size of the profile array */
#define AUTO_SENSOR_TICK                30      /* Every 30 seconds or so */
/* Chase Target stuff for use with auto_set_chasetarget_mode */
#define AUTO_CHASETARGET_ON             1       /* Turns it on and resets the values */
#define AUTO_CHASETARGET_OFF            2       /* Turns it off */
#define AUTO_CHASETARGET_REMEMBER       3       /* Turns it on only if the AI remembers it 
                                                   being on */
#define AUTO_CHASETARGET_SAVE           4       /* Turns it off and saves that it was on */
/*! \todo {Not sure what these are look into it} */
#define AUTO_GOET               15
#define AUTO_GOTT               240
#define Gunning(a)              ((a)->flags & AUTOPILOT_AUTOGUN)
#define StartGun(a)             (a)->flags |= AUTOPILOT_AUTOGUN
#define StopGun(a)              (a)->flags &= ~(AUTOPILOT_AUTOGUN|AUTOPILOT_GUNZOMBIE)
/* Do we have an assigned target */
#define AssignedTarget(a)       ((a)->flags & AUTOPILOT_ASSIGNED_TARGET)
#define AssignTarget(a)         (a)->flags |= AUTOPILOT_ASSIGNED_TARGET
#define UnassignTarget(a)       (a)->flags &= ~(AUTOPILOT_ASSIGNED_TARGET)
/* Is chasetarget mode on */
#define ChasingTarget(a)        ((a)->flags & AUTOPILOT_CHASETARG)
#define StartChasingTarget(a)   (a)->flags |= AUTOPILOT_CHASETARG
#define StopChasingTarget(a)    (a)->flags &= ~(AUTOPILOT_CHASETARG)
/* Was chasetarget mode on ? */
#define WasChasingTarget(a)         ((a)->flags & AUTOPILOT_WAS_CHASE_ON)
#define RememberChasingTarget(a)    (a)->flags |= AUTOPILOT_WAS_CHASE_ON
#define ForgetChasingTarget(a)      (a)->flags &= ~(AUTOPILOT_WAS_CHASE_ON) 
#define UpdateAutoSensor(a, flag) \
    AUTOEVENT(a, EVENT_AUTO_SENSOR, auto_sensor_event, 1, flag)
#define TrulyStartGun(a) \
    do { \
        AUTOEVENT(a, EVENT_AUTO_PROFILE, auto_update_profile_event, 1, 0); \
        AUTOEVENT(a, EVENT_AUTOGUN, auto_gun_event, 1, 0); \
        AUTOEVENT(a, EVENT_AUTO_SENSOR, auto_sensor_event, 1, 0); \
    } while (0)
#define DoStartGun(a) \
    do { \
        StartGun(a); \
        TrulyStartGun(a); \
        a->flags &= ~AUTOPILOT_GUNZOMBIE; \
    } while (0)
#define TrulyStopGun(a) \
    do { \
        muxevent_remove_type_data(EVENT_AUTO_PROFILE, a); \
        muxevent_remove_type_data(EVENT_AUTOGUN, a); \
        muxevent_remove_type_data(EVENT_AUTO_SENSOR, a); \
    } while (0)
#define DoStopGun(a)        \
    do { StopGun(a); TrulyStopGun(a); } while (0)
#define Zombify(a) \
    do { a->flags |= AUTOPILOT_GUNZOMBIE; TrulyStopGun(a); } while (0)
#define PilZombify(a) \
    do { a->flags |= AUTOPILOT_PILZOMBIE; } while (0)
#define UnZombify(a) \
    do { if (a->flags & AUTOPILOT_GUNZOMBIE) { TrulyStartGun(a);\
        a->flags &= ~AUTOPILOT_GUNZOMBIE; } } while (0)
#define UnZombifyAuto(a) \
    do { if (Gunning(a)) UnZombify(a); if (a->flags & AUTOPILOT_PILZOMBIE) { \
        a->flags &= ~AUTOPILOT_PILZOMBIE ;\
        AUTOEVENT(a, EVENT_AUTOCOM, auto_com_event, 1, 0); } } while (0)
#define UnZombifyMech(mech) \
    do { AUTO *au; if (MechAuto(mech) > 0 && \
        (au=FindObjectsData(MechAuto(mech)))) UnZombifyAuto(au); } while (0)
/*! \todo {Get rid of these} */
#define GVAL(a,b) \
    (((a->program_counter + (b)) < a->first_free) ? \
    a->commands[(a->program_counter+(b))] : -1)
#define CCLEN(a) \
       (1 + acom[a->commands[a->program_counter]].argcount)
#define PG(a) \
       a->program_counter
/* Command Macros */
/* Basic checks for the autopilot */
#define AUTO_CHECKS(a) \
    if (Location(a->mynum) != a->mymechnum) return; \
    if (Destroyed(mech)) return;
/* Shortcut to the auto_com_event function */
#define AUTO_COM(a,n) \
        AUTOEVENT(a, EVENT_AUTOCOM, auto_com_event, (n), 0);
/*! \todo {Get rid of this once we're done} */
#define ADVANCE_PG(a) \
        PG(a) += CCLEN(a); REDO(a,AUTOPILOT_NC_DELAY)
/* Force the unit to start up */
#define AUTO_PSTART(a,mech) \
        if (!Started(mech)) { auto_command_startup(a, mech); return; }
/* Force the unit to startup as well as try to stand */
#define AUTO_GSTART(a,mech) \
    AUTO_PSTART(a,mech); \
    if (MechType(mech) == CLASS_MECH && Fallen(mech) && \
            !(CountDestroyedLegs(mech) > 0)) { \
        if (!Standing(mech)) mech_stand(a->mynum, mech, ""); \
        AUTO_COM(a, AUTOPILOT_NC_DELAY); return; \
    }; \
    if (MechType(mech) == CLASS_VTOL && Landed(mech) && \
            !SectIsDestroyed(mech, ROTOR)) { \
        if (!TakingOff(mech)) aero_takeoff(a->mynum, mech, ""); \
        AUTO_COM(a, AUTOPILOT_NC_DELAY); return; \
    }
/* Macros for quick access to bitfield code */
#define HexOffSet(x, y) (x * MAPY + y)
#define HexOffSetNode(node) (HexOffSet(node->x, node->y))
#define WhichByte(hex_offset) ((hex_offset) >> 3)
#define WhichBit(hex_offset) ((hex_offset) & 7)
#define CheckHexBit(array, offset) (array[WhichByte(offset)] & (1 << WhichBit(offset)) ? 1 : 0)
#define SetHexBit(array, offset) (array[offset >> 3] = \
        array[offset >> 3] | (1 << (offset & 7)))
#define ClearHexBit(array, offset) (array[offset >> 3] = \
        array[offset >> 3] & ~(1 << (offset & 7)))
#ifdef DEBUG_AUTOGUN
    #define print_autogun_log(autopilot, args...) \
    do { \
        fprintf(stderr, "AI: %d AUTOGUN ", autopilot->mynum); \
        fprintf(stderr, args); \
        fprintf(stderr, "\n"); \
    } while(0)
#else
    #define print_autogun_log(autopilot, args...)
#endif
/*
 * Profile node structure
 */
typedef struct profile_node_t {
    int damage;
    int heat;
    rbtree *weaplist;
} profile_node;
/*
 * The Autopilot Structure
 */
typedef struct {
    dbref mynum;                    /* The AI's dbref number */
    MECH *mymech;                   /* The AI's unit */
    int mapindex;                   /* The map the AI is currently on */
    dbref mymechnum;                /* the dbref of the AI's mech */
    unsigned short speed;           /* % of speed (1-100) that the AI should drive at */
    int ofsx, ofsy;                 /* ? */
    unsigned char verbose_level;    /* How talkative should the AI be */
    dbref target;                   /* The AI's current target */
    int target_score;               /* Current score of the AI's target */
    int target_threshold;           /* Threshold at which to change to another target */
    int target_update_tick;         /* What autogun tick we currently at and should we update */
    dbref chase_target;             /* Current target we are chasing */
    int chasetarg_update_tick;      /* When should we update chasetarg */
    int follow_update_tick;         /* When should we update follow */
    /* Special AI flags */
    unsigned short flags;
    /* The autopilot's command list */
    dllist *commands;
    /* AI A* pathfinding stuff */
    dllist *astar_path;
    /* The AI's Weaplist for use with autogun */
    dllist *weaplist;
    /* Range Profile Array - for use with autogun */
    rbtree *profile[AUTO_PROFILE_MAX_SIZE];
    /* Max Range of AI's mech's weapons */
    int mech_max_range;
    /*! \todo {Figure out if we need to keep these variables} */
    /* Temporary AI-pathfind data variables */
    int ahead_ok;
    int auto_cmode;     /* 0 = Flee ; 1 = Close ; 2 = Maintain distances if possible */
    int auto_cdist;     /* Distance we're trying to maintain */
    int auto_goweight;
    int auto_fweight;
    int auto_nervous;
    int b_msc, w_msc, b_bsc, w_bsc, b_dan, w_dan, last_upd;
} AUTO;
/* command_node struct to store AI 
 * commands for the AI command list */
typedef struct command_node_t {
    char *args[AUTOPILOT_MAX_ARGS];         /* Store arguements - At most will ever have 5 */
    unsigned char argcount;                 /* Number of arguments */
    int command_enum;                       /* The ENUM value for the command */
    int (*ai_command_function)(AUTO *);     /* Function Pointer */
} command_node;
/* A structure to store info about the various AI commands */
typedef struct {
    char *name;
    int argcount;
    int command_enum;
    int (*ai_command_function)(AUTO *);
} ACOM;
/* astar node Structure for the A star pathfinding */
typedef struct astar_node_t {
    short x;
    short y;
    short x_parent;
    short y_parent;
    int g_score;
    int h_score;
    int f_score;
    int hexoffset;
} astar_node;
/* Weaplist node for storing info about weapons on the mech */
typedef struct weapon_node_t {
    short weapon_number;
    short weapon_db_number;
    short section;
    short critical;
    int range_scores[AUTO_PROFILE_MAX_SIZE];
} weapon_node;
/* Target node for storing target data */
typedef struct target_node_t {
    int target_score;
    dbref target_dbref;
} target_node;
/* Quick flags for use with the various autopilot
 * commands.  Check the ACOM array in autopilot_commands.c */
enum {
    GOAL_CHASETARGET,   /* An extension of follow for chasetarget */
    GOAL_DUMBFOLLOW,
    GOAL_DUMBGOTO,
    GOAL_ENTERBASE,     /* Revamp of enterbase so it keeps trying */
    GOAL_FOLLOW,        /* Uses the new Astar system */
    GOAL_GOTO,          /* Uses the new Astar system */ 
    GOAL_LEAVEBASE,
    GOAL_OLDGOTO,       /* Old implementation of goto */
    GOAL_ROAM,          /* unimplemented */
    GOAL_WAIT,          /* unimplemented */
    COMMAND_ATTACKLEG,  /* unimplemented */
    COMMAND_AUTOGUN,    /* New version that has 3 options 'on', 'off' and 'target dbref' */
    COMMAND_CHASEMODE,  /* unimplemented */
    COMMAND_CMODE,      /* unimplemented */
    COMMAND_DROPOFF, 
    COMMAND_EMBARK,
    COMMAND_ENTERBAY,   /* unimplemented */
    COMMAND_JUMP,       /* unimplemented */
    COMMAND_LOAD,       /* unimplemented */
    COMMAND_PICKUP,
    COMMAND_REPORT,     /* unimplemented */
    COMMAND_ROAMMODE,   /* unimplemented */
    COMMAND_SHUTDOWN,
    COMMAND_SPEED,
    COMMAND_STARTUP,
    COMMAND_STOPGUN,    /* unimplemented */
    COMMAND_SWARM,      /* unimplemented */
    COMMAND_SWARMMODE,  /* unimplemented */
    COMMAND_UDISEMBARK,
    COMMAND_UNLOAD,     /* unimplemented */
    AUTO_NUM_COMMANDS
};
/* Function Prototypes will go here */
/* From autopilot_core.c */
void auto_destroy_command_node(command_node *node);
void auto_save_commands(FILE *file, AUTO *autopilot);
void auto_load_commands(FILE *file, AUTO *autopilot);
void auto_delcommand(dbref player, void *data, char *buffer);
void auto_addcommand(dbref player, void *data, char *buffer);
void auto_listcommands(dbref player, void *data, char *buffer);
void auto_eventstats(dbref player, void *data, char *buffer);
void auto_set_comtitle(AUTO *autopilot, MECH * mech);
void auto_init(AUTO *autopilot, MECH *mech);
void auto_engage(dbref player, void *data, char *buffer);
void auto_disengage(dbref player, void *data, char *buffer);
void auto_goto_next_command(AUTO *autopilot, int time);
char *auto_get_command_arg(AUTO *autopilot, int command_number, int arg_number);
int auto_get_command_enum(AUTO *autopilot, int command_number);
void auto_newautopilot(dbref key, void **data, int selector);
/* From autopilot_commands.c */
void auto_cal_mapindex(MECH *mech);
void auto_set_chasetarget_mode(AUTO *autopilot, int mode);
void auto_command_startup(AUTO *autopilot, MECH *mech);
void auto_command_shutdown(AUTO *autopilot, MECH *mech);
void auto_command_pickup(AUTO *autopilot, MECH *mech);
void auto_command_dropoff(MECH *mech);
void auto_command_speed(AUTO *autopilot);
void auto_com_event(MUXEVENT *muxevent);
void auto_astar_goto_event(MUXEVENT *muxevent);
void auto_astar_follow_event(MUXEVENT *muxevent);
void auto_dumbgoto_event(MUXEVENT *muxevent);
void auto_dumbfollow_event(MUXEVENT *muxevent);
void auto_leave_event(MUXEVENT *muxevent);
void auto_enter_event(MUXEVENT *muxevent);
/* From autopilot_ai.c */
int auto_astar_generate_path(AUTO *autopilot, MECH *mech, short end_x, short end_y);
void auto_destroy_astar_path(AUTO *autopilot);
/* From autopilot_autogun.c */
int SearchLightInRange(MECH *mech, MAP *map);
int PrefVisSens(MECH *mech, MAP *map, int slite, MECH *target);
void auto_sensor_event(MUXEVENT *muxevent);
void auto_gun_event(MUXEVENT *muxevent);
void auto_destroy_weaplist(AUTO *autopilot);
void auto_update_profile_event(MUXEVENT *muxevent);
/* From autopilot_radio.c */
void auto_reply_event(MUXEVENT *muxevent);
void auto_reply(MECH *mech, char *buf);
void auto_parse_command(AUTO *autopilot, MECH *mech, int chn, char *buffer);
void auto_radio_command_autogun(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_chasetarg(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_dfollow(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_dgoto(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_dropoff(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_embark(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_enterbase(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_follow(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_goto(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_heading(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_help(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_hide(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_jumpjet(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_leavebase(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_ogoto(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_pickup(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_position(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_prone(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_report(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_reset(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_sensor(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_shutdown(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_speed(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_stand(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_startup(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_stop(AUTO *autopilot, MECH *mech, 
        char **args, int argc, char *mesg);
void auto_radio_command_sweight(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
void auto_radio_command_target(AUTO *autopilot, MECH *mech,
        char **args, int argc, char *mesg);
#include "p.autopilot.h"
#include "p.ai.h"
#include "p.autopilot_command.h"
#include "p.autopilot_commands.h"
#include "p.autogun.h"
#endif                  /* AUTOPILOT_H */