/
lib/banish/
lib/d/
lib/doc/
lib/doc/domains/
lib/doc/efun/
lib/doc/examples/
lib/doc/examples/armour/
lib/doc/examples/contain/
lib/doc/examples/food/
lib/doc/examples/magic/
lib/doc/examples/monster/
lib/doc/examples/room/
lib/doc/examples/weapons/
lib/function/
lib/include/
lib/include/fn_specs/
lib/include/skills/
lib/info/
lib/inherit/base/
lib/log/
lib/manuals/312/
lib/news/
lib/obj/party/
lib/objects/components/
lib/open/
lib/open/library/
lib/open/party/
lib/players/
lib/players/zilanthius/
lib/room/
lib/room/city/arena/
lib/room/city/creator/
lib/room/city/garden/monst/
lib/room/city/obj/
lib/room/city/shop/
lib/room/death/
lib/room/registry/
lib/secure/
lib/secure/UDP_CMD_DIR/
lib/skills/
lib/skills/fighter/
lib/skills/thief/
lib/usr/
lib/usr/creators/
lib/usr/players/
#pragma strict_types
#pragma save_types
#pragma combine_strings

#include <mudlib.h>
inherit LIVING;  


#include <monster.spc>
#include <spell.h>  /* Must come after monster.spc */


#define HEALING_TIME       60  /* 60 hearbeats */ 
#define STOLEN_TIME       180  /* 180 heart beats call reset(1) */  
#define FILE_SIZE         (int)"obj/master"->master_file_size

status move_at_reset;   /* True will make the monster wander at reset. */  
status aggressive;      /* True will make monster attack on sight. */  

int stolen;             /* has something been stolen */ 
int healing;            /* heart_beats since last healing */  
  
string *chat_head;       /* list of chat strings. */  
int chat_chance;         /* chance at each heart beat to chat */
  
string *a_chat_head;     /* list of attack chats */  
int a_chat_chance;       /* chance at each heart beat to use attack chat */
  
string *l_chat_head;     /* list of chats spoken in language */
string chat_language;    /* language chats spoken in */
int l_chat_chance;       /* chance each heart beat to chat */
  

object dead_ob;          /* alternative objects to call fn */  
object init_ob;  
object heart_ob;  
  
int random_pick;        /* Chance to pick up items every heart_beat. */  

int wander;             /* wander at this many heart beats */  
int wander_time;        /* Counter for wandering */  
 
int new_spell_chance;   /* chance each heart beat to cast spell */
string *new_spells;     /* list of 'player' spell commands */ 

int spell_chance;       /* chance each heart beat to cast spell */
mixed *default_spell;   /* specialized spell of monster */

int steal_chance;       /* chance per init to steal */

status no_kill_flag;   /* won't allow attacks on monster */

static string skill_cmd; /* current skill command */

/***************************************************************************/ 
  


void reset(status arg) {  
#ifdef NATIVE_MODE
  arg = 1;
#else
  ::reset(arg);
#endif /* native */
  if(arg) {  
    if(stolen) {
      if(environment()) environment()->reset(1); 
    }    
    stolen = 0;
    if(move_at_reset) random_move();  
    return;  
  }  

#ifdef NATIVE_MODE
}

void create() {
  ::create();
#endif /* native */
  is_npc = 1;  
  enable_commands();  
  add_class("fighter");
  /* all monsters start off as a fighter, add or subtract the ones u want */
  add_action("communicate","say");  
  add_action("skills", "", 1);
  default_spell = allocate(SPELL_ALLOCATE);
}  

  

/*************************************************************************/
/* sets */

object set_heart_ob(object ob) { return heart_ob = ob; }  
object set_dead_ob(object ob)  { return dead_ob = ob;  }  
object set_init_ob(object ob)  { return init_ob = ob;  }  

int set_random_pick(int r)     { return random_pick = r; }  
int set_steal_chance(int s)    { return steal_chance = s; }
int query_steal_chance()       { return steal_chance; }
status set_no_kill_flag(status s) { return no_kill_flag = (s) ? 1 : 0; }

status set_move_at_reset(status i) { return move_at_reset = (i) ? 1 : 0; }  
status set_aggressive(status a)    { return aggressive = (a) ? 1 : 0; }  

string set_name(string n) {  
  set_living_name(lower_case(n));  
  set_short(capitalize(n));
  set_alt_name(lower_case(n));
  return ::set_name(capitalize(n));
}  
 
/*** set level will automatically set monster to bare minimum requirements ***/ 
  

void set_level(int l) {  
   int *monster_exp; 
 
   if(!intp(l) || l < 1) l = 1; 
   level = l;  
   combat = l;       dexterity = l; strength = l; charisma = l; 
   intelligence = l; wisdom = l;    constitution = l; 


   /*** set thief skills ***/  
   if(query_class("thief")) { 
     stealth = l; locks = l; steal = l; climb = l; backstab = l; 
     appraisal = l; traps = l; 
   }
 
   /*** set fighting skills ***/ 
   if(query_class("fighter")) { 
     two_weapon = l; unarmed = l; multi_attack = l; multi_opponent = l;
   } 

   /*** set cleric skills ***/ 
   if(query_class("cleric")) { 
     healing_sphere = l; necromancy_sphere = l; combat_sphere = l; 
     stellar_sphere = l; protection_sphere = l; nature_sphere = l; 
     divination_sphere = l; 
     heal_cleric(l*2);
   }
 
   /*** set mage skills ***/ 
   if(query_class("mage")) {
     illusion = l; charm = l; conjuration = l; abjuration = l; 
     necromancy = l; evocation = l; divination = l; alteration = l; 
     heal_mage(l*2);
   } 
 
   /*** set psionicist skills ***/ 
 
/*
   if(query_class("psionicist")) {
     clairsentient = l; psycokinetic = l; psycoportive = l; 
     psycometabolic = l; telepathic = l; metapsionic = l; 
     max_psionic_points = 6 * l; 
   }
*/

   heal_self(10000); 
 
   /*** set weapon class ***/ 
 
   set_right_wc(l+5); 
   if(query_right_wc() > 30) set_right_wc(30); 
 
   /*** set armour class ***/ 
 
   if(l < 14) 
     armour_class = l/2 + 3; 
   else 
     armour_class = l - 3; 
   if(armour_class > 17) armour_class = 17; 
 
   /*** set hit points and spells points? ***/ 
 
   if(l < 5) 
     hp = l * 10 + 40; 
   else if(l < 10) 
     hp = l * 50 - 150; 
   else if(l < 15) 
     hp = l * 100 - 600; 
   else if(l < 20) 
     hp = l * 200 - 2000; 
   else if(l < 25)  
     hp = l * 300 - 3900; 
   else 
     hp = 3500; 
   max_hp = hp; 
 
   /*** set experience points ***/ 
 
   monster_exp = ({ 
                676,    1014,   1522,   2283,   3425,   5138, 
               7707,   11561,  17341,  26012,  39018,  58527, 
              87791,  131687, 197530, 296296, 444444, 666666, 
            1000000, 1500000,2000000,2500000,3000000,3500000, 
            4000000, 4500000 
                 }); 
 
   if(l > sizeof(monster_exp) - 1) { 
     experience = monster_exp[sizeof(monster_exp) - 1]; 
     experience += (l - (sizeof(monster_exp) - 1)) * 500000; 
   } 
   else 
     experience = monster_exp[l]; 
 
   /*** high level monsters do additional damage ***/ 
 
  if(l > 15) { 
    spell_chance = ((l - 14)*10 > 75) ? 75 : (l - 14)*10; 
    TARGET_MSG   = "A fireball erupts, cast directly at you!\n"; 
    ROOM_MSG     = "@@query_name:$this_player()$@@ casts a fireball at "+        
                   "@@query_name:$this_object()$@@.\n"; 
    IMMUNE_TYPE  = "fire"; 
    SPELL_TYPE   = "evocation"; 
    SPELL_DAM    = (l - 14) * 5 + random((l - 14) * 5); 
    SPELL_LEVEL  = l;
    SPELL_COST   = 2; /* spell strength runs out eventually */
  } 
}  
  

void add_class(string str) {
  ::add_class(str);

   /*** set thief skills ***/  
   if(query_class("thief")) { 
     stealth = level; locks = level; steal = level; climb = level; 
     backstab = level; appraisal = level; traps = level; 
   }
 
   /*** set fighting skills ***/ 
   if(query_class("fighter")) { 
     two_weapon = level; unarmed = level; multi_attack = level;
     multi_opponent = level;
   } 

   /*** set cleric skills ***/ 
   if(query_class("cleric")) { 
     healing_sphere = level; necromancy_sphere = level;
     combat_sphere = level; stellar_sphere = level;
     protection_sphere = level; nature_sphere = level; 
     divination_sphere = level; 
     heal_cleric(level*2);
   }
 
   /*** set mage skills ***/ 
   if(query_class("mage")) {
     illusion = level; charm = level; conjuration = level; abjuration = level; 
     necromancy = level; evocation = level; divination = level;
     alteration = level; 
     heal_mage(level*2);
   } 
 
   /*** set psionicist skills ***/ 
/*
   if(query_class("psionicist")) {
     clairsentient = level; psycokinetic = level; psycoportive = level; 
     psycometabolic = level; telepathic = level; metapsionic = level; 
     max_psionic_points = 6 * level; 
   }
*/
}


void set_wander(int chance, int time) {  
   wander = time;  
   set_heart_beat(1); 
}  

/************************************/
/* old way for spells */
 
int set_chance(int c)    { return spell_chance = c; }  
int set_spell_dam(int d) { return SPELL_DAM = d; }  
  
string set_spell_mess1(string m) { return TARGET_MSG = m; }  
string set_spell_mess2(string m) { return ROOM_MSG = m; }  
string set_spell_type(string t)  { return IMMUNE_TYPE = t; }  
 
string set_spell_skill_type(string str) { 
   string *spell_skills; 
 
   spell_skills = ({ 
     "healing_sphere", "necromancy_sphere", "combat_sphere", 
     "stellar_sphere", "protection_sphere", "nature_sphere", 
     "divination_sphere", "illusion", "charm", "conjuration", 
     "abjuration", "necromancy", "evocation", "divination", 
     "alteration", 
                  }); 
 
   if(!str || member_array(str, spell_skills) == -1) { 
     str = spell_skills[random(sizeof(spell_skills))]; 
   } 
   return SPELL_TYPE = str; 
}  
 
/**************************************/
/* new way */

void set_spell(mixed *arr) {
  int i;

  default_spell = allocate(SPELL_ALLOCATE); 
  for(i = 0; i < sizeof(arr); i++) {
    if(!stringp(arr[i])) continue;
    switch(arr[i]) {
      case "target":                    TARGET      = arr[++i];     break;
      case "name": case "spellname":    SPELL_NAME  = arr[++i];     break; 
      case "school": case "sphere":     SPELL_TYPE  = arr[++i];     break;
      case "cost":                      SPELL_COST  = arr[++i];     break;
      case "damage":                    SPELL_DAM   = arr[++i];     break;
      case "msg target":                TARGET_MSG  = arr[++i];     break;
      case "msg room":                  ROOM_MSG    = arr[++i];     break;
      case "msg caster":                CASTER_MSG  = arr[++i];     break;
#ifdef OLD_AREA_EFFECT
      case "area":                    AREA_EFFECT = this_object();  break;
#else
      case "area":    AREA_EFFECT = all_inventory(environment());   break;
#endif
      case "immune":                    IMMUNE_TYPE = arr[++i];     break;
      case "level": case "spell level": SPELL_LEVEL = arr[++i];     break;
      case "spell object":              SPELL_OBJ   = arr[++i];     break;
      case "time": case "cast time":    SPELL_TIME  = arr[++i];     break;
      case "casting msg":               PREPARE_MSG = arr[++i];     break;
      case "casting msg room":        PREP_MSG_ROOM = arr[++i];     break;    
      case "component":                 COMPONENT   = arr[++i];     break;
      case "passive":                   PASSIVE     = 1;            break;
      case "aggressive":                PASSIVE     = 0;            break;
      case "chance":                   spell_chance = arr[++i];     break;
    }
  }
}


/* can call player spells */

void load_spells(int c, string *s) { 
  sizeof(s); 
  new_spells = s; 
  new_spell_chance = c; 
} 
 

/*************************************************************************/
/* query */

status query_no_kill_flag() { return no_kill_flag; }
  




/***********************************************************************/
/* heart beat */

static status filter_players(object ob) {
  if(living(ob) && !ob->query_npc()) return 1;
  return 0;
}

void heart_beat() {  
  int player_here;
  string cmd;
  object *inv;
  
  if(!environment()) {
    set_heart_beat(0);
    return;
  }
  adj_age(1);  
  if(heart_ob) heart_ob->monster_heart_beat();  

  inv = all_inventory(environment());
  player_here = sizeof(filter_array(inv,"filter_players",this_object()));
  if(stolen) {
    if(stolen++ > STOLEN_TIME) reset(1);
  }
  if(!player_here) {
    if(hp < max_hp) {
      if(healing++ > HEALING_TIME) {
        healing = 0;
        heal_self(1);
      }     
    }
    else if(!wander && !stolen) { 
      set_heart_beat(0);  
      return; 
    }
    if(wander) {  
      if(wander_time++ > wander && !random(4)) {
        random_move();  
      } 
    }
    return;
  }
  if(primary_attack && present(primary_attack, environment())) {
    if(a_chat_chance > random(100) + 1) {  
      tell_room(environment(), a_chat_head[random(sizeof(a_chat_head))]);  
    }
    if(spell_chance > random(100) + 1) {
      if(objectp(SPELL_TYPE)) { /* wand */
        if((int)SPELL_TYPE->query_charges() > SPELL_COST) {
          set_loaded_spell(default_spell);
          SPELL_TYPE->adj_charges(-SPELL_COST);
        }
      }
      else if(query(SPELL_TYPE+"_points") > SPELL_COST) {
        set_loaded_spell(default_spell);
        call_other(this_object(),"adj_"+SPELL_TYPE+"_points",-SPELL_COST);
      }
    }
    if(new_spell_chance > random(100) + 1) { 
    } 
    if(query_wimpy() && hp < max_hp/5) random_move();  
  }  
  else {
    if(chat_chance > random(100) + 1) {  
      tell_room(environment(), chat_head[random(sizeof(chat_head))]);  
    }
    if(l_chat_chance > random(100) + 1) {  
      command("say "+l_chat_head[random(sizeof(l_chat_head))]);
    }  
  }  
  if(random_pick > random(100) + 1) {  
    pick_any_obj();  
  }  
  attack();  
}  

  
/**************************************************************************/  
 
status init_command(string cmd) {
      status cmd_flag;
      
      if(sscanf(cmd, "%s %s", skill_cmd, cmd)) {
        cmd = skill_cmd +" "+ cmd;
      }
      else {
        skill_cmd = cmd;
      }
      cmd_flag = command(cmd, this_object());
      skill_cmd = 0;
      return cmd_flag;
}
  



/***************************************************************************/  
/***   Load Chats and Attack chats.   ***/  
 
void load_chat(int chance, string *strs) {  
   if(!sizeof(strs)) return;     /* Just ensure that it is an array. */  
   chat_head = strs;  
   chat_chance = chance;  
}  
  
void load_a_chat(int chance, string *strs) {  
   if(!sizeof(strs)) return;     /* Just ensure that it is an array. */  
   a_chat_head = strs;  
   a_chat_chance = chance;  
}  
  
/***   Load language chat - Zilanthius ***/  
  
void load_l_chat(int chance, string lang, string *strs) {  
   if(!sizeof(strs)) return;     /* Just ensure that it is an array. */  
   l_chat_head = strs;  
   l_chat_chance = chance;  
   chat_language = lang;  
   if(!chat_language){  
     if(race)  
       chat_language = lower_case(race);  
     else  
       chat_language = "common";  
   }  
}    
  
  
/****************************************************************************/
  
status second_life() {  
  if(dead_ob) return (status)dead_ob->monster_died(this_object());  
}  
  

/****************************************************************************/
  
void pick_any_obj () {  
  object ob;  
  int weight;  
  
  ob = first_inventory(environment());  
  while (ob) {  
    if(ob->get() && ob->short()) {  
      weight = (int)ob->query_weight();  
      if(!add_weight(weight)) {  
        say (query_name()+" tries to take "+ob->short()+" but fails.\n");  
        return;  
      }  
#ifdef NATIVE_MODE
      ob->move(this_object());
#else
      move_object(ob, this_object());  
#endif /* NATIVE_MODE */
      say(query_name() +" takes "+ ob->short() +".\n");  
      return;  
    }  
    ob = next_inventory(ob);  
  }  
}  

/************************************************************************/


void init() {  
  string who;

  ::init();
  if(this_player() == this_object()) return;  
  if(init_ob && init_ob->monster_init(this_object())) return;  
  if(primary_attack) set_heart_beat(1);     /* Turn on heart beat */  
  if(!this_player()->query_npc()) {  
    if(!this_player()->query_entering()) {
      who = (string)this_player()->query_name(1);
      if(!sec_att_names) sec_att_names = ({});
      if(member_array(who, sec_att_names) != -1
      || (who && member_array(capitalize(who), sec_att_names) != -1)){
        add_secondary_attacker(this_player());
      }
    }
    set_heart_beat(1);  
    if(aggressive) { 
      string rname;

      rname = (string)this_player()->query_name(1);
      if(query_class("thief")) stealth_on = 1; /* needed for bs */
      if(this_player()->query_stealth_on()) {
        if(sizeof(compare("stealth","intelligence", ({ this_object(), })))) {
          if(query_class("thief")) {
            skill_cmd = "backstab";
            command("backstab "+ rname, this_object());
            skill_cmd = 0;
          }
          add_secondary_attacker(this_player()); /* get ready to attack */
        }
      }
      else if(this_player()->query_invis()) {          
        if(sizeof(compare("invis","intelligence", ({ this_object(), })))) {
          if(query_class("thief")) {
            skill_cmd = "backstab";
            command("backstab "+ rname, this_object());
            skill_cmd = 0;
          }
          add_secondary_attacker(this_player()); /* get ready to attack */
        }
      }
      else {
        if(query_class("thief")) {
          skill_cmd = "backstab";
          command("backstab "+ rname, this_object());
          skill_cmd = 0;
        }
        add_secondary_attacker(this_player()); /* get ready to attack */
      }  
    }  
    else { /* not aggressive */
      if(query_class("thief") && random(100) < steal_chance) { 
        steal_from_player(this_player(),0);
      }
    }
  }  
}
  
  
/*** stealing from a monster, call "steal" if something is stolen... ***/ 
 
void steal() { 
  stolen = 1; 
  set_heart_beat(1);
} 
 
void steal_from_player(object who, mixed item) {
  object *inv;
  int i;
  string item_name;

  if(!item) {
    inv = all_inventory(this_player());
    if(!(i = sizeof(inv))) return;
    i = random(i);
    if(!(item_name = (string)inv[i]->query_name())) return;
  }
  else if(objectp(item)) {
    item_name = (string)item->query_name(1);
  }
  else if(stringp(item)) {
    item_name = item;
  }
  else {
    return;
  }
  skill_cmd = "steal";
  command("steal "+ item_name +" from "+ (string)who->query_name(1));
  skill_cmd = 0;
}

/************************************************************************/ 

/* load function added by Crombie, Sept 8, 1993 */

void load_monster(mixed name, int lev, string race) {
  int i, size;

  if(!stringp(name)) {
    for(i = 0, size = sizeof(name); i < size; i++) {
      if(!stringp(name[i])) continue;
      switch(name[i]) {
        case "name":             set_name(name[++i]);          break;
        case "level":            set_level(name[++i]);         break;
        case "alias":            set_alias(name[++i]);         break;
        case "race":             set_race(name[++i]);          break;
        case "hp":               set_hp(name[++i]);            break;
        case "exp":              set_exp(name[++i]);           break;
        case "al":               set_al(name[++i]);            break;
        case "short":            set_short(name[++i]);         break;
        case "long":             set_long(name[++i]);          break;
        case "altname":          set_alt_name(name[++i]);      break;
        case "alt_name" :        set_alt_name(name[++i]);      break;
        case "wc":               set_right_wc(name[++i]);      break;
        case "ac":               set_ac(name[++i]);            break;
        case "aggressive":       set_aggressive(1);            break;
        case "whimpy":           set_wimpy(1);                 break;
        case "move_at_reset":    set_move_at_reset(1);         break;
        case "chance":           set_chance(name[++i]);        break;
        case "heart_ob":         set_heart_ob(this_object());  break;
        case "mess1":            set_spell_mess1(name[++i]);   break;
        case "spell_mess1":      set_spell_mess1(name[++i]);   break;
        case "mess2":            set_spell_mess2(name[++i]);   break;
        case "spell_mess2":      set_spell_mess2(name[++i]);   break;
        case "dam":              set_spell_dam(name[++i]);     break;
        case "spell_dam":        set_spell_dam(name[++i]);     break;
        case "spell_type":       set_spell_type(name[++i]);    break;
        case "spell_skill_type": set_spell_skill_type(name[++i]);     break;
        case "gender":           set_gender(name[++i]);        break;
        case "deadob":           set_dead_ob(this_object());   break;
        case "randompick":       set_random_pick(name[++i]);   break;
        case "init_command" :    init_command(name[++i]);      break;
        case "random_pick":      set_random_pick(name[++i]);   break;
        case "magic_resist" :    set_magic_resist(name[++i]);  break;
        case "add_money" :       add_money(name[++i]);         break;
        case "money" :           add_money(name[++i]);         break;
      }
    }
  }  
  else {
    set_name(name);
    set_level(lev);
    set_race(race);
  }
}


/*********************************************************************/

int hit_player(int dmg) {
  if(environment(this_player())->query_no_fight()) {
    if(this_player()) this_player()->stop_fight(this_object());
    stop_fight(this_player());
    write("Fighting is Not Allowed Here.\n");
    return 0;
  }
  return ::hit_player(dmg);
}


/*********************************************************************/
/* monster skill commands */

status skills(string str) {
  string verb;
  int i;

  verb = query_verb();
  /* verb must match current skill command */
  if(verb != skill_cmd) {
    skill_cmd = 0;
    return 0;
  }
  skill_cmd = 0;
    
  for(i = 0; i < sizeof(classes); i++) {
    if(FILE_SIZE("/skills/"+ classes[i] +"/"+ verb +".c") > 0) {
      if(call_other("skills/"+ classes[i] +"/"+ verb,verb,str)) return 1;
    }
  }
  return 0;
}