Being *BeingManager::findNearestLivingBeing(int x, int y, int maxTileDist, Being::Type type) const { Being *closestBeing = 0; int dist = 0; const int maxDist = maxTileDist * 32; Beings::const_iterator itr = mBeings.begin(); Beings::const_iterator itr_end = mBeings.end(); for (; itr != itr_end; ++itr) { Being *being = (*itr); const Vector &pos = being->getPosition(); int d = abs(((int) pos.x) - x) + abs(((int) pos.y) - y); if ((being->getType() == type || type == Being::UNKNOWN) && (d < dist || !closestBeing) // it is closer && being->isAlive()) // no dead beings { dist = d; closestBeing = being; } } return (maxDist >= dist) ? closestBeing : 0; }
void MapComposite::update() { // Update object status const std::vector< Entity * > &entities = getEverything(); for (std::vector< Entity * >::const_iterator it = entities.begin(), it_end = entities.end(); it != it_end; ++it) { (*it)->update(); } if (mUpdateCallback.isValid()) { Script *s = ScriptManager::currentState(); s->setMap(this); s->prepare(mUpdateCallback); s->push(mID); s->execute(); } // Move objects around and update zones. for (BeingIterator it(getWholeMapIterator()); it; ++it) { (*it)->move(); } for (int i = 0; i < mContent->mapHeight * mContent->mapWidth; ++i) { mContent->zones[i].destinations.clear(); } // Cannot use a WholeMap iterator as objects will change zones under its feet. for (std::vector< Entity * >::iterator i = mContent->entities.begin(), i_end = mContent->entities.end(); i != i_end; ++i) { if (!(*i)->canMove()) continue; Being *obj = static_cast< Being * >(*i); const Point &pos1 = obj->getOldPosition(), &pos2 = obj->getPosition(); MapZone &src = mContent->getZone(pos1), &dst = mContent->getZone(pos2); if (&src != &dst) { addZone(src.destinations, &dst - mContent->zones); src.remove(obj); dst.insert(obj); } } }
static Being *findBeingNear(Actor *p, int id) { MapComposite *map = p->getMap(); const Point &ppos = p->getPosition(); // See map.h for tiles constants const int pixelDist = DEFAULT_TILE_LENGTH * TILES_TO_BE_NEAR; for (BeingIterator i(map->getAroundPointIterator(ppos, pixelDist)); i; ++i) { Being *b = *i; if (b->getPublicID() != id) continue; return ppos.inRangeOf(b->getPosition(), pixelDist) ? b : 0; } return 0; }
bool operator() (ActorSprite *actor) { if (actor->getType() == ActorSprite::FLOOR_ITEM) return false; Game *game = Game::instance(); if (!game) return false; Being* b = static_cast<Being*>(actor); uint16_t other_y = y + ((b->getType() == ActorSprite::NPC) ? 1 : 0); const Vector &pos = b->getPosition(); return ((int) pos.x / game->getCurrentTileWidth() == x && ((int) pos.y / game->getCurrentTileHeight() == y || (int) pos.y / game->getCurrentTileHeight() == other_y) && b->isAlive() && (type == ActorSprite::UNKNOWN || b->getType() == type)); }
/** * Informs a player of what happened around the character. */ static void informPlayer(MapComposite *map, Character *p) { MessageOut moveMsg(GPMSG_BEINGS_MOVE); MessageOut damageMsg(GPMSG_BEINGS_DAMAGE); const Point &pold = p->getOldPosition(), ppos = p->getPosition(); int pid = p->getPublicID(), pflags = p->getUpdateFlags(); int visualRange = Configuration::getValue("game_visualRange", 448); // Inform client about activities of other beings near its character for (BeingIterator it(map->getAroundBeingIterator(p, visualRange)); it; ++it) { Being *o = *it; const Point &oold = o->getOldPosition(), opos = o->getPosition(); int otype = o->getType(); int oid = o->getPublicID(), oflags = o->getUpdateFlags(); int flags = 0; // Check if the character p and the moving object o are around. bool wereInRange = pold.inRangeOf(oold, visualRange) && !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP); bool willBeInRange = ppos.inRangeOf(opos, visualRange); if (!wereInRange && !willBeInRange) { // Nothing to report: o and p are far away from each other. continue; } if (wereInRange && willBeInRange) { // Send attack messages. if ((oflags & UPDATEFLAG_ATTACK) && oid != pid) { MessageOut AttackMsg(GPMSG_BEING_ATTACK); AttackMsg.writeInt16(oid); AttackMsg.writeInt8(o->getDirection()); AttackMsg.writeInt8(static_cast< Being * >(o)->getAttackId()); gameHandler->sendTo(p, AttackMsg); } // Send action change messages. if ((oflags & UPDATEFLAG_ACTIONCHANGE)) { MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE); ActionMsg.writeInt16(oid); ActionMsg.writeInt8(static_cast< Being * >(o)->getAction()); gameHandler->sendTo(p, ActionMsg); } // Send looks change messages. if (oflags & UPDATEFLAG_LOOKSCHANGE) { MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE); LooksMsg.writeInt16(oid); Character * c = static_cast<Character * >(o); serializeLooks(c, LooksMsg); LooksMsg.writeInt16(c->getHairStyle()); LooksMsg.writeInt16(c->getHairColor()); LooksMsg.writeInt16(c->getGender()); gameHandler->sendTo(p, LooksMsg); } // Send emote messages. if (oflags & UPDATEFLAG_EMOTE) { int emoteId = o->getLastEmote(); if (emoteId > -1) { MessageOut EmoteMsg(GPMSG_BEING_EMOTE); EmoteMsg.writeInt16(oid); EmoteMsg.writeInt16(emoteId); gameHandler->sendTo(p, EmoteMsg); } } // Send direction change messages. if (oflags & UPDATEFLAG_DIRCHANGE) { MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE); DirMsg.writeInt16(oid); DirMsg.writeInt8(o->getDirection()); gameHandler->sendTo(p, DirMsg); } // Send damage messages. if (o->canFight()) { Being *victim = static_cast< Being * >(o); const Hits &hits = victim->getHitsTaken(); for (Hits::const_iterator j = hits.begin(), j_end = hits.end(); j != j_end; ++j) { damageMsg.writeInt16(oid); damageMsg.writeInt16(*j); } } if (oold == opos) { // o does not move, nothing more to report. continue; } } if (!willBeInRange) { // o is no longer visible from p. Send leave message. MessageOut leaveMsg(GPMSG_BEING_LEAVE); leaveMsg.writeInt16(oid); gameHandler->sendTo(p, leaveMsg); continue; } if (!wereInRange) { // o is now visible by p. Send enter message. MessageOut enterMsg(GPMSG_BEING_ENTER); enterMsg.writeInt8(otype); enterMsg.writeInt16(oid); enterMsg.writeInt8(static_cast< Being *>(o)->getAction()); enterMsg.writeInt16(opos.x); enterMsg.writeInt16(opos.y); enterMsg.writeInt8(o->getDirection()); enterMsg.writeInt8(o->getGender()); switch (otype) { case OBJECT_CHARACTER: { Character *q = static_cast< Character * >(o); enterMsg.writeString(q->getName()); enterMsg.writeInt8(q->getHairStyle()); enterMsg.writeInt8(q->getHairColor()); serializeLooks(q, enterMsg); } break; case OBJECT_MONSTER: { Monster *q = static_cast< Monster * >(o); enterMsg.writeInt16(q->getSpecy()->getId()); enterMsg.writeString(q->getName()); } break; case OBJECT_NPC: { NpcComponent *npcComponent = o->getComponent<NpcComponent>(); enterMsg.writeInt16(npcComponent->getNpcId()); enterMsg.writeString(o->getName()); } break; default: assert(false); // TODO break; } gameHandler->sendTo(p, enterMsg); } if (opos != oold) { // Add position check coords every 5 seconds. if (currentTick % 50 == 0) flags |= MOVING_POSITION; flags |= MOVING_DESTINATION; } // Send move messages. moveMsg.writeInt16(oid); moveMsg.writeInt8(flags); if (flags & MOVING_POSITION) { moveMsg.writeInt16(oold.x); moveMsg.writeInt16(oold.y); } if (flags & MOVING_DESTINATION) { moveMsg.writeInt16(opos.x); moveMsg.writeInt16(opos.y); // We multiply the sent speed (in tiles per second) by ten // to get it within a byte with decimal precision. // For instance, a value of 4.5 will be sent as 45. moveMsg.writeInt8((unsigned short) (o->getModifiedAttribute(ATTR_MOVE_SPEED_TPS) * 10)); } } // Do not send a packet if nothing happened in p's range. if (moveMsg.getLength() > 2) gameHandler->sendTo(p, moveMsg); if (damageMsg.getLength() > 2) gameHandler->sendTo(p, damageMsg); // Inform client about status change. p->sendStatus(); // Inform client about health change of party members for (CharacterIterator i(map->getWholeMapIterator()); i; ++i) { Character *c = *i; // Make sure its not the same character if (c == p) continue; // make sure they are in the same party if (c->getParty() == p->getParty()) { int cflags = c->getUpdateFlags(); if (cflags & UPDATEFLAG_HEALTHCHANGE) { MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE); healthMsg.writeInt16(c->getPublicID()); healthMsg.writeInt16(c->getModifiedAttribute(ATTR_HP)); healthMsg.writeInt16(c->getModifiedAttribute(ATTR_MAX_HP)); gameHandler->sendTo(p, healthMsg); } } } // Inform client about items on the ground around its character MessageOut itemMsg(GPMSG_ITEMS); for (FixedActorIterator it(map->getAroundBeingIterator(p, visualRange)); it; ++it) { Actor *o = *it; assert(o->getType() == OBJECT_ITEM || o->getType() == OBJECT_EFFECT); Point opos = o->getPosition(); int oflags = o->getUpdateFlags(); bool willBeInRange = ppos.inRangeOf(opos, visualRange); bool wereInRange = pold.inRangeOf(opos, visualRange) && !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP); if (willBeInRange ^ wereInRange) { switch (o->getType()) { case OBJECT_ITEM: { ItemComponent *item = o->getComponent<ItemComponent>(); ItemClass *itemClass = item->getItemClass(); if (oflags & UPDATEFLAG_NEW_ON_MAP) { /* Send a specific message to the client when an item appears out of nowhere, so that a sound/animation can be performed. */ MessageOut appearMsg(GPMSG_ITEM_APPEAR); appearMsg.writeInt16(itemClass->getDatabaseID()); appearMsg.writeInt16(opos.x); appearMsg.writeInt16(opos.y); gameHandler->sendTo(p, appearMsg); } else { itemMsg.writeInt16(willBeInRange ? itemClass->getDatabaseID() : 0); itemMsg.writeInt16(opos.x); itemMsg.writeInt16(opos.y); } } break; case OBJECT_EFFECT: { EffectComponent *e = o->getComponent<EffectComponent>(); e->setShown(); // Don't show old effects if (!(oflags & UPDATEFLAG_NEW_ON_MAP)) break; if (Being *b = e->getBeing()) { MessageOut effectMsg(GPMSG_CREATE_EFFECT_BEING); effectMsg.writeInt16(e->getEffectId()); effectMsg.writeInt16(b->getPublicID()); gameHandler->sendTo(p, effectMsg); } else { MessageOut effectMsg(GPMSG_CREATE_EFFECT_POS); effectMsg.writeInt16(e->getEffectId()); effectMsg.writeInt16(opos.x); effectMsg.writeInt16(opos.y); gameHandler->sendTo(p, effectMsg); } } break; default: break; } // Switch } } // Do not send a packet if nothing happened in p's range. if (itemMsg.getLength() > 2) gameHandler->sendTo(p, itemMsg); }
void Monster::refreshTarget() { // Check potential attack positions Being *bestAttackTarget = mTarget = NULL; int bestTargetPriority = 0; Point bestAttackPosition; BeingDirection bestAttackDirection = DOWN; // Iterate through objects nearby int aroundArea = Configuration::getValue("game_visualRange", 448); for (BeingIterator i(getMap()->getAroundBeingIterator(this, aroundArea)); i; ++i) { // We only want to attack player characters if ((*i)->getType() != OBJECT_CHARACTER) continue; Being *target = static_cast<Being *>(*i); // Dead characters are ignored if (target->getAction() == DEAD) continue; // Determine how much we hate the target int targetPriority = 0; std::map<Being *, int, std::greater<Being *> >::iterator angerIterator; angerIterator = mAnger.find(target); if (angerIterator != mAnger.end()) { targetPriority = angerIterator->second; } else if (mSpecy->isAggressive()) { targetPriority = 1; } else { continue; } // Check all attack positions for (std::list<AttackPosition>::iterator j = mAttackPositions.begin(); j != mAttackPositions.end(); j++) { Point attackPosition = target->getPosition(); attackPosition.x += j->x; attackPosition.y += j->y; int posPriority = calculatePositionPriority(attackPosition, targetPriority); if (posPriority > bestTargetPriority) { bestAttackTarget = mTarget = target; bestTargetPriority = posPriority; bestAttackPosition = attackPosition; bestAttackDirection = j->direction; } } } // Check if an enemy has been found if (bestAttackTarget) { // Check which attacks have a chance to hit the target MonsterAttacks allAttacks = mSpecy->getAttacks(); std::map<int, MonsterAttack *> workingAttacks; int prioritySum = 0; const int distX = getPosition().x - bestAttackTarget->getPosition().x; const int distY = getPosition().y - bestAttackTarget->getPosition().y; const int distSquare = (distX * distX + distY * distY); for (MonsterAttacks::iterator i = allAttacks.begin(); i != allAttacks.end(); i++) { int maxDist = (*i)->range + bestAttackTarget->getSize(); if (maxDist * maxDist >= distSquare) { prioritySum += (*i)->priority; workingAttacks[prioritySum] = (*i); } } if (workingAttacks.empty() || !prioritySum) { //when no attack can hit move closer to attack position setDestination(bestAttackPosition); } else { // Prepare for using a random attack which can hit the enemy // Stop movement setDestination(getPosition()); // Turn into direction of enemy setDirection(bestAttackDirection); // Perform a random attack based on priority mCurrentAttack = workingAttacks.upper_bound(rand()%prioritySum)->second; setAction(ATTACK); raiseUpdateFlags(UPDATEFLAG_ATTACK); } } }
void handle_mouse(Event *evt) { if (!mapEngine->mapLoaded()) return; Point camPos = graphicsEngine->getCamera()->getPosition(); Point pos; pos.x = evt->x + camPos.x; pos.y = evt->y + camPos.y + mapEngine->getTileHeight(); // left mouse button has finished being pressed if (evt->button == SDL_BUTTON_LEFT && evt->type == 1) { // check user clicked on map Node *node = graphicsEngine->getNode(pos.x, pos.y); if (node) { // show name if player/NPC is clicked Being *being = beingManager->findBeing(node->getName()); if (being) { // toggle being name being->toggleName(); if (being->isNPC() && withinReach(being->getPosition(), player->getSelectedCharacter()->getPosition()) && !being->isTalking()) { Packet *p = new Packet(PGMSG_NPC_START_TALK); p->setInteger(being->getId()); networkManager->sendPacket(p); } return; } Point pt = mapEngine->convertPixelToTile(pos.x, pos.y); if (mapEngine->blocked(pt)) return; // send move message Packet *p = new Packet(PGMSG_PLAYER_MOVE); p->setInteger(pt.x); p->setInteger(pt.y); networkManager->sendPacket(p); //logger->logDebug("Sending move request"); // save destination for later player->getSelectedCharacter()->saveDestination(pt); } } if (evt->button == 0) { if (!interfaceManager->getMouse()->cursor) return; // pos.x += mapEngine->getTileWidth() >> 1; int mapWidth = mapEngine->getWidth() * mapEngine->getTileWidth(); int mapHeight = mapEngine->getHeight() * mapEngine->getTileHeight(); if (pos.x > mapWidth || -pos.x > mapWidth) return; if (pos.y > mapHeight || pos.y < 0) return; Point tilePos = mapEngine->convertPixelToTile(pos.x, pos.y); if (tilePos.x < 0 || tilePos.y < 0) return; if (tilePos.x == interfaceManager->getMouse()->cursorPos.x && tilePos.y == interfaceManager->getMouse()->cursorPos.y) return; interfaceManager->getMouse()->cursorPos = tilePos; Point screenPos; screenPos.x = 0.5 * (tilePos.x - tilePos.y) * mapEngine->getTileWidth(); screenPos.y = 0.5 * (tilePos.x + tilePos.y) * mapEngine->getTileHeight(); interfaceManager->getMouse()->cursor->moveNode(&screenPos); } }
void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) { while (msg.getUnreadLength()) { int id = msg.readInt16(); int flags = msg.readInt8(); Being *being = actorSpriteManager->findBeing(id); int sx = 0, sy = 0, dx = 0, dy = 0, speed = 0; if ((!flags & (MOVING_POSITION | MOVING_DESTINATION))) continue; if (flags & MOVING_POSITION) { sx = msg.readInt16(); sy = msg.readInt16(); } if (flags & MOVING_DESTINATION) { dx = msg.readInt16(); dy = msg.readInt16(); speed = msg.readInt8(); } if (!being) continue; if (speed) { /* * The being's speed is transfered in tiles per second * 10 * to keep it transferable in a Byte. * We set it back to tiles per second and in a float. */ float speedTilesSeconds = (float) speed / 10; being->setMoveSpeed(Vector(speedTilesSeconds, speedTilesSeconds, 0)); } // Ignore messages from the server for the local player if (being == player_node) continue; // If the position differs too much from the actual one, we resync // the being position if (flags & MOVING_POSITION) { if (!being->getMap()->containsPixel(sx, sy)) { logger->log("Warning: Received GPMSG_BEINGS_MOVE for being id " "%i with position outside the map boundaries " "(x = %i, y = %i)", id, sx, sy); continue; } Vector serverPos(sx, sy); if (serverPos.length() - being->getPosition().length() > POSITION_DIFF_TOLERANCE) being->setPosition(serverPos); } if (flags & MOVING_DESTINATION) { if (!being->getMap()->containsPixel(dx, dy)) { logger->log("Warning: Received GPMSG_BEINGS_MOVE for being id " "%i with destination outside the map boundaries " "(x = %i, y = %i)", id, dx, dy); continue; } being->setDestination(dx, dy); } } }
void Viewport::_drawDebugPath(Graphics *graphics) { if (mDebugFlags & Map::MAP_MOUSE_PATH) { // Get the current mouse position SDL_GetMouseState(&mMouseX, &mMouseY); // Prepare the walkmask corresponding to the protocol unsigned char walkMask; switch (Net::getNetworkType()) { case ServerInfo::TMWATHENA: walkMask = Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER; break; case ServerInfo::MANASERV: default: walkMask = Map::BLOCKMASK_WALL; break; } static Path debugPath; static Vector lastMouseDestination = Vector(0.0f, 0.0f); Vector mouseDestination(mMouseX + (int) mPixelViewX, mMouseY + (int) mPixelViewY); if (mouseDestination.x != lastMouseDestination.x || mouseDestination.y != lastMouseDestination.y) { const Vector &playerPos = player_node->getPosition(); // Adapt the path finding to the precision requested if (Net::getPlayerHandler()->usePixelPrecision()) { debugPath = mMap->findPixelPath((int) playerPos.x, (int) playerPos.y, mouseDestination.x, mouseDestination.y, player_node->getCollisionRadius(), walkMask); } else { debugPath = mMap->findTilePath((int) playerPos.x, (int) playerPos.y, mouseDestination.x, mouseDestination.y, walkMask); } lastMouseDestination = mouseDestination; } _drawPath(graphics, debugPath, gcn::Color(128, 0, 128, 150)); } // Draw the path debug information for every beings. ActorSpritesConstIterator it, it_end; const ActorSprites &actors = actorSpriteManager->getAll(); for (it = actors.begin(), it_end = actors.end() ; it != it_end; it++) { Being *being = dynamic_cast<Being*>(*it); if (!being) continue; const Vector &beingPos = being->getPosition(); graphics->setColor(gcn::Color(128, 128, 0, 150)); if (mDebugFlags & Map::MAP_BEING_COLLISION_RADIUS) { const int radius = being->getCollisionRadius(); graphics->fillRectangle(gcn::Rectangle( (int) beingPos.x - (int) mPixelViewX - radius, (int) beingPos.y - (int) mPixelViewY - radius, radius * 2, radius * 2)); } if (mDebugFlags & Map::MAP_BEING_PATH) _drawPath(graphics, being->getPath(), gcn::Color(0, 0, 255, 150)); if (mDebugFlags & Map::MAP_BEING_POSITION) { // Draw the absolute x, y position using a cross. graphics->setColor(gcn::Color(0, 0, 255, 255)); graphics->drawLine((int) beingPos.x - (int) mPixelViewX - 4, (int) beingPos.y - (int) mPixelViewY - 4, (int) beingPos.x - (int) mPixelViewX + 4, (int) beingPos.y - (int) mPixelViewY + 4); graphics->drawLine((int) beingPos.x - (int) mPixelViewX + 4, (int) beingPos.y - (int) mPixelViewY - 4, (int) beingPos.x - (int) mPixelViewX - 4, (int) beingPos.y - (int) mPixelViewY + 4); } } }