summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--src/CMakeLists.txt24
-rw-r--r--src/Makefile.am4
-rw-r--r--src/engine.cpp147
-rw-r--r--src/engine.h14
-rw-r--r--src/game.cpp10
-rw-r--r--src/gui/debugwindow.cpp10
-rw-r--r--src/gui/gui.cpp177
-rw-r--r--src/gui/gui.h52
-rw-r--r--src/gui/inventorywindow.cpp3
-rw-r--r--src/gui/viewport.cpp394
-rw-r--r--src/gui/viewport.h145
-rw-r--r--src/gui/windowcontainer.h3
13 files changed, 596 insertions, 398 deletions
diff --git a/ChangeLog b/ChangeLog
index c3fa0998..ee348a23 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2006-12-04 Bjørn Lindeijer <bjorn@lindeijer>
+
+ * src/game.cpp, src/engine.h, src/gui/windowcontainer.h,
+ src/gui/viewport.cpp, src/gui/gui.cpp, src/gui/gui.h,
+ src/gui/debugwindow.cpp, src/gui/inventorywindow.cpp,
+ src/gui/viewport.h, src/engine.cpp, src/CMakeLists.txt,
+ src/Makefile.am: 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.
+
2006-12-03 Bjørn Lindeijer <bjorn@lindeijer.nl>
* src/sound.cpp, src/monster.cpp, src/sound.h,
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
*/