/*
    Daimonin, the Massive Multiuser Online Role Playing Game
    Server Applicatiom

    Copyright (C) 2001 Michael Toennies

    A split from Crossfire, a Multiplayer game for X-windows.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to info@daimonin.org
*/

#ifndef __MONSTER_H
#define __MONSTER_H

#include <aiconfig.h>

/** Maximum number of ticks we trust an older rangevector to a known object_t */
#define MAX_KNOWN_OBJ_RV_AGE 1

/** Maximum number of ticks a mob remembers an object that it can't see */
#define MAX_KNOWN_OBJ_AGE 80 /* 80 = 10 seconds */

/** A path request has been enqueued for this mob */
#define PATHFINDFLAG_PATH_REQUESTED 0
/** Last pathfinding failed */
#define PATHFINDFLAG_PATH_FAILED    1
/** Number of defined pathfinding flags */
#define NROF_PATHFIND_FLAGS         2

/** Struct for pathfinding related data. Each mob has one of these */
struct mob_pathfinding_t
{
    /** either an obj on map or a waypoint
     * (or a mob base info object) (or NULL)
     * @{ */
    object_t                 *target_obj;
    tag_t                   target_count;
    /** @} */

    /** target is those coords if target_obj is NULL
     * @{ */
    const char             *target_map;
    sint16                  target_x;
    sint16                  target_y;
    /** @} */

    /** Original goal. Used to keep track of moving objects
     * @{ */
    const char             *goal_map;
    int                     goal_x, goal_y;
    /** @} */

    struct path_segment    *path; /**< Precomputed path from pathfinder.c   */

    uint16                  goal_delay_counter; /**< compared to wp->goal_delay */
    sint16                  best_distance; /**< to subgoal */
    sint16                  last_best_distance; /**< to subgoal */
    uint8                   tried_steps; /**< steps that didn't get us closer to subgoal */

    uint32                  flags[ (NROF_PATHFIND_FLAGS/32)+1 ];
};

/** The object is known to make use of distance attacks */
#define AI_OBJFLAG_USES_DISTANCE_ATTACK 0
/** The object is the missile or similar from a distance attack */
#define AI_OBJFLAG_IS_MISSILE 1
/** Total number of flags in the mob_known_obj struct */
#define NROF_AI_KNOWN_OBJ_FLAGS 2

/** Keeps track of other objects known to a mob
 * (enemies, friends and nearby objects). Works as a mob's short-time
 * memory about other objects.
 */
struct mob_known_obj
{
    struct mob_known_obj   *next, *prev; /** < linked list */

    /** The actual object we remember
     * @{ */
    object_t                 *obj;
    tag_t                   obj_count;
    /** @} */

    uint32                  last_seen;  /**< tick that this thing was last seen. Used for timeout */
    /** Last known position.
     * @{ */
    const char             *last_map;
    sint16                  last_x;
    sint16                  last_y;
    /** @} */

    /** this stored rv saves some CPU at the cost of some memory, is it really worth it? */
    rv_t               rv;
    uint32                  rv_time;  /**< Last time the rv was recalculated (or 0 for invalid) */

    /** Cumulative friendship and fear values
     * (negative friendship is enemosity and negative attraction is fear) */
    int                     friendship, attraction;
    /** Temporary values recalculated every now and then */
    int                     tmp_friendship, tmp_attraction;

    /** Knowledge bits */
    uint32                  flags[ (NROF_AI_KNOWN_OBJ_FLAGS/32)+1 ];
};

/** Convenience macros for accessing parameters in behaviour functions
 * @{ */
#define AI_PARAM_PRESENT   1 /* The parameter is present */

/** Is the param present? */
#define AIPARAM_PRESENT(param) (params[(param)].flags & AI_PARAM_PRESENT)
/** Retrieve the param integer value */
#define AIPARAM_INT(param) params[(param)].intvalue
/** Retrieve the param string value */
#define AIPARAM_STRING(param) params[(param)].stringvalue
/** @} */

/** A behaviour parameter */
struct mob_behaviour_param
{
    struct mob_behaviour_param *next;        /**< Linked list of multiple definitions*/
    const char                 *stringvalue; /**< Parameter value as (shared) string */
    long                        intvalue;    /**< Integer value */
    int                         flags;
};

/** Call info (presence and parameter list) for a behaviour */
struct mob_behaviour
{
    struct behaviour_decl      *declaration; /**< static info about this behaviour */
    struct mob_behaviour       *next;        /**< Linked list */
    struct mob_behaviour_param *parameters;  /**< Parameter array */
};

/** A unique collection of behaviours. Possibly shared by multiple mobs */
struct mob_behaviourset
{
    struct mob_behaviourset    *prev, *next; /**< Linked list */
    int                         refcount;    /**< Nr of active mobs using this def */
    const char                 *definition;  /**< The string definition (for manual behaviours) */
    uint32                      bghash;      /**< Hash for generated behaviours */

    /** one linked list of behaviours for each behaviour class */
    struct mob_behaviour       *behaviours[NROF_BEHAVIOURCLASSES];

    struct mob_behaviour_param *attitudes;  /**< Quicklink to behaviours["ATTITUDE"]->parameters */
    struct mob_behaviour_param *attractions;/**< Quicklink to behaviours["ATTRACTION"]->parameters */
    struct mob_behaviour_param *groups;     /**< Quicklink to behaviours["GROUPS"]->parameters */
};

/** The mob "mind". This contains everything the mob knows and understands */
struct mobdata
{
    object_t            *ob;
    mob_pathfinding_t    pathfinding;   /**< Pathfinding data */

    struct mob_known_obj       *known_mobs;    /**< List of recently detected mobs */
    struct mob_known_obj       *known_objs;    /**< List of recently detected objects */
    hashtable_t                *known_objs_ht; /**< another view of known_objs. @note can be NULL */

    struct mob_known_obj       *owner, *enemy; /**< Important other mobs */

    struct mob_behaviourset    *behaviours;    /**< This mob's behaviours */

    object_t                     *spawn_info;    /**< quick pointer to spawn info (and so to its spawn point - if one) */

    /** Antilure timer */
    int antiluring_timer;

    /** Self-estimated combat strength */
    int combat_strength;

    uint8 idle_time;            /**< How long have we been standing still not doing anything */
    uint8 move_speed_factor;    /**< Wanted speed factor. 2 is normal speed. @see set_mobile_speed() */

    /** DEBUG DATA STORAGE */
    struct behaviour_decl  *last_movement_behaviour;
};

#define MOB_DATA(ob) ((struct mobdata *)((ob)->custom_attrset))
#define MOB_PATHDATA(ob) (&(((struct mobdata *)((ob)->custom_attrset))->pathfinding))
#define SETUP_MOB_DATA(_O_) \
    (_O_)->custom_attrset = get_poolchunk(pool_mob_data); \
    MOB_DATA((_O_))->ob = (_O_); \
    MOB_DATA((_O_))->behaviours = setup_behaviours((_O_));

/** A few friendship delta values
 * @{ */
#define FRIENDSHIP_ENEMY_BONUS  -50 /**< Bonus to help focus current enemy */
#define FRIENDSHIP_ATTACK      -100 /**< Added if attacked */
#define FRIENDSHIP_TRY_ATTACK   -50 /**< Added if attacked but failed */
#define FRIENDSHIP_PUSH         -10 /**< Added if pushed */
#define FRIENDSHIP_NEUTRAL        0
#define FRIENDSHIP_DIST_MAX      50 /**< Max effect of distance */
#define FRIENDSHIP_HELP         100 /**< Added if helped */
#define FRIENDSHIP_PET         5000 /**< Base for pets */
/** @} */

/* Similar values for attraction/fear */
#define ATTRACTION_NEUTRAL        0
#define ATTRACTION_HOME        1000 /* Home sweet home (or pet owner) */

/** Possible movement behaviour response types */
typedef enum
{
    /** No response, let someone else decide */
    MOVE_RESPONSE_NONE,
    /** simply move in a direction, dir 0 is stand still */
    MOVE_RESPONSE_DIR,
    /** Move in any of the given directions. one is picked randomly */
    MOVE_RESPONSE_DIRS,
    /** move towards a (permanent) waypoint */
    MOVE_RESPONSE_WAYPOINT,
    /** move towards a generic coordinate */
    MOVE_RESPONSE_COORD,
    /** move towards a (possibly moving) object_t */
    MOVE_RESPONSE_OBJECT
}    move_response_type;

/** Data for movement behaviour responses */
typedef struct behaviour_move_response
{
    move_response_type  type; /**< @see move_response_type */
    uint16              forbidden; /**< bitmap of forbidden directions */
    void (*success_callback)(object_t *op, int dir); /**< callback function in case the movement turned out successful */
    union
    {
        int direction;  /**< single direction to move in */
        int directions; /**< bitmap of selected directions */

        /** Move to a target coordinate */
        struct
        {
            map_t  *map;
            sint16  x;
            sint16  y;
        } coord;

        /** Move towards a target object_t */
        struct
        {
            object_t *obj;
            tag_t   obj_count;
        } target;
    } data;
} move_response;

/* monster.c */
extern object_t           *get_active_waypoint(object_t *op);
extern object_t           *get_aggro_waypoint(object_t *op);
extern object_t           *get_return_waypoint(object_t *op);
extern object_t           *find_waypoint(object_t *op, const char *name);
extern object_t           *get_random_waypoint(object_t *op, object_t *ignore);
extern object_t           *get_next_waypoint(object_t *op, object_t *wp);
extern int                 move_monster(object_t *op, int mode);
extern void                object_accept_path(object_t *op);
extern void                dump_abilities(void);
extern void                print_monsters(void);
/* monster_memory.c */
extern void                cleanup_mob_knowns(object_t *op, struct mob_known_obj **first, hashtable_t *ht);
extern void                clear_mob_knowns(object_t *op, struct mob_known_obj **first, hashtable_t *ht);
extern struct mob_known_obj *update_npc_knowledge(object_t *npc, object_t *other, int delta_friendship, int delta_attraction);
extern void                update_npc_known_obj(struct mob_known_obj *known, int delta_friendship, int delta_attraction);
extern struct mob_known_obj *register_npc_known_obj(object_t *npc, object_t *other, int friendship, int attraction, int check_los);
extern rv_t               *get_known_obj_rv(object_t *op, struct mob_known_obj *known_obj, int maxage);
/* monster_behaviourset.c */
extern struct mob_behaviourset *parse_behaviourconfig(const char *conf_text, object_t *op);
extern void                init_arch_default_behaviours();
extern void                initialize_mob_data(struct mobdata *data);
extern void                cleanup_mob_data(struct mobdata *data);
extern struct mob_behaviourset *setup_behaviours(object_t *op);
extern void                cleanup_behaviourset(struct mob_behaviourset *data);
extern void                cleanup_mob_known_obj(struct mob_known_obj *data);
extern int                 can_hit(object_t *ob1, object_t *ob2, rv_t *rv);
extern void                cleanup_all_behavioursets();
extern void                reload_behaviours(object_t *op);
/* monster_behaviours.c */
extern int                 mob_can_see_obj(object_t *op, object_t *obj, struct mob_known_obj *known_obj);
extern int                 get_friendship(object_t *op, object_t *obj);
extern int                 get_attitude(object_t *op, object_t *obj);
extern object_t           *monster_choose_random_spell(object_t *monster);
extern void                monster_check_pickup(object_t *monster);
extern void                monster_check_apply(object_t *mon, object_t *item);
extern void                npc_call_help(object_t *op);

#endif /* ifndef __MONSTER_H */
