diff options
author | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2006-12-04 11:20:54 +0000 |
---|---|---|
committer | Bjørn Lindeijer <bjorn@lindeijer.nl> | 2006-12-04 11:20:54 +0000 |
commit | eccafb8d815068d908b998bb6c0b581b076dc78f (patch) | |
tree | 6967d3ca5fa1dc77b177c2f735469503ca04cd14 /src | |
parent | 061b9e817ffe73acca163510b276e6ca7dd8f6c2 (diff) | |
download | mana-eccafb8d815068d908b998bb6c0b581b076dc78f.tar.gz mana-eccafb8d815068d908b998bb6c0b581b076dc78f.tar.bz2 mana-eccafb8d815068d908b998bb6c0b581b076dc78f.tar.xz mana-eccafb8d815068d908b998bb6c0b581b076dc78f.zip |
Introduced a new class Viewport which combines the drawing code from Engine
with the (rather misplaced) input handling from the Gui class. Also, it's a
Container itself which should allow for extending it to show Guichan widgets on
map coordinates.
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 24 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/engine.cpp | 147 | ||||
-rw-r--r-- | src/engine.h | 14 | ||||
-rw-r--r-- | src/game.cpp | 10 | ||||
-rw-r--r-- | src/gui/debugwindow.cpp | 10 | ||||
-rw-r--r-- | src/gui/gui.cpp | 177 | ||||
-rw-r--r-- | src/gui/gui.h | 52 | ||||
-rw-r--r-- | src/gui/inventorywindow.cpp | 3 | ||||
-rw-r--r-- | src/gui/viewport.cpp | 394 | ||||
-rw-r--r-- | src/gui/viewport.h | 145 | ||||
-rw-r--r-- | src/gui/windowcontainer.h | 3 |
12 files changed, 585 insertions, 398 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd207dbe..b6a77e27 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,8 @@ SET(SRCS gui/textfield.h gui/trade.cpp gui/trade.h + gui/viewport.cpp + gui/viewport.h gui/window.cpp gui/window.h gui/windowcontainer.cpp @@ -199,23 +201,23 @@ SET(SRCS net/tradehandler.h resources/ambientoverlay.cpp resources/ambientoverlay.h - resources/equipmentdb.cpp - resources/equipmentdb.h - resources/equipmentinfo.h + resources/equipmentdb.cpp + resources/equipmentdb.h + resources/equipmentinfo.h resources/image.cpp resources/image.h resources/imagewriter.cpp resources/imagewriter.h - resources/itemdb.cpp - resources/itemdb.h - resources/iteminfo.h - resources/iteminfo.cpp + resources/itemdb.cpp + resources/itemdb.h + resources/iteminfo.h + resources/iteminfo.cpp resources/mapreader.cpp resources/mapreader.h resources/monsterdb.h - resources/monsterdb.cpp - resources/monsterinfo.h - resources/monsterinfo.cpp + resources/monsterdb.cpp + resources/monsterinfo.h + resources/monsterinfo.cpp resources/music.h resources/music.cpp resources/openglsdlimageloader.h @@ -315,4 +317,4 @@ TARGET_LINK_LIBRARIES(tmw INSTALL(TARGETS tmw RUNTIME DESTINATION ${PKG_BINDIR}) -SET_TARGET_PROPERTIES(tmw PROPERTIES COMPILE_FLAGS "${FLAGS}")
\ No newline at end of file +SET_TARGET_PROPERTIES(tmw PROPERTIES COMPILE_FLAGS "${FLAGS}") diff --git a/src/Makefile.am b/src/Makefile.am index 94b0f2af..3b7ed4d5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -107,6 +107,8 @@ tmw_SOURCES = graphic/imagerect.h \ gui/textfield.h \ gui/trade.cpp \ gui/trade.h \ + gui/viewport.cpp \ + gui/viewport.h \ gui/window.cpp \ gui/window.h \ gui/windowcontainer.cpp \ @@ -268,4 +270,4 @@ INCLUDES = \ tmw_LDFLAGS = $(all_libraries) $(LIBSDL_RPATH) `pkg-config --libs libxml-2.0` tmw_CXXFLAGS = -Wall -Werror $(OPENGL_CFLAGS) $(LIBSDL_CFLAGS) `pkg-config --cflags libxml-2.0` $(CURL_CFLAGS) tmw_LDADD = $(LIBSDL_LIBS) -lguichan_sdl $(OPENGL_LIBS) $(CURL_LIBS) -tmw_TARGET = tmw
\ No newline at end of file +tmw_TARGET = tmw diff --git a/src/engine.cpp b/src/engine.cpp index 2b2f168a..c9c5dcba 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -40,11 +40,11 @@ #include "gui/gui.h" #include "gui/minimap.h" +#include "gui/viewport.h" #include "net/messageout.h" #include "net/protocol.h" - #include "resources/mapreader.h" #include "resources/monsterdb.h" #include "resources/resourcemanager.h" @@ -56,14 +56,12 @@ extern Minimap *minimap; char itemCurrenyQ[10] = "0"; -int camera_x, camera_y; Spriteset *emotionset; Spriteset *npcset; std::vector<Spriteset *> weaponset; Engine::Engine(Network *network): - mShowDebugPath(false), mCurrentMap(NULL), mNetwork(network) { @@ -124,6 +122,7 @@ void Engine::changeMap(const std::string &mapPath) } minimap->setMapImage(mapImage); beingManager->setMap(newMap); + viewport->setMap(newMap); // Start playing new music file when necessary std::string oldMusic = ""; @@ -155,147 +154,5 @@ void Engine::logic() void Engine::draw(Graphics *graphics) { - static int lastTick = tick_time; - - // Avoid freaking out when tick_time overflows - if (tick_time < lastTick) - { - lastTick = tick_time; - } - - // Calculate viewpoint - int midTileX = graphics->getWidth() / 32 / 2; - int midTileY = graphics->getHeight() / 32 / 2; - - int player_x = (player_node->mX - midTileX) * 32 + player_node->getXOffset(); - int player_y = (player_node->mY - midTileY) * 32 + player_node->getYOffset(); - - scrollLaziness = (int)config.getValue("ScrollLaziness", 32); - scrollRadius = (int)config.getValue("ScrollRadius", 32); - - if (scrollLaziness < 1) - scrollLaziness = 1; // Avoids division by zero - - // Apply lazy scrolling - while (lastTick < tick_time) - { - if (player_x > view_x + scrollRadius) - { - view_x += (player_x - view_x - scrollRadius) / scrollLaziness; - } - if (player_x < view_x - scrollRadius) - { - view_x += (player_x - view_x + scrollRadius) / scrollLaziness; - } - if (player_y > view_y + scrollRadius) - { - view_y += (player_y - view_y - scrollRadius) / scrollLaziness; - } - if (player_y < view_y - scrollRadius) - { - view_y += (player_y - view_y + scrollRadius) / scrollLaziness; - } - lastTick++; - } - - // Auto center when player is off screen - if ( player_x - view_x > graphics->getWidth() / 2 - || view_x - player_x > graphics->getWidth() / 2 - || view_y - player_y > graphics->getHeight() / 2 - || player_y - view_y > graphics->getHeight() / 2 - ) - { - view_x = player_x; - view_y = player_y; - }; - - if (mCurrentMap) { - if (view_x < 0) { - view_x = 0; - } - if (view_y < 0) { - view_y = 0; - } - if (view_x > (mCurrentMap->getWidth() - midTileX) * 32) { - view_x = (mCurrentMap->getWidth() - midTileX) * 32; - } - if (view_y > (mCurrentMap->getHeight() - midTileY) * 32) { - view_y = (mCurrentMap->getHeight() - midTileY) * 32; - } - } - - camera_x = (int) (view_x + 16) / 32; - camera_y = (int) (view_y + 16) / 32; - - // Draw tiles and sprites - if (mCurrentMap != NULL) - { - mCurrentMap->draw(graphics, (int) view_x, (int) view_y, 0); - mCurrentMap->draw(graphics, (int) view_x, (int) view_y, 1); - mCurrentMap->draw(graphics, (int) view_x, (int) view_y, 2); - mCurrentMap->drawOverlay( graphics, - view_x, - view_y, - (int) config.getValue("OverlayDetail", 2) - ); - } - - // Find a path from the player to the mouse, and draw it. This is for debug - // purposes. - if (mShowDebugPath && mCurrentMap != NULL) - { - // Get the current mouse position - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - - int mouseTileX = mouseX / 32 + camera_x; - int mouseTileY = mouseY / 32 + camera_y; - - Path debugPath = mCurrentMap->findPath( - player_node->mX, player_node->mY, - mouseTileX, mouseTileY); - - graphics->setColor(gcn::Color(255, 0, 0)); - for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) - { - int squareX = i->x * 32 - int(view_x) + 12; - int squareY = i->y * 32 - int(view_y) + 12; - - graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); - graphics->drawText( - toString(mCurrentMap->getMetaTile(i->x, i->y)->Gcost), - squareX + 4, squareY + 12, gcn::Graphics::CENTER); - } - } - - // Draw player nickname, speech, and emotion sprite as needed - Beings &beings = beingManager->getAll(); - for (BeingIterator i = beings.begin(); i != beings.end(); i++) - { - (*i)->drawSpeech(graphics, -(int) view_x, -(int) view_y); - (*i)->drawName(graphics, -(int) view_x, -(int) view_y); - (*i)->drawEmotion(graphics, -(int) view_x, -(int) view_y); - } - - // Draw target marker if needed - Being *target; - if ((target = player_node->getTarget())) - { - graphics->setFont(speechFont); - graphics->setColor(gcn::Color(255, 32, 32)); - int dy = (target->getType() == Being::PLAYER) ? 80 : 42; - - std::string mobName = ""; - - if (target->mJob >= 1002 ) - { - int mobId = target->mJob - 1002; - mobName = MonsterDB::get(mobId).getName(); - } - - graphics->drawText(mobName, target->getPixelX() - (int)view_x + 15, - target->getPixelY() - (int)view_y - dy, gcn::Graphics::CENTER); - } - gui->draw(); } diff --git a/src/engine.h b/src/engine.h index 3ae380d6..dbe1fddc 100644 --- a/src/engine.h +++ b/src/engine.h @@ -26,8 +26,6 @@ #include <iosfwd> -extern int camera_x, camera_y; - class Graphics; class Map; class Network; @@ -68,21 +66,9 @@ class Engine */ void draw(Graphics *graphics); - /** - * Toggles whether the path debug graphics are shown - */ - void toggleDebugPath() { mShowDebugPath = !mShowDebugPath; }; - private: - bool mShowDebugPath; - Map *mCurrentMap; Network *mNetwork; - - int scrollRadius; - int scrollLaziness; - float view_x; // current viewpoint in pixels - float view_y; // current viewpoint in pixels }; extern Engine *engine; diff --git a/src/game.cpp b/src/game.cpp index a241e8bb..0cfc72e5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -46,20 +46,22 @@ //#include "gui/chargedialog.h" #include "gui/chat.h" #include "gui/confirm_dialog.h" +#include "gui/debugwindow.h" #include "gui/equipmentwindow.h" +#include "gui/gui.h" #include "gui/help.h" #include "gui/inventorywindow.h" +#include "gui/menuwindow.h" #include "gui/minimap.h" +#include "gui/ministatus.h" #include "gui/npclistdialog.h" #include "gui/npc_text.h" #include "gui/sell.h" #include "gui/setup.h" #include "gui/skill.h" -#include "gui/menuwindow.h" #include "gui/status.h" -#include "gui/ministatus.h" #include "gui/trade.h" -#include "gui/debugwindow.h" +#include "gui/viewport.h" #include "net/beinghandler.h" #include "net/buysellhandler.h" @@ -567,7 +569,7 @@ void Game::handleInput() case SDLK_f: // Find path to mouse (debug purpose) - engine->toggleDebugPath(); + viewport->toggleDebugPath(); used = true; break; } diff --git a/src/gui/debugwindow.cpp b/src/gui/debugwindow.cpp index 2f788d05..c44ac1d1 100644 --- a/src/gui/debugwindow.cpp +++ b/src/gui/debugwindow.cpp @@ -72,15 +72,15 @@ DebugWindow::logic() // Get the current mouse position int mouseX, mouseY; SDL_GetMouseState(&mouseX, &mouseY); - int mouseTileX = mouseX / 32 + camera_x; - int mouseTileY = mouseY / 32 + camera_y; + //int mouseTileX = mouseX / 32 + camera_x; + //int mouseTileY = mouseY / 32 + camera_y; mFPSLabel->setCaption("[" + toString(fps) + " FPS"); mFPSLabel->adjustSize(); - mTileMouseLabel->setCaption("[Mouse: " + - toString(mouseTileX) + ", " + toString(mouseTileY) + "]"); - mTileMouseLabel->adjustSize(); + //mTileMouseLabel->setCaption("[Mouse: " + + // toString(mouseTileX) + ", " + toString(mouseTileY) + "]"); + //mTileMouseLabel->adjustSize(); Map *currentMap = engine->getCurrentMap(); if (currentMap != NULL) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index cceba658..8d323fb8 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -35,22 +35,14 @@ #endif #include "focushandler.h" -#include "popupmenu.h" #include "window.h" #include "windowcontainer.h" +#include "viewport.h" -#include "../being.h" -#include "../beingmanager.h" #include "../configlistener.h" #include "../configuration.h" -#include "../engine.h" -#include "../flooritemmanager.h" #include "../graphics.h" -#include "../localplayer.h" #include "../log.h" -#include "../main.h" -#include "../map.h" -#include "../npc.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" @@ -58,7 +50,8 @@ // Guichan stuff Gui *gui; -gcn::SDLInput *guiInput; // GUI input +Viewport *viewport; /**< Viewport on the map. */ +gcn::SDLInput *guiInput; /**< GUI input. */ // Fonts used in showing hits gcn::Font *hitRedFont; @@ -88,10 +81,7 @@ class GuiConfigListener : public ConfigListener Gui::Gui(Graphics *graphics): mHostImageLoader(NULL), mMouseCursor(NULL), - mCustomCursor(false), - mPopupActive(false), - mPlayerFollowMouse(false), - mWalkTime(0) + mCustomCursor(false) { logger->log("Initializing GUI..."); // Set graphics @@ -122,7 +112,6 @@ Gui::Gui(Graphics *graphics): guiTop->setDimension(gcn::Rectangle(0, 0, graphics->getWidth(), graphics->getHeight())); guiTop->setOpaque(false); - guiTop->addMouseListener(this); Window::setWindowContainer(guiTop); setTop(guiTop); @@ -172,13 +161,15 @@ Gui::Gui(Graphics *graphics): mConfigListener = new GuiConfigListener(this); config.addListener("customcursor", mConfigListener); - mPopup = new PopupMenu(); + // Create the viewport + viewport = new Viewport(); + viewport->setDimension(gcn::Rectangle(0, 0, + graphics->getWidth(), graphics->getHeight())); + guiTop->add(viewport); } Gui::~Gui() { - delete mPopup; - config.removeListener("customcursor", mConfigListener); delete mConfigListener; @@ -208,16 +199,6 @@ Gui::logic() // Work around Guichan bug of only applying focus on mouse or keyboard // events. mFocusHandler->applyChanges(); - - int mouseX, mouseY; - Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); - - if ( mPlayerFollowMouse && button & SDL_BUTTON(1) && - mWalkTime != player_node -> mWalkTime) - { - player_node->setDestination(mouseX / 32 + camera_x, mouseY / 32 + camera_y); - mWalkTime = player_node -> mWalkTime; - } } void @@ -241,128 +222,6 @@ Gui::draw() } void -Gui::mousePress(int mx, int my, int button) -{ - // Mouse pressed on window container (basically, the map) - mPlayerFollowMouse = false; - - // Are we in-game yet? - if (state != GAME_STATE) - return; - - // Check if we are alive and kickin' - if (!player_node || player_node->mAction == Being::DEAD) - return; - - // Check if we are busy - if (current_npc) - return; - - int tilex = mx / 32 + camera_x; - int tiley = my / 32 + camera_y; - - // Right click might open a popup - if (button == gcn::MouseInput::RIGHT) - { - Being *being; - FloorItem *floorItem; - - if ((being = beingManager->findBeing(tilex, tiley)) && - being->getType() != Being::LOCALPLAYER) - { - showPopup(mx, my, being); - return; - } - else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) - { - showPopup(mx, my, floorItem); - return; - } - } - - // If a popup is active, just remove it - if (mPopupActive) - { - mPopup->setVisible(false); - mPopupActive = false; - return; - } - - // Left click can cause different actions - if (button == gcn::MouseInput::LEFT) - { - Being *being; - FloorItem *item; - - // Interact with some being - if ((being = beingManager->findBeing(tilex, tiley))) - { - switch (being->getType()) - { - case Being::NPC: - dynamic_cast<NPC*>(being)->talk(); - break; - - case Being::MONSTER: - case Being::PLAYER: - if (being->mAction == Being::DEAD) - break; - - player_node->attack(being, true); - break; - - default: - break; - } - } - // Pick up some item - else if ((item = floorItemManager->findByCoordinates(tilex, tiley))) - { - player_node->pickUp(item); - } - // Just walk around - else if (engine->getCurrentMap()->getWalk(tilex, tiley)) - { - // XXX XXX XXX REALLY UGLY! - Uint8 *keys = SDL_GetKeyState(NULL); - if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) - { - player_node->setDestination(tilex, tiley); - player_node->stopAttack(); - } - mPlayerFollowMouse = true; - } - } - - if (button == gcn::MouseInput::MIDDLE) - { - // Find the being nearest to the clicked position - Being *target = beingManager->findNearestLivingBeing( - tilex, tiley, - 20, Being::MONSTER); - - if (target) - { - player_node->setTarget(target); - } - } -} - -void -Gui::mouseMotion(int mx, int my) -{ - if (mPlayerFollowMouse && mWalkTime == player_node -> mWalkTime) - player_node->setDestination(mx / 32 + camera_x, my / 32 + camera_y); -} - -void -Gui::mouseRelease(int mx, int my, int button) -{ - mPlayerFollowMouse = false; -} - - -void Gui::setUseCustomCursor(bool customCursor) { if (customCursor != mCustomCursor) @@ -394,21 +253,3 @@ Gui::setUseCustomCursor(bool customCursor) } } } - -void Gui::showPopup(int x, int y, Item *item) -{ - mPopup->showPopup(x, y, item); - mPopupActive = true; -} - -void Gui::showPopup(int x, int y, FloorItem *floorItem) -{ - mPopup->showPopup(x, y, floorItem); - mPopupActive = true; -} - -void Gui::showPopup(int x, int y, Being *being) -{ - mPopup->showPopup(x, y, being); - mPopupActive = true; -} diff --git a/src/gui/gui.h b/src/gui/gui.h index 0cb3f60b..caf27744 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -25,17 +25,13 @@ #define _TMW_GUI #include <guichan/gui.hpp> -#include <guichan/mouselistener.hpp> #include "../guichanfwd.h" -class Being; -class FloorItem; class GuiConfigListener; class Graphics; class Image; -class Item; -class PopupMenu; +class Viewport; /** * \defgroup GUI Core GUI related classes (widgets) @@ -50,7 +46,7 @@ class PopupMenu; * * \ingroup GUI */ -class Gui : public gcn::Gui, public gcn::MouseListener +class Gui : public gcn::Gui { public: /** @@ -77,24 +73,6 @@ class Gui : public gcn::Gui, public gcn::MouseListener draw(); /** - * Handles mouse press on map. - */ - void - mousePress(int mx, int my, int button); - - /** - * Handles mouse move on map - */ - void - mouseMotion(int mx, int my); - - /** - * Handles mouse button release on map. - */ - void - mouseRelease(int mx, int my, int button); - - /** * Return game font */ gcn::Font* @@ -106,39 +84,17 @@ class Gui : public gcn::Gui, public gcn::MouseListener void setUseCustomCursor(bool customCursor); - /** - * Shows a popup for an item - * TODO Find some way to get rid of Item here - */ - void showPopup(int x, int y, Item *item); - - /** - * Shows a popup for a floor item - * TODO Find some way to get rid of FloorItem here - */ - void showPopup(int x, int y, FloorItem *floorItem); - - /** - * Shows a popup for a being - * TODO Find some way to get rid of Being here - */ - void showPopup(int x, int y, Being *being); - private: GuiConfigListener *mConfigListener; gcn::ImageLoader *mHostImageLoader; /**< For loading images in GL */ gcn::ImageLoader *mImageLoader; /**< For loading images */ - gcn::Font *mGuiFont; /**< The global GUI font */ + gcn::Font *mGuiFont; /**< The global GUI font */ Image *mMouseCursor; /**< Mouse cursor image */ bool mCustomCursor; /**< Show custom cursor */ - - PopupMenu *mPopup; /**< Popup window */ - bool mPopupActive; - bool mPlayerFollowMouse; - int mWalkTime; }; extern Gui *gui; /**< The GUI system */ +extern Viewport *viewport; /**< The viewport */ extern gcn::SDLInput *guiInput; /**< GUI input */ /** diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index eb85220b..75b63208 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -34,6 +34,7 @@ #include "item_amount.h" #include "itemcontainer.h" #include "scrollarea.h" +#include "viewport.h" #include "../item.h" #include "../localplayer.h" @@ -169,7 +170,7 @@ void InventoryWindow::mouseClick(int x, int y, int button, int count) */ int mx = x + getX(); int my = y + getY(); - gui->showPopup(mx, my, item); + viewport->showPopup(mx, my, item); } } diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp new file mode 100644 index 00000000..eabb717c --- /dev/null +++ b/src/gui/viewport.cpp @@ -0,0 +1,394 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "viewport.h" + +#include <guichan/sdl/sdlinput.hpp> + +#include "gui.h" +#include "popupmenu.h" + +#include "../beingmanager.h" +#include "../configuration.h" +#include "../flooritemmanager.h" +#include "../graphics.h" +#include "../localplayer.h" +#include "../map.h" +#include "../npc.h" + +#include "../resources/monsterdb.h" + +#include "../utils/tostring.h" + +Viewport::Viewport(): + mMap(0), + mViewX(0.0f), + mViewY(0.0f), + mCameraX(0), + mCameraY(0), + mShowDebugPath(false), + mPopupActive(false) +{ + setOpaque(false); + addMouseListener(this); + + mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); + mScrollRadius = (int) config.getValue("ScrollRadius", 32); + + config.addListener("ScrollLaziness", this); + config.addListener("ScrollRadius", this); + + mPopupMenu = new PopupMenu(); +} + +Viewport::~Viewport() +{ + delete mPopupMenu; +} + +void +Viewport::setMap(Map *map) +{ + mMap = map; +} + +void +Viewport::draw(gcn::Graphics *gcnGraphics) +{ + static int lastTick = tick_time; + + if (!mMap || !player_node) + 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() / 32 / 2; + int midTileY = graphics->getHeight() / 32 / 2; + + int player_x = (player_node->mX - midTileX) * 32 + + player_node->getXOffset(); + int player_y = (player_node->mY - midTileY) * 32 + + player_node->getYOffset(); + + if (mScrollLaziness < 1) + mScrollLaziness = 1; // Avoids division by zero + + // Apply lazy scrolling + while (lastTick < tick_time) + { + if (player_x > mViewX + mScrollRadius) + { + mViewX += (player_x - mViewX - mScrollRadius) / mScrollLaziness; + } + if (player_x < mViewX - mScrollRadius) + { + mViewX += (player_x - mViewX + mScrollRadius) / mScrollLaziness; + } + if (player_y > mViewY + mScrollRadius) + { + mViewY += (player_y - mViewY - mScrollRadius) / mScrollLaziness; + } + if (player_y < mViewY - mScrollRadius) + { + mViewY += (player_y - mViewY + mScrollRadius) / mScrollLaziness; + } + lastTick++; + } + + // Auto center when player is off screen + if ( player_x - mViewX > graphics->getWidth() / 2 + || mViewX - player_x > graphics->getWidth() / 2 + || mViewY - player_y > graphics->getHeight() / 2 + || player_y - mViewY > graphics->getHeight() / 2 + ) + { + mViewX = player_x; + mViewY = player_y; + }; + + if (mMap) { + if (mViewX < 0) { + mViewX = 0; + } + if (mViewY < 0) { + mViewY = 0; + } + if (mViewX > (mMap->getWidth() - midTileX) * 32) { + mViewX = (mMap->getWidth() - midTileX) * 32; + } + if (mViewY > (mMap->getHeight() - midTileY) * 32) { + mViewY = (mMap->getHeight() - midTileY) * 32; + } + } + + mCameraX = (int) (mViewX + 16) / 32; + mCameraY = (int) (mViewY + 16) / 32; + + // Draw tiles and sprites + if (mMap) + { + mMap->draw(graphics, (int) mViewX, (int) mViewY, 0); + mMap->draw(graphics, (int) mViewX, (int) mViewY, 1); + mMap->draw(graphics, (int) mViewX, (int) mViewY, 2); + mMap->drawOverlay(graphics, mViewX, mViewY, + (int) config.getValue("OverlayDetail", 2)); + } + + // Find a path from the player to the mouse, and draw it. This is for debug + // purposes. + if (mShowDebugPath && mMap) + { + // Get the current mouse position + int mouseX, mouseY; + SDL_GetMouseState(&mouseX, &mouseY); + + int mouseTileX = mouseX / 32 + mCameraX; + int mouseTileY = mouseY / 32 + mCameraY; + + Path debugPath = mMap->findPath( + player_node->mX, player_node->mY, + mouseTileX, mouseTileY); + + graphics->setColor(gcn::Color(255, 0, 0)); + for (PathIterator i = debugPath.begin(); i != debugPath.end(); i++) + { + int squareX = i->x * 32 - (int) mViewX + 12; + int squareY = i->y * 32 - (int) mViewY + 12; + + graphics->fillRectangle(gcn::Rectangle(squareX, squareY, 8, 8)); + graphics->drawText( + toString(mMap->getMetaTile(i->x, i->y)->Gcost), + squareX + 4, squareY + 12, gcn::Graphics::CENTER); + } + } + + // Draw player nickname, speech, and emotion sprite as needed + Beings &beings = beingManager->getAll(); + for (BeingIterator i = beings.begin(); i != beings.end(); i++) + { + (*i)->drawSpeech(graphics, -(int) mViewX, -(int) mViewY); + (*i)->drawName(graphics, -(int) mViewX, -(int) mViewY); + (*i)->drawEmotion(graphics, -(int) mViewX, -(int) mViewY); + } + + // Draw target marker if needed + Being *target; + if ((target = player_node->getTarget())) + { + graphics->setFont(speechFont); + graphics->setColor(gcn::Color(255, 32, 32)); + int dy = (target->getType() == Being::PLAYER) ? 80 : 42; + + std::string mobName = ""; + + if (target->mJob >= 1002) + { + int mobId = target->mJob - 1002; + mobName = MonsterDB::get(mobId).getName(); + + graphics->drawText(mobName, + target->getPixelX() - (int) mViewX + 15, + target->getPixelY() - (int) mViewY - dy, + gcn::Graphics::CENTER); + } + } + + // Draw contained widgets + WindowContainer::draw(gcnGraphics); +} + +void +Viewport::logic() +{ + WindowContainer::logic(); + + if (!mMap || !player_node) + return; + + int mouseX, mouseY; + Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); + + if (mPlayerFollowMouse && button & SDL_BUTTON(1) && + mWalkTime != player_node->mWalkTime) + { + player_node->setDestination(mouseX / 32 + mCameraX, + mouseY / 32 + mCameraY); + mWalkTime = player_node->mWalkTime; + } +} + +void +Viewport::mousePress(int mx, int my, int button) +{ + // Check if we are alive and kickin' + if (!mMap || !player_node || player_node->mAction == Being::DEAD) + return; + + // Check if we are busy + if (current_npc) + return; + + mPlayerFollowMouse = false; + + int tilex = mx / 32 + mCameraX; + int tiley = my / 32 + mCameraY; + + // Right click might open a popup + if (button == gcn::MouseInput::RIGHT) + { + Being *being; + FloorItem *floorItem; + + if ((being = beingManager->findBeing(tilex, tiley)) && + being->getType() != Being::LOCALPLAYER) + { + showPopup(mx, my, being); + return; + } + else if((floorItem = floorItemManager->findByCoordinates(tilex, tiley))) + { + showPopup(mx, my, floorItem); + return; + } + } + + // If a popup is active, just remove it + if (mPopupActive) + { + mPopupMenu->setVisible(false); + mPopupActive = false; + return; + } + + // Left click can cause different actions + if (button == gcn::MouseInput::LEFT) + { + Being *being; + FloorItem *item; + + // Interact with some being + if ((being = beingManager->findBeing(tilex, tiley))) + { + switch (being->getType()) + { + case Being::NPC: + dynamic_cast<NPC*>(being)->talk(); + break; + + case Being::MONSTER: + case Being::PLAYER: + if (being->mAction == Being::DEAD) + break; + + player_node->attack(being, true); + break; + + default: + break; + } + } + // Pick up some item + else if ((item = floorItemManager->findByCoordinates(tilex, tiley))) + { + player_node->pickUp(item); + } + // Just walk around + else if (mMap->getWalk(tilex, tiley)) + { + // XXX XXX XXX REALLY UGLY! + Uint8 *keys = SDL_GetKeyState(NULL); + if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) + { + player_node->setDestination(tilex, tiley); + player_node->stopAttack(); + } + mPlayerFollowMouse = true; + } + } + + if (button == gcn::MouseInput::MIDDLE) + { + // Find the being nearest to the clicked position + Being *target = beingManager->findNearestLivingBeing( + tilex, tiley, + 20, Being::MONSTER); + + if (target) + { + player_node->setTarget(target); + } + } +} + +void +Viewport::mouseMotion(int mx, int my) +{ + if (!mMap || !player_node) + return; + + if (mPlayerFollowMouse && mWalkTime == player_node->mWalkTime) + { + player_node->setDestination(mx / 32 + mCameraX, my / 32 + mCameraY); + } +} + +void +Viewport::mouseRelease(int mx, int my, int button) +{ + mPlayerFollowMouse = false; +} + +void +Viewport::showPopup(int x, int y, Item *item) +{ + mPopupMenu->showPopup(x, y, item); + mPopupActive = true; +} + +void +Viewport::showPopup(int x, int y, FloorItem *floorItem) +{ + mPopupMenu->showPopup(x, y, floorItem); + mPopupActive = true; +} + +void +Viewport::showPopup(int x, int y, Being *being) +{ + mPopupMenu->showPopup(x, y, being); + mPopupActive = true; +} + +void +Viewport::optionChanged(const std::string &name) +{ + mScrollLaziness = (int) config.getValue("ScrollLaziness", 32); + mScrollRadius = (int) config.getValue("ScrollRadius", 32); +} diff --git a/src/gui/viewport.h b/src/gui/viewport.h new file mode 100644 index 00000000..df78b1da --- /dev/null +++ b/src/gui/viewport.h @@ -0,0 +1,145 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_VIEWPORT_H_ +#define _TMW_VIEWPORT_H_ + +#include <guichan/mouselistener.hpp> + +#include "windowcontainer.h" + +#include "../configlistener.h" + +class Map; +class Being; +class FloorItem; +class Item; +class PopupMenu; + +/** + * The viewport on the map. Displays the current map and handles mouse input + * and the popup menu. + * + * TODO: This class is planned to be extended to allow floating widgets on top + * of it such as NPC messages, which are positioned using map pixel + * coordinates. + */ +class Viewport : public WindowContainer, public gcn::MouseListener, + public ConfigListener +{ + public: + /** + * Constructor. + */ + Viewport(); + + /** + * Destructor. + */ + ~Viewport(); + + /** + * Sets the map displayed by the viewport. + */ + void + setMap(Map *map); + + /** + * Draws the viewport. + */ + void + draw(gcn::Graphics *graphics); + + /** + * Implements player to keep following mouse. + */ + void + logic(); + + /** + * Toggles whether the path debug graphics are shown + */ + void toggleDebugPath() { mShowDebugPath = !mShowDebugPath; } + + /** + * Handles mouse press on map. + */ + void + mousePress(int mx, int my, int button); + + /** + * Handles mouse move on map + */ + void + mouseMotion(int mx, int my); + + /** + * Handles mouse button release on map. + */ + void + mouseRelease(int mx, int my, int button); + + /** + * Shows a popup for an item. + * TODO Find some way to get rid of Item here + */ + void showPopup(int x, int y, Item *item); + + /** + * A relevant config option changed. + */ + void + optionChanged(const std::string &name); + + private: + /** + * Shows a popup for a floor item. + * TODO Find some way to get rid of FloorItem here + */ + void showPopup(int x, int y, FloorItem *floorItem); + + /** + * Shows a popup for a being. + * TODO Find some way to get rid of Being here + */ + void showPopup(int x, int y, Being *being); + + + Map *mMap; /**< The current map. */ + + int mScrollRadius; + int mScrollLaziness; + float mViewX; /**< Current viewpoint in pixels. */ + float mViewY; /**< Current viewpoint in pixels. */ + int mCameraX; + int mCameraY; + bool mShowDebugPath; /**< Show a path from player to pointer. */ + + bool mPlayerFollowMouse; + int mWalkTime; + + PopupMenu *mPopupMenu; /**< Popup menu. */ + bool mPopupActive; +}; + +#endif diff --git a/src/gui/windowcontainer.h b/src/gui/windowcontainer.h index b860fa3c..df255f84 100644 --- a/src/gui/windowcontainer.h +++ b/src/gui/windowcontainer.h @@ -27,7 +27,8 @@ #include <guichan/widgets/container.hpp> /** - * A window container. This container makes draggable windows possible. + * A window container. This container adds functionality for more convenient + * widget (windows in particular) destruction. * * \ingroup GUI */ |