예제 #1
0
void monster::move_to(game *g, int x, int y)
{
 int mondex = g->mon_at(x, y);
 if (mondex == -1) { //...assuming there's no monster there
  if (plans.size() > 0)
   plans.erase(plans.begin());
  if (has_flag(MF_SWIMS) && g->m.has_flag(swimmable, x, y))
   moves += 50;
  if (!has_flag(MF_DIGS) && !has_flag(MF_FLIES) &&
      (!has_flag(MF_SWIMS) || !g->m.has_flag(swimmable, x, y)))
   moves -= (g->m.move_cost(x, y) - 2) * 50;
  posx = x;
  posy = y;
  footsteps(g, x, y);
  if (!has_flag(MF_DIGS) && !has_flag(MF_FLIES) &&
      g->m.tr_at(posx, posy) != tr_null) { // Monster stepped on a trap!
   trap* tr = g->traps[g->m.tr_at(posx, posy)];
   if (dice(3, sk_dodge + 1) < dice(3, tr->avoidance)) {
    trapfuncm f;
    (f.*(tr->actm))(g, this, posx, posy);
   }
  }
// Diggers turn the dirt into dirtmound
  if (has_flag(MF_DIGS))
   g->m.ter(posx, posy) = t_dirtmound;
// Acid trail monsters leave... a trail of acid
  if (has_flag(MF_ACIDTRAIL))
   g->m.add_field(g, posx, posy, fd_acid, 1);
 } else if (has_flag(MF_ATTACKMON) || g->z[mondex].friendly != 0)
// If there IS a monster there, and we fight monsters, fight it!
  hit_monster(g, mondex);
}
예제 #2
0
void monster::friendly_move(game *g)
{
 point next;
 bool moved = false;
 moves -= 100;
 if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
     (can_move_to(g->m, plans[0].x, plans[0].y) ||
     (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
  next = plans[0];
  plans.erase(plans.begin());
  moved = true;
 } else
  stumble(g, moved);
 if (moved) {
  int mondex = g->mon_at(next.x, next.y);
  int npcdex = g->npc_at(next.x, next.y);
  if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0)
   hit_monster(g, mondex);
  else if (npcdex != -1 && type->melee_dice > 0)
   hit_player(g, g->active_npc[g->npc_at(next.x, next.y)]);
  else if (mondex == -1 && npcdex == -1 && can_move_to(g->m, next.x, next.y))
   move_to(g, next.x, next.y);
  else if ((!can_move_to(g->m, next.x, next.y) || one_in(3)) &&
           g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) {
   std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug!
   int bashskill = int(type->melee_dice * type->melee_sides);
   g->m.bash(next.x, next.y, bashskill, bashsound);
   g->sound(next.x, next.y, 18, bashsound);
  } else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) {
   g->m.destroy(g, next.x, next.y, true);
   moves -= 250;
  }
 }
}
예제 #3
0
bool monster::attack_at( const tripoint &p )
{
    if( p.z != posz() ) {
        return false; // TODO: Remove this
    }
    if( has_effect( "pacified" ) ) {
        return false;
    }

    if( p == g->u.pos3() ) {
        melee_attack( g->u, true );
        return true;
    }

    const int mondex = g->mon_at( p );
    if( mondex != -1 ) {
        monster &mon = g->zombie( mondex );

        // Don't attack yourself.
        if( &mon == this ) {
            return false;
        }

        // Special case: Target is hallucination
        if( mon.is_hallucination() ) {
            mon.die( nullptr );

            // We haven't actually attacked anything, i.e. we can still do things.
            // Hallucinations(obviously) shouldn't affect the way real monsters act.
            return false;
        }

        // With no melee dice, we can't attack, but we had to process until here
        // because hallucinations require no melee dice to destroy.
        if( type->melee_dice <= 0 ) {
            return false;
        }

        auto attitude = attitude_to( mon );
        // MF_ATTACKMON == hulk behavior, whack everything in your way
        if( attitude == A_HOSTILE || has_flag( MF_ATTACKMON ) ) {
            hit_monster( mon );
            return true;
        }

        return false;
    }

    const int npcdex = g->npc_at( p );
    if( npcdex != -1 && type->melee_dice > 0 ) {
        // For now we're always attacking NPCs that are getting into our
        // way. This is consistent with how it worked previously, but
        // later on not hitting allied NPCs would be cool.
        melee_attack( *g->active_npc[npcdex], true );
        return true;
    }

    // Nothing to attack.
    return false;
}
예제 #4
0
void monster::move_to(game *g, int x, int y)
{
 int mondex = g->mon_at(x, y);
 if (mondex == -1) { //...assuming there's no monster there
  if (has_effect(ME_BEARTRAP)) {
   moves = 0;
   return;
  }
  
  if (plans.size() > 0)
   plans.erase(plans.begin());

  moves -= calc_movecost(g, posx, posy, x, y);

  if (has_flag(MF_SLUDGETRAIL)){
   g->m.add_field(g, posx, posy, fd_sludge, 3);
   g->m.add_field(g, posx+1, posy, fd_sludge, 2);
   g->m.add_field(g, posx, posy+1, fd_sludge, 2);
   g->m.add_field(g, posx-1, posy, fd_sludge, 2);
   g->m.add_field(g, posx, posy-1, fd_sludge, 2);
  }

  posx = x;
  posy = y;
  footsteps(g, x, y);
  if (type->size != MS_TINY && g->m.has_flag(sharp, posx, posy) && !one_in(4))
     hurt(rng(2, 3));
  if (type->size != MS_TINY && g->m.has_flag(rough, posx, posy) && one_in(6))
     hurt(rng(1, 2));
  if (!has_flag(MF_DIGS) && !has_flag(MF_FLIES) &&
      g->m.tr_at(posx, posy) != tr_null) { // Monster stepped on a trap!
   trap* tr = g->traps[g->m.tr_at(posx, posy)];
   if (dice(3, type->sk_dodge + 1) < dice(3, tr->avoidance)) {
    trapfuncm f;
    (f.*(tr->actm))(g, this, posx, posy);
   }
  }
// Diggers turn the dirt into dirtmound
  if (has_flag(MF_DIGS)){
   g->m.ter_set(posx, posy, t_dirtmound);
  }
// Acid trail monsters leave... a trail of acid
  if (has_flag(MF_ACIDTRAIL)){
   g->m.add_field(g, posx, posy, fd_acid, 1);
  }
  if (has_flag(MF_ACIDTRAIL)){
   g->m.add_field(g, posx, posy, fd_acid, 1);
  }
  if (has_flag(MF_ACIDTRAIL)){
   g->m.add_field(g, posx, posy, fd_acid, 1);
  }

 } else if (has_flag(MF_ATTACKMON) || g->z[mondex].friendly != 0)
// If there IS a monster there, and we fight monsters, fight it!
  hit_monster(g, mondex);
}
예제 #5
0
int monster::attack_at(int x, int y) {

    if (has_effect("pacified")) return 0;

    int mondex = g->mon_at(x, y);
    int npcdex = g->npc_at(x, y);

    if(x == g->u.posx && y == g->u.posy) {
        melee_attack(g->u);
        return 1;
    }

    if(mondex != -1) {
        // Currently, there are only pro-player and anti-player groups,
        // this makes it easy for us.
        monster& mon = g->zombie(mondex);

        // Don't attack yourself.
        if(&mon == this) {
            return 0;
        }

        // Special case: Target is hallucination
        if(mon.is_hallucination()) {
            mon.die( nullptr );

            // We haven't actually attacked anything, i.e. we can still do things.
            // Hallucinations(obviously) shouldn't affect the way real monsters act.
            return 0;
        }

        // With no melee dice, we can't attack, but we had to process until here
        // because hallucinations require no melee dice to destroy.
        if(type->melee_dice <= 0) {
            return 0;
        }

        bool is_enemy = mon.friendly != friendly;
        is_enemy = is_enemy || has_flag(MF_ATTACKMON); // I guess the flag means all monsters are enemies?

        if(is_enemy) {
            hit_monster(mon);
            return 1;
        }
    } else if(npcdex != -1  && type->melee_dice > 0) {
        // For now we're always attacking NPCs that are getting into our
        // way. This is consistent with how it worked previously, but
        // later on not hitting allied NPCs would be cool.
        melee_attack(*g->active_npc[npcdex]);
        return 1;
    }

    // Nothing to attack.
    return 0;
}
예제 #6
0
/*
Function: friendly_move
The per-turn movement and action calculation of any friendly monsters.
*/
void monster::friendly_move(game *g)
{
	point next;
	bool moved = false;
	//If we sucessfully calculated a plan in the generic monster movement function, begin executing it.
	if (plans.size() > 0 && (plans[0].x != g->u.posx || plans[0].y != g->u.posy) &&
		(can_move_to(g, plans[0].x, plans[0].y) ||
		(g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
			next = plans[0];
			plans.erase(plans.begin());
			moved = true;
	} else {
		//Otherwise just stumble around randomly until we formulate a plan.
		moves -= 100;
		stumble(g, moved);
	}
	if (moved) {
		//We have a plan.
		int mondex = g->mon_at(next.x, next.y);
		int npcdex = g->npc_at(next.x, next.y);
		//If there is an unfriendly mosnter in the target square we want to move into, hit them if we have a melee attack.
		if (mondex != -1 && g->z[mondex].friendly == 0 && type->melee_dice > 0){
			hit_monster(g, mondex);
		}
		//If there is an npc (any npc?) we hit them assuming we have a melee attack.
		else if (npcdex != -1 && type->melee_dice > 0){
			hit_player(g, *g->active_npc[g->npc_at(next.x, next.y)]);
		}
		//If no one is there and its a walkable square, walk there.
		else if (mondex == -1 && npcdex == -1 && can_move_to(g, next.x, next.y)){
			move_to(g, next.x, next.y);
		}
		//If there is a bashable object in our way, bash it down.
		else if ((!can_move_to(g, next.x, next.y) || one_in(3)) &&
			g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) {
				std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug!
				int bashskill = int(type->melee_dice * type->melee_sides);
				g->m.bash(next.x, next.y, bashskill, bashsound);
				g->sound(next.x, next.y, 18, bashsound);
				moves -= 100;
		}
		//If there is a destroyable object in our way, destroy it.
		else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) {
			g->m.destroy(g, next.x, next.y, true);
			moves -= 250;
		}
		//If all else fails in our plan (an issue with pathfinding maybe) stumble around instead.
		else {
			stumble(g, moved);
			moves -= 100;
		}
	}
}
예제 #7
0
/* Attempt to attack or move in a given direction, depending on context */
void attempt_act(level_t* level, player_t* player, int deltaX, int deltaY)
{
	int newX = player->x + deltaX;
	int newY = player->y + deltaY;

	if (newX < 0 || newX >= level->width || newY < 0 || newY >= level->height)
		return;

	if (level->mon_map[newX][newY]) {
		hit_monster(level->mon_map[newX][newY], level, player);
	} else if (level->terrain[newX][newY] == '.') {
		player->x = newX;
		player->y = newY;
	}
}
예제 #8
0
void
missile(int ydelta, int xdelta)
{
    struct object *obj;
    struct linked_list *item, *nitem;

    /*
     * Get which thing we are hurling
     */
    if ((item = get_item("throw", WEAPON)) == NULL)
	return;
    obj = (struct object *) ldata(item);
    if (!dropcheck(obj) || is_current(obj))
	return;
    /*
     * Get rid of the thing.  If it is a non-multiple item object, or
     * if it is the last thing, just drop it.  Otherwise, create a new
     * item with a count of one.
     */
    if (obj->o_count < 2)
    {
	detach(pack, item);
	inpack--;
    }
    else
    {
	obj->o_count--;
	if (obj->o_group == 0)
	    inpack--;
	nitem = (struct linked_list *) new_item(sizeof *obj);
	obj = (struct object *) ldata(nitem);
	*obj = *((struct object *) ldata(item));
	obj->o_count = 1;
	item = nitem;
    }
    do_motion(obj, ydelta, xdelta);
    /*
     * AHA! Here it has hit something.  If it is a wall or a door,
     * or if it misses (combat) the mosnter, put it on the floor
     */
    if (!ismons(CMVWINCH(mw, obj->o_pos.y, obj->o_pos.x))
	|| !hit_monster(unc(obj->o_pos), obj))
	    fall(item, TRUE);
    mvwaddrawch(cw, hero.y, hero.x, PLAYER);
}
void
missile(int ydelta, int xdelta)
{
    THING *obj;

    /*
     * Get which thing we are hurling
     */
    if ((obj = get_item("throw", WEAPON)) == NULL)
	return;
    if (!dropcheck(obj) || is_current(obj))
	return;
    obj = leave_pack(obj, TRUE, FALSE);
    do_motion(obj, ydelta, xdelta);
    /*
     * AHA! Here it has hit something.  If it is a wall or a door,
     * or if it misses (combat) the monster, put it on the floor
     */
    if (moat(obj->o_pos.y, obj->o_pos.x) == NULL ||
	!hit_monster(unc(obj->o_pos), obj))
	    fall(obj, TRUE);
}
예제 #10
0
void
missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp)
{
    struct object   *obj;
    struct linked_list  *nitem;

    if (item == NULL)   /* Get which thing we are hurling */
        return;

    obj = OBJPTR(item);

    if (!dropcheck(obj) || is_current(obj))
        return;

    /*
     * Get rid of the thing. If it is a non-multiple item object, or if
     * it is the last thing, just drop it. Otherwise, create a new item
     * with a count of one.
    */

    if (obj->o_count < 2)
    {
        if (tp->t_pack == pack)
            rem_pack(obj);
        else
            detach(tp->t_pack, item);
    }
    else
    {
        obj->o_count--;
        nitem = (struct linked_list *) new_item(sizeof *obj);
        obj = OBJPTR(nitem);
        *obj = *(OBJPTR(item));
        obj->o_count = 1;
        item = nitem;
    }

    switch (obj->o_type)
    {
        case ARTIFACT:
            has_artifact &= ~(1 << obj->o_which);
            break;

        case SCROLL:
            if (obj->o_which == S_SCARE && obj->o_flags & ISBLESSED)
                obj->o_flags &= ~ISBLESSED;
            else
                obj->o_flags |= ISCURSED;
    }

    updpack();
    obj->o_pos = do_motion(obj->o_type, ydelta, xdelta, tp);

    /*
     * AHA! Here it has hit something. If it is a wall or a door, or if
     * it misses (combat) the monster, put it on the floor
     */

    if (!hit_monster(obj->o_pos.y, obj->o_pos.x, obj, tp))
    {
        if (obj->o_type == WEAPON && obj->o_which == GRENADE)
        {
            hearmsg("BOOOM!");
            aggravate();

            if (ntraps + 1 < 2 * MAXTRAPS &&
                fallpos(obj->o_pos, &traps[ntraps].tr_pos))
            {
                mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x,
                    TRAPDOOR);
                traps[ntraps].tr_type = TRAPDOOR;
                traps[ntraps].tr_flags = ISFOUND;
                traps[ntraps].tr_show = TRAPDOOR;
                ntraps++;
                light(&hero);
            }
            discard(item);
        }
        else if (obj->o_flags & ISLOST)
        {
            if (obj->o_type == WEAPON)
                addmsg("The %s", weaps[obj->o_which].w_name);
            else
                addmsg(inv_name(obj, LOWERCASE));

            msg(" vanishes in a puff of greasy smoke.");
            discard(item);
        }
        else
        {
            fall(&player, item, TRUE, TRUE);

            if (obj->o_flags & CANRETURN)
                msg("You have %s.", inv_name(obj, LOWERCASE));
        }
    }
    else if (obj->o_flags & ISOWNED)
    {
        add_pack(item, NOMESSAGE);
        msg("You have %s.", inv_name(obj, LOWERCASE));
    }

    mvwaddch(cw, hero.y, hero.x, PLAYER);
}
예제 #11
0
// General movement.
// Currently, priority goes:
// 1) Special Attack
// 2) Sight-based tracking
// 3) Scent-based tracking
// 4) Sound-based tracking
void monster::move(game *g)
{
// We decrement wandf no matter what.  We'll save our wander_to plans until
// after we finish out set_dest plans, UNLESS they time out first.
 if (wandf > 0)
  wandf--;

// First, use the special attack, if we can!
 if (sp_timeout > 0)
  sp_timeout--;
 if (sp_timeout == 0 && (friendly == 0 || has_flag(MF_FRIENDLY_SPECIAL))) {
  mattack ma;
  (ma.*type->sp_attack)(g, this);
 }
 if (moves < 0)
  return;
 if (has_flag(MF_IMMOBILE)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_STUNNED)) {
  stumble(g, false);
  moves = 0;
  return;
 }
 if (has_effect(ME_DOWNED)) {
  moves = 0;
  return;
 }
 if (has_effect(ME_BOULDERING)){
  moves -= 20;
  if (moves < 0)
   moves = 0;
  return;
 }
 if (friendly != 0) {
  if (friendly > 0)
   friendly--;
  friendly_move(g);
  return;
 }

 bool moved = false;
 point next;
 int mondex = (plans.size() > 0 ? g->mon_at(plans[0].x, plans[0].y) : -1);

 monster_attitude current_attitude = attitude();
 if (friendly == 0)
  current_attitude = attitude(&(g->u));
// If our plans end in a player, set our attitude to consider that player
 if (plans.size() > 0) {
  if (plans.back().x == g->u.posx && plans.back().y == g->u.posy)
   current_attitude = attitude(&(g->u));
  else {
   for (int i = 0; i < g->active_npc.size(); i++) {
    if (plans.back().x == g->active_npc[i]->posx &&
        plans.back().y == g->active_npc[i]->posy)
     current_attitude = attitude((g->active_npc[i]));
   }
  }
 }

 if (current_attitude == MATT_IGNORE ||
     (current_attitude == MATT_FOLLOW && plans.size() <= MONSTER_FOLLOW_DIST)) {
  moves -= 100;
  stumble(g, false);
  return;
 }

 if (plans.size() > 0 && !is_fleeing(g->u) &&
     (mondex == -1 || g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON)) &&
     (can_move_to(g, plans[0].x, plans[0].y) ||
      (plans[0].x == g->u.posx && plans[0].y == g->u.posy) ||
     (g->m.has_flag(bashable, plans[0].x, plans[0].y) && has_flag(MF_BASHES)))){
  // CONCRETE PLANS - Most likely based on sight
  next = plans[0];
  moved = true;
 } else if (has_flag(MF_SMELLS)) {
// No sight... or our plans are invalid (e.g. moving through a transparent, but
//  solid, square of terrain).  Fall back to smell if we have it.
  point tmp = scent_move(g);
  if (tmp.x != -1) {
   next = tmp;
   moved = true;
  }
 }
 if (wandf > 0 && !moved) { // No LOS, no scent, so as a fall-back follow sound
  point tmp = sound_move(g);
  if (tmp.x != posx || tmp.y != posy) {
   next = tmp;
   moved = true;
  }
 }

// Finished logic section.  By this point, we should have chosen a square to
//  move to (moved = true).
 if (moved) {	// Actual effects of moving to the square we've chosen
  mondex = g->mon_at(next.x, next.y);
  int npcdex = g->npc_at(next.x, next.y);
  if (next.x == g->u.posx && next.y == g->u.posy && type->melee_dice > 0)
   hit_player(g, g->u);
  else if (mondex != -1 && g->z[mondex].type->species == species_hallu) {
   g->kill_mon(mondex);
   moves -= 100;
  } else if (mondex != -1 && type->melee_dice > 0 && this != &(g->z[mondex]) &&
             (g->z[mondex].friendly != 0 || has_flag(MF_ATTACKMON)))
   hit_monster(g, mondex);
  else if (npcdex != -1 && type->melee_dice > 0)
   hit_player(g, *g->active_npc[npcdex]);
  else if ((!can_move_to(g, next.x, next.y) || one_in(3)) &&
             g->m.has_flag(bashable, next.x, next.y) && has_flag(MF_BASHES)) {
   std::string bashsound = "NOBASH"; // If we hear "NOBASH" it's time to debug!
   int bashskill = int(type->melee_dice * type->melee_sides);
   g->m.bash(next.x, next.y, bashskill, bashsound);
   g->sound(next.x, next.y, 18, bashsound);
   moves -= 100;
  } else if (g->m.move_cost(next.x, next.y) == 0 && has_flag(MF_DESTROYS)) {
   g->m.destroy(g, next.x, next.y, true);
   moves -= 250;
  } else if (can_move_to(g, next.x, next.y) && g->is_empty(next.x, next.y))
   move_to(g, next.x, next.y);
  else
   moves -= 100;
 } else
  moves -= 100;

// If we're close to our target, we get focused and don't stumble
 if ((has_flag(MF_STUMBLES) && (plans.size() > 3 || plans.size() == 0)) ||
     !moved)
  stumble(g, moved);
}
/*
 * missile:
 *	Fire a missile in a given direction
 */
void
missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp)
{
	register struct object *obj ;
	register struct linked_list *nitem ;

	/*
	* Get which thing we are hurling
	*/
	if (item == NULL) {
		return ;
	}
	obj = (struct object *) ldata(item) ;
	if (!dropcheck(obj) || is_current(obj)) {
		return ;
	}
	/*
	* Get rid of the thing. If it is a non-multiple item object, or
	* if it is the last thing, just drop it. Otherwise, create a new
	* item with a count of one.
	*/
	if (obj->o_count < 2) {
		detach(tp->t_pack, item) ;
		if (tp->t_pack == pack) {
			inpack-- ;
			freeletter(item);
		}
	} else {
		obj->o_count-- ;
		nitem = (struct linked_list *) new_item(sizeof *obj) ;
		obj = (struct object *) ldata(nitem) ;
		*obj = *((struct object *) ldata(item)) ;
		obj->o_count = 1 ;
		item = nitem ;
	}
	if (obj->o_type == ARTIFACT)
		has_artifact &= ~(1 << obj->o_which);
	if (obj->o_type == SCROLL && obj->o_which == S_SCARE) {
		if (obj->o_flags & ISBLESSED)
		    obj->o_flags &= ~ISBLESSED;
		else 
		    obj->o_flags |= ISCURSED;
	}
	updpack (FALSE);
	do_motion(obj, ydelta, xdelta, tp) ;
	/*
	* AHA! Here it has hit something. If it is a wall or a door,
	* or if it misses (combat) the monster, put it on the floor
	*/
	if (!hit_monster(unc(obj->o_pos), obj, tp)) {
		if (obj->o_type == WEAPON && obj->o_which == GRENADE) {
			register struct room *rp;
            static coord fpos;

            msg("BOOOM!");
            aggravate();
            rp = roomin(&obj->o_pos);
            if (ntraps + 1 < MAXTRAPS + MAXTRAPS &&
					fallpos(&obj->o_pos, &fpos, TRUE)) {
				mvaddch(fpos.y, fpos.x, TRAPDOOR);
                traps[ntraps].tr_type = TRAPDOOR;
                traps[ntraps].tr_flags = ISFOUND;
                traps[ntraps].tr_show = TRAPDOOR;
                traps[ntraps].tr_pos.y = fpos.y;
                traps[ntraps++].tr_pos.x = fpos.x;
                light(&hero);
            }
            discard(item);
        }
	    else if (obj->o_flags & ISLOST) {
		if (obj->o_type == WEAPON)
		    addmsg("The %s", weaps[obj->o_which].w_name);
		else
		    addmsg(inv_name(obj, TRUE));
		msg(" vanishes in a puff of greasy smoke.");
		discard(item);
	    }
	    else {
		if (fall(item, TRUE))
		if (obj->o_flags & CANRETURN)
		    msg("You have %s.", inv_name(obj, TRUE));
	    }
	}
	else if (obj->o_flags & ISOWNED) {
		add_pack(item, TRUE);
		msg("You have %s.", inv_name(obj, TRUE));
	}
	mvwaddch(cw, hero.y, hero.x, PLAYER) ;
}