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 }
void CmdRemoveTile_c::undo() { m_map->addTile( getTileX(), getTileY(), getTileType() ); }
void CmdRemoveTile_c::redo() { m_map->removeTile( getTileX(), getTileY() ); }
void CmdAddTile_c::undo() { m_map->removeTile( getTileX(), getTileY() ); }
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 }
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 }