diff options
author | Andrei Karas <akaras@inbox.ru> | 2011-01-02 01:48:38 +0200 |
---|---|---|
committer | Andrei Karas <akaras@inbox.ru> | 2011-01-02 02:41:24 +0200 |
commit | 3eeae12c498d1a4dbe969462d2ba841f77ee3ccb (patch) | |
tree | ff8eab35e732bc0749fc11677c8873a7b3a58704 /src/gui/viewport.cpp | |
download | manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.gz manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.bz2 manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.tar.xz manaplus-3eeae12c498d1a4dbe969462d2ba841f77ee3ccb.zip |
Initial commit.
This code based on mana client http://www.gitorious.org/mana/mana
and my private repository.
Diffstat (limited to 'src/gui/viewport.cpp')
-rw-r--r-- | src/gui/viewport.cpp | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp new file mode 100644 index 000000000..e2bab0621 --- /dev/null +++ b/src/gui/viewport.cpp @@ -0,0 +1,763 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "gui/viewport.h" + +#include "actorsprite.h" +#include "actorspritemanager.h" +#include "client.h" +#include "configuration.h" +#include "graphics.h" +#include "itemshortcut.h" +#include "keyboardconfig.h" +#include "localplayer.h" +#include "map.h" +#include "textmanager.h" + +#include "gui/beingpopup.h" +#include "gui/chat.h" +#include "gui/gui.h" +#include "gui/ministatus.h" +#include "gui/popupmenu.h" +#include "gui/statuspopup.h" +#include "gui/textpopup.h" + +#include "gui/widgets/button.h" +#include "gui/widgets/chattab.h" + +#include "net/net.h" + +#include "resources/resourcemanager.h" + +#include "utils/stringutils.h" + +extern volatile int tick_time; + +Viewport::Viewport(): + mMap(0), + mMouseX(0), + mMouseY(0), + mPixelViewX(0.0f), + mPixelViewY(0.0f), +// mTileViewX(0), +// mTileViewY(0), + mShowDebugPath(false), + mCameraMode(0), + mPlayerFollowMouse(false), + mLocalWalkTime(-1), + mHoverBeing(0), + mHoverItem(0), + mHoverSign(0), + mCameraRelativeX(0), + mCameraRelativeY(0) +{ + setOpaque(false); + addMouseListener(this); + + mScrollLaziness = config.getIntValue("ScrollLaziness"); + mScrollRadius = config.getIntValue("ScrollRadius"); + mScrollCenterOffsetX = config.getIntValue("ScrollCenterOffsetX"); + mScrollCenterOffsetY = config.getIntValue("ScrollCenterOffsetY"); + + config.addListener("ScrollLaziness", this); + config.addListener("ScrollRadius", this); + + mPopupMenu = new PopupMenu; + mBeingPopup = new BeingPopup; + mTextPopup = new TextPopup; + + setFocusable(true); +} + +Viewport::~Viewport() +{ + config.removeListener("ScrollLaziness", this); + config.removeListener("ScrollRadius", this); + + delete mPopupMenu; + mPopupMenu = 0; + delete mBeingPopup; + mBeingPopup = 0; + delete mTextPopup; + mTextPopup = 0; +} + +void Viewport::setMap(Map *map) +{ + if (mMap && map) + map->setDebugFlags(mMap->getDebugFlags()); + mMap = map; +} + +extern MiniStatusWindow *miniStatusWindow; + +void Viewport::draw(gcn::Graphics *gcnGraphics) +{ + static int lastTick = tick_time; + + if (!mMap || !player_node) + { + gcnGraphics->setColor(gcn::Color(64, 64, 64)); + gcnGraphics->fillRectangle( + gcn::Rectangle(0, 0, getWidth(), getHeight())); + return; + } + + Graphics *graphics = static_cast<Graphics*>(gcnGraphics); + + // Avoid freaking out when tick_time overflows + if (tick_time < lastTick) + lastTick = tick_time; + + // Calculate viewpoint + int midTileX = (graphics->getWidth() + mScrollCenterOffsetX) / 2; + int midTileY = (graphics->getHeight() + mScrollCenterOffsetX) / 2; + + const Vector &playerPos = player_node->getPosition(); + const int player_x = static_cast<int>(playerPos.x) + - midTileX + mCameraRelativeX; + const int player_y = static_cast<int>(playerPos.y) + - midTileY + mCameraRelativeY; + + if (mScrollLaziness < 1) + mScrollLaziness = 1; // Avoids division by zero + + // Apply lazy scrolling + while (lastTick < tick_time) + { + if (player_x > static_cast<int>(mPixelViewX) + mScrollRadius) + { + mPixelViewX += static_cast<float>(player_x + - static_cast<int>(mPixelViewX) - mScrollRadius) / + static_cast<float>(mScrollLaziness); + } + if (player_x < static_cast<int>(mPixelViewX) - mScrollRadius) + { + mPixelViewX += static_cast<float>(player_x + - static_cast<int>(mPixelViewX) + mScrollRadius) / + static_cast<float>(mScrollLaziness); + } + if (player_y > static_cast<int>(mPixelViewY) + mScrollRadius) + { + mPixelViewY += static_cast<float>(player_y + - static_cast<int>(mPixelViewY) - mScrollRadius) / + static_cast<float>(mScrollLaziness); + } + if (player_y < static_cast<int>(mPixelViewY) - mScrollRadius) + { + mPixelViewY += static_cast<float>(player_y + - static_cast<int>(mPixelViewY) + mScrollRadius) / + static_cast<float>(mScrollLaziness); + } + lastTick++; + } + + // Auto center when player is off screen + if (player_x - static_cast<int>(mPixelViewX) > graphics->getWidth() / 2 + || static_cast<int>(mPixelViewX) - player_x > graphics->getWidth() / 2 + || static_cast<int>(mPixelViewY) - player_y + > graphics->getHeight() / 2 + || player_y - static_cast<int>(mPixelViewY) + > graphics->getHeight() / 2) + { + mPixelViewX = static_cast<float>(player_x); + mPixelViewY = static_cast<float>(player_y); + }; + + // Don't move camera so that the end of the map is on screen + const int viewXmax = + mMap->getWidth() * mMap->getTileWidth() - graphics->getWidth(); + const int viewYmax = + mMap->getHeight() * mMap->getTileHeight() - graphics->getHeight(); + if (mMap) + { + if (mPixelViewX < 0) + mPixelViewX = 0; + if (mPixelViewY < 0) + mPixelViewY = 0; + if (mPixelViewX > viewXmax) + mPixelViewX = static_cast<float>(viewXmax); + if (mPixelViewY > viewYmax) + mPixelViewY = static_cast<float>(viewYmax); + } + + // Draw tiles and sprites + if (mMap) + { + mMap->draw(graphics, static_cast<int>(mPixelViewX), + static_cast<int>(mPixelViewY)); + + if (mShowDebugPath) + { + mMap->drawCollision(graphics, + static_cast<int>(mPixelViewX), + static_cast<int>(mPixelViewY), + mShowDebugPath); + if (mShowDebugPath == Map::MAP_DEBUG) + _drawDebugPath(graphics); + } + } + + if (player_node->getCheckNameSetting()) + { + player_node->setCheckNameSetting(false); + player_node->setName(player_node->getName()); + } + + // Draw text + if (textManager) + textManager->draw(graphics, static_cast<int>(mPixelViewX), + static_cast<int>(mPixelViewY)); + + // Draw player names, speech, and emotion sprite as needed + const ActorSprites &actors = actorSpriteManager->getAll(); + for (ActorSpritesConstIterator it = actors.begin(), it_end = actors.end(); + it != it_end; it++) + { + if ((*it)->getType() == ActorSprite::FLOOR_ITEM) + continue; + Being *b = static_cast<Being*>(*it); + + b->drawSpeech(static_cast<int>(mPixelViewX), + static_cast<int>(mPixelViewY)); + b->drawEmotion(graphics, static_cast<int>(mPixelViewX), + static_cast<int>(mPixelViewY)); + } + + if (miniStatusWindow) + miniStatusWindow->drawIcons(graphics); + + // Draw contained widgets + WindowContainer::draw(gcnGraphics); +} + +void Viewport::logic() +{ + WindowContainer::logic(); + + // Make the player follow the mouse position + // if the mouse is dragged elsewhere than in a window. + _followMouse(); +} + +void Viewport::_followMouse() +{ + Uint8 button = SDL_GetMouseState(&mMouseX, &mMouseY); + // If the left button is dragged + if (mPlayerFollowMouse && button & SDL_BUTTON(1)) + { + // We create a mouse event and send it to mouseDragged. + Uint8 *keys = SDL_GetKeyState(NULL); + gcn::MouseEvent mouseEvent(NULL, + (keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT]), + false, + false, + false, + gcn::MouseEvent::DRAGGED, + gcn::MouseEvent::LEFT, + mMouseX, + mMouseY, + 0); + + mouseDragged(mouseEvent); + } +} + +void Viewport::_drawDebugPath(Graphics *graphics) +{ + // Get the current mouse position + SDL_GetMouseState(&mMouseX, &mMouseY); + + Path debugPath; + + if (Net::getNetworkType() == ServerInfo::TMWATHENA) + { + const int mouseTileX = (mMouseX + static_cast<int>(mPixelViewX)) / 32; + const int mouseTileY = (mMouseY + static_cast<int>(mPixelViewY)) / 32; + const Vector &playerPos = player_node->getPosition(); + + debugPath = mMap->findPath( + static_cast<int>(playerPos.x - 16) / 32, + static_cast<int>(playerPos.y - 32) / 32, + mouseTileX, mouseTileY, 0, 500); + + _drawPath(graphics, debugPath); + } + else if (Net::getNetworkType() == ServerInfo::MANASERV) + { + const Vector &playerPos = player_node->getPosition(); + const int playerRadius = player_node->getCollisionRadius(); + // Draw player collision rectangle + graphics->setColor(gcn::Color(128, 128, 0, 120)); + graphics->fillRectangle( + gcn::Rectangle(static_cast<int>(playerPos.x) + - static_cast<int>(mPixelViewX) - playerRadius, + static_cast<int>(playerPos.y) + - static_cast<int>(mPixelViewY) - playerRadius, + playerRadius * 2, playerRadius * 2)); + + debugPath = mMap->findPixelPath( + static_cast<int>(playerPos.x), + static_cast<int>(playerPos.y), + mMouseX + static_cast<int>(mPixelViewX), + mMouseY + static_cast<int>(mPixelViewY), + playerRadius, 0xFF); + + // We draw the path proposed by mouse + _drawPath(graphics, debugPath, gcn::Color(128, 0, 128)); + + // But also the one currently walked on. + _drawPath(graphics, player_node->getPath(), gcn::Color(0, 0, 255)); + } +} + +void Viewport::_drawPath(Graphics *graphics, const Path &path, + gcn::Color color) +{ + graphics->setColor(color); + + if (Net::getNetworkType() == ServerInfo::TMWATHENA) + { + for (Path::const_iterator i = path.begin(); i != path.end(); ++i) + { + int squareX = i->x * 32 - static_cast<int>(mPixelViewX) + 12; + int squareY = i->y * 32 - static_cast<int>(mPixelViewY) + 12; + + graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); + if (mMap) + { + graphics->drawText( + toString(mMap->getMetaTile(i->x, i->y)->Gcost), + squareX + 4, squareY + 12, gcn::Graphics::CENTER); + } + } + } + else if (Net::getNetworkType() == ServerInfo::MANASERV) + { + for (Path::const_iterator i = path.begin(); i != path.end(); ++i) + { + int squareX = i->x - static_cast<int>(mPixelViewX); + int squareY = i->y - static_cast<int>(mPixelViewY); + + graphics->fillRectangle(gcn::Rectangle(squareX - 4, squareY - 4, + 8, 8)); + if (mMap) + { + graphics->drawText( + toString(mMap->getMetaTile(i->x / 32, i->y / 32)->Gcost), + squareX + 4, squareY + 12, gcn::Graphics::CENTER); + } + } + + } +} + +void Viewport::mousePressed(gcn::MouseEvent &event) +{ + if (event.getSource() != this) + return; + + // Check if we are alive and kickin' +// if (!mMap || !player_node || !player_node->isAlive()) + if (!mMap || !player_node) + return; + + // Check if we are busy + // if commented, allow context menu if npc dialog open + if (Being::isTalking()) + return; + + mPlayerFollowMouse = false; + + const int pixelX = event.getX() + static_cast<int>(mPixelViewX); + const int pixelY = event.getY() + static_cast<int>(mPixelViewY); + + // Right click might open a popup + if (event.getButton() == gcn::MouseEvent::RIGHT) + { + if (mHoverBeing) + { + if (actorSpriteManager) + { + std::list<Being*> beings; + const int x = getMouseX() + static_cast<int>(mPixelViewX); + const int y = getMouseY() + static_cast<int>(mPixelViewY); + actorSpriteManager->findBeingsByPixel(beings, x, y, true); + if (beings.size() > 1) + { + mPopupMenu->showPopup(event.getX(), event.getY(), beings); + return; + } + else + { + mPopupMenu->showPopup(event.getX(), event.getY(), + mHoverBeing); + return; + } + } + } + else if (mHoverItem) + { + mPopupMenu->showPopup(event.getX(), event.getY(), mHoverItem); + return; + } + else if (mHoverSign) + { + mPopupMenu->showPopup(event.getX(), event.getY(), mHoverSign); + return; + } + } + + // If a popup is active, just remove it + if (mPopupMenu->isVisible()) + { + mPopupMenu->setVisible(false); + return; + } + + // Left click can cause different actions + if (event.getButton() == gcn::MouseEvent::LEFT) + { + // Interact with some being + if (mHoverBeing) + { + if (!mHoverBeing->isAlive()) + return; + + if (mHoverBeing->canTalk()) + { + mHoverBeing->talkTo(); + } + else + { + if (mHoverBeing->getType() == ActorSprite::PLAYER) + { + if (actorSpriteManager) + actorSpriteManager->heal(player_node, mHoverBeing); + } + else if (player_node->withinAttackRange(mHoverBeing) || + keyboard.isKeyActive(keyboard.KEY_ATTACK)) + { + player_node->attack(mHoverBeing, + !keyboard.isKeyActive(keyboard.KEY_TARGET)); + } + else if (!keyboard.isKeyActive(keyboard.KEY_ATTACK)) + { + player_node->setGotoTarget(mHoverBeing); + } + } + // Picks up a item if we clicked on one + } + else if (mHoverItem) + { + player_node->pickUp(mHoverItem); + } + // Just walk around + else if (!keyboard.isKeyActive(keyboard.KEY_ATTACK)) + { + player_node->stopAttack(); + player_node->cancelFollow(); + mPlayerFollowMouse = true; + + // Make the player go to the mouse position + _followMouse(); + } + } + else if (event.getButton() == gcn::MouseEvent::MIDDLE) + { + // Find the being nearest to the clicked position + if (actorSpriteManager) + { + Being *target = actorSpriteManager->findNearestLivingBeing( + pixelX, pixelY, 20, ActorSprite::MONSTER); + + if (target) + player_node->setTarget(target); + } + } +} + +void Viewport::mouseDragged(gcn::MouseEvent &event) +{ + if (!mMap || !player_node) + return; + + if (mPlayerFollowMouse && !event.isShiftPressed()) + { + if (Net::getNetworkType() == ServerInfo::MANASERV) + { + if (get_elapsed_time(mLocalWalkTime) >= walkingMouseDelay) + { + mLocalWalkTime = tick_time; + player_node->setDestination(event.getX() + + static_cast<int>(mPixelViewX), + event.getY() + + static_cast<int>(mPixelViewY)); + player_node->pathSetByMouse(); + } + } + else + { + if (mLocalWalkTime != player_node->getActionTime()) + { + mLocalWalkTime = player_node->getActionTime(); + int destX = static_cast<int>((static_cast<float>(event.getX()) + + mPixelViewX) + / static_cast<float>(mMap->getTileWidth())); + int destY = static_cast<int>((static_cast<float>(event.getY()) + + mPixelViewY) + / static_cast<float>(mMap->getTileHeight())); + player_node->setDestination(destX, destY); + } + } + } +} + +void Viewport::mouseReleased(gcn::MouseEvent &event _UNUSED_) +{ + mPlayerFollowMouse = false; + + // Only useful for eAthena but doesn't hurt under ManaServ + mLocalWalkTime = -1; +} + +void Viewport::showPopup(Window *parent, int x, int y, Item *item, + bool isInventory) +{ + mPopupMenu->showPopup(parent, x, y, item, isInventory); +} + +void Viewport::showPopup(MapItem *item) +{ + mPopupMenu->showPopup(getMouseX(), getMouseY(), item); +} + +void Viewport::showPopup(Window *parent, Item *item, bool isInventory) +{ + mPopupMenu->showPopup(parent, getMouseX(), getMouseY(), item, isInventory); +} + +void Viewport::showItemPopup(Item *item) +{ + mPopupMenu->showItemPopup(getMouseX(), getMouseY(), item); +} + +void Viewport::showItemPopup(int itemId) +{ + mPopupMenu->showItemPopup(getMouseX(), getMouseY(), itemId); +} + +void Viewport::showDropPopup(Item *item) +{ + mPopupMenu->showDropPopup(getMouseX(), getMouseY(), item); +} + +void Viewport::showOutfitsPopup(int x, int y) +{ + mPopupMenu->showOutfitsPopup(x, y); +} + +void Viewport::showOutfitsPopup() +{ + mPopupMenu->showOutfitsPopup(getMouseX(), getMouseY()); +} + +void Viewport::showSpellPopup(TextCommand *cmd) +{ + mPopupMenu->showSpellPopup(getMouseX(), getMouseY(), cmd); +} + +void Viewport::showChatPopup(int x, int y, ChatTab *tab) +{ + mPopupMenu->showChatPopup(x, y, tab); +} + +void Viewport::showChatPopup(ChatTab *tab) +{ + mPopupMenu->showChatPopup(getMouseX(), getMouseY(), tab); +} + +void Viewport::showPopup(int x, int y, Being *being) +{ + mPopupMenu->showPopup(x, y, being); +} + +void Viewport::showPlayerPopup(std::string nick) +{ + mPopupMenu->showPlayerPopup(getMouseX(), getMouseY(), nick); +} + +void Viewport::showPopup(int x, int y, Button *button) +{ + mPopupMenu->showPopup(x, y, button); +} + +void Viewport::closePopupMenu() +{ + if (mPopupMenu) + mPopupMenu->handleLink("cancel", 0); +} + +void Viewport::optionChanged(const std::string &name _UNUSED_) +{ + mScrollLaziness = config.getIntValue("ScrollLaziness"); + mScrollRadius = config.getIntValue("ScrollRadius"); +} + +void Viewport::mouseMoved(gcn::MouseEvent &event _UNUSED_) +{ + // Check if we are on the map + if (!mMap || !player_node || !actorSpriteManager) + return; + + const int x = getMouseX() + static_cast<int>(mPixelViewX); + const int y = getMouseY() + static_cast<int>(mPixelViewY); + + mHoverBeing = actorSpriteManager->findBeingByPixel(x, y, true); + if (mHoverBeing && mHoverBeing->getType() == Being::PLAYER) + { + mTextPopup->setVisible(false); + mBeingPopup->show(getMouseX(), getMouseY(), mHoverBeing); + } + else + { + mBeingPopup->setVisible(false); + } + + mHoverItem = 0; + if (!mHoverBeing && actorSpriteManager) + { + mHoverItem = actorSpriteManager->findItem(x / mMap->getTileWidth(), + y / mMap->getTileHeight()); + } + if (!mHoverBeing && !mHoverItem) + { + SpecialLayer *specialLayer = mMap->getSpecialLayer(); + if (specialLayer) + { + int mouseTileX = (getMouseX() + getCameraX()) + / mMap->getTileWidth(); + int mouseTileY = (getMouseY() + getCameraY()) + / mMap->getTileHeight(); + + mHoverSign = specialLayer->getTile(mouseTileX, mouseTileY); + if (mHoverSign && mHoverSign->getType() != MapItem::EMPTY) + { + if (!mHoverSign->getComment().empty()) + { + if (mBeingPopup) + mBeingPopup->setVisible(false); + mTextPopup->show(getMouseX(), getMouseY(), + mHoverSign->getComment()); + } + else + { + if (mTextPopup->isVisible()) + mTextPopup->setVisible(false); + } + return; + } + } + } + if (mTextPopup->isVisible()) + mTextPopup->setVisible(false); + + if (mHoverBeing) + { + switch (mHoverBeing->getType()) + { + // NPCs + case ActorSprite::NPC: + gui->setCursorType(Gui::CURSOR_TALK); + break; + + // Monsters + case ActorSprite::MONSTER: + gui->setCursorType(Gui::CURSOR_FIGHT); + break; + default: + gui->setCursorType(Gui::CURSOR_POINTER); + break; + } + } + // Item mouseover + else if (mHoverItem) + { + gui->setCursorType(Gui::CURSOR_PICKUP); + } + else + { + gui->setCursorType(Gui::CURSOR_POINTER); + } +} + +void Viewport::toggleDebugPath() +{ + mShowDebugPath++; + if (mShowDebugPath > Map::MAP_BLACKWHITE) + mShowDebugPath = Map::MAP_NORMAL; + if (mMap) + mMap->setDebugFlags(mShowDebugPath); +} + +void Viewport::toggleCameraMode() +{ + mCameraMode++; + if (mCameraMode > 1) + mCameraMode = 0; + if (!mCameraMode) + { + mCameraRelativeX = 0; + mCameraRelativeY = 0; + } + if (miniStatusWindow) + miniStatusWindow->updateStatus(); +} + +void Viewport::hideBeingPopup() +{ + if (mBeingPopup) + mBeingPopup->setVisible(false); + if (mTextPopup) + mTextPopup->setVisible(false); +} + +void Viewport::clearHover(ActorSprite *actor) +{ + if (mHoverBeing == actor) + mHoverBeing = 0; + + if (mHoverItem == actor) + mHoverItem = 0; +} + +void Viewport::cleanHoverItems() +{ + mHoverBeing = 0; + mHoverItem = 0; + mHoverSign = 0; +} + +void Viewport::moveCamera(int dx, int dy) +{ + mCameraRelativeX += dx; + mCameraRelativeY += dy; +}
\ No newline at end of file |