void ribi::xnz::SpriteEnemyMedium::Shoot(std::vector<boost::shared_ptr<Sprite> >& newSprites)
{
  ++mTimer;
  if (mTimer % mTimeShoot == 0)
  {
    const int missileY = GetY() + GetHeight();
    const int missileX = GetX()
      + (GetWidth() / 2)
      + (mTurretDirection == turretDownLeft  ? -2 : 0)
      + (mTurretDirection == turretDownRight ?  2 : 0);
    const int dx = 0
      + (mTurretDirection == turretDownLeft  ? -1 : 0)
      + (mTurretDirection == turretDownRight ?  1 : 0);
    const int dy = 1;
    boost::shared_ptr<Sprite> missile(new SpriteMissile(missileX,missileY,dx,dy,2));
    newSprites.push_back(missile);

  }
  else
  {
    mTurretDirection = static_cast<EnumTurretDirection>(std::rand() % 3);
    switch(mTurretDirection)
    {
      case turretDownLeft:  SetGraphic(GetSpriteShootDownLeft()); break;
      case turretDown:      SetGraphic(GetSpriteShootDown()); break;
      case turretDownRight: SetGraphic(GetSpriteShootDownRight()); break;
      default: assert(!"Should not get here");
    }
  }
}
Exemple #2
0
void CombatShip::FireAt(CombatObjectPtr target)
{
    float range_squared = (target->position() - position()).lengthSquared();
    float structure_factor = FractionalStructure();

    for (CombatShip::SRVec::reverse_iterator it = m_unfired_SR_weapons.rbegin();
         it != m_unfired_SR_weapons.rend();
         ++it) {
        if (range_squared < it->m_range * it->m_range) {
            Listener().ShipFired(shared_from_this(), target, it->m_name);
            target->Damage(it->m_damage, CombatObject::NON_PD_DAMAGE);
        } else {
            m_unfired_SR_weapons.resize(std::distance(it, m_unfired_SR_weapons.rend()));
            break;
        }
    }
    std::size_t i = 0;
    for (std::multimap<float, const PartType*>::const_iterator it =
             GetShip()->Design()->LRWeapons().begin();
         it != GetShip()->Design()->LRWeapons().end();
         ++it, ++i)
    {
        if (m_next_LR_fire_turns[i] < m_turn) {
            float weapon_range_squared = it->first * it->first;
            if (range_squared < weapon_range_squared) {
                OpenSteer::Vec3 direction = (target->position() - position()).normalize();
                MissilePtr missile(
                    new Missile(GetShip(), *it->second, target,
                                position(), direction, *m_pathing_engine));
                m_pathing_engine->AddObject(missile);
                GetShip()->RemoveMissiles(it->second->Name(), 1);
                if (m_next_LR_fire_turns[i] == INVALID_TURN)
                    m_next_LR_fire_turns[i] = m_turn;
                m_next_LR_fire_turns[i] +=
                    GetShip()->GetPartMeter(METER_ROF, it->second->Name())->Current() * structure_factor;
                Listener().MissileLaunched(missile);
            }
        }
    }
    for (CombatShip::PDList::iterator it = m_unfired_PD_weapons.begin();
         it != m_unfired_PD_weapons.end();
         ++it) {
        if (range_squared < it->m_range * it->m_range) {
            Listener().ShipFired(shared_from_this(), target, it->m_name);
            target->Damage(it->m_damage, CombatObject::PD_DAMAGE);
        } else {
            m_unfired_PD_weapons.erase(it, m_unfired_PD_weapons.end());
            break;
        }
    }
}
Exemple #3
0
/**
 * Activate is basically a switch/redirect to the appropriate function
 */
bool PowerManager::activate(int power_index, StatBlock *src_stats, Point target) {

	// logic for different types of powers are very different.  We allow these
	// separate functions to handle the details.
	if (powers[power_index].type == POWTYPE_SINGLE)
		return single(power_index, src_stats, target);
	else if (powers[power_index].type == POWTYPE_MISSILE)
		return missile(power_index, src_stats, target);
	else if (powers[power_index].type == POWTYPE_REPEATER)
		return repeater(power_index, src_stats, target);
	else if (powers[power_index].type == POWTYPE_EFFECT)
		return effect(power_index, src_stats, target);
	
	return false;
}
void MissileLauncher::create_missile(double x_pos, double y_pos, double x_vel, double y_vel) {
    std::unique_ptr<Missile> missile(
        new Missile(x_pos, y_pos, x_vel, y_vel, 5, missile_graphic_path_));
    game_object_mediator_.add_projectile(team_, std::move(missile));
    game_object_mediator_.play_sound(missile_launch_sound_path_);
}
int
chase(struct thing *tp, coord *ee, int flee)
{
    int x, y;
    int dist, thisdist, monst_dist = INT_MAX;
    struct linked_list  *weapon;
    coord   *er = &tp->t_pos;
    coord shoot;
    coord *shootit_dir = NULL;
    int ch;
    char   mch;
    int    next_player = FALSE;

    /* Take care of shooting directions */

    if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST))
    {
        if (good_monster(*tp))
        {
            shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */

            if (wizard && shootit_dir)
                msg("Found monster to attack towards (%d,%d).",
                    shootit_dir->x, shootit_dir->y);
        }
        else
            shootit_dir = can_shoot(er, ee, &shoot);  /* shoot hero */
    }

    /*
     * If the thing is confused, let it move randomly. Some monsters are
     * slightly confused all of the time.
     */

    if ((on(*tp, ISHUH) && rnd(10) < 8) ||
        ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) ||
        (on(player, ISINVIS) && off(*tp, CANSEE)))
    {   /* Player is invisible */

        /* get a valid random move */

        tp->t_nxtpos = rndmove(tp);

        dist = DISTANCE(tp->t_nxtpos, *ee);

        if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */
            turn_off(*tp, ISHUH);

        /*
         * check to see if random move takes creature away from
         * player if it does then turn off ISHELD
         */

        if (dist > 1 && on(*tp, DIDHOLD))
        {
            turn_off(*tp, DIDHOLD);
            turn_on(*tp, CANHOLD);

            if (--hold_count == 0)
                turn_off(player, ISHELD);
        }
    } /* If we can breathe, we may do so */
    else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) &&
         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) &&
         (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH))
    {
        int   chance;
        char    *breath;

        /* Will it breathe at random */

        if (on(*tp, CANBRANDOM))
        {
            if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1
                && !(good_monster(*tp)))
                turn_off(*tp, CANBRANDOM);

            /* Select type of breath */

            chance = rnd(100);

            if (chance < 11)
                breath = "acid";
            else if (chance < 22)
                breath = "flame";
            else if (chance < 33)
                breath = "lightning bolt";
            else if (chance < 44)
                breath = "chlorine gas";
            else if (chance < 55)
                breath = "ice";
            else if (chance < 66)
                breath = "nerve gas";
            else if (chance < 77)
                breath = "sleeping gas";
            else if (chance < 88)
                breath = "slow gas";
            else
                breath = "fear gas";
        } /* Or can it breathe acid? */
        else if (on(*tp, CANBACID))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBACID);

            breath = "acid";
        } /* Or can it breathe fire */
        else if (on(*tp, CANBFIRE))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBFIRE);

            breath = "flame";
        } /* Or can it breathe electricity? */
        else if (on(*tp, CANBBOLT))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBBOLT);

            breath = "lightning bolt";
        } /* Or can it breathe gas? */
        else if (on(*tp, CANBGAS))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBGAS);

            breath = "chlorine gas";
        } /* Or can it breathe ice? */
        else if (on(*tp, CANBICE))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBICE);

            breath = "ice";
        }
        else if (on(*tp, CANBPGAS))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBPGAS);

            breath = "nerve gas";
        }
        else if (on(*tp, CANBSGAS))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBSGAS);

            breath = "sleeping gas";
        }
        else if (on(*tp, CANBSLGAS))
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBSLGAS);

            breath = "slow gas";
        }
        else
        {
            if (!good_monster(*tp) && rnd(level / 15) == 0)
                turn_off(*tp, CANBFGAS);

            breath = "fear gas";
        }

        shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)),
               tp->t_index, breath, roll(tp->t_stats.s_lvl, 6));

        tp->t_nxtpos = *er;

        dist = DISTANCE(tp->t_nxtpos, *ee);

        if (!curr_mons)
            return (TRUE);
    }
    else if (shootit_dir && on(*tp, CANCAST) &&
         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
    {
        /*
            If we can cast spells we might do so - even if adjacent fleeing
            monsters are restricted to certain spells
        */

        incant(tp, *shootit_dir);
        tp->t_nxtpos = *er;
        dist = DISTANCE(tp->t_nxtpos, *ee);
    }
    else if (shootit_dir && on(*tp, CANSHOOT)) 
    {
	weapon = get_hurl(tp);
	
	if (weapon &&
         (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) &&
         (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)))
	{
	    /*
	        Should we shoot or throw something? fleeing monsters 
		may to shoot anyway if far enough away
	    */

	    missile(shootit_dir->y, shootit_dir->x, weapon, tp);
	    tp->t_nxtpos = *er;
	    dist = DISTANCE(tp->t_nxtpos, *ee);
	}
    }
    else
    {
        /*
            Otherwise, find the empty spot next to the chaser that is closest
            to the chasee.
        */
        int ey, ex;
        struct room *rer, *ree;
        int dist_to_old = INT_MIN;   /* Dist from goal to old position */

        /* Get rooms */
        rer = roomin(*er);
        ree = roomin(*ee);

        /*
         * This will eventually hold where we move to get closer. If
         * we can't find an empty spot, we stay where we are.
         */

        dist = flee ? 0 : INT_MAX;
        tp->t_nxtpos = *er;

        /* Are we at our goal already? */

        if (!flee && ce(tp->t_nxtpos, *ee))
            return (FALSE);

        ey = er->y + 1;
        ex = er->x + 1;

        for (x = er->x - 1; x <= ex; x++)
            for (y = er->y - 1; y <= ey; y++)
            {
                coord   tryp; /* test position */

                /* Don't try off the screen */

                if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2))
                    continue;

                /*
                 * Don't try the player if not going after
                 * the player or he's disguised and monster is dumb
                 */

                if (((off(*tp, ISFLEE) && !ce(hero, *ee)) ||
                     (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6))
                     || good_monster(*tp))
                    && x == hero.x && y == hero.y)
                    continue;

                tryp.x = x;
                tryp.y = y;

                /*
                 * Is there a monster on this spot closer to
                 * our goal? Don't look in our spot or where
                 * we were.
                 */

                if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
                    isalpha( (mch = CCHAR(mvwinch(mw, y, x))) ) )
                {
                    int test_dist;

                    test_dist = DISTANCE(tryp,*ee);
                    if (test_dist <= 25 &&  /* Let's be fairly close */
                        test_dist < monst_dist)
                    {

                        /* Could we really move there? */

                        mvwaddch(mw, y, x, ' '); /* Temp blank monst */

                        if (diag_ok(er, &tryp, tp))
                            monst_dist = test_dist;

                        mvwaddch(mw, y, x, mch);    /* Restore monster */
                    }
                }

                if (!diag_ok(er, &tryp, tp))
                    continue;

                ch = mvwinch(cw, y, x); /* Screen character */

                /*
                 * Stepping on player is NOT okay if we are
                 * fleeing
                 */

                if (on(*tp, ISFLEE) && (ch == PLAYER))
				    next_player = TRUE;
					
                if (step_ok(y, x, NOMONST, tp) &&
                    (off(*tp, ISFLEE) || ch != PLAYER))
                {

                    /*
                     * If it is a trap, an intelligent
                     * monster may not step on it (unless
                     * our hero is on top!)
                     */

                    if (isatrap(ch))
                    {
                        if (!(ch == RUSTTRAP) &&
                            !(ch == FIRETRAP && on(*tp, NOFIRE)) &&
                            rnd(10) < tp->t_stats.s_intel &&
                        (y != hero.y || x != hero.x))
                            continue;
                    }

                    /*
                     * OK -- this place counts
                     */

                    thisdist = DISTANCE(tryp, *ee);
					
                    /*
                     * Adjust distance if we are being
                     * shot at to moving out of line of sight.
                     */

                    if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
                        ce(hero, *ee))
                    {
                        /* Move out of line of sight */
                        if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL))
                        {
                            if (flee)
                                thisdist -= SHOTPENALTY;
                            else
                                thisdist += SHOTPENALTY;
                        }

                        /*
                         * But do we want to leave
                         * the room?
                         */
                        else if (rer && rer == ree && ch == DOOR)
                            thisdist += DOORPENALTY;
                    }

                    /*
                     * Don't move to the last position if
                     * we can help it
                     */

                    if (ce(tryp, tp->t_oldpos))
                        dist_to_old = thisdist;
                    else if ((flee && (thisdist > dist)) ||
                         (!flee && (thisdist < dist)))
                    {
                        tp->t_nxtpos = tryp;
                        dist = thisdist;
                    }
                }
            }

        /*
         * If we are running from the player and he is in our way, go
         * ahead and slug him.
         */

        if (next_player && DISTANCE(*er,*ee) < dist &&
            step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp))
        {
            tp->t_nxtpos = tp->t_chasee->t_pos;    /* Okay to hit player */
            return(FALSE);
        }


        /*
         * If we can't get closer to the player (if that's our goal)
         * because other monsters are in the way, just stay put
         */

        if (!flee && ce(hero, *ee) && monst_dist < INT_MAX &&
            DISTANCE(*er, hero) < dist)
            tp->t_nxtpos = *er;

        /* Do we want to go back to the last position? */
        else if (dist_to_old != INT_MIN &&   /* It is possible to move back */
             ((flee && dist == 0) ||        /* No other possible moves */
              (!flee && dist == INT_MAX)))
        {
            /* Do we move back or just stay put (default)? */

            dist = DISTANCE(*er,*ee); /* Current distance */

            if (!flee || (flee && (dist_to_old > dist)))
                tp->t_nxtpos = tp->t_oldpos;
        }
    }

    /* Make sure we have the real distance now */
    dist = DISTANCE(tp->t_nxtpos, *ee);

    /* Mark monsters in a wall */

    switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x))
    {
        case WALL:
        case '-':
        case '|':
            turn_on(*tp, ISINWALL);
            break;
        default:
            turn_off(*tp, ISINWALL);
    }

    if (off(*tp, ISFLEE) &&
        !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL)))
        return(dist != 0);
    else /* May actually hit here from a confused move */
        return(!ce(tp->t_nxtpos, hero));
}
Exemple #6
0
	void ZombieGame::init() {
		keyboard_ = DevicePtr(new InputKeyboard(SDLK_UP, SDLK_DOWN, SDLK_LEFT,
			SDLK_RIGHT, SDLK_SPACE, SDLK_r, SDLK_LSHIFT, SDLK_e));		
		clipsize_ = 0;
		bulletsInWeapon_ = 0;
		health_ = 0;
		scale_ = 1.f;
		lastSpawnTime_ = engine_.getTime();
		spawnPeriod_ = 0.5f;

		addKeyListener([&](gui::Component& component, const SDL_Event& keyEvent) {
			keyboard_->eventUpdate(keyEvent);
		});

		if (zombieEntry_.getDeepChildEntry("music switch").getBool()) {
			music_ = zombieEntry_.getDeepChildEntry("music track").getMusic();
			music_.setVolume(zombieEntry_.getDeepChildEntry("music volume").getFloat());
			music_.play(-1);
		}

		tree_ = zombieEntry_.getDeepChildEntry("tree image").getSprite();
		wall_ = zombieEntry_.getDeepChildEntry("buildings wallImage").getSprite();
		nbrUnits_ = 0;

		unitMaxLimit_ = zombieEntry_.getDeepChildEntry("settings unitLimit").getInt();

		innerSpawnRadius_ = zombieEntry_.getDeepChildEntry("settings innerSpawnRadius").getFloat();
		outerSpawnRadius_ = zombieEntry_.getDeepChildEntry("settings outerSpawnRadius").getFloat();
		
		terrain_.loadRoadSprites(zombieEntry_.getDeepChildEntry("roads"));
		loadTerrain();
		
		explosionProperties_ = zombie::loadExplosion(zombieEntry_.getDeepChildEntry("explosion"));

		humanInjured_ = zombieEntry_.getDeepChildEntry("human injuredAnimation").getAnimation();
		humanDie_ = zombieEntry_.getDeepChildEntry("human dieAnimation").getAnimation();
		Unit2D human(loadUnit(this, "human", zombieEntry_, false));
		human.setDieSound(zombieEntry_.getDeepChildEntry("human dieSound").getSound());
		human.setHitSound(zombieEntry_.getDeepChildEntry("human hitSound").getSound());
		
		zombieInjured_ = zombieEntry_.getDeepChildEntry("zombie injuredAnimation").getAnimation();
		zombieDie_ = zombieEntry_.getDeepChildEntry("zombie dieAnimation").getAnimation();
		Unit2D zombie(loadUnit(this, "zombie", zombieEntry_, true));
		zombie.setDieSound(zombieEntry_.getDeepChildEntry("zombie dieSound").getSound());
		zombie.setHitSound(zombieEntry_.getDeepChildEntry("zombie hitSound").getSound());

		// Add human to engine.
		{
			State state(Position(85,120), ORIGO, 0);
			//Position p = generatePosition(spawningPoints_);
			//State state(Position(200,200), ORIGO, 0);
			Unit* unit = units_.pushBack(human);
			engine_.add(unit);
			unit->setState(state);
			unit->setActive(true);
			unit->setAwake(true);
			players_.push_back(std::unique_ptr<HumanPlayer>(new HumanPlayer(keyboard_, unit)));
			viewPosition_ = state.position_;
			refViewPosition_ = viewPosition_;
			++nbrUnits_;
		}

		// Add zombies to engine.
		calculateValidSpawningPoints(units_[0]);
		unsigned int unitLevel = zombieEntry_.getDeepChildEntry("settings unitLevel").getInt();
		for (unsigned int i = 1; i <= unitLevel && i < units_.getMaxSize(); ++i) {
			Position p = generatePosition(vaildSpawningPoints_);
			float angle = calculateAnglePointToPoint(p, units_[0].getPosition());
			State state(p, ORIGO, angle);
			Unit* unit = units_.pushBack(zombie);
			engine_.add(unit);
			unit->setState(state);
			unit->setActive(true);
			unit->setAwake(true);
			players_.push_back(std::unique_ptr<ZombieBehavior>(new ZombieBehavior(unit)));
		}
	
		// Add cars to engine.
		Car2D car(zombie::loadCar(zombieEntry_.getDeepChildEntry("car")));
		for (unsigned int i = 0; i < 8 && i < units_.getMaxSize(); ++i) {
			State state(Position(85,130), ORIGO, 0);
			Car* c = cars_.pushBack(car);
			engine_.add(c);
			c->setState(state);
			c->setActive(true);
			c->setAwake(true);
		}

		// Add missile to engine.
		Missile2D missile(loadMissile2D(this, zombieEntry_.getDeepChildEntry("equipment missile")));
		for (unsigned int i = 0; i < 10 && i < units_.getMaxSize(); ++i) {
			engine_.add(missiles_.emplaceBack(missile));
		}

		setBackgroundColor(0, 0.1f, 0);
		zombiesKilled_ = 0;

		drawBuildings_.createVBO(buildings_, wall_.getTexture());
	}
/*
 * chase:
 *	Find the spot for the chaser(er) to move closer to the
 *	chasee(ee).  Returns TRUE if we want to keep on chasing later
 *	FALSE if we reach the goal.
 */
int
chase(struct thing *tp, coord *ee, int flee, int *mdead)
{
    register int x, y;
    register int dist, thisdist, monst_dist = MAXINT;
    register struct linked_list *weapon;
    register coord *er = &tp->t_pos, *shoot_dir;
    register int ch, mch;
    register int next_player = FALSE;
    int deadflg;

	if (mdead != NULL)
		*mdead = 0;

	shoot_dir = can_shoot(er, ee);
    weapon = get_hurl(tp);

    /*
     * If the thing is confused, let it move randomly. Invisible
     * Stalkers are slightly confused all of the time.
     */
    if ((on(*tp, ISHUH) && rnd(10) < 8) ||
	((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) ||
	(on(player, ISINVIS) && off(*tp, CANSEE))) { /* Player is invisible */
	/*
	 * get a valid random move
	 */
	ch_ret = *rndmove(tp);
	dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);

    if (on(*tp, ISHUH) && rnd(20) == 0)  /* monster might lose confusion */
        turn_off(*tp, ISHUH);

	/*
	 * check to see if random move takes creature away from player
	 * if it does then turn off ISHELD
	 */
	if (dist > 1 && on(*tp, DIDHOLD)) {
	    turn_off(*tp, DIDHOLD);
	    turn_on(*tp, CANHOLD);
	    if (--hold_count <= 0)
	    {
	        hold_count = 0;
	        turn_off(player, ISHELD);
	    }
	}
    }

    /* If we can breathe, we may do so */
    else if (on(*tp, CANBREATHE) && (shoot_dir) &&
	     (rnd(100) < 67) &&
		 (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) &&
	     (DISTANCE(er->y, er->x, ee->y, ee->x) < BOLT_LENGTH*BOLT_LENGTH)) {
		register int chance;
		register char *breath;

		/* Will it breathe at random */
		if (on(*tp, CANBRANDOM)) {
		    if (rnd(level/20) == 0 && tp->t_index != NUMMONST+1)
			turn_off(*tp, CANBRANDOM);

		    /* Select type of breath */
		    chance = rnd(100);
		    if (chance < 11) breath = "acid";
		    else if (chance < 22) breath = "flame";
		    else if (chance < 33) breath = "lightning bolt";
		    else if (chance < 44) breath = "chlorine gas";
		    else if (chance < 55) breath = "ice";
		    else if (chance < 66) breath = "nerve gas";
		    else if (chance < 77) breath = "sleeping gas";
		    else if (chance < 88) breath = "slow gas";
		    else breath = "fear gas";
		}

		/* Or can it breathe acid? */
		else if (on(*tp, CANBACID)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBACID);
		    breath = "acid";
		}

		/* Or can it breathe fire */
		else if (on(*tp, CANBFIRE)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBFIRE);
		    breath = "flame";
		}

		/* Or can it breathe electricity? */
		else if (on(*tp, CANBBOLT)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBBOLT);
		    breath = "lightning bolt";
		}

		/* Or can it breathe gas? */
		else if (on(*tp, CANBGAS)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBGAS);
		    breath = "chlorine gas";
		}

		/* Or can it breathe ice? */
		else if (on(*tp, CANBICE)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBICE);
		    breath = "ice";
		}

		else if (on(*tp, CANBPGAS)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBPGAS);
		    breath = "nerve gas";
		}

		else if (on(*tp, CANBSGAS)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBSGAS);
		    breath = "sleeping gas";
		}

		else if (on(*tp, CANBSLGAS)) {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBSLGAS);
		    breath = "slow gas";
		}

		else {
		    if (rnd(level/20) == 0)
			turn_off(*tp, CANBFGAS);
		    breath = "fear gas";
		}

		/* Now breathe -- returns TRUE if kills itself */
		deadflg =
		    shoot_bolt(tp, *er, *shoot_dir, FALSE, tp->t_index, breath,
			       (save(VS_BREATH) ? tp->t_stats.s_hpt/2
					        : tp->t_stats.s_hpt));

		if (deadflg && mdead != NULL)
			*mdead = 1;

		ch_ret = *er;
		dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);
		if (deadflg) return(TRUE);
	}

    /* 
     * If we can shoot or throw something, we might do so 
     * if we are running away, we may decide to shoot anyway if we are far 
     * enough
     */
    else if((off(*tp, ISFLEE) || rnd(DISTANCE(er->y,er->x,ee->y,ee->x)) > 2) && 
	     on(*tp, CANSHOOT) && (off(player, ISDISGUISE) ||
		 (rnd(tp->t_stats.s_lvl) > 6)) &&
	    (shoot_dir) &&
	    (weapon)) {
		missile(shoot_dir->y, shoot_dir->x, weapon, tp);
		ch_ret = *er;
		dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);
	}

    /*
     * Otherwise, find the empty spot next to the chaser that is
     * closest to the chasee.
     */
    else {
	register int ey, ex;
	register struct room *rer, *ree;
	register int dist_to_old = MININT; /* Dist from goal to old position */

	/* Get rooms */
	rer = roomin(er);
	ree = roomin(ee);

	/*
	 * This will eventually hold where we move to get closer
	 * If we can't find an empty spot, we stay where we are.
	 */
	dist = flee ? 0 : MAXINT;
	ch_ret = *er;

	/* Are we at our goal already? */
	if (!flee && ce(ch_ret, *ee)) return(FALSE);

	ey = er->y + 1;
	ex = er->x + 1;
	for (x = er->x - 1; x <= ex; x++)
	    for (y = er->y - 1; y <= ey; y++) {
		coord tryp;

		/* Don't try off the board */
		if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2))
		    continue;

		/* Don't try the player if not going after the player */
		/* or he's disguised */
		if ( ((off(*tp, ISFLEE) && !ce(hero, *ee)) ||
			(on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6))) &&
			x == hero.x && y == hero.y)
		    continue;

		tryp.x = x;
		tryp.y = y;

		/* Is there a monster on this spot closer to our goal?
		 * Don't look in our spot or where we were.
		 */
		if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) &&
		    isalpha(mch = CCHAR( mvwinch(mw, y, x) ))) {
		    register int test_dist;

		    test_dist = DISTANCE(y, x, ee->y, ee->x);
		    if (test_dist <= 25 &&   /* Let's be fairly close */
			test_dist < monst_dist) {
			/* Could we really move there? */
			mvwaddch(mw, y, x, ' '); /* Temporarily blank monst */
			if (diag_ok(er, &tryp, tp)) monst_dist = test_dist;
			mvwaddch(mw, y, x, mch); /* Restore monster */
		    }
		}

		if (!diag_ok(er, &tryp, tp))
		    continue;
		ch = CCHAR( mvwinch(cw, y, x) );	/* Screen character */
		if (on(*tp, ISFLEE) && (ch == PLAYER)) next_player = TRUE;

		/* Stepping on player is NOT okay if we are fleeing */
		if (step_ok(y, x, NOMONST, tp) &&
		    (off(*tp, ISFLEE) || ch != PLAYER))
		{
		    /*
		     * If it is a trap, an intelligent monster may not
		     * step on it (unless our hero is on top!)
		     */
		    if (isatrap(ch)) {
			if (!(ch == RUSTTRAP) &&
				!(ch == FIRETRAP && on(*tp,NOFIRE)) &&
			    rnd(10) < tp->t_stats.s_intel &&
			    (y != hero.y || x != hero.x))
				continue;
		    }

		    /*
		     * OK -- this place counts
		     */
		    thisdist = DISTANCE(y, x, ee->y, ee->x);

		    /* Adjust distance if we are being shot at */
		    if (tp->t_wasshot && tp->t_stats.s_intel > 5 &&
			ce(hero, *ee)) {
			/* Move out of line of sight */
			if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) {
			    if (flee) thisdist -= SHOTPENALTY;
			    else thisdist += SHOTPENALTY;
			}

			/* But do we want to leave the room? */
			else if (rer && rer == ree && ch == DOOR)
			    thisdist += DOORPENALTY;
		    }

		    /* Don't move to the last position if we can help it */
		    if (ce(tryp, tp->t_oldpos)) dist_to_old = thisdist;

		    else if ((flee && (thisdist > dist)) ||
			(!flee && (thisdist < dist)))
		    {
			ch_ret = tryp;
			dist = thisdist;
		    }
		}
	    }

	/* If we are running from the player and he is in our way,
	 * go ahead and slug him.
	 */
	if (next_player && DISTANCE(er->y, er->x, ee->y, ee->x) < dist &&
	    step_ok(tp->t_dest->y, tp->t_dest->x, NOMONST, tp)) {
	    ch_ret = *(tp->t_dest);	/* Okay to hit player */
	    return FALSE;
	}


	/* If we can't get closer to the player (if that's our goal)
	 * because other monsters are in the way, just stay put
	 */
	if (!flee && ce(hero, *ee) && monst_dist < MAXINT &&
	    DISTANCE(er->y, er->x, hero.y, hero.x) < dist)
		ch_ret = *er;

	/* Do we want to go back to the last position? */
	else if (dist_to_old != MININT &&      /* It is possible to move back */
	    ((flee && dist == 0) ||	/* No other possible moves */
	     (!flee && dist == MAXINT))) {
	    /* Do we move back or just stay put (default)? */
	    dist = DISTANCE(er->y, er->x, ee->y, ee->x); /* Current distance */
	    if (!flee || (flee && (dist_to_old > dist))) ch_ret = tp->t_oldpos;
	}
    }

    /* Make sure we have the real distance now */
    dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x);

    /* Mark monsters in a wall */
    if (isrock(CCHAR( mvinch(ch_ret.y, ch_ret.x) ))) 
	turn_on(*tp, ISINWALL);
    else 
	turn_off(*tp, ISINWALL);

    if (off(*tp, ISFLEE) &&
	((tp->t_dest != &hero) || off(player, ISINWALL) || on(*tp, CANINWALL)))
	return (dist != 0);

    /* May actually hit here from a confused move */
    else return(!ce(ch_ret, hero));
}