version 1.27 | | version 1.28 |
---|
| | |
/* | | /* |
* 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 $"; |
*/ | | */ |
| | |
/* | | /* |
| | |
| | |
#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; |
} | | } |
| | |
| | |
*/ | | */ |
| | |
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; |
} | | } |
| | |
| | |
} | | } |
| | |
/* 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); |
| | |
} | | } |
| | |
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); |
| | |
| | |
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)) |
| | |
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); |
| | |
| | |
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); |
| | |
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 */ |
| | |
*/ | | */ |
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; |
| | |
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 */ |
| | |
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)) |
| | |
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; |
} | | } |
} | | } |
| | |
| | |
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; |
| | |
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)); |
| | |
| | |
| | |
} | | } |
} | | } |
| | |
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); |
} | | } |
| | |
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++; |
| | |
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; |
} | | } |
| | |
/* 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; |
| | |
| | |
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; |
| | |
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 */ |
| | |
/* 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; |
| | |
| | |
/* 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. |
| | |
*/ | | */ |
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; |
} | | } |
| | |
| | |
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, |
| | |
/* 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; |