Difference for server/monster.c from version 1.27 to 1.28


version 1.27 version 1.28
Line 1
 
Line 1
 /*  /*
  * static char *rcsid_monster_c =   * static char *rcsid_monster_c =
  *    "$Id: monster.c,v 1.27 2001/07/14 04:11:18 mwedel Exp $";   *    "$Id: monster.c,v 1.28 2001/08/21 05:39:30 mwedel Exp $";
  */   */
   
 /*  /*
Line 50
 
Line 50
   
 #define MIN_MON_RADIUS 3 /* minimum monster detection radius */  #define MIN_MON_RADIUS 3 /* minimum monster detection radius */
   
 object *get_enemy(object *npc) {  /* checks npc->enemy and returns that enemy if still valid,
   if ((npc->move_type & HI4) == 16) {   * NULL otherwise.  This doesn't really get the enemy, so is
    * misnamed - it should really be 'check_enemy'.
    * this is map tile aware.
    */
   
   object *get_enemy(object *npc, rv_vector *rv) {
       if ((npc->move_type & HI4) == PETMOVE) {
     if (npc->owner != NULL)      if (npc->owner != NULL)
       return npc->enemy = npc->owner->enemy;        return npc->enemy = npc->owner->enemy;
     else npc->enemy = NULL;      else npc->enemy = NULL;
   }    }
       /* periodically, a monster mayu change its target.  Also, if the object
        * has been destroyed, etc, clear the enemy.
        */
   if(npc->enemy) {    if(npc->enemy) {
     if(QUERY_FLAG(npc->enemy,FLAG_REMOVED)||QUERY_FLAG(npc->enemy,FLAG_FREED)      if(QUERY_FLAG(npc->enemy,FLAG_REMOVED)||QUERY_FLAG(npc->enemy,FLAG_FREED)
        ||!(RANDOM()%20)||       ||!(RANDOM()%20)|| (npc->enemy->type!=PLAYER&&npc->enemy->type!=GOLEM)||
        (npc->enemy->type!=PLAYER&&npc->enemy->type!=GOLEM)||       !on_same_map(npc, npc->enemy))
        (npc->enemy->type==PLAYER&&npc->enemy->contr->state)||  
        npc->enemy->map!=npc->map)  
       npc->enemy=NULL;        npc->enemy=NULL;
   }    }
   return can_detect_enemy(npc,npc->enemy)?npc->enemy:NULL;      return can_detect_enemy(npc,npc->enemy,rv)?npc->enemy:NULL;
 }  }
   
   /* Returns the nearest living creature (monster or generator).
    * Modified to deal with tiled maps properly.
    * Also fixed logic so that monsters in the lower directions were more
    * likely to be skipped - instead of just skipping the 'start' number
    * of direction, revisit them after looking at all the other spaces.
    *
    * Note that being this may skip some number of spaces, it will
    * not necessarily find the nearest living creature - it basically
    * chooses one from within a 3 space radius, and since it skips
    * the first few directions, it could very well choose something
    * 3 spaces away even though something directly north is closer.
    *
    * this function is map tile aware.
    */
 object *find_nearest_living_creature(object *npc) {  object *find_nearest_living_creature(object *npc) {
     int i,max=SIZEOFFREE;      int i,j=0,start;
   int nx,ny;    int nx,ny;
   int x, y;  
   mapstruct *m;    mapstruct *m;
   object *tmp;    object *tmp;
   
   x = npc->x;      start = (RANDOM()%8)+1;
   y = npc->y;      for(i=start;j<SIZEOFFREE;j++, i=(i+1)%SIZEOFFREE) {
   m = npc->map;   nx = npc->x + freearr_x[i];
    ny = npc->y + freearr_y[i];
   for(i=(RANDOM()%8)+1;i<max;i++) {   if (out_of_map(npc->map,nx,ny)) continue;
     nx = x + freearr_x[i];   m = get_map_from_coord(npc->map, &nx, &ny);
     ny = y + freearr_y[i];  
     if(!out_of_map(m,nx,ny)) {   if (GET_MAP_FLAGS(m, nx, ny) & P_IS_ALIVE) {
       tmp=get_map_ob(m,nx,ny);        tmp=get_map_ob(m,nx,ny);
       while(tmp!=NULL && !QUERY_FLAG(tmp,FLAG_MONSTER)&&        while(tmp!=NULL && !QUERY_FLAG(tmp,FLAG_MONSTER)&&
      !QUERY_FLAG(tmp,FLAG_GENERATOR ) && tmp->type!=PLAYER)        !QUERY_FLAG(tmp,FLAG_GENERATOR ) && tmp->type!=PLAYER)
                 tmp=tmp->above;                  tmp=tmp->above;
       if(tmp!=NULL && can_see_monsterP(m,x,y,i))  
        if (!tmp) {
    LOG(llevDebug,"find_nearest_living_creature: map %s (%d,%d) has is_alive set but did not find a mosnter?\n",
        m->path, nx, ny);
        }
        else {
    if(can_see_monsterP(m,nx,ny,i))
         return tmp;          return tmp;
     }      }
    } /* is something living on this space */
   }    }
   return 0;  /* nothing found */      return NULL;  /* nothing found */
 }  }
   
   
 object *find_enemy(object *npc) {  /* Tries to find an enmy for npc.  We pass the range vector since
    * our caller will find the information useful.
    * Currently, only move_monster calls this function.
    */
   
   object *find_enemy(object *npc, rv_vector *rv) {
   object *tmp=NULL;    object *tmp=NULL;
      
   if(QUERY_FLAG(npc,FLAG_BERSERK)) {    if(QUERY_FLAG(npc,FLAG_BERSERK)) {
     return find_nearest_living_creature(npc);      return find_nearest_living_creature(npc);
   }    }
   if ((npc->move_type & HI4) == PETMOVE)    if ((npc->move_type & HI4) == PETMOVE)
     return get_pet_enemy(npc);   return get_pet_enemy(npc,rv);
   if((tmp=get_enemy(npc))==NULL)  
       if((tmp=get_enemy(npc, rv))==NULL) {
     if(!QUERY_FLAG(npc, FLAG_UNAGGRESSIVE)) {      if(!QUERY_FLAG(npc, FLAG_UNAGGRESSIVE)) {
       tmp = get_nearest_player(npc);        tmp = get_nearest_player(npc);
       if(QUERY_FLAG(npc, FLAG_FRIENDLY)&&tmp)        if(QUERY_FLAG(npc, FLAG_FRIENDLY)&&tmp)
         tmp = get_enemy(tmp);   tmp = get_enemy(tmp,rv);
    }
     }      }
   return tmp;    return tmp;
 }  }
   
 int check_wakeup(object *op, object *enemy) {  /* Sees if this monster should wake up.
    * Currently, this is only called from move_monster, and
    * if enemy is set, then so should be rv.
    */
   
   int check_wakeup(object *op, object *enemy, rv_vector *rv) {
   int radius = op->stats.Wis>MIN_MON_RADIUS?op->stats.Wis:MIN_MON_RADIUS;    int radius = op->stats.Wis>MIN_MON_RADIUS?op->stats.Wis:MIN_MON_RADIUS;
   
       /* Trim work - if no enemy, no need to do anything below */
       if (!enemy) return 0;
   
   /* blinded monsters can only find nearby objects to attack */    /* blinded monsters can only find nearby objects to attack */
   if(QUERY_FLAG(op, FLAG_BLIND) && !QUERY_FLAG(op, FLAG_SEE_INVISIBLE))     if(QUERY_FLAG(op, FLAG_BLIND) && !QUERY_FLAG(op, FLAG_SEE_INVISIBLE))
  radius = MIN_MON_RADIUS;   radius = MIN_MON_RADIUS;
   
   /* This covers the situation where the monster is in the dark     /* This covers the situation where the monster is in the dark
    * and has an enemy. If the enemy has no carried light (or isnt      * and has an enemy. If the enemy has no carried light (or isnt
    * glowing!) then the monster has trouble finding the enemy.      * glowing!) then the monster has trouble finding the enemy.
    * Remember we already checked to see if the monster can see in      * Remember we already checked to see if the monster can see in
    * the dark. */     * the dark. */
   
   else if(op->map&&op->map->darkness>0&&enemy&&!enemy->invisible&&    else if(op->map&&op->map->darkness>0&&enemy&&!enemy->invisible&&
     !stand_in_light(enemy)&&(!QUERY_FLAG(op,FLAG_SEE_IN_DARK)||      !stand_in_light(enemy)&&(!QUERY_FLAG(op,FLAG_SEE_IN_DARK)||
     !QUERY_FLAG(op,FLAG_SEE_INVISIBLE)))        !QUERY_FLAG(op,FLAG_SEE_INVISIBLE))) {
   {  
    int dark = radius/(op->map->darkness);     int dark = radius/(op->map->darkness);
    radius = (dark>MIN_MON_RADIUS)?(dark+1):MIN_MON_RADIUS;     radius = (dark>MIN_MON_RADIUS)?(dark+1):MIN_MON_RADIUS;
   }     }
   else if(!QUERY_FLAG(op,FLAG_SLEEP)) return 1;    else if(!QUERY_FLAG(op,FLAG_SLEEP)) return 1;
   
     /* enemy should already be on this map, so don't really need to check      /* enemy should already be on this map, so don't really need to check
      * for that.       * for that.
      */       */
     if (enemy) {      if (rv->distance < QUERY_FLAG(enemy, FLAG_STEALTH)?(radius/2)+1:radius) {
  int dist = distance(op, enemy);  
   
  if (dist < QUERY_FLAG(enemy, FLAG_STEALTH)?(radius/2)+1:radius) {  
      CLEAR_FLAG(op,FLAG_SLEEP);       CLEAR_FLAG(op,FLAG_SLEEP);
      return 1;       return 1;
  }   }
     }  
   return 0;    return 0;
 }  }
   
 int move_randomly(object *op) {  int move_randomly(object *op) {
   int i;    int i;
   
   for(i=0;i<15;i++);      /* Give up to 15 chances for a monster to move randomly */
       for(i=0;i<15;i++) {
     if(move_object(op,RANDOM()%8+1))      if(move_object(op,RANDOM()%8+1))
       return 1;        return 1;
       }
   return 0;    return 0;
 }  }
   
Line 158
 
Line 200
  */   */
   
 int move_monster(object *op) {  int move_monster(object *op) {
     int dir=0,diff;      int dir, diff;
     object *part, *owner, *enemy = find_enemy(op);      object  *owner, *enemy, *part;
       rv_vector rv;
   
     /* Monsters not on maps don't do anything.  These monsters are things      /* Monsters not on maps don't do anything.  These monsters are things
      * Like royal guards in city dwellers inventories.       * Like royal guards in city dwellers inventories.
      */       */
     if (!op->map) return 0;      if (!op->map) return 0;
   
       enemy= find_enemy(op, &rv);
   
     if(QUERY_FLAG(op, FLAG_SLEEP)||QUERY_FLAG(op, FLAG_BLIND)      if(QUERY_FLAG(op, FLAG_SLEEP)||QUERY_FLAG(op, FLAG_BLIND)
        ||((op->map->darkness>0)&&!QUERY_FLAG(op,FLAG_SEE_IN_DARK)         ||((op->map->darkness>0)&&!QUERY_FLAG(op,FLAG_SEE_IN_DARK)
    &&!QUERY_FLAG(op,FLAG_SEE_INVISIBLE))) {     &&!QUERY_FLAG(op,FLAG_SEE_INVISIBLE))) {
  if(!check_wakeup(op,enemy))   if(!check_wakeup(op,enemy,&rv))
      return 0;       return 0;
     }      }
   
Line 274
 
Line 317
     }      }
   
     /* We have an enemy.  Block immediately below is for pets */      /* We have an enemy.  Block immediately below is for pets */
     if((op->type&HI4) == PETMOVE && (owner = get_owner(op)) != NULL &&      if((op->type&HI4) == PETMOVE && (owner = get_owner(op)) != NULL && !on_same_map(op,owner)) {
        op->map != owner->map) {  
   
  follow_owner(op, owner);   follow_owner(op, owner);
  if(QUERY_FLAG(op, FLAG_REMOVED) && FABS(op->speed) > MIN_ACTIVE_SPEED) {   if(QUERY_FLAG(op, FLAG_REMOVED) && FABS(op->speed) > MIN_ACTIVE_SPEED) {
      remove_friendly_object(op);       remove_friendly_object(op);
Line 294
 
Line 335
     }      }
   
     for (part=op; part!=NULL; part=part->more) {      for (part=op; part!=NULL; part=part->more) {
  dir=find_dir_2(part->x-enemy->x,part->y-enemy->y);   rv_vector   rv1;
   
    get_rangevector(part, enemy, &rv1, 0x1);
   
    dir=rv1.direction;
   
  if(QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op,FLAG_RUN_AWAY))   if(QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op,FLAG_RUN_AWAY))
      dir=absdir(dir+4);       dir=absdir(dir+4);
Line 304
 
Line 349
   
  if (!QUERY_FLAG(op, FLAG_SCARED)) {   if (!QUERY_FLAG(op, FLAG_SCARED)) {
      if(QUERY_FLAG(op,FLAG_CAST_SPELL))       if(QUERY_FLAG(op,FLAG_CAST_SPELL))
  if(monster_cast_spell(op,part,enemy,dir))   if(monster_cast_spell(op,part,enemy,dir,&rv1))
      return 0;       return 0;
      if(QUERY_FLAG(op,FLAG_READY_WAND)&&!(RANDOM()%3))       if(QUERY_FLAG(op,FLAG_READY_WAND)&&!(RANDOM()%3))
  if(monster_use_wand(op,part,enemy,dir))   if(monster_use_wand(op,part,enemy,dir))
Line 323
 
Line 368
      return 0;       return 0;
  }   }
     } /* for processing of all parts */      } /* for processing of all parts */
     part = get_nearest_part(op,enemy);  
   
     /* I'm not really sure if we need to re-do the direction calculation     
      * again - we did it above.  However, part may differe than the      get_rangevector(op, enemy, &rv, 0);
      * direction above, so this may be necessary.      part = rv.part;
      */      dir=rv.direction;
     dir=find_dir_2(part->x-enemy->x,part->y-enemy->y);  
     if(QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op,FLAG_RUN_AWAY))      if(QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op,FLAG_RUN_AWAY))
  dir=absdir(dir+4);   dir=absdir(dir+4);
   
Line 339
 
Line 383
     if ((op->move_type & LO4) && !QUERY_FLAG(op, FLAG_SCARED)) {      if ((op->move_type & LO4) && !QUERY_FLAG(op, FLAG_SCARED)) {
  switch (op->move_type & LO4) {   switch (op->move_type & LO4) {
      case DISTATT:       case DISTATT:
  dir = dist_att (dir,op,enemy,part);   dir = dist_att (dir,op,enemy,part,&rv);
  break;   break;
      case RUNATT:       case RUNATT:
  dir = run_att (dir,op,enemy,part);   dir = run_att (dir,op,enemy,part,&rv);
  break;   break;
      case HITRUN:       case HITRUN:
  dir = hitrun_att(dir,op,enemy);   dir = hitrun_att(dir,op,enemy);
  break;   break;
      case WAITATT:       case WAITATT:
  dir = wait_att (dir,op,enemy,part);   dir = wait_att (dir,op,enemy,part,&rv);
  break;   break;
      case RUSH:       case RUSH:
      case ALLRUN:       case ALLRUN:
  break;    break;
      case DISTHIT:       case DISTHIT:
  dir = disthit_att (dir,op,enemy,part);   dir = disthit_att (dir,op,enemy,part,&rv);
  break;   break;
      case WAIT2:       case WAIT2:
  dir = wait_att2 (dir,op,enemy,part);   dir = wait_att2 (dir,op,enemy,part,&rv);
  break;   break;
      default:       default:
  LOG(llevDebug,"Illegal low mon-move: %d\n",op->move_type & LO4);   LOG(llevDebug,"Illegal low mon-move: %d\n",op->move_type & LO4);
Line 369
 
Line 413
     if (!QUERY_FLAG(op,FLAG_STAND_STILL)) {      if (!QUERY_FLAG(op,FLAG_STAND_STILL)) {
  if(move_object(op,dir)) /* Can the monster move directly toward player? */   if(move_object(op,dir)) /* Can the monster move directly toward player? */
      return 0;       return 0;
  if(QUERY_FLAG(op, FLAG_SCARED) || !can_hit(part,enemy)    if(QUERY_FLAG(op, FLAG_SCARED) || !can_hit(part,enemy,&rv)
     || QUERY_FLAG(op,FLAG_RUN_AWAY)) {      || QUERY_FLAG(op,FLAG_RUN_AWAY)) {
   
      /* Try move around corners if !close */       /* Try move around corners if !close */
Line 399
 
Line 443
      */       */
     if (!QUERY_FLAG(op, FLAG_FRIENDLY) && enemy == op->enemy) {      if (!QUERY_FLAG(op, FLAG_FRIENDLY) && enemy == op->enemy) {
  object *nearest_player = get_nearest_player(op);   object *nearest_player = get_nearest_player(op);
  if (nearest_player && nearest_player != enemy && !can_hit(part,enemy)) {   if (nearest_player && nearest_player != enemy && !can_hit(part,enemy,&rv)) {
      op->enemy = NULL;       op->enemy = NULL;
      enemy = nearest_player;       enemy = nearest_player;
  }   }
     }      }
   
     if(!QUERY_FLAG(op, FLAG_SCARED)&&can_hit(part,enemy)) {      if(!QUERY_FLAG(op, FLAG_SCARED)&&can_hit(part,enemy,&rv)) {
  if(QUERY_FLAG(op,FLAG_RUN_AWAY)) {   if(QUERY_FLAG(op,FLAG_RUN_AWAY)) {
      signed char tmp = (signed char)((float)part->stats.wc*(float)2);       signed char tmp = (signed char)((float)part->stats.wc*(float)2);
      part->stats.wc+=tmp;       part->stats.wc+=tmp;
Line 425
 
Line 469
     return 0;      return 0;
 }  }
   
 int can_hit(object *ob1,object *ob2) {  int can_hit(object *ob1,object *ob2, rv_vector *rv) {
   if(QUERY_FLAG(ob1,FLAG_CONFUSED)&&!(RANDOM()%3))    if(QUERY_FLAG(ob1,FLAG_CONFUSED)&&!(RANDOM()%3))
     return 0;      return 0;
   return abs(ob1->x-ob2->x)<2&&abs(ob1->y-ob2->y)<2;      return abs(rv->distance_x)<2&&abs(rv->distance_y)<2;
 }  }
   
 /*Someday we may need this check */  /*Someday we may need this check */
Line 464
 
Line 508
   return altern[RANDOM()%i];    return altern[RANDOM()%i];
 }  }
   
 int monster_cast_spell(object *head, object *part,object *pl,int dir) {  int monster_cast_spell(object *head, object *part,object *pl,int dir, rv_vector *rv) {
   object *spell_item;    object *spell_item;
   spell *sp;    spell *sp;
   int sp_typ, ability;    int sp_typ, ability;
   object *owner;    object *owner;
       rv_vector rv1;
   
   if(!(RANDOM()%3)) /* Don't want to cast spells so often */    if(!(RANDOM()%3)) /* Don't want to cast spells so often */
     return 0;      return 0;
   
       /* If you want monsters to cast spells over friends, this spell should
        * be removed.  It probably should be in most cases, since monsters still
        * don't care about residual effects (ie, casting a cone which may have a
        * clear path to the player, the side aspects of the code will still hit
        * other monsters)
        */
   if(!(dir=path_to_player(part,pl,0)))    if(!(dir=path_to_player(part,pl,0)))
     return 0;      return 0;
   
   if(QUERY_FLAG(head,FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) {    if(QUERY_FLAG(head,FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) {
     int dir2 = find_dir_2(head->x-owner->x, head->y-owner->y);   get_rangevector(head, owner, &rv1, 0x1);
     if(dirdiff(dir,dir2) < 2)   if(dirdiff(dir,rv1.direction) < 2)
       return 0; /* Might hit owner with spell */        return 0; /* Might hit owner with spell */
   }    }
   if(QUERY_FLAG(head,FLAG_CONFUSED))    if(QUERY_FLAG(head,FLAG_CONFUSED))
Line 487
 
Line 539
   if(head->spellitem==NULL) {    if(head->spellitem==NULL) {
     if((spell_item=monster_choose_random_spell(head))==NULL) {      if((spell_item=monster_choose_random_spell(head))==NULL) {
       LOG(llevMonster,"Turned off spells in %s\n",head->name);        LOG(llevMonster,"Turned off spells in %s\n",head->name);
       CLEAR_FLAG(head, FLAG_CAST_SPELL); /* Will be turned on when picking up book       CLEAR_FLAG(head, FLAG_CAST_SPELL); /* Will be turned on when picking up book */
 s */  
       return 0;        return 0;
     }      }
   }    }
Line 497
 
Line 548
   
   if(spell_item->stats.hp) {    if(spell_item->stats.hp) {
     /* Alternate long-range spell: check how far away enemy is */      /* Alternate long-range spell: check how far away enemy is */
     if(isqrt(distance(part,pl))>6)   if(rv->distance>6)
       sp_typ=spell_item->stats.hp;        sp_typ=spell_item->stats.hp;
     else      else
       sp_typ=spell_item->stats.sp;        sp_typ=spell_item->stats.sp;
   } else    } else
     sp_typ=spell_item->stats.sp;      sp_typ=spell_item->stats.sp;
   
   if((sp=find_spell(sp_typ))==NULL) {    if((sp=find_spell(sp_typ))==NULL) {
     LOG(llevError,"Warning: Couldn't find spell in item.\n");      LOG(llevError,"Warning: Couldn't find spell in item.\n");
     return 0;      return 0;
Line 513
 
Line 565
   if(head->stats.sp<SP_level_spellpoint_cost(head,head,sp_typ))    if(head->stats.sp<SP_level_spellpoint_cost(head,head,sp_typ))
    /* Monster doesn't have enough spell-points */     /* Monster doesn't have enough spell-points */
     return 0;      return 0;
   
   ability = (spell_item->type==ABILITY && !(spell_item->attacktype&AT_MAGIC));    ability = (spell_item->type==ABILITY && !(spell_item->attacktype&AT_MAGIC));
   
   
Line 1002
 
Line 1055
     }      }
 }  }
   
 int dist_att (int dir , object *ob, object *enemy, object *part) {  int dist_att (int dir , object *ob, object *enemy, object *part, rv_vector *rv) {
   int dist;    int dist;
   if (can_hit(part,enemy))  
       if (can_hit(part,enemy,rv))
     return dir;      return dir;
   dist = distance (part,enemy);      if (rv->distance < 10)
   if (dist < 10)  
     return absdir(dir+4);      return absdir(dir+4);
   else if (dist>81) {      /* This was 81 below?  That seems outragously far - I'm thinking that was
        * a typo and it shoud be 18
        */
       else if (dist>18) {
     return dir;      return dir;
   }    }
   return 0;    return 0;
 }  }
   
 int run_att (int dir, object *ob, object *enemy,object *part) {  int run_att (int dir, object *ob, object *enemy,object *part, rv_vector *rv) {
   if ((can_hit (part,enemy) && ob->move_status <20) || ob->move_status <20)  
   {      if ((can_hit (part,enemy,rv) && ob->move_status <20) || ob->move_status <20) {
     ob->move_status++;      ob->move_status++;
     return (dir);      return (dir);
   }    }
Line 1036
 
Line 1092
   return absdir(dir+4);    return absdir(dir+4);
 }  }
   
 int wait_att (int dir, object *ob,object *enemy,object *part) {  int wait_att (int dir, object *ob,object *enemy,object *part,rv_vector *rv) {
   int inrange = can_hit (part, enemy);  
       int inrange = can_hit (part, enemy,rv);
               
   if (ob->move_status || inrange)    if (ob->move_status || inrange)
     ob->move_status++;      ob->move_status++;
Line 1052
 
Line 1109
   return 0;    return 0;
 }  }
   
 int disthit_att (int dir, object *ob, object *enemy, object *part) {  int disthit_att (int dir, object *ob, object *enemy, object *part,rv_vector *rv) {
   if (ob->stats.hp < (signed short)(((float)ob->run_away/(float)50*  
                      (float)ob->stats.maxhp)))  
     return dir;  
   return dist_att (dir,ob,enemy,part);  
 }  
   
 int wait_att2 (int dir, object *ob,object *enemy,object *part) {      /* The logic below here looked plain wrong before.  Basically, what should
   int dist = distance (ob,enemy);       * happen is that if the creatures hp percentage falls below run_away,
        * the creature should run away (dir+4)
        */
       if ((ob->stats.hp*100)/ob->stats.maxhp < ob->run_away)
    return absdir(dir+4);
       return dist_att (dir,ob,enemy,part,rv);
   }
   
   if ( dist < 9)  int wait_att2 (int dir, object *ob,object *enemy,object *part, rv_vector *rv) {
       if (rv->distance < 9)
     return absdir (dir+4);      return absdir (dir+4);
   return 0;    return 0;
 }  }
Line 1479
 
Line 1538
 /* determine if we can 'detect' the enemy. Check for walls blocking the  /* determine if we can 'detect' the enemy. Check for walls blocking the
  * los. Also, just because its hidden/invisible, we may be sensitive/smart    * los. Also, just because its hidden/invisible, we may be sensitive/smart
  * enough (based on Wis & Int) to figure out where the enemy is. -b.t.    * enough (based on Wis & Int) to figure out where the enemy is. -b.t.
    * modified by MSW to use the get_rangevector so that map tiling works
    * properly.  I also so odd code in place that checked for x distance
    * OR y distance being within some range - that seemed wrong - both should
    * be within the valid range. MSW 2001-08-05
  */   */
   
 int can_detect_enemy (object *op, object *enemy) {  int can_detect_enemy (object *op, object *enemy, rv_vector *rv) {
   
   /* null detection for any of these condtions always */    /* null detection for any of these condtions always */
   if(!op || !enemy || !op->map || !enemy->map || op->map!=enemy->map)       if(!op || !enemy || !op->map || !enemy->map)
    return 0;
   
       /* If the monster (op) has no way to get to the enemy, do nothing */
       if (!on_same_map(op, enemy))
     return 0;      return 0;
   
 #if 0  #if 0
   /* this causes problems, dunno why.. */    /* this causes problems, dunno why.. */
   /* are we trying to look through a wall? */     /* are we trying to look through a wall? */
       /* probably isn't safe for multipart maps either */
   if(path_to_player(op->head?op->head:op,enemy,0)==0) return 0;    if(path_to_player(op->head?op->head:op,enemy,0)==0) return 0;
 #endif  #endif
   
       get_rangevector(op, enemy, rv, 0);
   
   /* opponent is unseen? We still have a chance to find them if    /* opponent is unseen? We still have a chance to find them if
    * they are 1) standing in dark square, 2) hidden or 3) low-quality     * they are 1) standing in dark square, 2) hidden or 3) low-quality
    * invisible (the basic invisibility spell).     * invisible (the basic invisibility spell).
    */     */
   if(!can_see_enemy(op,enemy)) {    if(!can_see_enemy(op,enemy)) {
     object *part = get_nearest_part(op,enemy);  
     int radius = MIN_MON_RADIUS;      int radius = MIN_MON_RADIUS;
     int x_dist = abs(part->x-enemy->x), y_dist = abs(part->y-enemy->y);  
     /* This is percentage change of being discovered while standing      /* This is percentage change of being discovered while standing
      * *adjacent* to the monster */       * *adjacent* to the monster */
     int hide_discovery = enemy->hide?op->stats.Int/5:-1;      int hide_discovery = enemy->hide?op->stats.Int/5:-1;
   
     /* The rest of this is for monsters. Players are on their own for      /* The rest of this is for monsters. Players are on their own for
      * finding enemies! */   * finding enemies!
    */
     if(op->type==PLAYER) return 0;      if(op->type==PLAYER) return 0;
   
     /* Quality invisible? Bah, we wont see them w/o SEE_INVISIBLE      /* Quality invisible? Bah, we wont see them w/o SEE_INVISIBLE
      * flag (which was already checked). Lets get out of here */   * flag (which was already checked). Lets get out of here
    */
     if(enemy->invisible && (!enemy->contr || !enemy->contr->tmp_invis))      if(enemy->invisible && (!enemy->contr || !enemy->contr->tmp_invis))
        return 0;         return 0;
   
Line 1520
 
Line 1590
     else { /* a level/INT/Dex adjustment for hiding */      else { /* a level/INT/Dex adjustment for hiding */
       object *sk_hide;        object *sk_hide;
       int bonus = (op->level/2) + (op->stats.Int/5);        int bonus = (op->level/2) + (op->stats.Int/5);
   
       if(enemy->type==PLAYER) {        if(enemy->type==PLAYER) {
         if((sk_hide = find_skill(enemy,SK_HIDING)))          if((sk_hide = find_skill(enemy,SK_HIDING)))
           bonus -= sk_hide->level;            bonus -= sk_hide->level;
Line 1529
 
Line 1600
           radius=radius<MIN_MON_RADIUS?MIN_MON_RADIUS:radius;            radius=radius<MIN_MON_RADIUS?MIN_MON_RADIUS:radius;
         }          }
       }        }
       else       else /* enemy is not a player */
         bonus -= enemy->level;          bonus -= enemy->level;
   
       radius += bonus/5;        radius += bonus/5;
       hide_discovery += bonus*5;        hide_discovery += bonus*5;
     }   } /* else creature has modifiers for hiding */
   
     /* Radii stealth adjustment. Only if you are stealthy       /* Radii stealth adjustment. Only if you are stealthy
      * will you be able to sneak up closer to creatures */        * will you be able to sneak up closer to creatures */
Line 1547
 
Line 1618
        /* on dark maps body heat can help indicate location with infravision.         /* on dark maps body heat can help indicate location with infravision.
  * There was a check for immunity for fire here (to increase radius) -   * There was a check for immunity for fire here (to increase radius) -
  * I'm not positive if that makes sense - something could be immune to fire   * I'm not positive if that makes sense - something could be immune to fire
  * but not be any warmer blodoed than something else.        * but not be any warmer blooded than something else.
  */   */
        if(QUERY_FLAG(op,FLAG_SEE_IN_DARK) && is_true_undead(enemy))         if(QUERY_FLAG(op,FLAG_SEE_IN_DARK) && is_true_undead(enemy))
  radius += op->map->darkness/2;   radius += op->map->darkness/2;
Line 1556
 
Line 1627
   
        /* op next to a monster (and not in complete darkness)          /* op next to a monster (and not in complete darkness)
  * the monster should have a chance to see you. */   * the monster should have a chance to see you. */
  if(radius<MIN_MON_RADIUS && op->map->darkness<5 && (x_dist<=1||y_dist<=1))        if(radius<MIN_MON_RADIUS && op->map->darkness<5 && rv->distance<=1)
    radius = MIN_MON_RADIUS;     radius = MIN_MON_RADIUS;
     }   } /* if on dark map */
   
     /* Lets not worry about monsters that have incredible detection      /* Lets not worry about monsters that have incredible detection
      * radii, we only need to worry here about things the player can       * radii, we only need to worry here about things the player can
      * (potentially) see. */   * (potentially) see.
     if(radius>5) radius = 5;   * Increased this from 5 to 13 - with larger map code, things that
    * far out are visible.  Note that the distance field in the
    * vector is real distance, so in theory this should be 18 to
    * find that.
    */
    if(radius>10) radius = 13;
   
     /* Enemy in range! Now test for detection */      /* Enemy in range! Now test for detection */
     if(x_dist<=radius||y_dist<=radius) {    if (rv->distance <= radius) {
        /* ah, we are within range, detected? take cases */         /* ah, we are within range, detected? take cases */
        if(!enemy->invisible) /* enemy in dark squares... are seen! */         if(!enemy->invisible) /* enemy in dark squares... are seen! */
          return 1;           return 1;
        else if(enemy->hide||(enemy->contr&&enemy->contr->tmp_invis)) {          else if(enemy->hide||(enemy->contr&&enemy->contr->tmp_invis)) {
          /* hidden or low-quality invisible */             /* hidden or low-quality invisible */ 
   
          /* THere is a a small chance each time we check this function    /* There is a a small chance each time we check this function
           * that we can detect hidden enemy. This means the longer you stay             * that we can detect hidden enemy. This means the longer you stay
           * near something, the greater the chance you have of being             * near something, the greater the chance you have of being
           * discovered. */            * discovered. */
  if(enemy->hide && (x_dist<=1||y_dist<=1) && (RANDOM()%100<=hide_discovery)   if(enemy->hide && (rv->distance <= 1) && (RANDOM()%100<=hide_discovery)) {
          ) {   
     make_visible(enemy);      make_visible(enemy);
     /* inform players of new status */      /* inform players of new status */
     if(enemy->type==PLAYER && player_can_view(enemy,op))       if(enemy->type==PLAYER && player_can_view(enemy,op))
       new_draw_info_format(NDI_UNIQUE,0, enemy,        new_draw_info_format(NDI_UNIQUE,0, enemy,
                "You are discovered by %s!",op->name);                 "You are discovered by %s!",op->name);
            return 1; /* detected enemy */              return 1; /* detected enemy */
          }   } /* if enemy is hiding */
   
          /* If the hidden/tmp_invis enemy is nearby we excellerate the time of    /* If the hidden/tmp_invis enemy is nearby we accellerate the time of
           * becoming unhidden/visible (ie as finding the enemy is easier)            * becoming unhidden/visible (ie as finding the enemy is easier)
    * In order to leave actual discovery (invisible=0 state) to     * In order to leave actual discovery (invisible=0 state) to
    * be handled above (not here) we only decrement so that      * be handled above (not here) we only decrement so that
    * enemy->invisible>1 is preserved. */   * enemy->invisible>1 is preserved.
    */
           if((enemy->invisible-=RANDOM()%(op->stats.Int+2))<1)             if((enemy->invisible-=RANDOM()%(op->stats.Int+2))<1)
             enemy->invisible=1;              enemy->invisible=1;
        } /* enemy is hidding or invisible */
        }  
   
        /* MESSAGING: SO we didnt find them (wah!), we may warn a          /* MESSAGING: SO we didnt find them (wah!), we may warn a
  * player that the monster is getting close to discovering them.    * player that the monster is getting close to discovering them.
Line 1606
 
Line 1681
     */      */
        if(enemy->type==PLAYER          if(enemy->type==PLAYER
     && (RANDOM()%(enemy->stats.Int+10)> MAX_STAT/2)      && (RANDOM()%(enemy->stats.Int+10)> MAX_STAT/2)
            && player_can_view(enemy,op)          && player_can_view(enemy,op)) {
        ) {        new_draw_info_format(NDI_UNIQUE,0, enemy,
          char buf[MAX_BUF];   "You see %s noticing your position.", query_name(op));
          sprintf(buf,"You see %s noticing your position.", query_name(op));       } /* enemy is a player */
  new_draw_info(NDI_UNIQUE,0, enemy, buf);  
        }  
   
        return 0;         return 0;
     }   } /* creature is withing range */
   }      } /* if creature can see its enemy */
       /* returning 1 here suggests to me that if the enemy is visible, no matter
        * how far away, the creature can see them.  Is that really what we want?
   ---------------9     */
   return 1;    return 1;
 }   }
   
Line 1658
 
Line 1733
   object *looker = op->head?op->head:op;    object *looker = op->head?op->head:op;
   
   /* safety */    /* safety */
   if(!looker||!enemy||looker->map!=enemy->map||!QUERY_FLAG(looker,FLAG_ALIVE))    if(!looker||!enemy||!QUERY_FLAG(looker,FLAG_ALIVE))
     return 0;       return 0;
   
   /* we dont give a full treatment of xrays here (shorter range than normal,    /* we dont give a full treatment of xrays here (shorter range than normal,
Line 1694
 
Line 1769
   /* ENEMY IN DARK MAP. Without infravision, the enemy is not seen     /* ENEMY IN DARK MAP. Without infravision, the enemy is not seen
    * unless they carry a light or stand in light. Darkness doesnt     * unless they carry a light or stand in light. Darkness doesnt
    * inhibit the undead per se (but we should give their archs     * inhibit the undead per se (but we should give their archs
    * CAN_SEE_IN_DARK, this is just a safety  */     * CAN_SEE_IN_DARK, this is just a safety 
   if(looker->map->darkness>0&&!stand_in_light(enemy)      * we care about the enemy maps status, not the looker.
      * only relevant for tiled maps, but it is possible that the
      * enemy is on a bright map and the looker on a dark - in that
      * case, the looker can still see the enemy
      */
     if(enemy->map->darkness>0&&!stand_in_light(enemy)
      &&(!QUERY_FLAG(looker,FLAG_SEE_IN_DARK)||       &&(!QUERY_FLAG(looker,FLAG_SEE_IN_DARK)||
         !is_true_undead(looker)||!QUERY_FLAG(looker,FLAG_XRAYS)))          !is_true_undead(looker)||!QUERY_FLAG(looker,FLAG_XRAYS)))
     return 0;      return 0;


Legend:
line(s) removed in v.1.27 
line(s) changed
 line(s) added in v.1.28

File made using version 1.98 of cvs2html by leaf at 2011-07-21 17:38