/* 
 * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge
 * http://www.xevil.com
 * satan@xevil.com
 *
 * 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, the file "gpl.txt"; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA, or visit http://www.gnu.org.
 */

// "intel.h"

#ifndef INTEL_H
#define INTEL_H

#if X11
	#ifndef NO_PRAGMAS
	#pragma interface
	#endif
#endif

// Include Files
#include "utils.h"
#include "coord.h"
#include "id.h"
#include "locator.h"
#include "world.h"


// Intel options defines
#define ITnone 0L
#define ITharmless (1L<<0)
#define ITclassFriends (1L<<1)
#define ITpsychotic (1L<<2)
#define ITignoreItems (1L<<3)
#define ITlimitedLifespan (1L<<4)
typedef unsigned long ITmask;


// Defines
#define IT_STRING_LENGTH 80
#define IT_INFINITE_LIVES -1

// Range that machine (and human for WIN32) players can see.
#define IT_VISION_RANGE 500


class Physical; 
typedef Physical *PhysicalP;
class Holder;
typedef Holder *HolderP;
class Modifier;
typedef Modifier *ModifierP;



struct IntelOptions {
  // Do not attack members of same class.
  Boolean classFriends; 
  // Will not attack even if it has the ability to do so.
  Boolean harmless; 
  // Will never run away.  harmless overrides psychotic.
  Boolean psychotic; 
  // Don't try to pick up items.
  Boolean ignoreItems; 
  // Only for DoppelIntel and children.
  // If non-zero, kill self after fixed number of turns.
  int limitedLifespan; 
};



// IntelStatus used by ui.
// Remember Intel::die
struct IntelStatus {
  // strings and ClassIds must be kept consistent.

  char name[IT_STRING_LENGTH];
  ClassId classId;
  char className[IT_STRING_LENGTH];
  Health health; // -1 means dead.
  Health healthmax;//for health bar in windows version
  Mass mass;

  ClassId weaponClassId;  
  char weapon[IT_STRING_LENGTH];
  Boolean weaponReady;
  int ammo;

  ClassId itemClassId;
  char item[IT_STRING_LENGTH];
  int itemCount; // Number of the item.

  int lives; // Can be IT_INFINITE_LIVES.
  int humanKills;
  int enemyKills;
  int soups; // Human killed by something other than a human.  Only looked at
             // when game style is UIsettings::DUEL.

  
  void read(InStreamP);
  int get_write_length();
  void write(OutStreamP);
  /* EFFECTS: IO functions. */

  Boolean operator == (const IntelStatus&);
  /* EFFECTS: Member-wise comparison. */
};

  

// Class Definitions.
class Intel {
 public:
  Intel(WorldP w,LocatorP l,char *name,
       	const IntelOptions *ops,ITmask opMask); 
  /* EFFECTS: Create a new intel with the options specified in opMakse that are
     in ops.  ops will never be referenced if opMask is ITnone.  lives now
     dealt with in class Human. */

  Intel(InStreamP,WorldP,LocatorP);
  Boolean creation_ok() {return creationOk;}
  virtual void update_from_stream(InStreamP);
  virtual int get_write_length();
  virtual void write(OutStreamP);

  virtual ~Intel();
  
  virtual Boolean is_human();
  /* NOTE: is_machine() == (!is_human()). */
  virtual Boolean is_enemy();
  /* NOTE: is_neutral() == (!is_human() &&  !is_enemy()). */
  virtual Boolean is_seal_intel();
  virtual Boolean is_pet();

  Boolean alive() {return living;}
  WorldP get_world() {return world;}
  LocatorP get_locator() {return locator;}

  const Id &get_id() {return id;}
  /* NOTE: If dead, return an invalid id. */

  Boolean reincarnate_me();
  /* EFFECTS: Should *this be reincarnated. */

  Boolean intel_status_changed() {return intelStatusChanged;}

  void make_intel_status_dirty() {intelStatusChanged = True;}
  /* EFFECTS: Force a redraw on the UI. Used for shared lives. */

  const IntelStatus *get_intel_status();

  const char *get_name() {return intelStatus.name;}
  int get_lives() {return _get_lives();} // Can be IT_INFINITE_LIVES.
  int get_human_kills() {return intelStatus.humanKills;}
  int get_enemy_kills() {return intelStatus.enemyKills;}
  int get_soups() {return intelStatus.soups;}
  /* NOTE: Could achieve the same thing with get_intel_status.  Convenience 
   functions. */

  Boolean is_playing();
  /* EFFECTS: Is the intel still playing in the game, i.e. either living or
     dead but with extra lives left. */

  IntelId get_killer_intel_id() {return killerIntelId;}

  IntelId get_intel_id() {return intelId;}

  IntelId get_dont_collide() {return dontCollide;}
  /* NOTE: Used by Locator. */

  ModifierP get_modifiers() {return modifiers;}
  /* EFFECTS: Return modifier list for this intel.  Never returns NULL.  
     This is not a copy,
     mutate the list to add modifiers to the intel. */

  void set_dont_collide(const IntelId &val) {dontCollide = val;}
  /* EFFECTS: Locator will not allow the physical with this intel to collide 
     with the physical with the intel dontCollide. */

  void set_id(const Id &i) {id = i;}
  /* REQUIRES: i is valid. */
  /* NOTE: Only needed for Physical::set_intel.  id is updated on every 
     clock. */

  void set_lives(int lives)
    {_set_lives(lives); intelStatusChanged = True;}
  /* NOTE: Can be IT_INFINITE_LIVES. */

  void set_name(char *name);
  /* NOTE: Used by Server to set the name provided by the client. */

  virtual void add_human_kill();

  virtual void add_enemy_kill();

  virtual void add_soup();

  void set_killer_intel_id(const IntelId &k) {killerIntelId = k;}

  void set_intel_id(const IntelId &i) {intelId = i;}
  /* NOTE: Should only be used by the Locator. */

  static ITcommand dir_to_command(Dir dir);
  /* REQUIRES: dir is a "pure" direction or CO_air. */
  /* NOTE: CO_air -> IT_CENTER. */

  static ITcommand dir_to_command_weapon(Dir dir);

  static Dir command_weapon_to_dir_4(ITcommand);

  static Dir command_weapon_to_dir_8(ITcommand);
  /* EFFECTS: If command is a weapon command return the corresponding Dir. */
  /* NOTE: Like Gun::compute_weapon_dir. */

  static Boolean is_command_weapon(ITcommand command)
  {return (command >= IT_WEAPON_R && command <= IT_WEAPON_UP_R) || 
     command == IT_WEAPON_CENTER;}

  static ITcommand center_pos_to_command(const Pos &pos);
  /* EFFECTS: Returns the command corresponding to the direction from (0,0) to
     pos. */
  /* NOTE: pos can have negative components. */
     
  static Size command_to_size(ITcommand command,int mag);
  /* EFFECTS: Change all commands to a Size where each coordinate
     has length -mag,0, or mag.  All weapon or item commands give (0,0). E.g. IT_L 
	 will return (-mag,0), IT_DN_R gives (mag,mag). */

#if 0
  virtual IntelP compatible_composite(IntelP intel) = 0;
  /* EFFECTS: If intel is a legal intel to be a member of
     the same composite object as this, return NULL. 
     Else, create and return a new intel that is 
     compatible. */
#endif

  void die(); 
  /* EFFECTS: Tell *this that it no longer has anything clocking it. */
  /* NOTE: This MUST be called at death.  Game requires it to refill enemy
     players. */

  void reincarnate();
  /* EFFECTS: Tell *this that it will be clocked again. */

  virtual void clock(PhysicalP p);
  /* NOTE: Should be called by Intel's children. */


#ifndef PROTECTED_IS_PUBLIC
 protected:
#endif
  const IntelOptions &get_intel_options() {return intelOptions;}

  virtual int _get_lives();
  virtual void _set_lives(int);
  /* EFFECTS: Allow children to override behavior of lives. */

  void creation_failed() {creationOk = False;}


 private:
  IntelStatus intelStatus; // intelStatus.lives is not used here.
  Boolean intelStatusChanged; // Since last get_intel_status.
  Boolean living;
  WorldP world;
  LocatorP locator;
  Id id;
  IntelId killerIntelId;  // Intel that most recently attacked the Intel.
  IntelId intelId;
  IntelOptions intelOptions;
  IntelId dontCollide; 
  ModifierP modifiers;
  Boolean creationOk;

  const static IntelOptions intelOptionsDefault;
};
typedef Intel *IntelP;



// Simply buffers input from a user interface.
class Human: public Intel {
 public: 
  Human(WorldP w,LocatorP l,char *name,int lives,
        int *sharedLives,ColorNum colorNum);
  /* NOTE: Uses all default options.  intelOptions should be meaningless for 
     a human. If "sharedLives" is non-null, ignore "lives" and share the lives. */

  Human(InStreamP,WorldP,LocatorP);
  virtual void update_from_stream(InStreamP);
  void _update_from_stream(InStreamP);
  virtual int get_write_length();
  virtual void write(OutStreamP);


  ColorNum get_color_num() {return colorNum;}

  virtual Boolean is_human();
    
  void set_command(ITcommand c);
  /* EFFECTS: Asynchronously set the command for the current turn to be c.
     Overrides previous settings. */

  ITcommand get_command() {return command;}
  /* EFFECTS: Return the most recently set command. */

  virtual void clock(PhysicalP p);
  /* EFFECTS: Sets the command for p. */

  void set_data(void *d) {data = d;}
  void *get_data() {return data;}
  /* NOTE: Generic data used by Game. E.g. for depth in ZIG-ZAG. */


#ifndef PROTECTED_IS_PUBLIC
 protected:
#endif
  virtual int _get_lives();
  virtual void _set_lives(int);
  /* NOTES: Allow for shared lives. */


 private:
  void *data;
  ITcommand command;
  ColorNum colorNum;
  int lives;
  int *sharedLives;
  ITcommand noRepeat;  // The command that can't be repeated.
};
typedef Human *HumanP;



class Machine: public Intel {
public:
  Machine(WorldP w,LocatorP l,char *name,
	  const IntelOptions *ops,ITmask opMask);
	  
  virtual IntelId get_master_intel_id();
  /* EFFECTS: If a slave, return the IntelId of the master.  Otherwise, 
     return an invalid IntelId. */
		 
  virtual void clock(PhysicalP);
  /* NOTE: Not called by Machine's children. */


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  enum Strategy {
    doNothing, 
    toPos,         // Move toward targetPos.
    toTarget,      // Move toward Physical targetId
    awayTarget,	   // Move away from Physical targetId
    attackTarget,	 // like toTarget, but attack if possible
    retreatTarget, // like awayTarget, but attack if possible
    toDir,         // Move in direction targetDir
  };

  Strategy get_strategy() {return strategy;}
  
  Timer &get_strategy_change() {return strategyChange;}
  /* EFFECTS: Return the timer that controls when to change strategy. */

  virtual int _get_reflexes_time();
  /* EFFECTS: Override this to set reflexes. */
  
  Boolean strategy_uses_target_id() 
  {return strategy == toTarget || strategy == awayTarget ||
     strategy == attackTarget || strategy == retreatTarget;} 
  Id get_target_id() {assert(strategy_uses_target_id()); return targetId;}
  
  void set_doNothing() {strategy = doNothing;}
  void set_toPos(const Pos &pos) {strategy = toPos; targetPos = pos;} 
  void set_toTarget(const Id &id)	{strategy = toTarget; targetId = id;}
  void set_awayTarget(const Id &id) {strategy = awayTarget; targetId = id;}
  void set_attackTarget(const Id &id)	{strategy = attackTarget; targetId = id;}
  void set_retreatTarget(const Id &id)	{strategy = retreatTarget; targetId = id;}
  void set_toDir(Dir dir) {strategy = toDir; targetDir = dir;}
  /* EFFECTS: Mutators to set the strategy. */
  /* NOTE: Designed so that no more that one of {targetPos,targetId,targetDir}
	   is set at once. And that one corresponds to the strategy. */
  /* NOTE: Not necessarirly called in the choose_strategy phase. */
  
  virtual void choose_strategy(PhysicalP);
  /* EFFECTS: Sets the strategy.  May use or set the strategyChange timer. */
  
  PhysicalP choose_target(Boolean &isEnemy,PhysicalP p,int range);
  /* MODIFIES: isEnemy */
  /* EFFECTS: Return a new target for p (within range distance).
		Sets isEnemy to True 
		iff returned pointer is an enemy (as opposed to an item).
    Returns NULL if no suitable target found. */
  /* NOTE: Doesn't set targetId or strategy. */
  /* NOTE: Not virtual, but can be called by children inside
     choose_strategy(). */
  
  virtual Boolean filter_target(PhysicalP p);
  /* EFFECTS: Used by children to filter out things the Machine shouldn't
     set as the target. Return whether p is a valid target. */
  /* NOTE: Can call up the tree. */

  static Boolean attack_target(PhysicalP p,PhysicalP target);
  static Boolean move_target(PhysicalP p,PhysicalP target,Timer &ladderJump);
  static Boolean away_target(PhysicalP p,PhysicalP target,Timer &ladderJump);
  static Boolean move_pos(PhysicalP p,const Pos &targetPos,Timer &ladderJump);
  static Boolean move_dir(PhysicalP p,Dir d,Timer &ladderJump);
  /* REQUIRES: p and target must be non-NULL. */
  /* REQUIRES: d is an element of {CO_R..CO_UP_R,CO_air} */
  /* EFFECTS: The above are all utility functions that attempt to set the
     command for p to do something.  Returns whether the command was set or
     not. */
  /* IMPLEMENTATION NOTE: Declared static to ensure that no data members of 
     Machine are used in these utility functions.  */


  static Boolean has_gun(HolderP);
  static Boolean has_cutter(HolderP);
  static Boolean has_shield(HolderP);
  static Boolean has_drugs(HolderP);
  static Boolean has_item(HolderP,ClassId);
  static Boolean weapon_current_is_gun(HolderP);
  static Boolean weapon_current_is_cutter(HolderP);
  /* EFFECTS: Return info about a Holder. */

  static Boolean weapon_current_is_drop_liftable(PhysicalP);
  /* NOTE: Semi-hack to check for dropping Liftables. */


private:
  void use_items(Boolean &commandSet,PhysicalP p);
  /* EFFECTS: Called in Machine::clock(), choose which
     item to use and use it.  Weapons are used in attack_target().
     p is the Physical this is controlling. */
  /* MODIFIES: commandSet */
  
  Strategy strategy;
  Timer strategyChange;
  
  // Only one of the following three is valid.
  Pos targetPos;
  Id targetId;
  Dir targetDir;
  
  Timer reflexes;
  Timer ladderJump; // don't get back on ladder immediately after jumping off.
};
typedef Machine *MachineP;



class Enemy: public Machine {
public:
  Enemy(WorldP w,LocatorP l,char *name,
	const IntelOptions *ops,ITmask opMask);

  static void set_reflexes_time(int time) {reflexesTime = time;}
  static int get_reflexes_time() {return reflexesTime;}
  /* EFFECTS: How fast machines react.  1 means every turn. 2 is 
  every other turn, etc. */
	  
  virtual Boolean is_enemy();


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  int _get_reflexes_time();
  /* NOTE: To pass info to Machine. */

  
private:
  static int reflexesTime;
};
typedef Enemy *EnemyP;



class Neutral: public Machine {
public:
  Neutral(WorldP w,LocatorP l,char *name,
	const IntelOptions *ops,ITmask opMask);
	  
  virtual Boolean is_enemy();
};
typedef Neutral *NeutralP;



class SealIntel: public Neutral {
public:
  SealIntel(WorldP w,LocatorP l,char *name,const Id &homeId);
  
  virtual Boolean is_seal_intel();


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
 	virtual void choose_strategy(PhysicalP);
	/* EFFECTS: Sets the strategy.  May use or set the strategyChange timer. */


 private:
  Id homeId;
};



class DoppelIntel: public Neutral {
public:
	DoppelIntel(WorldP w,LocatorP l,char *name,
    const IntelOptions *ops,ITmask opMask,
	  IntelP master = NULL);
	
	virtual IntelId get_master_intel_id();
  
	virtual void add_human_kill();

  virtual void add_enemy_kill();

  virtual void add_soup();

  virtual void clock(PhysicalP);

  static IntelOptions suggestedOptions;
  static ITmask suggestedMask;


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
	void set_master_intel_id(const IntelId &id) {masterIntelId = id;}
	
	virtual void choose_strategy(PhysicalP p);
	/* EFFECTS: Filters out the master or other objects controlled by
		the same master. */
	/* NOTE: p is the Physical this is controlling. */

	virtual Boolean filter_target(PhysicalP candidate);

	PhysicalP lookup_and_compute_dist_2(int &dist_2,
																      PhysicalP p,const Id &id);
	/* EFFECTS: A helper routine. If both p and Locator::lookup(id)
	are non-NULL. Return the result of looking up id, and set
	dist_2 to be the square of the distance between the two. Else return NULL. */
	/* MODIFIES: dist_2 */

  
private:
  Timer suicideTimer;
  IntelId masterIntelId;
};



// Pet acts like a finite state machine with four states, 
// Heel, Return, Seek, and Attacking.  Transitions are implemented
// in choose_strategy() and go_get_em().
class Pet: public DoppelIntel {
public:
  Pet(WorldP w,LocatorP l,char *name,
	    const IntelOptions *ops,ITmask opMask,
	    IntelP master = NULL);
    
  Boolean is_pet();

  void go_get_em(PhysicalP p,ITcommand command);
  /* EFFECTS: p is ordering the pet to attack in the specified direction if 
	IT_WEAPON_{some dir}, or to come back to him if command is IT_WEAPON_CENTER. 
	This will also set p to be the new master. */

	static char *mode_string(int mode);
	/* NOTE: For debugging. */


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  virtual int _get_reflexes_time();

  virtual void choose_strategy(PhysicalP p);

  virtual Boolean filter_target(PhysicalP candidate);


private:
  enum {Heel, Returning, Seek, Attacking};
  enum {RETURN_TIME = 500, 
	HEEL_RADIUS = 18, 
	ATTACK_RADIUS = 100, 
	GIVE_UP_ATTACK_RADIUS = 400};
  // Squares of distances used for comparisons.
	enum {HEEL_RADIUS_2 = HEEL_RADIUS * HEEL_RADIUS,
	      ATTACK_RADIUS_2 = ATTACK_RADIUS * ATTACK_RADIUS,
	      GIVE_UP_ATTACK_RADIUS_2 = GIVE_UP_ATTACK_RADIUS * GIVE_UP_ATTACK_RADIUS};
  
  int mode;
};
typedef Pet *PetP;
#endif

