bool LocalPlayer::withinAttackRange(Being *target)
{
#ifdef TMWSERV_SUPPORT
    const Vector &targetPos = target->getPosition();
    const Vector &pos = getPosition();
    const int dx = abs(targetPos.x - pos.x);
    const int dy = abs(targetPos.y - pos.y);
    const int range = getAttackRange();

    return !(dx > range || dy > range);
#else
    int dist_x = abs(target->getTileX() - getTileY());
    int dist_y = abs(target->getTileY() - getTileX());

    if (dist_x > getAttackRange() || dist_y > getAttackRange())
    {
        return false;
    }

    return true;
#endif
}
Exemple #2
0
void CmdRemoveTile_c::undo()
{
	m_map->addTile( getTileX(), getTileY(), getTileType() );
}
Exemple #3
0
void CmdRemoveTile_c::redo()
{
	m_map->removeTile( getTileX(), getTileY() );
}
Exemple #4
0
void CmdAddTile_c::undo()
{
	m_map->removeTile( getTileX(), getTileY() );
}
Exemple #5
0
void CmdAddTile_c::redo()
{
	m_map->addTile( getTileX(), getTileY(), getTileType() );
}
void LocalPlayer::walk(unsigned char dir)
{
    // TODO: Evaluate the implementation of this method for tmwserv
    if (!mMap || !dir)
        return;

#ifdef TMWSERV_SUPPORT
    const Vector &pos = getPosition();
#endif

    if (mAction == WALK && !mPath.empty())
    {
        // Just finish the current action, otherwise we get out of sync
#ifdef TMWSERV_SUPPORT
        Being::setDestination(pos.x, pos.y);
#else
        Being::setDestination(getTileX(), getTileY());
#endif
        return;
    }

    int dx = 0, dy = 0;
    if (dir & UP)
        dy--;
    if (dir & DOWN)
        dy++;
    if (dir & LEFT)
        dx--;
    if (dir & RIGHT)
        dx++;

    // Prevent skipping corners over colliding tiles
#ifdef TMWSERV_SUPPORT
    if (dx && !mMap->getWalk(((int) pos.x + dx) / 32,
                             (int) pos.y / 32, getWalkMask()))
        dx = 16 - (int) pos.x % 32;
    if (dy && !mMap->getWalk((int) pos.x / 32,
                             ((int) pos.y + dy) / 32, getWalkMask()))
        dy = 16 - (int) pos.y % 32;
#else
    if (dx && !mMap->getWalk(getTileX() + dx, getTileY(), getWalkMask()))
        dx = 0;
    if (dy && !mMap->getWalk(getTileX(), getTileY() + dy, getWalkMask()))
        dy = 0;
#endif

    // Choose a straight direction when diagonal target is blocked
#ifdef TMWSERV_SUPPORT
    if (dx && dy && !mMap->getWalk((pos.x + dx) / 32,
                                   (pos.y + dy) / 32, getWalkMask()))
        dx = 16 - (int) pos.x % 32;

    int dScaler; // Distance to walk

    // Checks our path up to 1 tiles, if a blocking tile is found
    // We go to the last good tile, and break out of the loop
    for (dScaler = 1; dScaler <= 32; dScaler++)
    {
        if ( (dx || dy) &&
             !mMap->getWalk( ((int) pos.x + (dx * dScaler)) / 32,
                             ((int) pos.y + (dy * dScaler)) / 32, getWalkMask()) )
        {
            dScaler--;
            break;
        }
    }

    if (dScaler >= 0)
    {
        effectManager->trigger(15, (int) pos.x + (dx * dScaler), (int) pos.y + (dy * dScaler));
        setDestination((int) pos.x + (dx * dScaler), (int) pos.y + (dy * dScaler));
    }
#else
    if (dx && dy && !mMap->getWalk(getTileX() + dx, getTileY() + dy, getWalkMask()))
        dx = 0;

    // Walk to where the player can actually go
    if ((dx || dy) && mMap->getWalk(getTileX() + dx, getTileY() + dy, getWalkMask()))
    {
        setDestination(getTileX() + dx, getTileY() + dy);
    }
#endif
    else if (dir)
    {
        // If the being can't move, just change direction
        Net::getPlayerHandler()->setDirection(dir);
        setDirection(dir);
    }
}
void LocalPlayer::attack(Being *target, bool keep)
{
#ifdef TMWSERV_SUPPORT
    if (mLastAction != -1)
        return;

    // Can only attack when standing still
    if (mAction != STAND && mAction != ATTACK)
        return;
#endif

    mKeepAttacking = keep;

    if (!target || target->getType() == Being::NPC)
        return;

    if (mTarget != target || !mTarget)
    {
        mLastTarget = -1;
        setTarget(target);
    }
#ifdef TMWSERV_SUPPORT
    Vector plaPos = this->getPosition();
    Vector tarPos = mTarget->getPosition();
    int dist_x = plaPos.x - tarPos.x;
    int dist_y = plaPos.y - tarPos.y;
#else
    int dist_x = target->getTileX() - getTileX();
    int dist_y = target->getTileY() - getTileY();

    // Must be standing to attack
    if (mAction != STAND)
        return;
#endif

#ifdef TMWSERV_SUPPORT
    if (abs(dist_y) >= abs(dist_x))
    {
        if (dist_y < 0)
            setDirection(DOWN);
        else
            setDirection(UP);
    }
    else
    {
        if (dist_x < 0)
            setDirection(RIGHT);
        else
            setDirection(LEFT);
    }
#else
    if (abs(dist_y) >= abs(dist_x))
    {
        if (dist_y > 0)
            setDirection(DOWN);
        else
            setDirection(UP);
    }
    else
    {
        if (dist_x > 0)
            setDirection(RIGHT);
        else
            setDirection(LEFT);
    }
#endif

#ifdef TMWSERV_SUPPORT
    mLastAction = tick_time;
#else
    mWalkTime = tick_time;
    mTargetTime = tick_time;
#endif

    setAction(ATTACK);

    if (mEquippedWeapon)
    {
        std::string soundFile = mEquippedWeapon->getSound(EQUIP_EVENT_STRIKE);
        if (!soundFile.empty())
            sound.playSfx(soundFile);
    }
    else
    {
        sound.playSfx("sfx/fist-swish.ogg");
    }

    Net::getPlayerHandler()->attack(target->getId());
#ifdef EATHENA_SUPPORT
    if (!keep)
        stopAttack();
#endif
}
Exemple #8
0
std::string Avatar::str() const {
	return (boost::format("Avatar tile=%1%(%2%,%3%) tilePosition=%4%m,%5%m,%6%m rotation=%7%°,%8%°,%9%° headYaw=%10%° headPitch=%11%° centerEye=%12%m,%13%m,%14%m") %
		getTileZoom() % getTileX() % getTileY() % getTilePosition()[0] % getTilePosition()[1] % getTilePosition()[2] %
		getRotation()[0] % getRotation()[1] % getRotation()[2] % getHeadYaw() % getHeadPitch() %
		getCenterEyePosition()[0] % getCenterEyePosition()[1] % getCenterEyePosition()[2] ).str();
}
void LocalPlayer::logic()
{
    // Actions are allowed once per second
    if (get_elapsed_time(mLastAction) >= 1000)
        mLastAction = -1;

    // Show XP messages
    if (!mMessages.empty())
    {
        if (mMessageTime == 0)
        {
            //const Vector &pos = getPosition();

            MessagePair info = mMessages.front();

            particleEngine->addTextRiseFadeOutEffect(
                    info.first,
                    /*(int) pos.x,
                    (int) pos.y - 48,*/
                    getPixelX(),
                    getPixelY() - 48,
                    &guiPalette->getColor(info.second),
                    gui->getInfoParticleFont(), true);

            mMessages.pop_front();
            mMessageTime = 30;
        }
        mMessageTime--;
    }

    if ((mSpecialRechargeUpdateNeeded%11) == 0)
    {
        mSpecialRechargeUpdateNeeded = 0;
        for (std::map<int, Special>::iterator i = mSpecials.begin();
             i != mSpecials.end();
             i++)
        {
            i->second.currentMana += i->second.recharge;
            if (i->second.currentMana > i->second.neededMana)
            {
                i->second.currentMana = i->second.neededMana;
            }
        }
    }
    mSpecialRechargeUpdateNeeded++;

#ifdef EATHENA_SUPPORT
    // Targeting allowed 4 times a second
    if (get_elapsed_time(mLastTarget) >= 250)
        mLastTarget = -1;

    // Remove target if its been on a being for more than a minute
    if (get_elapsed_time(mTargetTime) >= 60000)
    {
        mTargetTime = -1;
        setTarget(NULL);
        mLastTarget = -1;
    }
#endif

    if (mTarget)
    {
        if (mTarget->getType() == Being::NPC)
        {
            // NPCs are always in range
            mTarget->setTargetAnimation(
                mTargetCursor[0][mTarget->getTargetCursorSize()]);
        }
        else
        {
#ifdef TMWSERV_SUPPORT
            // Find whether target is in range
            const int rangeX = abs(mTarget->getPosition().x - getPosition().x);
            const int rangeY = abs(mTarget->getPosition().y - getPosition().y);
#else
            // Find whether target is in range
            const int rangeX = abs(mTarget->getTileX() - getTileX());
            const int rangeY = abs(mTarget->getTileY() - getTileY());
#endif
            const int attackRange = getAttackRange();
            const int inRange = rangeX > attackRange || rangeY > attackRange
                                                                    ? 1 : 0;
            mTarget->setTargetAnimation(
                mTargetCursor[inRange][mTarget->getTargetCursorSize()]);

            if (mTarget->mAction == DEAD)
                stopAttack();

            if (mKeepAttacking && mTarget)
                attack(mTarget, true);
        }
    }

    Player::logic();
}
bool PlayerCharacterEntity::goNumTilesAway(int numToMove)
{
  // set an immediate GO_TO instruction if a suitable tile is found
  set<coord> searched;
  list<pair<int,coord> > toSearch;

  toSearch.push_front(pair<int, coord>(0, coord(getTileX(), getTileY())));

  while (!toSearch.empty())
  {
    // get head of toSearch
    pair<int, coord> currentLocation = toSearch.front();
    toSearch.pop_front();

    // is this the tile we want?
    if (currentLocation.first >= numToMove)
    {
      // it is, set GO_TO instruction and be happy
      AIInstruction * ins = new AIInstruction(AIInstruction::GO_TO,
                                              0, getType(), currentLocation.second.first, currentLocation.second.second);
      m_insList.push_front(ins);

      return true;
    }
    else
    {
      // add valid adjoining tiles to toSearch list

      searched.insert(currentLocation.second);

      Map * staticMap = Map::getInstance();
      AIGameView * view = AIGameView::getInstance();

      for (int i = 0; i < 4; ++i)
      {
        int x, y;
        x = currentLocation.second.first;
        y = currentLocation.second.second;

        switch (i)
        {
          case 0:
            x++;
            break;
          case 1:
            x--;
            break;
          case 2:
            y++;
            break;
          case 3:
            y--;
            break;
        }

        if (staticMap->staticTileAt(x, y) == Map::EMPTY)
        {
          // check for hostile mines
          bool badMineFound = false;

          vector<EntityInfo> info = view->getEntityInfoAtLocation(x, y);
          vector<EntityInfo>::iterator it;
          for (it = info.begin(); it != info.end(); ++it)
          {
            if (it->type == MINE && it->team != getTeam())
            {
              badMineFound = true;
              break;
            }
          }

          if (badMineFound)
          {
            continue;
          }

          coord newCoord(x, y);
          pair<int, coord> newLocation(currentLocation.first + 1, newCoord);
          // check searched set
          if (searched.find(newCoord) == searched.end())
          {
            toSearch.push_back(newLocation);
          }
        }

      } // end for 0..3

    } // end else static entry found

  } // end while toSearch not-empty

  return false;
}
void PlayerCharacterEntity::processNextInstruction()
{
  if (m_insList.empty())
  {
    return;
  }

  AIInstruction * ins = m_insList.front();
  m_insList.pop_front();

  switch (ins->m_type)
  {
    case AIInstruction::CREATE_ENTITY:
      {
        if (m_health < 15)
        {
          break;
        }
        // check if this tile already has a static entity
        bool staticAtLocation = this->staticEntityAtLocation(getTileX(), getTileY());

        if (staticAtLocation)
        {
          // there is already a static entity

          // if this is an egg type required, move to nearest "empty" tile
          if (   ins->m_targetType == EGG1
                 || ins->m_targetType == EGG2
                 || ins->m_targetType == EGG3
                 || ins->m_targetType == EGG4
                 || ins->m_targetType == EGG5 )
          {
            // find nearest empty tile and move there, replace instruction

            if (goToNearestEmptyTile(ins->m_targetTileX, ins->m_targetTileY))
            {
              // can go to an empty tile
              AIInstruction * copiedIns =
                new AIInstruction(*ins);

              // put CREATE instruction after GOTO at head of list
              AIInstruction * goToIns = m_insList.front();
              m_insList.pop_front();
              m_insList.push_front(copiedIns);
              m_insList.push_front(goToIns);
            }
            else
            {
              // failed to find a nearby empty tile

              // forget this instruction for now

            }
          }
          else
          {
            // is a weapon/block type
            // probably not very helpful to move location
            // so forget this instruction

          }


        }
        else
        {
          // there isn't a static entity

          // if this is an egg, is it a safe enough location?

          // is this a weapon? if so, check desired spot
          if (ins->m_targetType == BOMB ||
              ins->m_targetType == BLOCK ||
              ins->m_targetType == MINE ||
              ins->m_targetType == ROCKET_SPIN)
          {
            if (ins->m_targetType == BOMB ||
                ins->m_targetType == MINE)
            {
              if (!safeLocationToBomb(m_tileX, m_tileY))
              {
                break;
              }
            }

            if (ins->m_targetTileX == m_tileX
                && ins->m_targetTileY == m_tileY)
            {
              createEntity(ins->m_targetType);
            }
          }
          else
          {
            // check this location is safe for an egg

            bool bombOrMineFound = false;

            for (int i = 0; i < 4; ++i)
            {
              int x, y;
              x = m_tileX;
              y = m_tileY;

              switch (i)
              {
                case 0:
                  x++;
                  break;
                case 1:
                  x--;
                  break;
                case 2:
                  y++;
                  break;
                case 3:
                  y--;
                  break;
              }

              vector<EntityInfo> eiVec = AIGameView::getInstance()->getEntityInfoAtLocation(x, y);
              vector<EntityInfo>::iterator it;

              for (it = eiVec.begin(); it != eiVec.end(); ++it)
              {
                // even our team's bombs and mines are dangerous to eggs
                if (it->type == BOMB || it->type == MINE)
                {
                  bombOrMineFound = true;
                  break;
                }

              }

              if (bombOrMineFound)
              {
                break;
              }

            } // end for 0..3

            if (!bombOrMineFound)
            {
              createEntity(ins->m_targetType);
            }
            else
            {
              // move elsewhere
              AIInstruction * copy = new AIInstruction(*ins);
              m_insList.push_front(copy);

              goNumTilesAway(3);
            }
          }
        }


      }
      break;
    case AIInstruction::GO_TO:
      {
        // check for simple 0 or 1 tile movement
        int xDist, yDist;
        xDist = m_tileX - ins->m_targetTileX;
        yDist = m_tileY - ins->m_targetTileY;

        bool moved = false;


        if (xDist == 0 && yDist == 0)
        {
          // in target tile, close enough to center?
          if (nearTileCenter())
          {
            // finished this instruction
            break;
          }
          else
          {
            // keep moving in current direction
            DIRECTION dir = static_cast<DIRECTION>(m_currentAnimState - 1);
            moveInDirection(dir);
            moved = true;
          }
        }

        // only one tile to go?

        // check target tile isn't blocked
        if (Map::getInstance()->staticTileAt(ins->m_targetTileX, ins->m_targetTileY) != Map::EMPTY)
        {
          break;
        }

        if (xDist == -1 && yDist == 0)
        {
          moveInDirection(RIGHT);
          moved = true;
        }
        else if (xDist == 1 && yDist == 0)
        {
          moveInDirection(LEFT);
          moved = true;
        }
        else if (xDist == 0 && yDist == 1)
        {
          moveInDirection(UP);
          moved = true;
        }
        else if (xDist == 0 && yDist == -1)
        {
          moveInDirection(DOWN);
          moved = true;
        }



        if (moved)
        {
          // copy instruction
          AIInstruction * copyIns = new AIInstruction(*ins);
          m_insList.push_front(copyIns);

          incAnimFrame();
          break;
        }


        // there is a multiple tile distance, use path finding

        // get next tile by pathfinding
        coord next = getNextTileFromPathfinding(ins->m_targetTileX, ins->m_targetTileY);
        // did pathfinding work?
        if (next.first == getTileX() && next.second == getTileY())
        {
          // did not work, or we're already where we want to be


        }
        else
        {
          // copy instruction
          AIInstruction * copyIns = new AIInstruction(*ins);
          m_insList.push_front(copyIns);

          // add this tile as goto
          AIInstruction * newIns = new AIInstruction(AIInstruction::GO_TO,
              0, getType(), next.first, next.second);
          m_insList.push_front(newIns);
          processNextInstruction();
        }

      }
      break;
    case AIInstruction::MOVE_FROM_LOCATION:
      {
        goNumTilesAway(ins->m_targetAmount);
      }
      break;
    case AIInstruction::WAIT:
      {
        int waitAmount = ins->m_targetAmount - 1;
        if (waitAmount > 0)
        {
          AIInstruction * newIns = new AIInstruction(ins->m_type,
              waitAmount, ins->m_targetType, ins->m_targetTileX, ins->m_targetTileY);
          m_insList.push_front(newIns);
        }
        // else nothing more to do
      }
      break;
  }

  delete ins;
}
void PlayerCharacterEntity::determineInstructionsFromGoal()
{
  if (m_goalList.empty())
  {
    return;
  }

  AIGoal * currentGoal = m_goalList.front();
  m_goalList.pop_front();

  switch (currentGoal->m_goalType)
  {
    case AIGoal::GOAL_INCREASE_ENERGY:
      {
        // create 8 energy drops at once

        AIGoal * create = new AIGoal(AIGoal::GOAL_CREATE_ENTITIES, 2 + rand()%5,
                                     getTileX(), getTileY(), EGG1, 0);
        m_goalList.push_back(create);

        determineInstructionsFromGoal();

        AIInstruction * wait = new AIInstruction(AIInstruction::WAIT,
            33, getType(), 0, 0);
        m_insList.push_back(wait);



      }
      break;
    case AIGoal::GOAL_SECURE_AREA:
      {
        // use path flooding to certain depth and place blocks at end
        /*
        const int SECURE_DISTANCE = 3;

        int x1, x2, y1, y2;
        x1 = m_tileX - SECURE_DISTANCE;
        x2 = m_tileX + SECURE_DISTANCE;
        y1 = m_tileY - SECURE_DISTANCE;
        y2 = m_tileY + SECURE_DISTANCE;

        for (int counter = x1; counter <= x2; ++counter)
        {
          // at (x1, y1 + counter), (x1 + counter, y1)
          // (x2, y1 + counter), (x1 + counter, y2)
          for (int i = 0; i < 4; ++i)
          {
            int xCheck, yCheck;
            switch (i)
            {
            case 0:
              xCheck = x1;
              yCheck = y1 + counter;
              break;
            case 1:
              xCheck = x1 + counter;
              yCheck = y1;
              break;
            case 2:
              xCheck = x2;
              yCheck = y1 + counter;
              break;
            case 3:
              xCheck = x1 + counter;
              yCheck = y2;
              break;
            }

            if (xCheck <= 0 || xCheck >= Map::MAP_WIDTH - 1)
              continue;
            if (yCheck <= 0 || yCheck >= Map::MAP_HEIGHT - 1)
              continue;

            // is location accessible
            coord next = getNextTileFromPathfinding(xCheck, yCheck);

            if (Map::getInstance()->staticTileAt(xCheck, yCheck) != Map::EMPTY)
              continue; // blocked

            if (next.first == m_tileX && next.second == m_tileY)
              continue; // can't get there

            // else place block there
            AIInstruction * goTo, * place;//, * returnJourney;

            goTo = new AIInstruction(AIInstruction::GO_TO,
              0, getType(), xCheck, yCheck);
            m_insList.push_back(goTo);

            place = new AIInstruction(AIInstruction::CREATE_ENTITY,
              1, BLOCK, xCheck, yCheck);
            m_insList.push_back(place);

            //returnJourney = new AIInstruction(AIInstruction::GO_TO,
            //  0, getType(), next.first, next.second);
            //m_insList.push_back(returnJourney);



          }


        }*/
      }
      break;
    case AIGoal::GOAL_CREATE_ENTITIES:
      {
        // this can be done by just adding instructions
        // to create the right kind and number of eggs

        // the instruction handing will move character as needed
        for (int i = 0; i < currentGoal->m_targetAmount; ++i)
        {
          // add pause to simulate human player decision/action delay
          // could remove for more difficult AI bots
          AIInstruction * waitIns =
            new AIInstruction(AIInstruction::WAIT, 5 + (rand()%11), getType(), -1, -1);
          m_insList.push_back(waitIns);


          AIInstruction * ins =
            new AIInstruction(AIInstruction::CREATE_ENTITY, 1, currentGoal->m_targetType, getTileX(), getTileY());
          m_insList.push_back(ins);


        }

      }
      break;
    case AIGoal::GOAL_ATTACK_TEAM:
      {


      }
      break;
    case AIGoal::GOAL_ATTACK_ENTITY:
      {


      }
      break;
    case AIGoal::GOAL_GET_POWERUP:
      {
        if (currentGoal->m_tileX == m_tileX && currentGoal->m_tileY == m_tileY)
        {
          break;  // we're already there
        }

        // check powerup still exists
        vector<EntityInfo> info = AIGameView::getInstance()->getEntityInfoAtLocation(
                                    currentGoal->m_tileX, currentGoal->m_tileY);
        vector<EntityInfo>::iterator it;
        bool targetExists = false;
        for (it = info.begin(); it != info.end(); ++it)
        {
          if (it->id == currentGoal->m_targetID)
          {
            targetExists = true;
            break;
          }
        }
        if (!targetExists)
        {
          break;
        }

        // keep this goal for later REMOVE??
        // as we may need multiple block bombings
        //AIGoal * copy = new AIGoal(*currentGoal);
        //m_goalList.push_front(copy);

        // set "final" goto
        AIInstruction * target = new AIInstruction(AIInstruction::GO_TO, 0, currentGoal->m_targetType,
            currentGoal->m_tileX, currentGoal->m_tileY);
        m_insList.push_front(target);

        // go to powerup with demolition enabled
        coord next = getNextTileFromPathfinding(currentGoal->m_tileX, currentGoal->m_tileY, true);

        // seeing as we've already calculated it...
        AIInstruction * firstGoTo = new AIInstruction(AIInstruction::GO_TO,
            0, currentGoal->m_targetType, next.first, next.second);
        m_insList.push_front(firstGoTo);


      }
      break;
  }

  delete currentGoal;
}
void PlayerCharacterEntity::determineGoals()
{
  AIGameView * view = AIGameView::getInstance();

  // check surrounding area
  static const int RANGE = 4;
  bool goalFound = false;
  for (int x = m_tileX - RANGE; x <= m_tileX + RANGE; ++x)
  {
    if (x < 0 || x >= Map::MAP_WIDTH)
    {
      continue;
    }
    for (int y = m_tileY - RANGE; y <= m_tileY + RANGE; ++y)
    {
      if (y < 0 || y >= Map::MAP_HEIGHT)
      {
        continue;
      }

      if (x == m_tileX && y == m_tileY)
      {
        continue;
      }

      // should be optimised with an outward-spiralling search pattern,
      // and first powerup found will be approx. nearest
      // (as much as the current *solution* is "nearest")

      vector<EntityInfo> info = view->getEntityInfoAtLocation(x, y);
      vector<EntityInfo>::iterator it;

      for (it = info.begin(); it != info.end(); ++it)
      {
        // is there a powerup?
        if (it->groupType == POWERUP_GROUP || it->type == ENERGY_DROP)
        {
          // add to goals if this is closest powerup
          list<AIGoal *>::iterator goalIt;
          for (goalIt = m_goalList.begin(); goalIt != m_goalList.end(); ++goalIt)
          {
            AIGoal * oldGoal = *goalIt;
            if (oldGoal->m_goalType == AIGoal::GOAL_GET_POWERUP)
            {
              int oldDistance, newDistance;
              oldDistance = abs( oldGoal->m_tileX - getTileX() )
                            + abs( oldGoal->m_tileY - getTileY() );
              newDistance = abs( x - getTileX() ) + abs( y - getTileY() );

              if (newDistance < oldDistance)
              {
                // replace old goal with closer one
                m_goalList.remove(oldGoal);
                delete oldGoal;

                m_goalList.push_front(new AIGoal(AIGoal::GOAL_GET_POWERUP, 0, x, y, it->type, it->id));

                goalFound = true;
                break;
              }
              // else ignore this powerup for now

            } // end if goal type == GET_POWERUP

          } // end for loop on goalList

          if (!goalFound)
          {
            m_goalList.push_front(new AIGoal(AIGoal::GOAL_GET_POWERUP, 0, x, y, it->type, it->id));
          }
          return;
        }



      } // end iteration of entity info

    } // end for y
  } // end for x

  // other goals
  if (m_health < 45)
  {
    AIGoal * g1 = new AIGoal(AIGoal::GOAL_SECURE_AREA, 0, getTileX(), getTileY(), getType(), 0);
    m_goalList.push_back(g1);
  }
  if (m_health < 100)
  {
    AIGoal * g2 = new AIGoal(AIGoal::GOAL_INCREASE_ENERGY, 130, getTileX(), getTileY(), getType(), 0);
    m_goalList.push_back(g2);
    return;
  }
  if (m_health >= 100)
  {
    // create some entities

    // drone or robot
    if (rand()%2 > 0)
    {
      // tanks
      AIGoal * goal = new AIGoal(AIGoal::GOAL_CREATE_ENTITIES,
                                 1 + rand()%3, getTileX(), getTileY(), EGG5, 0);
      m_goalList.push_back(goal);
    }
    else
    {
      // drones
      AIGoal * goal = new AIGoal(AIGoal::GOAL_CREATE_ENTITIES,
                                 1 + rand()%4, getTileX(), getTileY(), EGG4, 0);
      m_goalList.push_back(goal);

    }
    if (rand()%2 > 0)
    {
      // wall eater or two
      AIGoal * goal = new AIGoal(AIGoal::GOAL_CREATE_ENTITIES,
                                 (rand()%2) + 1, getTileX(), getTileY(), EGG3, 0);
      m_goalList.push_back(goal);
    }
    if (rand()%10 > 3)
    {
      // egg thief?
      AIGoal * goal = new AIGoal(AIGoal::GOAL_CREATE_ENTITIES,
                                 (rand()%2) + 1, getTileX(), getTileY(), EGG2, 0);
      m_goalList.push_back(goal);
    }


  }
}
void PlayerCharacterEntity::checkEnvironment()
{
  // what is the closest enemy in a straight line? might want to launch a rocket
  EntityInfo nearest;
  nearest.id = -1; // marks no suitable enemy found (sorry for the "-1 bashing", free the first non-positive!)

  // modified from RocketSpinEntity::update() code
  bool dirFinished[4] = { false, false, false, false };
  int distance = 0;
  int targetX = 0, targetY = 0;

  while ((!dirFinished[0]) || (!dirFinished[1])
         || (!dirFinished[2]) || (!dirFinished[3]))
  {
    // try each direction in turn once
    // so that rocket targets nearest enemy
    distance++;
    for (int d = 0; d < 4; d++)
    {
      if (dirFinished[d])
      {
        continue;
      }

      DIRECTION dir;
      int xMultiplier, yMultiplier;
      // set these variables
      switch (d)
      {
        case 0:
          dir = UP;
          xMultiplier = 0;
          yMultiplier = -1;
          break;
        case 1:
          dir = DOWN;
          xMultiplier = 0;
          yMultiplier = 1;
          break;
        case 2:
          dir = LEFT;
          xMultiplier = -1;
          yMultiplier = 0;
          break;
        case 3:
          dir = RIGHT;
          xMultiplier = 1;
          yMultiplier = 0;
          break;
      }
      //unused. avoid compiler warning
      (void)dir;
      // get x and y co-ords to test
      int xTile, yTile;
      xTile = m_tileX + (distance * xMultiplier);
      yTile = m_tileY + (distance * yMultiplier);

      if (Map::getInstance()->staticTileAt(xTile, yTile) == Map::EMPTY)
      {
        // test this tile for enemies to launch at
        vector<EntityInfo> info = AIGameView::getInstance()->getEntityInfoAtLocation(xTile, yTile);
        vector<EntityInfo>::iterator infoIt;
        for (infoIt = info.begin(); infoIt != info.end(); ++infoIt)
        {
          if (infoIt->team != m_team && (infoIt->groupType == CHARACTER_GROUP
                                         || infoIt->groupType == PLAYER_SPAWN_GROUP
                                         || infoIt->groupType == ROBOT_GROUP) )
          {
            nearest = *infoIt;
            targetX = xTile;
            targetY = yTile;
            break;
          }


        }
        if (nearest.id != -1)
        {
          break;
        }
      }
      else
      {
        // reached a wall or block tile in this direction
        dirFinished[d] = true;
      }


    } // end for each direction

    if (nearest.id != -1)
    {
      break;
    }
  } // end while loop

  if (nearest.id != -1)
  {
    // enemy nearby in straight line
    // check distance is enough not to get caught in blast
    // and safe location to bomb
    if (distance > 1 && safeLocationToBomb(targetX, targetY) && m_health > 10)
    {
      // fire a rocket now! (if possible)
      AIInstruction * fireRocket = new AIInstruction(
        AIInstruction::CREATE_ENTITY, 1, ROCKET_SPIN, getTileX(), getTileY());
      m_insList.push_front(fireRocket);
      return;
    }

  }


  //  are there enemy eggs nearby?
  const int EGG_RANGE = 2;
  if (m_health > 24)
  {
    for (int x = m_tileX - EGG_RANGE; x <= m_tileX + EGG_RANGE; ++x)
    {
      if (x < 0 || x >= Map::MAP_WIDTH)
      {
        continue;
      }
      for (int y = m_tileY - EGG_RANGE; y <= m_tileY + EGG_RANGE; ++y)
      {
        if (y < 0 || y >= Map::MAP_HEIGHT)
        {
          continue;
        }

        if (x == m_tileX && y == m_tileY)
        {
          continue;
        }

        vector<EntityInfo> info = AIGameView::getInstance()->getEntityInfoAtLocation(x, y);
        vector<EntityInfo>::iterator infoIt;
        for (infoIt = info.begin(); infoIt != info.end(); ++infoIt)
        {
          if (infoIt->team != m_team && infoIt->groupType == EGG_GROUP
              && infoIt->type != EGG1 )
          {
            if (nearest.id == -1)
            {
              // set as found nearest egg entity
              nearest = *infoIt;
              targetX = x;
              targetY = y;
            }
            else
            {
              // new nearest?
              int oldDistance, newDistance;
              oldDistance = abs(targetX - m_tileX) + abs(targetY - m_tileY);
              newDistance = abs(x - m_tileX) + abs(y - m_tileY);
              if (newDistance < oldDistance)
              {
                // replace old found entity
                nearest = *infoIt;
                targetX = x;
                targetY = y;
              }
            }
            break;
          }


        }


      }
    }
  }

  if (nearest.id != -1)
  {
    // we've found a nearby enemy egg

    // check for nearby friendly egg thieves
    bool nearbyThief = false;
    for (int x = targetX - EGG_RANGE; x <= targetX + EGG_RANGE; ++x)
    {
      if (x < 0 || x >= Map::MAP_WIDTH)
      {
        continue;
      }
      for (int y = targetY - EGG_RANGE; y <= targetY + EGG_RANGE; ++y)
      {
        if (y < 0 || y >= Map::MAP_HEIGHT)
        {
          continue;
        }

        if (x == m_tileX && y == m_tileY)
        {
          continue;
        }

        vector<EntityInfo> info = AIGameView::getInstance()->getEntityInfoAtLocation(x, y);
        vector<EntityInfo>::iterator infoIt;
        for (infoIt = info.begin(); infoIt != info.end(); ++infoIt)
        {
          if (infoIt->team == m_team && infoIt->type == EGG_THIEF)
          {
            nearbyThief = true;
            break;
          }
        }

        if (nearbyThief)
        {
          break;
        }
      }
      if (nearbyThief)
      {
        break;
      }
    }


    if ( safeLocationToBomb(targetX, targetY) && !nearbyThief )
    {
      // check possible bombing locations
      coord locations[4];

      for (int i = 0; i < 4; ++i)
      {
        int x, y;
        x = targetX;
        y = targetY;
        switch (i)
        {
          case 0:
            x++;
            break;
          case 1:
            x--;
            break;
          case 2:
            y++;
            break;
          case 3:
            y--;
            break;
        }

        if ( Map::getInstance()->staticTileAt(x, y) == Map::EMPTY
             && safeLocationToBomb(x, y) )
        {
          locations[i].first = x;
          locations[i].second = y;
        }
        else
        {
          locations[i].first = -1;
          locations[i].second = -1;
        }

      } // end for 0..3

      // which location is nearest?
      int nearestLocation = -1;
      for (int i = 0; i <= 3; ++i)
      {
        if (locations[i].first != -1 &&
            safeLocationToBomb(locations[i].first, locations[i].second) )
        {
          // this is a valid location
          if (nearestLocation == -1)
          {
            nearestLocation = i;
          }
          else
          {
            int oldDistance, newDistance;
            oldDistance = abs(locations[nearestLocation].first - m_tileX)
                          + abs(locations[nearestLocation].second - m_tileY);
            newDistance = abs(locations[i].first - m_tileX)
                          + abs(locations[i].second - m_tileY);

            if (newDistance < oldDistance)
            {
              nearestLocation = i;
            }
          }
        }
      }

      if (nearestLocation != -1)
      {
        // we've got a target and a position! :D
        clearGoalsAndInstructions();

        AIInstruction * goTo, * placeBomb, * runAway, * pause;

        goTo = new AIInstruction(
          AIInstruction::GO_TO, 0, getType(),
          locations[nearestLocation].first,
          locations[nearestLocation].second);

        placeBomb = new AIInstruction(
          AIInstruction::CREATE_ENTITY, 1, BOMB,
          locations[nearestLocation].first,
          locations[nearestLocation].second);

        runAway = new AIInstruction(
          AIInstruction::MOVE_FROM_LOCATION, 3,
          getType(), targetX, targetY);

        pause = new AIInstruction(
          AIInstruction::WAIT, 10, getType(), 0, 0);

        m_insList.push_front(pause);
        m_insList.push_front(runAway);
        m_insList.push_front(placeBomb);
        m_insList.push_front(goTo);

        m_checkTimer = 97;

        return;
      }
    }

  }


  // if we're pretty alone, maybe worth placing a mine




}