summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBertram <bertram@cegetel.net>2010-03-03 23:36:37 +0100
committerBertram <bertram@cegetel.net>2010-03-03 23:36:37 +0100
commit8cc31b582f372238ce6bd2c86888d312cf1fe5b2 (patch)
tree8db5f864348d08a05b8533c7ede58e76741a98f8
parentb1845e9e081df1fc77d9bcbed3ab95792d6ba682 (diff)
parentd564943867452ad76e6d313a28870e640715dded (diff)
downloadmana-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.gz
mana-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.bz2
mana-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.xz
mana-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.zip
Merge branch 'master' of gitorious.org:mana/mana
Conflicts: src/being.cpp
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt3
-rwxr-xr-xconfigure.ac18
-rw-r--r--docs/example.mana (renamed from data/branding.xml)9
-rw-r--r--mana.cbp40
-rw-r--r--mana.files6
-rw-r--r--src/CMakeLists.txt19
-rw-r--r--src/Makefile.am8
-rw-r--r--src/being.cpp33
-rw-r--r--src/being.h9
-rw-r--r--src/client.cpp135
-rw-r--r--src/client.h15
-rw-r--r--src/game.cpp6
-rw-r--r--src/gui/beingpopup.cpp4
-rw-r--r--src/gui/beingpopup.h6
-rw-r--r--src/gui/equipmentwindow.cpp2
-rw-r--r--src/gui/inventorywindow.cpp7
-rw-r--r--src/gui/itemamount.cpp4
-rw-r--r--src/gui/palette.cpp1
-rw-r--r--src/gui/palette.h1
-rw-r--r--src/gui/popupmenu.cpp10
-rw-r--r--src/gui/popupmenu.h6
-rw-r--r--src/gui/recorder.cpp2
-rw-r--r--src/gui/setup_colors.cpp1
-rw-r--r--src/gui/socialwindow.cpp4
-rw-r--r--src/gui/storagewindow.cpp29
-rw-r--r--src/gui/storagewindow.h26
-rw-r--r--src/gui/viewport.cpp5
-rw-r--r--src/gui/viewport.h6
-rw-r--r--src/gui/widgets/itemshortcutcontainer.cpp2
-rw-r--r--src/gui/widgets/textfield.cpp16
-rw-r--r--src/gui/widgets/textfield.h2
-rw-r--r--src/guild.cpp62
-rw-r--r--src/guild.h26
-rw-r--r--src/localplayer.cpp11
-rw-r--r--src/localplayer.h15
-rw-r--r--src/log.cpp12
-rw-r--r--src/main.cpp45
-rw-r--r--src/map.cpp3
-rw-r--r--src/net/ea/beinghandler.cpp19
-rw-r--r--src/net/ea/generalhandler.cpp14
-rw-r--r--src/net/ea/gui/guildtab.cpp117
-rw-r--r--src/net/ea/gui/guildtab.h52
-rw-r--r--src/net/ea/guildhandler.cpp330
-rw-r--r--src/net/ea/guildhandler.h6
-rw-r--r--src/net/ea/inventoryhandler.cpp88
-rw-r--r--src/net/ea/inventoryhandler.h31
-rw-r--r--src/net/ea/partyhandler.cpp19
-rw-r--r--src/net/ea/playerhandler.cpp2
-rw-r--r--src/net/guildhandler.h4
-rw-r--r--src/net/inventoryhandler.h1
-rw-r--r--src/net/manaserv/guildhandler.cpp10
-rw-r--r--src/net/manaserv/guildhandler.h4
-rw-r--r--src/net/manaserv/partyhandler.cpp3
-rw-r--r--src/party.cpp73
-rw-r--r--src/party.h26
-rw-r--r--src/player.cpp33
-rw-r--r--src/player.h10
-rw-r--r--src/utils/copynpaste.cpp319
-rw-r--r--src/utils/copynpaste.h33
-rw-r--r--src/utils/mkdir.cpp101
-rw-r--r--src/utils/mkdir.h26
-rw-r--r--src/utils/specialfolder.cpp78
-rw-r--r--src/utils/specialfolder.h30
64 files changed, 1700 insertions, 339 deletions
diff --git a/AUTHORS b/AUTHORS
index f419f60b..7bf2325a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ Blue Sans Douze (Blue112) <bluesansdouze gmail.com>
Björn Steinbrink (Doener) <b.steinbrink gmx.de>
Bjørn Lindeijer <bjorn lindeijer.nl>
Cedric Borgese (moi1392) <cedric.borgese gmail.com>
+Chuck Miller (Kage) <shadowmil gmail.com>
David Athay <ko2fan gmail.com>
Dennis Friis <peavey placid.dk>
Douglas Boffey <dougaboffey netscape.net>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 48e63788..ccd92f84 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,6 +30,9 @@ IF (WIN32)
LIST(GET _VERSION 3 VER_BUILD)
CONFIGURE_FILE(src/winver.h.in src/winver.h)
ELSE (WIN32)
+ IF (!OSX)
+ OPTION(USE_X11 "Use X11 Clipboard functionality" ON)
+ ENDIF (!OSX)
SET(PKG_DATADIR ${CMAKE_INSTALL_PREFIX}/share/mana)
SET(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale)
SET(PKG_BINDIR ${CMAKE_INSTALL_PREFIX}/bin)
diff --git a/configure.ac b/configure.ac
index 739b1cb1..54b510d3 100755
--- a/configure.ac
+++ b/configure.ac
@@ -92,6 +92,24 @@ AC_CHECK_LIB(SDL_gfx, rotozoomSurfaceXY, ,
AC_MSG_ERROR([ *** Unable to find SDL_gfx library (http://www.ferzkopp.net/joomla/software-mainmenu-14/4-ferzkopps-linux-software/19-sdlgfx)]))
AC_CHECK_HEADERS(SDL_rotozoom.h, ,)
+# === Check for X11 (check borrowed from Wormux) ========================
+# Deactivated on purpose under OSX (in case X11 SDK is installed)
+if test "x$OSX" != "xyes" ; then
+ AC_CHECK_HEADER(X11/Xlib.h, check_x11="yes",check_x11="no")
+ if test x${check_x11} = xno ; then
+ AC_CHECK_HEADER(X11R6/Xlib.h,
+ [ check_x11="yes"
+ LDFLAGS="-L/usr/X11R6/include $CFLAGS"],
+ check_x11="no")
+ fi
+ if test x${check_x11} = xyes ; then
+ AC_CHECK_LIB(X11, XOpenDisplay,
+ [ LIBS="$LIBS -lX11"
+ AC_DEFINE(USE_X11, 1, [Define to use X11 copy'n'paste]) ],
+ [])
+ fi
+fi
+
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h])
diff --git a/data/branding.xml b/docs/example.mana
index 38f74e40..795d8d40 100644
--- a/data/branding.xml
+++ b/docs/example.mana
@@ -1,12 +1,11 @@
<?xml version="1.0"?>
<!--
-Branding information
+Example branding file
-All values in here are the default values which are hardcoded into the Mana
-client. So it would make no difference for the official version when this file
-is missing. It is basically an example for developers of Mana servers to help them
-writing a branding.xml for their servers.
+With a branding file you can customize the appearance and various
+other default settings. To use a branding file just pass its
+filename / path as a command line parameter
-->
<configuration>
diff --git a/mana.cbp b/mana.cbp
index 896e04aa..6b5e6dd8 100644
--- a/mana.cbp
+++ b/mana.cbp
@@ -93,6 +93,7 @@
<Add directory="lib" />
</Linker>
<Unit filename="src\SDLMain.h" />
+ <Unit filename="src\SDLMain.m" />
<Unit filename="src\animatedsprite.cpp" />
<Unit filename="src\animatedsprite.h" />
<Unit filename="src\animationparticle.cpp" />
@@ -107,6 +108,8 @@
<Unit filename="src\channel.h" />
<Unit filename="src\channelmanager.cpp" />
<Unit filename="src\channelmanager.h" />
+ <Unit filename="src\client.cpp" />
+ <Unit filename="src\client.h" />
<Unit filename="src\commandhandler.cpp" />
<Unit filename="src\commandhandler.h" />
<Unit filename="src\configlistener.h" />
@@ -116,7 +119,6 @@
<Unit filename="src\effectmanager.h" />
<Unit filename="src\emoteshortcut.cpp" />
<Unit filename="src\emoteshortcut.h" />
- <Unit filename="src\engine.cpp" />
<Unit filename="src\engine.h" />
<Unit filename="src\equipment.h" />
<Unit filename="src\flooritem.cpp" />
@@ -286,6 +288,8 @@
<Unit filename="src\gui\widgets\popup.h" />
<Unit filename="src\gui\widgets\progressbar.cpp" />
<Unit filename="src\gui\widgets\progressbar.h" />
+ <Unit filename="src\gui\widgets\progressindicator.cpp" />
+ <Unit filename="src\gui\widgets\progressindicator.h" />
<Unit filename="src\gui\widgets\radiobutton.cpp" />
<Unit filename="src\gui\widgets\radiobutton.h" />
<Unit filename="src\gui\widgets\resizegrip.cpp" />
@@ -350,11 +354,18 @@
<Unit filename="src\log.h" />
<Unit filename="src\main.cpp" />
<Unit filename="src\main.h" />
+ <Unit filename="src\mana-ea.rc">
+ <Option compilerVar="WINDRES" />
+ </Unit>
+ <Unit filename="src\mana.rc">
+ <Option compilerVar="WINDRES" />
+ </Unit>
<Unit filename="src\map.cpp" />
<Unit filename="src\map.h" />
<Unit filename="src\monster.cpp" />
<Unit filename="src\monster.h" />
<Unit filename="src\net\adminhandler.h" />
+ <Unit filename="src\net\charhandler.cpp" />
<Unit filename="src\net\charhandler.h" />
<Unit filename="src\net\chathandler.h" />
<Unit filename="src\net\download.cpp" />
@@ -373,18 +384,12 @@
<Unit filename="src\net\ea\gamehandler.h" />
<Unit filename="src\net\ea\generalhandler.cpp" />
<Unit filename="src\net\ea\generalhandler.h" />
+ <Unit filename="src\net\ea\gui\guildtab.cpp" />
+ <Unit filename="src\net\ea\gui\guildtab.h" />
<Unit filename="src\net\ea\gui\partytab.cpp" />
<Unit filename="src\net\ea\gui\partytab.h" />
- <Unit filename="src\net\ea\guildhandler.cpp">
- <Option target="Unix eAthena" />
- <Option target="Windows" />
- <Option target="Unix" />
- </Unit>
- <Unit filename="src\net\ea\guildhandler.h">
- <Option target="Unix eAthena" />
- <Option target="Windows" />
- <Option target="Unix" />
- </Unit>
+ <Unit filename="src\net\ea\guildhandler.cpp" />
+ <Unit filename="src\net\ea\guildhandler.h" />
<Unit filename="src\net\ea\inventoryhandler.cpp" />
<Unit filename="src\net\ea\inventoryhandler.h" />
<Unit filename="src\net\ea\itemhandler.cpp" />
@@ -408,12 +413,14 @@
<Unit filename="src\net\ea\protocol.h" />
<Unit filename="src\net\ea\specialhandler.cpp" />
<Unit filename="src\net\ea\specialhandler.h" />
+ <Unit filename="src\net\ea\token.h" />
<Unit filename="src\net\ea\tradehandler.cpp" />
<Unit filename="src\net\ea\tradehandler.h" />
<Unit filename="src\net\gamehandler.h" />
<Unit filename="src\net\generalhandler.h" />
<Unit filename="src\net\guildhandler.h" />
<Unit filename="src\net\inventoryhandler.h" />
+ <Unit filename="src\net\logindata.h" />
<Unit filename="src\net\loginhandler.h" />
<Unit filename="src\net\manaserv\adminhandler.cpp" />
<Unit filename="src\net\manaserv\adminhandler.h" />
@@ -472,8 +479,10 @@
<Unit filename="src\net\npchandler.h" />
<Unit filename="src\net\partyhandler.h" />
<Unit filename="src\net\playerhandler.h" />
+ <Unit filename="src\net\serverinfo.h" />
<Unit filename="src\net\specialhandler.h" />
<Unit filename="src\net\tradehandler.h" />
+ <Unit filename="src\net\worldinfo.h" />
<Unit filename="src\npc.cpp" />
<Unit filename="src\npc.h" />
<Unit filename="src\openglgraphics.cpp" />
@@ -566,18 +575,21 @@
<Unit filename="src\utils\dtor.h" />
<Unit filename="src\utils\gettext.h" />
<Unit filename="src\utils\mathutils.h" />
+ <Unit filename="src\utils\mkdir.cpp" />
+ <Unit filename="src\utils\mkdir.h" />
<Unit filename="src\utils\mutex.h" />
<Unit filename="src\utils\sha256.cpp" />
<Unit filename="src\utils\sha256.h" />
+ <Unit filename="src\utils\specialfolder.cpp" />
+ <Unit filename="src\utils\specialfolder.h" />
<Unit filename="src\utils\stringutils.cpp" />
<Unit filename="src\utils\stringutils.h" />
<Unit filename="src\utils\xml.cpp" />
<Unit filename="src\utils\xml.h" />
<Unit filename="src\vector.cpp" />
<Unit filename="src\vector.h" />
- <Unit filename="src\winver.h">
- <Option target="Windows" />
- </Unit>
+ <Unit filename="src\winver.h" />
+ <Unit filename="src\winver.h.in" />
<Extensions>
<code_completion />
<envvars />
diff --git a/mana.files b/mana.files
index 3658807e..8cba046c 100644
--- a/mana.files
+++ b/mana.files
@@ -327,6 +327,8 @@
./src/net/ea/generalhandler.h
./src/net/ea/guildhandler.cpp
./src/net/ea/guildhandler.h
+./src/net/ea/gui/guildtab.cpp
+./src/net/ea/gui/guildtab.h
./src/net/ea/gui/partytab.cpp
./src/net/ea/gui/partytab.h
./src/net/ea/inventoryhandler.cpp
@@ -512,9 +514,13 @@
./src/units.h
./src/utils/base64.cpp
./src/utils/base64.h
+./src/utils/copynpaste.cpp
+./src/utils/copynpaste.h
./src/utils/dtor.h
./src/utils/gettext.h
./src/utils/mathutils.h
+./src/utils/mkdir.cpp
+./src/utils/mkdir.h
./src/utils/mutex.h
./src/utils/sha256.cpp
./src/utils/sha256.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0a7458ac..b84e59c6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -55,6 +55,12 @@ IF (WITH_OPENGL)
SET(FLAGS "${FLAGS} -DUSE_OPENGL")
ENDIF (WITH_OPENGL)
+IF (USE_X11)
+ FIND_PACKAGE(X11 REQUIRED)
+ INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR})
+ SET(FLAGS "${FLAGS} -DUSE_X11")
+ENDIF (USE_X11)
+
INCLUDE_DIRECTORIES(
${CMAKE_CURRENT_SOURCE_DIR}
${SDL_INCLUDE_DIR}
@@ -378,6 +384,8 @@ SET(SRCS
resources/wallpaper.h
utils/base64.cpp
utils/base64.h
+ utils/copynpaste.cpp
+ utils/copynpaste.h
utils/dtor.h
utils/gettext.h
utils/mathutils.h
@@ -386,6 +394,8 @@ SET(SRCS
utils/stringutils.cpp
utils/stringutils.h
utils/mutex.h
+ utils/mkdir.cpp
+ utils/mkdir.h
utils/xml.cpp
utils/xml.h
animatedsprite.cpp
@@ -493,6 +503,8 @@ SET(SRCS
)
SET(SRCS_EA
+ net/ea/gui/guildtab.cpp
+ net/ea/gui/guildtab.h
net/ea/gui/partytab.cpp
net/ea/gui/partytab.h
net/ea/adminhandler.cpp
@@ -589,7 +601,12 @@ SET(SRCS_MANA
)
IF (WIN32)
- SET(SRCS_MANA ${SRCS_MANA} mana.rc)
+ SET(SRCS_MANA
+ ${SRCS_MANA}
+ utils/specialfolder.cpp
+ utils/specialfolder.h
+ mana.rc
+ )
ENDIF ()
SET (PROGRAMS mana)
diff --git a/src/Makefile.am b/src/Makefile.am
index 0a34bddd..f36fcdf2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -283,11 +283,17 @@ mana_SOURCES = gui/widgets/avatarlistbox.cpp \
resources/wallpaper.h \
utils/base64.cpp \
utils/base64.h \
+ utils/copynpaste.cpp \
+ utils/copynpaste.h \
utils/dtor.h \
utils/gettext.h \
utils/mathutils.h \
+ utils/mkdir.cpp \
+ utils/mkdir.h \
utils/sha256.cpp \
utils/sha256.h \
+ utils/specialfolder.cpp \
+ utils/specialfolder.h \
utils/stringutils.cpp \
utils/stringutils.h \
utils/mutex.h \
@@ -446,6 +452,8 @@ mana_SOURCES += \
net/manaserv/tradehandler.h
mana_SOURCES += \
+ net/ea/gui/guildtab.cpp \
+ net/ea/gui/guildtab.h \
net/ea/gui/partytab.cpp \
net/ea/gui/partytab.h \
net/ea/adminhandler.cpp \
diff --git a/src/being.cpp b/src/being.cpp
index b5d0cedb..afa0cd0e 100644
--- a/src/being.cpp
+++ b/src/being.cpp
@@ -127,7 +127,7 @@ void Being::setPosition(const Vector &pos)
(int)pos.y - getHeight() - mText->getHeight() - 6);
}
-Position Being::checkNodeOffsets(Position position)
+Position Being::checkNodeOffsets(const Position &position) const
{
// Pre-computing character's position in tiles
const int tx = position.x / 32;
@@ -197,22 +197,17 @@ void Being::setDestination(int dstX, int dstY)
return;
}
- // Check the walkability of the destination:
- // If the destination is unwalkable,
- // don't bother finding a path or set a destination.
+ // If the destination is unwalkable, don't bother trying to get there
if (!mMap->getWalk(dstX / 32, dstY / 32))
return;
- // We check the destination in order to handle
- // surrounding blocking tiles gracefully...
Position dest = checkNodeOffsets(dstX, dstY);
mDest.x = dest.x;
mDest.y = dest.y;
int srcX = mPos.x;
int srcY = mPos.y;
- // We initialize an empty path...
- Path thisPath = Path();
+ Path thisPath;
if (mMap)
{
@@ -244,16 +239,12 @@ void Being::setDestination(int dstX, int dstY)
int i = 0;
while (it != thisPath.end())
{
- it->x = (it->x * 32) + startX + (changeX * i);
- it->y = (it->y * 32) + startY + (changeY * i);
-
- // We check each path node and correct the
- // tile position's offsets whenever needed.
- Position pos = checkNodeOffsets(*it);
- it->x = pos.x;
- it->y = pos.y;
- i++;
- it++;
+ // A position that is valid on the start and end tile is not
+ // necessarily valid on all the tiles in between, so check the offsets.
+ *it = checkNodeOffsets(it->x * 32 + startX + changeX * i,
+ it->y * 32 + startY + changeY * i);
+ i++;
+ it++;
}
// Remove the last path node, as it's more clever to go to mDest instead.
@@ -273,6 +264,7 @@ void Being::clearPath()
void Being::setPath(const Path &path)
{
mPath = path;
+
if ((Net::getNetworkType() == ServerInfo::EATHENA) &&
mAction != WALK && mAction != DEAD)
{
@@ -387,13 +379,9 @@ void Being::takeDamage(Being *attacker, int amount, AttackType type)
}
if (type != CRITICAL)
- {
effectManager->trigger(26, this);
- }
else
- {
effectManager->trigger(28, this);
- }
}
}
@@ -401,6 +389,7 @@ void Being::handleAttack(Being *victim, int damage, AttackType type)
{
if (this != player_node)
setAction(Being::ATTACK, 1);
+
if (getType() == PLAYER && victim)
{
if (mEquippedWeapon)
diff --git a/src/being.h b/src/being.h
index 5140717c..ff21825c 100644
--- a/src/being.h
+++ b/src/being.h
@@ -648,12 +648,11 @@ class Being : public Sprite, public ConfigListener
Vector mDest; /**< destination coordinates. */
/**
- * Check the current position against surrounding
- * blocking tiles, and correct the position offset within
- * tile when needed.
+ * Check the current position against surrounding blocking tiles, and
+ * correct the position offset within tile when needed.
*/
- Position checkNodeOffsets(Position position);
- Position checkNodeOffsets(int x, int y)
+ Position checkNodeOffsets(const Position &position) const;
+ Position checkNodeOffsets(int x, int y) const
{ return checkNodeOffsets(Position(x, y)); }
private:
diff --git a/src/client.cpp b/src/client.cpp
index cf1a6496..cd40615b 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -73,6 +73,7 @@
#include "resources/resourcemanager.h"
#include "utils/gettext.h"
+#include "utils/mkdir.h"
#include "utils/stringutils.h"
#ifdef __APPLE__
@@ -84,6 +85,7 @@
#ifdef WIN32
#include <SDL_syswm.h>
+#include "utils/specialfolder.h"
#else
#include <cerrno>
#endif
@@ -204,20 +206,25 @@ Client::Client(const Options &options):
assert(!mInstance);
mInstance = this;
+ logger = new Logger;
+
// Load branding information
- branding.init("data/branding.xml");
+ if (!options.brandingPath.empty())
+ {
+ branding.init(options.brandingPath);
+ }
initHomeDir(options);
- initScreenshotDir(options.screenshotDir);
// Configure logger
- logger = new Logger;
- logger->setLogFile(homeDir + std::string("/mana.log"));
+ logger->setLogFile(localDataDir + std::string("/mana.log"));
// Log the mana version
logger->log("Mana %s", FULL_VERSION);
initConfiguration(options);
+ initScreenshotDir(options.screenshotDir);
+
logger->setLogToStandardOut(config.getValue("logToStandardOut", 0));
// Initialize SDL
@@ -236,14 +243,14 @@ Client::Client(const Options &options):
ResourceManager *resman = ResourceManager::getInstance();
- if (!resman->setWriteDir(homeDir))
+ if (!resman->setWriteDir(localDataDir))
{
logger->error(strprintf("%s couldn't be set as home directory! "
- "Exiting.", homeDir.c_str()));
+ "Exiting.", localDataDir.c_str()));
}
- // Add the user's homedir to PhysicsFS search path
- resman->addToSearchPath(homeDir, false);
+ // Add the local data directory to PhysicsFS search path
+ resman->addToSearchPath(localDataDir, false);
// Add the main data directories to our PhysicsFS search path
if (!options.dataPath.empty())
@@ -575,7 +582,7 @@ int Client::exec()
SkinLoader::instance()->setMinimumOpacity(0.8f);
currentDialog = new ServerDialog(&currentServer,
- homeDir);
+ configDir);
}
else
{
@@ -670,7 +677,7 @@ int Client::exec()
{
logger->log("State: UPDATE");
currentDialog = new UpdaterWindow(updateHost,
- homeDir + "/" + updatesDir,options.dataPath.empty());
+ localDataDir + "/" + updatesDir,options.dataPath.empty());
}
break;
@@ -953,31 +960,53 @@ void Client::action(const gcn::ActionEvent &event)
*/
void Client::initHomeDir(const Options &options)
{
- homeDir = options.homeDir;
+ localDataDir = options.localDataDir;
- if (homeDir.empty())
+ if (localDataDir.empty())
{
#ifdef __APPLE__
// Use Application Directory instead of .mana
- homeDir = std::string(PHYSFS_getUserDir()) +
+ localDataDir = std::string(PHYSFS_getUserDir()) +
"/Library/Application Support/" +
branding.getValue("appName", "Mana");
+#elif defined WIN32
+ localDataDir = getSpecialFolderLocation(CSIDL_LOCAL_APPDATA);
+ if (localDataDir.empty())
+ localDataDir = std::string(PHYSFS_getUserDir());
+ localDataDir += "/Mana";
#else
- homeDir = std::string(PHYSFS_getUserDir()) +
- "/." + branding.getValue("appShort", "mana");
+ localDataDir = std::string(PHYSFS_getUserDir()) +
+ "/.local/share/mana";
#endif
}
-#if defined WIN32
- if (!CreateDirectory(homeDir.c_str(), 0) &&
- GetLastError() != ERROR_ALREADY_EXISTS)
+
+ if (mkdir_r(localDataDir.c_str()))
+ {
+ logger->error(strprintf(_("%s doesn't exist and can't be created! "
+ "Exiting."), localDataDir.c_str()));
+ }
+
+ configDir = options.configDir;
+
+ if (configDir.empty()){
+#ifdef __APPLE__
+ configDir = localDataDir;
+#elif defined WIN32
+ configDir = getSpecialFolderLocation(CSIDL_APPDATA);
+ if (configDir.empty())
+ configDir = localDataDir;
+ else
+ configDir += "/mana/" + branding.getValue("appName", "Mana");
#else
- // Create home directory if it doesn't exist already
- if ((mkdir(homeDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) &&
- (errno != EEXIST))
+ configDir = std::string(PHYSFS_getUserDir()) +
+ "/.config/mana/" + branding.getValue("appShort", "mana");
#endif
+ }
+
+ if (mkdir_r(configDir.c_str()))
{
logger->error(strprintf(_("%s doesn't exist and can't be created! "
- "Exiting."), homeDir.c_str()));
+ "Exiting."), configDir.c_str()));
}
}
@@ -1009,15 +1038,15 @@ void Client::initConfiguration(const Options &options)
"http://updates.themanaworld.org");
config.setValue("updatehost", defaultUpdateHost);
config.setValue("customcursor", true);
+ config.setValue("useScreenshotDirectorySuffix", true);
config.setValue("ChatLogLength", 128);
// Checking if the configuration file exists... otherwise create it with
// default options.
FILE *configFile = 0;
- std::string configPath = options.configPath;
+ std::string configPath;
- if (configPath.empty())
- configPath = homeDir + "/config.xml";
+ configPath = configDir + "/config.xml";
configFile = fopen(configPath.c_str(), "r");
@@ -1090,7 +1119,7 @@ void Client::initUpdatesDir()
if (!resman->mkdir("/" + updatesDir))
{
#if defined WIN32
- std::string newDir = homeDir + "\\" + updatesDir;
+ std::string newDir = localDataDir + "\\" + updatesDir;
std::string::size_type loc = newDir.find("/", 0);
while (loc != std::string::npos)
@@ -1109,7 +1138,7 @@ void Client::initUpdatesDir()
}
#else
logger->log("Error: %s/%s can't be made, but doesn't exist!",
- homeDir.c_str(), updatesDir.c_str());
+ localDataDir.c_str(), updatesDir.c_str());
errorMessage = _("Error creating updates directory!");
state = STATE_ERROR;
#endif
@@ -1119,16 +1148,52 @@ void Client::initUpdatesDir()
void Client::initScreenshotDir(const std::string &dir)
{
- if (dir.empty())
+ if (!dir.empty())
+ screenshotDir = dir;
+ else
{
- screenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop";
- // If ~/Desktop does not exist, we save screenshots in the user's home.
- struct stat statbuf;
- if (stat(screenshotDir.c_str(), &statbuf))
- screenshotDir = std::string(PHYSFS_getUserDir());
+ std::string configScreenshotDir =
+ config.getValue("screenshotDirectory", "");
+ if (!configScreenshotDir.empty())
+ screenshotDir = configScreenshotDir;
+ else
+ {
+#ifdef WIN32
+ screenshotDir = getSpecialFolderLocation(CSIDL_MYPICTURES);
+ if (screenshotDir.empty())
+ screenshotDir = getSpecialFolderLocation(CSIDL_DESKTOP);
+#else
+ screenshotDir = std::string(PHYSFS_getUserDir()) + "Desktop";
+ // If ~/Desktop does not exist, we save screenshots in the user's home.
+ struct stat statbuf;
+ if (stat(screenshotDir.c_str(), &statbuf))
+ screenshotDir = std::string(PHYSFS_getUserDir());
+#endif
+ }
+ config.setValue("screenshotDirectory", screenshotDir);
+
+ if (config.getValue("useScreenshotDirectorySuffix", true))
+ {
+ std::string configScreenshotSuffix =
+ config.getValue("screenshotDirectorySuffix",
+ branding.getValue("appShort", "Mana"));
+
+ if (!configScreenshotSuffix.empty())
+ {
+ screenshotDir += "/" + configScreenshotSuffix;
+ config.setValue("screenshotDirectorySuffix",
+ configScreenshotSuffix);
+ }
+ }
+ }
+
+ if (mkdir_r(screenshotDir.c_str()))
+ {
+ logger->log("Directory %s doesn't exist and can't be created! "
+ "Setting screenshot directory to home.",
+ screenshotDir.c_str());
+ screenshotDir = std::string(PHYSFS_getUserDir());
}
- else
- screenshotDir = dir;
}
void Client::accountLogin(LoginData *loginData)
diff --git a/src/client.h b/src/client.h
index 47328c4d..f0fdd508 100644
--- a/src/client.h
+++ b/src/client.h
@@ -138,10 +138,11 @@ public:
std::string username;
std::string password;
std::string character;
- std::string configPath;
+ std::string brandingPath;
std::string updateHost;
std::string dataPath;
- std::string homeDir;
+ std::string configDir;
+ std::string localDataDir;
std::string screenshotDir;
std::string serverName;
@@ -164,8 +165,11 @@ public:
static State getState()
{ return instance()->state; }
- static const std::string &getHomeDirectory()
- { return instance()->homeDir; }
+ static const std::string &getConfigDirectory()
+ { return instance()->configDir; }
+
+ static const std::string &getLocalDataDirectory()
+ { return instance()->localDataDir; }
static const std::string &getScreenshotDirectory()
{ return instance()->screenshotDir; }
@@ -185,7 +189,8 @@ private:
Options options;
- std::string homeDir;
+ std::string configDir;
+ std::string localDataDir;
std::string updateHost;
std::string updatesDir;
std::string screenshotDir;
diff --git a/src/game.cpp b/src/game.cpp
index 40450fee..b3670641 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -106,7 +106,6 @@ StatusWindow *statusWindow;
MiniStatusWindow *miniStatusWindow;
InventoryWindow *inventoryWindow;
SkillDialog *skillDialog;
-StorageWindow *storageWindow;
Minimap *minimap;
EquipmentWindow *equipmentWindow;
TradeWindow *tradeWindow;
@@ -154,7 +153,6 @@ static void createGuiWindows()
chatWindow = new ChatWindow;
tradeWindow = new TradeWindow;
equipmentWindow = new EquipmentWindow(player_node->mEquipment.get());
- storageWindow = new StorageWindow;
statusWindow = new StatusWindow;
miniStatusWindow = new MiniStatusWindow;
inventoryWindow = new InventoryWindow;
@@ -202,7 +200,6 @@ static void destroyGuiWindows()
del_0(debugWindow)
del_0(itemShortcutWindow)
del_0(emoteShortcutWindow)
- del_0(storageWindow)
del_0(outfitWindow)
del_0(specialsWindow)
del_0(socialWindow)
@@ -868,7 +865,8 @@ void Game::handleInput()
}
// Talk to the nearest NPC if 't' pressed
- if ( keyboard.isKeyActive(keyboard.KEY_TALK) )
+ if (event.type == SDL_KEYDOWN &&
+ keyboard.getKeyIndex(event.key.keysym.sym) == KeyboardConfig::KEY_TALK)
{
Being *target = player_node->getTarget();
diff --git a/src/gui/beingpopup.cpp b/src/gui/beingpopup.cpp
index 3d60589f..7fd99371 100644
--- a/src/gui/beingpopup.cpp
+++ b/src/gui/beingpopup.cpp
@@ -38,14 +38,14 @@
BeingPopup::BeingPopup():
Popup("BeingPopup")
{
- // Item Name
+ // Being Name
mBeingName = new TextBox();
mBeingName->setFont(boldFont);
mBeingName->setPosition(getPadding(), getPadding());
const int fontHeight = getFont()->getHeight();
- // Item Description
+ // Being's party
mBeingParty = new TextBox();
mBeingParty->setPosition(getPadding(), fontHeight);
diff --git a/src/gui/beingpopup.h b/src/gui/beingpopup.h
index b803aacf..078b84d9 100644
--- a/src/gui/beingpopup.h
+++ b/src/gui/beingpopup.h
@@ -29,18 +29,18 @@ class Player;
class TextBox;
/**
- * A popup that displays information about an item.
+ * A popup that displays information about a being.
*/
class BeingPopup : public Popup
{
public:
/**
- * Constructor. Initializes the item popup.
+ * Constructor. Initializes the being popup.
*/
BeingPopup();
/**
- * Destructor. Cleans up the item popup on deletion.
+ * Destructor. Cleans up the being popup on deletion.
*/
~BeingPopup();
diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp
index 4400c9f5..581fd818 100644
--- a/src/gui/equipmentwindow.cpp
+++ b/src/gui/equipmentwindow.cpp
@@ -207,7 +207,7 @@ void EquipmentWindow::mousePressed(gcn::MouseEvent& mouseEvent)
*/
const int mx = x + getX();
const int my = y + getY();
- viewport->showPopup(mx, my, item, true);
+ viewport->showPopup(this, mx, my, item, true);
}
}
}
diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp
index 9c5f138e..ed008e63 100644
--- a/src/gui/inventorywindow.cpp
+++ b/src/gui/inventorywindow.cpp
@@ -220,19 +220,20 @@ void InventoryWindow::mouseClicked(gcn::MouseEvent &event)
*/
const int mx = event.getX() + getX();
const int my = event.getY() + getY();
- viewport->showPopup(mx, my, item);
+ viewport->showPopup(this, mx, my, item);
}
if (event.getButton() == gcn::MouseEvent::LEFT)
{
- if (storageWindow && keyboard.isKeyActive(keyboard.KEY_EMOTE))
+ if (StorageWindow::isActive() &&
+ keyboard.isKeyActive(keyboard.KEY_EMOTE))
{
Item *item = mItems->getSelectedItem();
if(!item)
return;
- storageWindow->addStore(item, item->getQuantity());
+ StorageWindow::addStore(item, item->getQuantity());
}
}
}
diff --git a/src/gui/itemamount.cpp b/src/gui/itemamount.cpp
index c4c38a9d..1dc5425f 100644
--- a/src/gui/itemamount.cpp
+++ b/src/gui/itemamount.cpp
@@ -54,10 +54,10 @@ void ItemAmountWindow::finish(Item *item, int amount, Usage usage)
Net::getInventoryHandler()->splitItem(item, amount);
break;
case StoreAdd:
- storageWindow->addStore(item, amount);
+ StorageWindow::addStore(item, amount);
break;
case StoreRemove:
- storageWindow->removeStore(item, amount);
+ StorageWindow::removeStore(item, amount);
break;
default:
break;
diff --git a/src/gui/palette.cpp b/src/gui/palette.cpp
index e7c49c89..d2309399 100644
--- a/src/gui/palette.cpp
+++ b/src/gui/palette.cpp
@@ -99,6 +99,7 @@ Palette::Palette() :
addColor(WHISPER, 0x00feaf, STATIC, indent + _("Whisper"), 'W');
addColor(IS, 0xa08527, STATIC, indent + _("Is"), 'I');
addColor(PARTY, 0xf48055, STATIC, indent + _("Party"), 'P');
+ addColor(GUILD, 0xf48055, STATIC, indent + _("Guild"), 'U');
addColor(SERVER, 0x8415e2, STATIC, indent + _("Server"), 'S');
addColor(LOGGER, 0x919191, STATIC, indent + _("Logger"), 'L');
addColor(HYPERLINK, 0xe50d0d, STATIC, indent + _("Hyperlink"), '<');
diff --git a/src/gui/palette.h b/src/gui/palette.h
index e353f318..0c1d2df0 100644
--- a/src/gui/palette.h
+++ b/src/gui/palette.h
@@ -71,6 +71,7 @@ class Palette : public gcn::ListModel
ENTRY(WHISPER)\
ENTRY(IS)\
ENTRY(PARTY)\
+ ENTRY(GUILD)\
ENTRY(SERVER)\
ENTRY(LOGGER)\
ENTRY(HYPERLINK)\
diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp
index e9c71496..7e4bfd65 100644
--- a/src/gui/popupmenu.cpp
+++ b/src/gui/popupmenu.cpp
@@ -320,8 +320,8 @@ void PopupMenu::handleLink(const std::string &link)
else if (link == "retrieve")
{
- ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove,
- storageWindow, mItem);
+ ItemAmountWindow::showWindow(ItemAmountWindow::StoreRemove, mWindow,
+ mItem);
}
else if (link == "party" && being && being->getType() == Being::PLAYER)
@@ -356,10 +356,12 @@ void PopupMenu::handleLink(const std::string &link)
mItem = NULL;
}
-void PopupMenu::showPopup(int x, int y, Item *item, bool isInventory)
+void PopupMenu::showPopup(Window *parent, int x, int y, Item *item,
+ bool isInventory)
{
assert(item);
mItem = item;
+ mWindow = parent;
mBrowserBox->clearRows();
if (isInventory)
@@ -384,7 +386,7 @@ void PopupMenu::showPopup(int x, int y, Item *item, bool isInventory)
mBrowserBox->addRow(strprintf("@@split|%s@@", _("Split")));
}
- if (player_node->getInStorage())
+ if (StorageWindow::isActive())
{
mBrowserBox->addRow(strprintf("@@store|%s@@", _("Store")));
}
diff --git a/src/gui/popupmenu.h b/src/gui/popupmenu.h
index 3299694f..3bb49967 100644
--- a/src/gui/popupmenu.h
+++ b/src/gui/popupmenu.h
@@ -29,6 +29,7 @@ class Being;
class BrowserBox;
class FloorItem;
class Item;
+class Window;
/**
* Window showing popup menu.
@@ -56,7 +57,8 @@ class PopupMenu : public Popup, public LinkHandler
* Shows the related popup menu when right click on the inventory
* at the specified mouse coordinates.
*/
- void showPopup(int x, int y, Item *item, bool isInventory);
+ void showPopup(Window *parent, int x, int y, Item *item,
+ bool isInventory);
/**
* Handles link action.
@@ -70,6 +72,8 @@ class PopupMenu : public Popup, public LinkHandler
FloorItem* mFloorItem;
Item *mItem;
+ Window *mWindow;
+
/**
* Shared code for the various showPopup functions.
*/
diff --git a/src/gui/recorder.cpp b/src/gui/recorder.cpp
index 7cb6e055..257afd7f 100644
--- a/src/gui/recorder.cpp
+++ b/src/gui/recorder.cpp
@@ -102,7 +102,7 @@ void Recorder::setRecordingFile(const std::string &msg)
* recorded.
*/
localChatTab->chatLog(_("Starting to record..."), BY_SERVER);
- const std::string file = Client::getHomeDirectory() + msgCopy;
+ const std::string file = Client::getLocalDataDirectory() + "/" + msgCopy;
mStream.open(file.c_str(), std::ios_base::trunc);
diff --git a/src/gui/setup_colors.cpp b/src/gui/setup_colors.cpp
index c476e971..bc86e362 100644
--- a/src/gui/setup_colors.cpp
+++ b/src/gui/setup_colors.cpp
@@ -300,6 +300,7 @@ void Setup_Colors::valueChanged(const gcn::SelectionEvent &event)
case Palette::WHISPER:
case Palette::IS:
case Palette::PARTY:
+ case Palette::GUILD:
case Palette::SERVER:
case Palette::LOGGER:
case Palette::HYPERLINK:
diff --git a/src/gui/socialwindow.cpp b/src/gui/socialwindow.cpp
index 48019bd0..26184cae 100644
--- a/src/gui/socialwindow.cpp
+++ b/src/gui/socialwindow.cpp
@@ -98,6 +98,8 @@ public:
{
setCaption(guild->getName());
+ setTabColor(&guiPalette->getColor(Palette::GUILD));
+
mList = new AvatarListBox(guild);
mScroll = new ScrollArea(mList);
@@ -161,6 +163,8 @@ public:
{
setCaption(party->getName());
+ setTabColor(&guiPalette->getColor(Palette::PARTY));
+
mList = new AvatarListBox(party);
mScroll = new ScrollArea(mList);
diff --git a/src/gui/storagewindow.cpp b/src/gui/storagewindow.cpp
index ff0ba162..bf54afb9 100644
--- a/src/gui/storagewindow.cpp
+++ b/src/gui/storagewindow.cpp
@@ -52,9 +52,11 @@
#include <string>
-StorageWindow::StorageWindow(int invSize):
+StorageWindow::WindowList StorageWindow::instances;
+
+StorageWindow::StorageWindow(Inventory *inventory):
Window(_("Storage")),
- mMaxSlots(invSize),
+ mInventory(inventory),
mItemDesc(false)
{
setWindowName("Storage");
@@ -70,19 +72,19 @@ StorageWindow::StorageWindow(int invSize):
mCloseButton = new Button(_("Close"), "close", this);
- mItems = new ItemContainer(player_node->getStorage(), true);
+ mItems = new ItemContainer(mInventory, true);
mItems->addSelectionListener(this);
gcn::ScrollArea *invenScroll = new ScrollArea(mItems);
invenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
- mUsedSlots = player_node->getStorage()->getNumberOfSlotsUsed();
+ mUsedSlots = mInventory->getNumberOfSlotsUsed();
mSlotsLabel = new Label(_("Slots:"));
mSlotsBar = new ProgressBar(0.0f, 100, 20, gcn::Color(225, 200, 25));
- mSlotsBar->setText(strprintf("%d/%d", mUsedSlots, mMaxSlots));
- mSlotsBar->setProgress((float) mUsedSlots / mMaxSlots);
+ mSlotsBar->setText(strprintf("%d/%d", mUsedSlots, mInventory->getSize()));
+ mSlotsBar->setProgress((float) mUsedSlots / mInventory->getSize());
setMinHeight(130);
setMinWidth(200);
@@ -98,10 +100,14 @@ StorageWindow::StorageWindow(int invSize):
layout.setRowHeight(0, mStoreButton->getHeight());
loadWindowState();
+
+ instances.push_back(this);
+ setVisible(true);
}
StorageWindow::~StorageWindow()
{
+ instances.remove(this);
}
void StorageWindow::logic()
@@ -111,15 +117,16 @@ void StorageWindow::logic()
Window::logic();
- const int usedSlots = player_node->getStorage()->getNumberOfSlotsUsed();
+ const int usedSlots = mInventory->getNumberOfSlotsUsed();
if (mUsedSlots != usedSlots)
{
mUsedSlots = usedSlots;
- mSlotsBar->setProgress((float) mUsedSlots / mMaxSlots);
+ mSlotsBar->setProgress((float) mUsedSlots / mInventory->getSize());
- mSlotsBar->setText(strprintf("%d/%d", mUsedSlots, mMaxSlots));
+ mSlotsBar->setText(strprintf("%d/%d", mUsedSlots,
+ mInventory->getSize()));
}
}
@@ -173,7 +180,7 @@ void StorageWindow::mouseClicked(gcn::MouseEvent &event)
*/
const int mx = event.getX() + getX();
const int my = event.getY() + getY();
- viewport->showPopup(mx, my, item, false);
+ viewport->showPopup(this, mx, my, item, false);
}
if (event.getButton() == gcn::MouseEvent::LEFT)
{
@@ -211,4 +218,6 @@ void StorageWindow::removeStore(Item *item, int amount)
void StorageWindow::close()
{
Net::getInventoryHandler()->closeStorage(Net::InventoryHandler::STORAGE);
+
+ scheduleDelete();
}
diff --git a/src/gui/storagewindow.h b/src/gui/storagewindow.h
index 766f2b1a..046b7613 100644
--- a/src/gui/storagewindow.h
+++ b/src/gui/storagewindow.h
@@ -49,8 +49,7 @@ class StorageWindow : public Window, gcn::ActionListener,
/**
* Constructor.
*/
- StorageWindow(int invSize = Net::getInventoryHandler()
- ->getSize(Net::InventoryHandler::STORAGE));
+ StorageWindow(Inventory *inventory);
/**
* Destructor.
@@ -75,22 +74,31 @@ class StorageWindow : public Window, gcn::ActionListener,
void mouseClicked(gcn::MouseEvent &event);
/**
+ * Closes the Storage Window, as well as telling the server that the
+ * window has been closed.
+ */
+ void close();
+
+ /**
* Add the specified ammount of the specified item to storage
*/
- void addStore(Item* item, int amount);
+ static void addStore(Item* item, int amount);
/**
* Remove the specified ammount of the specified item from storage
*/
- void removeStore(Item* item, int amount);
+ static void removeStore(Item* item, int amount);
/**
- * Closes the Storage Window, as well as telling the server that the
- * window has been closed.
+ * Returns true if any instances exist.
*/
- void close();
+ static bool isActive() { return instances.size() > 0; }
private:
+ typedef std::list<StorageWindow*> WindowList;
+ static WindowList instances;
+
+ Inventory *mInventory;
ItemContainer *mItems;
int mSlots;
@@ -101,11 +109,7 @@ class StorageWindow : public Window, gcn::ActionListener,
ProgressBar *mSlotsBar;
- int mMaxSlots;
-
bool mItemDesc;
};
-extern StorageWindow *storageWindow;
-
#endif // STORAGEWINDOW_H
diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp
index 48f19b9a..763d0c43 100644
--- a/src/gui/viewport.cpp
+++ b/src/gui/viewport.cpp
@@ -449,9 +449,10 @@ void Viewport::mouseReleased(gcn::MouseEvent &event)
mLocalWalkTime = -1;
}
-void Viewport::showPopup(int x, int y, Item *item, bool isInventory)
+void Viewport::showPopup(Window *parent, int x, int y, Item *item,
+ bool isInventory)
{
- mPopupMenu->showPopup(x, y, item, isInventory);
+ mPopupMenu->showPopup(parent, x, y, item, isInventory);
}
void Viewport::closePopupMenu()
diff --git a/src/gui/viewport.h b/src/gui/viewport.h
index 9a64ff78..5b92d950 100644
--- a/src/gui/viewport.h
+++ b/src/gui/viewport.h
@@ -30,13 +30,14 @@
#include <guichan/mouselistener.hpp>
class Being;
+class BeingPopup;
class FloorItem;
class Graphics;
class ImageSet;
class Item;
class Map;
class PopupMenu;
-class BeingPopup;
+class Window;
/** Delay between two mouse calls when dragging mouse and move the player */
const int walkingMouseDelay = 500;
@@ -107,7 +108,8 @@ class Viewport : public WindowContainer, public gcn::MouseListener,
* Shows a popup for an item.
* TODO Find some way to get rid of Item here
*/
- void showPopup(int x, int y, Item *item, bool isInventory = true);
+ void showPopup(Window *parent, int x, int y, Item *item,
+ bool isInventory = true);
/**
* Closes the popup menu. Needed for when the player dies or switching
diff --git a/src/gui/widgets/itemshortcutcontainer.cpp b/src/gui/widgets/itemshortcutcontainer.cpp
index b2ddcf0c..66e053d8 100644
--- a/src/gui/widgets/itemshortcutcontainer.cpp
+++ b/src/gui/widgets/itemshortcutcontainer.cpp
@@ -193,7 +193,7 @@ void ItemShortcutContainer::mousePressed(gcn::MouseEvent &event)
// Convert relative to the window coordinates to absolute screen
// coordinates.
- viewport->showPopup(viewport->getMouseX(), viewport->getMouseY(), item);
+ viewport->showPopup(NULL, viewport->getMouseX(), viewport->getMouseY(), item);
}
}
diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp
index 803de396..278b4cb9 100644
--- a/src/gui/widgets/textfield.cpp
+++ b/src/gui/widgets/textfield.cpp
@@ -31,6 +31,7 @@
#include "resources/image.h"
#include "resources/resourcemanager.h"
+#include "utils/copynpaste.h"
#include "utils/dtor.h"
#include <guichan/font.hpp>
@@ -250,8 +251,23 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent)
if (mLoseFocusOnTab)
return;
break;
+
+ case 22: // Control code 22, SYNCHRONOUS IDLE, sent on Ctrl+v
+ handlePaste();
+ break;
}
keyEvent.consume();
fixScroll();
}
+
+void TextField::handlePaste()
+{
+ std::string text = getText();
+ std::string::size_type caretPos = getCaretPosition();
+
+ if (RetrieveBuffer(text, caretPos)) {
+ setText(text);
+ setCaretPosition(caretPos);
+ }
+}
diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h
index c66139cd..58e37f5c 100644
--- a/src/gui/widgets/textfield.h
+++ b/src/gui/widgets/textfield.h
@@ -91,6 +91,8 @@ class TextField : public gcn::TextField
int getValue() const;
private:
+ void handlePaste();
+
static int instances;
static float mAlpha;
static ImageRect skin;
diff --git a/src/guild.cpp b/src/guild.cpp
index b4ecaa7e..65515cd7 100644
--- a/src/guild.cpp
+++ b/src/guild.cpp
@@ -24,22 +24,19 @@
#include "beingmanager.h"
#include "player.h"
-GuildMember::GuildMember(int guildId, int id, const std::string &name):
- Avatar(name), mId(id)
+GuildMember::GuildMember(Guild *guild, int id, const std::string &name):
+ Avatar(name), mId(id), mGuild(guild)
{
- mGuild = Guild::getGuild(guildId);
}
-GuildMember::GuildMember(int guildId, int id):
- mId(id)
+GuildMember::GuildMember(Guild *guild, int id):
+ mId(id), mGuild(guild)
{
- mGuild = Guild::getGuild(guildId);
}
-GuildMember::GuildMember(int guildId, const std::string &name):
- Avatar(name), mId(0)
+GuildMember::GuildMember(Guild *guild, const std::string &name):
+ Avatar(name), mId(0), mGuild(guild)
{
- mGuild = Guild::getGuild(guildId);
}
Guild::GuildMap Guild::guilds;
@@ -51,16 +48,49 @@ Guild::Guild(short id):
guilds[id] = this;
}
-void Guild::addMember(GuildMember *member)
+GuildMember *Guild::addMember(int id, const std::string &name)
{
- if (member->mGuild > 0 && member->mGuild != this)
- throw "Member in another guild!";
+ GuildMember *m;
+ if ((m = getMember(id)))
+ {
+ return m;
+ }
+
+ m = new GuildMember(this, id, name);
+
+ mMembers.push_back(m);
- if (!isMember(member))
+ return m;
+}
+
+GuildMember *Guild::addMember(int id)
+{
+ GuildMember *m;
+ if ((m = getMember(id)))
{
- mMembers.push_back(member);
- member->mGuild = this;
+ return m;
}
+
+ m = new GuildMember(this, id);
+
+ mMembers.push_back(m);
+
+ return m;
+}
+
+GuildMember *Guild::addMember(const std::string &name)
+{
+ GuildMember *m;
+ if ((m = getMember(name)))
+ {
+ return m;
+ }
+
+ m = new GuildMember(this, name);
+
+ mMembers.push_back(m);
+
+ return m;
}
GuildMember *Guild::getMember(int id)
@@ -211,7 +241,7 @@ bool Guild::isMember(const std::string &name) const
return false;
}
-const void Guild::getNames(std::vector<std::string> &names) const
+void Guild::getNames(std::vector<std::string> &names) const
{
names.clear();
MemberList::const_iterator it = mMembers.begin(),
diff --git a/src/guild.h b/src/guild.h
index 640e495e..9b0eff9c 100644
--- a/src/guild.h
+++ b/src/guild.h
@@ -35,12 +35,6 @@ class Guild;
class GuildMember : public Avatar
{
public:
- GuildMember(int guildId, int id, const std::string &name);
-
- GuildMember(int guildId, int id);
-
- GuildMember(int guildId, const std::string &name);
-
int getID() const { return mId; }
void setID(int id) { mId = id; }
@@ -50,6 +44,12 @@ public:
protected:
friend class Guild;
+ GuildMember(Guild *guild, int id, const std::string &name);
+
+ GuildMember(Guild *guild, int id);
+
+ GuildMember(Guild *guild, const std::string &name);
+
int mId;
Guild *mGuild;
};
@@ -69,7 +69,17 @@ public:
/**
* Adds member to the list.
*/
- void addMember(GuildMember *member);
+ GuildMember *addMember(int id, const std::string &name);
+
+ /**
+ * Adds member to the list.
+ */
+ GuildMember *addMember(int id);
+
+ /**
+ * Adds member to the list.
+ */
+ GuildMember *addMember(const std::string &name);
/**
* Find a member by ID.
@@ -149,7 +159,7 @@ public:
bool isMember(const std::string &name) const;
- const void getNames(std::vector<std::string> &names) const;
+ void getNames(std::vector<std::string> &names) const;
static Guild *getGuild(int id);
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index d7f64113..8ce03bba 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -77,7 +77,6 @@ LocalPlayer *player_node = NULL;
LocalPlayer::LocalPlayer(int id, int job):
Player(id, job, 0),
mEquipment(new Equipment),
- mInStorage(false),
mAttackRange(0),
mTargetTime(-1),
mLastTarget(-1),
@@ -101,8 +100,6 @@ LocalPlayer::LocalPlayer(int id, int job):
mInventory(new Inventory(Net::getInventoryHandler()
->getSize(Net::InventoryHandler::INVENTORY))),
mLocalWalkTime(-1),
- mStorage(new Inventory(Net::getInventoryHandler()
- ->getSize(Net::InventoryHandler::STORAGE))),
mMessageTime(0)
{
// Variable to keep the local player from doing certain actions before a map
@@ -124,7 +121,6 @@ LocalPlayer::LocalPlayer(int id, int job):
LocalPlayer::~LocalPlayer()
{
delete mInventory;
- delete mStorage;
config.removeListener("showownname", this);
@@ -1095,13 +1091,6 @@ void LocalPlayer::loadTargetCursor(const std::string &filename,
mTargetCursor[index][size] = currentCursor;
}
-void LocalPlayer::setInStorage(bool inStorage)
-{
- mInStorage = inStorage;
-
- storageWindow->setVisible(inStorage);
-}
-
void LocalPlayer::addMessageToQueue(const std::string &message,
Palette::ColorType color)
{
diff --git a/src/localplayer.h b/src/localplayer.h
index 6279d546..919b5540 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -133,11 +133,6 @@ class LocalPlayer : public Player
Inventory *getInventory() const { return mInventory; }
/**
- * Returns the player's storage
- */
- Inventory *getStorage() const { return mStorage; }
-
- /**
* Check the player has permission to invite users to specific guild
*/
bool checkInviteRights(const std::string &guildName);
@@ -264,12 +259,6 @@ class LocalPlayer : public Player
*/
void pickedUp(const ItemInfo &itemInfo, int amount);
- /**
- * Accessors for mInStorage
- */
- bool getInStorage() { return mInStorage; }
- void setInStorage(bool inStorage);
-
int getHp() const
{ return mHp; }
@@ -430,8 +419,6 @@ class LocalPlayer : public Player
void startWalking(unsigned char dir);
- bool mInStorage; /**< Whether storage is currently accessible */
-
int mAttackRange;
int mTargetTime; /** How long the being has been targeted **/
@@ -482,8 +469,6 @@ class LocalPlayer : public Player
int mLocalWalkTime; /**< Timestamp used to control keyboard walk
messages flooding */
- Inventory *mStorage;
-
/** Load the target cursors into memory */
void initTargetCursor();
diff --git a/src/log.cpp b/src/log.cpp
index 432d1b04..ba1610fd 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -35,7 +35,7 @@
#include <sys/time.h>
Logger::Logger():
- mLogToStandardOut(false),
+ mLogToStandardOut(true),
mChatWindow(NULL)
{
}
@@ -61,11 +61,6 @@ void Logger::setLogFile(const std::string &logFilename)
void Logger::log(const char *log_text, ...)
{
- if (!mLogFile.is_open())
- {
- return;
- }
-
char* buf = new char[1024];
va_list ap;
@@ -94,7 +89,10 @@ void Logger::log(const char *log_text, ...)
<< (int)((tv.tv_usec / 10000) % 100)
<< "] ";
- mLogFile << timeStr.str() << buf << std::endl;
+ if (mLogFile.is_open())
+ {
+ mLogFile << timeStr.str() << buf << std::endl;
+ }
if (mLogToStandardOut)
{
diff --git a/src/main.cpp b/src/main.cpp
index 2fb9c112..56019976 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,26 +40,27 @@ static void printHelp()
using std::endl;
std::cout
- << _("mana") << endl << endl
+ << _("mana [options] [mana-file]") << endl << endl
<< _("Options:") << endl
- << _(" -C --config-file : Configuration file to use") << endl
- << _(" -d --data : Directory to load game data from") << endl
- << _(" -D --default : Choose default character server and "
- "character") << endl
+ << _(" -v --version : Display the version") << endl
<< _(" -h --help : Display this help") << endl
- << _(" -S --home-dir : Directory to use as home directory") << endl
- << _(" -i --screenshot-dir : Directory to store screenshots") << endl
- << _(" -H --update-host : Use this update host") << endl
+ << _(" -C --config-dir : Configuration directory to use") << endl
+ << _(" -U --username : Login with this username") << endl
<< _(" -P --password : Login with this password") << endl
<< _(" -c --character : Login with this character") << endl
- << _(" -p --port : Login server port") << endl
<< _(" -s --server : Login server name or IP") << endl
+ << _(" -p --port : Login server port") << endl
+ << _(" --update-host : Use this update host") << endl
+ << _(" -D --default : Choose default character server and "
+ "character") << endl
<< _(" -u --skip-update : Skip the update downloads") << endl
- << _(" -U --username : Login with this username") << endl
+ << _(" -d --data : Directory to load game data from") << endl
+ << _(" -L --localdata-dir : Directory to use as local data directory") << endl
+ << _(" --screenshot-dir : Directory to store screenshots") << endl
#ifdef USE_OPENGL
- << _(" -O --no-opengl : Disable OpenGL for this session") << endl
+ << _(" --no-opengl : Disable OpenGL for this session") << endl
#endif
- << _(" -v --version : Display the version") << endl;
+ ;
}
static void printVersion()
@@ -69,16 +70,16 @@ static void printVersion()
static void parseOptions(int argc, char *argv[], Client::Options &options)
{
- const char *optstring = "hvud:U:P:Dc:s:p:C:H:S:Oi:";
+ const char *optstring = "hvud:U:P:Dc:p:C:L:";
const struct option long_options[] = {
- { "config-file", required_argument, 0, 'C' },
+ { "config-dir", required_argument, 0, 'C' },
{ "data", required_argument, 0, 'd' },
{ "default", no_argument, 0, 'D' },
{ "password", required_argument, 0, 'P' },
{ "character", required_argument, 0, 'c' },
{ "help", no_argument, 0, 'h' },
- { "home-dir", required_argument, 0, 'S' },
+ { "localdata-dir", required_argument, 0, 'L' },
{ "update-host", required_argument, 0, 'H' },
{ "port", required_argument, 0, 'p' },
{ "server", required_argument, 0, 's' },
@@ -100,7 +101,7 @@ static void parseOptions(int argc, char *argv[], Client::Options &options)
switch (result)
{
case 'C':
- options.configPath = optarg;
+ options.configDir = optarg;
break;
case 'd':
options.dataPath = optarg;
@@ -108,7 +109,8 @@ static void parseOptions(int argc, char *argv[], Client::Options &options)
case 'D':
options.chooseDefault = true;
break;
- default: // Unknown option
+ case '?': // Unknown option
+ case ':': // Missing argument
case 'h':
options.printHelp = true;
break;
@@ -137,7 +139,7 @@ static void parseOptions(int argc, char *argv[], Client::Options &options)
options.printVersion = true;
break;
case 'S':
- options.homeDir = optarg;
+ options.localDataDir = optarg;
break;
case 'O':
options.noOpenGL = true;
@@ -147,6 +149,13 @@ static void parseOptions(int argc, char *argv[], Client::Options &options)
break;
}
}
+
+ // when there are still options left use the last
+ // one as branding file
+ if (optind < argc)
+ {
+ options.brandingPath = argv[optind];
+ }
}
#ifdef WIN32
diff --git a/src/map.cpp b/src/map.cpp
index 6d332d48..da3f2185 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -601,7 +601,8 @@ Path Map::findPath(int startX, int startY, int destX, int destY,
std::priority_queue<Location> openList;
// Return when destination not walkable
- if (!getWalk(destX, destY, walkmask)) return path;
+ if (!getWalk(destX, destY, walkmask))
+ return path;
// Reset starting tile's G cost to 0
MetaTile *startTile = getMetaTile(startX, startY);
diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp
index 8803fafb..dbfc0f3b 100644
--- a/src/net/ea/beinghandler.cpp
+++ b/src/net/ea/beinghandler.cpp
@@ -25,6 +25,7 @@
#include "beingmanager.h"
#include "client.h"
#include "effectmanager.h"
+#include "guild.h"
#include "localplayer.h"
#include "log.h"
#include "npc.h"
@@ -106,7 +107,7 @@ void BeingHandler::handleMessage(Net::MessageIn &msg)
int param1;
int stunMode;
Uint32 statusEffects;
- int type;
+ int type, guild;
Uint16 status;
Being *srcBeing, *dstBeing;
Player *player;
@@ -179,9 +180,19 @@ void BeingHandler::handleMessage(Net::MessageIn &msg)
hairColor = msg.readInt16();
shoes = msg.readInt16(); // clothes color - "abused" as shoes
gloves = msg.readInt16(); // head dir - "abused" as gloves
- msg.readInt16(); // guild
- msg.readInt16(); // unknown
- msg.readInt16(); // unknown
+ guild = msg.readInt32(); // guild
+ if (player)
+ {
+ if (guild == 0)
+ {
+ player->clearGuilds();
+ }
+ else
+ {
+ player->addGuild(Guild::getGuild(guild));
+ }
+ }
+ msg.readInt16(); // guild emblem
msg.readInt16(); // manner
dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3
msg.readInt8(); // karma
diff --git a/src/net/ea/generalhandler.cpp b/src/net/ea/generalhandler.cpp
index f7d495a6..6ca853fb 100644
--- a/src/net/ea/generalhandler.cpp
+++ b/src/net/ea/generalhandler.cpp
@@ -49,6 +49,7 @@
#include "net/ea/tradehandler.h"
#include "net/ea/specialhandler.h"
+#include "net/ea/gui/guildtab.h"
#include "net/ea/gui/partytab.h"
#include "net/messagein.h"
@@ -68,6 +69,7 @@ namespace EAthena {
ServerInfo charServer;
ServerInfo mapServer;
+extern Guild *eaGuild;
extern Party *eaParty;
GeneralHandler::GeneralHandler():
@@ -226,12 +228,14 @@ void GeneralHandler::guiWindowsLoaded()
void GeneralHandler::guiWindowsUnloaded()
{
+ socialWindow->removeTab(eaGuild);
socialWindow->removeTab(eaParty);
- if (partyTab)
- {
- delete partyTab;
- partyTab = 0;
- }
+
+ delete guildTab;
+ guildTab = 0;
+
+ delete partyTab;
+ partyTab = 0;
}
void GeneralHandler::clearHandlers()
diff --git a/src/net/ea/gui/guildtab.cpp b/src/net/ea/gui/guildtab.cpp
new file mode 100644
index 00000000..e88c289e
--- /dev/null
+++ b/src/net/ea/gui/guildtab.cpp
@@ -0,0 +1,117 @@
+/*
+ * The Mana Client
+ * Copyright (C) 2008-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 "net/ea/gui/guildtab.h"
+
+#include "commandhandler.h"
+#include "guild.h"
+#include "localplayer.h"
+
+#include "gui/palette.h"
+
+#include "net/net.h"
+#include "net/guildhandler.h"
+
+#include "resources/iteminfo.h"
+#include "resources/itemdb.h"
+
+#include "utils/dtor.h"
+#include "utils/gettext.h"
+#include "utils/stringutils.h"
+
+namespace EAthena {
+
+extern Guild *eaGuild;
+
+GuildTab::GuildTab() :
+ ChatTab(_("Guild"))
+{
+ setTabColor(&guiPalette->getColor(Palette::GUILD));
+}
+
+GuildTab::~GuildTab()
+{
+}
+
+void GuildTab::handleInput(const std::string &msg)
+{
+ Net::getGuildHandler()->chat(eaGuild->getId(), msg);
+}
+
+void GuildTab::showHelp()
+{
+ chatLog(_("/help > Display this help."));
+ chatLog(_("/invite > Invite a player to your guild"));
+ chatLog(_("/leave > Leave the guild you are in"));
+ chatLog(_("/kick > Kick some one from the guild you are in"));
+}
+
+bool GuildTab::handleCommand(const std::string &type, const std::string &args)
+{
+ if (type == "help")
+ {
+ if (args == "invite")
+ {
+ chatLog(_("Command: /invite <nick>"));
+ chatLog(_("This command invites <nick> to the guild you're in."));
+ chatLog(_("If the <nick> has spaces in it, enclose it in "
+ "double quotes (\")."));
+ }
+ else if (args == "leave")
+ {
+ chatLog(_("Command: /leave"));
+ chatLog(_("This command causes the player to leave the guild."));
+ }
+ else
+ return false;
+ }
+ else if (type == "create" || type == "new")
+ {
+ if (args.empty())
+ chatLog(_("Guild name is missing."), BY_SERVER);
+ else
+ Net::getGuildHandler()->create(args);
+ }
+ else if (type == "invite")
+ {
+ Net::getGuildHandler()->invite(eaGuild->getId(), args);
+ }
+ else if (type == "leave")
+ {
+ Net::getGuildHandler()->leave(eaGuild->getId());
+ }
+ else if (type == "kick")
+ {
+ Net::getGuildHandler()->kick(eaGuild->getMember(args));
+ }
+ else
+ return false;
+
+ return true;
+}
+
+void GuildTab::getAutoCompleteList(std::vector<std::string> &names) const
+{
+ if (eaGuild)
+ eaGuild->getNames(names);
+}
+
+} // namespace EAthena
diff --git a/src/net/ea/gui/guildtab.h b/src/net/ea/gui/guildtab.h
new file mode 100644
index 00000000..58c8f539
--- /dev/null
+++ b/src/net/ea/gui/guildtab.h
@@ -0,0 +1,52 @@
+/*
+ * The Mana Client
+ * Copyright (C) 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/>.
+ */
+
+#ifndef EA_GUILDTAB_H
+#define EA_GUILDTAB_H
+
+#include "gui/widgets/chattab.h"
+
+namespace EAthena {
+
+/**
+ * A tab for a guild chat channel.
+ */
+class GuildTab : public ChatTab
+{
+ public:
+ GuildTab();
+ ~GuildTab();
+
+ void showHelp();
+
+ bool handleCommand(const std::string &type, const std::string &args);
+
+ protected:
+ void handleInput(const std::string &msg);
+
+ void getAutoCompleteList(std::vector<std::string> &names) const;
+};
+
+extern GuildTab *guildTab;
+
+} // namespace EAthena
+
+#endif // EA_GUILDTAB_H
diff --git a/src/net/ea/guildhandler.cpp b/src/net/ea/guildhandler.cpp
index 66576da4..bb2fdff7 100644
--- a/src/net/ea/guildhandler.cpp
+++ b/src/net/ea/guildhandler.cpp
@@ -20,26 +20,55 @@
#include "net/ea/guildhandler.h"
+#include "guild.h"
#include "localplayer.h"
#include "log.h"
-#include "gui/widgets/chattab.h"
+#include "gui/socialwindow.h"
#include "net/ea/messagein.h"
#include "net/ea/protocol.h"
+#include "net/ea/gui/guildtab.h"
+
#include "utils/gettext.h"
extern Net::GuildHandler *guildHandler;
namespace EAthena {
+GuildTab *guildTab = 0;
+Guild *eaGuild;
+
GuildHandler::GuildHandler()
{
static const Uint16 _messages[] = {
SMSG_GUILD_CREATE_RESPONSE,
- SMSG_GUILD_INVITE_ACK,
+ SMSG_GUILD_POSITION_INFO,
+ SMSG_GUILD_MEMBER_LOGIN,
+ SMSG_GUILD_MASTER_OR_MEMBER,
+ SMSG_GUILD_BASIC_INFO,
+ SMSG_GUILD_ALIANCE_INFO,
+ SMSG_GUILD_MEMBER_LIST,
+ SMSG_GUILD_POS_NAME_LIST,
+ SMSG_GUILD_POS_INFO_LIST,
+ SMSG_GUILD_POSITION_CHANGED,
+ SMSG_GUILD_MEMBER_POS_CHANGE,
+ SMSG_GUILD_EMBLEM,
+ SMSG_GUILD_SKILL_INFO,
+ SMSG_GUILD_NOTICE,
SMSG_GUILD_INVITE,
+ SMSG_GUILD_INVITE_ACK,
+ SMSG_GUILD_LEAVE,
+ SMSG_GUILD_EXPULSION,
+ SMSG_GUILD_EXPULSION_LIST,
+ SMSG_GUILD_MESSAGE,
+ SMSG_GUILD_SKILL_UP,
+ SMSG_GUILD_REQ_ALLIANCE,
+ SMSG_GUILD_REQ_ALLIANCE_ACK,
+ SMSG_GUILD_DEL_ALLIANCE,
+ SMSG_GUILD_OPPOSITION_ACK,
+ SMSG_GUILD_BROKEN,
0
};
handledMessages = _messages;
@@ -47,6 +76,12 @@ GuildHandler::GuildHandler()
guildHandler = this;
}
+GuildHandler::~GuildHandler()
+{
+ delete guildTab;
+ guildTab = 0;
+}
+
void GuildHandler::handleMessage(Net::MessageIn &msg)
{
switch (msg.getId())
@@ -73,53 +108,280 @@ void GuildHandler::handleMessage(Net::MessageIn &msg)
}
}
break;
- case SMSG_GUILD_INVITE_ACK:
+
+ case SMSG_GUILD_POSITION_INFO:
{
- int flag = msg.readInt8();
+ int guildId = msg.readInt32();
+ int emblem = msg.readInt32();
+ int posMode = msg.readInt32();
+ msg.readInt32(); // Unused
+ msg.readInt8(); // Unused
+ std::string guildName = msg.readString(24);
- if (flag == 0)
+ logger->log("Guild position info: %d %d %d %s\n", guildId,
+ emblem, posMode, guildName.c_str());
+ }
+ break;
+
+ case SMSG_GUILD_MEMBER_LOGIN:
+ msg.readInt32(); // Account ID
+ msg.readInt32(); // Char ID
+ msg.readInt32(); // Flag
+ break;
+
+ case SMSG_GUILD_MASTER_OR_MEMBER:
+ msg.readInt32(); // Type (0x57 for member, 0xd7 for master)
+ break;
+
+ case SMSG_GUILD_BASIC_INFO:
+ {
+ int guildID = msg.readInt32(); // Guild ID
+ msg.readInt32(); // Guild level
+ msg.readInt32(); // 'Connect member' (number online?)
+ msg.readInt32(); // 'Max member'
+ msg.readInt32(); // Average level
+ msg.readInt32(); // Exp
+ msg.readInt32(); // Next exp
+ msg.skip(16); // unused
+ std::string name = msg.readString(24); // Name
+ msg.readString(24); // Master's name
+ msg.readString(20); // Castles (ie: "Six Castles" or "None Taken")
+
+ Guild *g = Guild::getGuild(guildID);
+ g->setName(name);
+ }
+ break;
+
+ case SMSG_GUILD_ALIANCE_INFO:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 32;
+
+ for (int i = 0; i < count; i++)
{
- // Fail (already in guild, busy, etc)
+ msg.readInt32(); // 'Opposition'
+ msg.readInt32(); // Other guild ID
+ msg.readString(24); // Other guild name
}
- else if (flag == 1)
+ }
+ break;
+
+ case SMSG_GUILD_MEMBER_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 104;
+
+ eaGuild->clearMembers();
+
+ for (int i = 0; i < count; i++)
{
- // Rejected
+ int id = msg.readInt32(); // Account ID
+ msg.readInt32(); // Char ID
+ msg.readInt16(); // Hair
+ msg.readInt16(); // Hair color
+ msg.readInt16(); // Gender
+ msg.readInt16(); // Class
+ msg.readInt16(); // Level
+ msg.readInt32(); // Exp
+ int online = msg.readInt32(); // Online
+ msg.readInt32(); // Position
+ msg.skip(50); // unused
+ std::string name = msg.readString(24); // Name
+
+ GuildMember *m = eaGuild->addMember(id, name);
+ m->setOnline(online);
}
- else if (flag == 2)
+ }
+ break;
+
+ case SMSG_GUILD_POS_NAME_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 28;
+
+ for (int i = 0; i < count; i++)
{
- // Accepted
+ msg.readInt32(); // ID
+ msg.readString(24); // Position name
}
- else if (flag == 3)
+ }
+ break;
+
+ case SMSG_GUILD_POS_INFO_LIST:
+ {
+ int length = msg.readInt16();
+ int count = (length - 4) / 16;
+
+ for (int i = 0; i < count; i++)
{
- // Guild full
+ msg.readInt32(); // ID
+ msg.readInt32(); // Mode
+ msg.readInt32(); // Same ID
+ msg.readInt32(); // Exp mode
}
}
break;
+ case SMSG_GUILD_POSITION_CHANGED:
+ msg.readInt16(); // Always 44
+ msg.readInt32(); // ID
+ msg.readInt32(); // Mode
+ msg.readInt32(); // Same ID
+ msg.readInt32(); // Exp mode
+ msg.readString(24); // Name
+ break;
+
+ case SMSG_GUILD_MEMBER_POS_CHANGE:
+ msg.readInt16(); // Always 16
+ msg.readInt32(); // Account ID
+ msg.readInt32(); // Char ID
+ msg.readInt32(); // Position
+ break;
+
+ case SMSG_GUILD_EMBLEM:
+ {
+ int length = msg.readInt16();
+
+ msg.readInt32(); // Guild ID
+ msg.readInt32(); // Emblem ID
+ msg.skip(length - 12); // Emblem data (unknown format)
+ }
+ break;
+
+ case SMSG_GUILD_SKILL_INFO:
+ {
+ int length = msg.readInt16();
+ int count = (length - 6) / 37;
+
+ msg.readInt16(); // 'Skill point'
+
+ for (int i = 0; i < count; i++)
+ {
+ msg.readInt16(); // ID
+ msg.readInt16(); // 'Info' (unknown atm)
+ msg.readInt16(); // unused
+ msg.readInt16(); // Level
+ msg.readInt16(); // SP
+ msg.readInt16(); // 'Range'
+ msg.skip(24); // unused
+ msg.readInt8(); // Can be increased
+ }
+ }
+ break;
+
+ case SMSG_GUILD_NOTICE:
+ msg.readString(60); // Mes1
+ msg.readString(120); // Mes2
+ break;
+
case SMSG_GUILD_INVITE:
{
int guildId = msg.readInt32();
std::string guildName = msg.readString(24);
- printf("Guild invite for %d (%s)\n", guildId, guildName.c_str());
+ socialWindow->showGuildInvite(guildName, guildId, "");
+ }
+ break;
+
+ case SMSG_GUILD_INVITE_ACK:
+ {
+ int flag = msg.readInt8();
+
+ switch (flag)
+ {
+ case 0:
+ guildTab->chatLog(_("Could not inivte user to guild."),
+ BY_SERVER);
+ break;
+
+ case 1:
+ guildTab->chatLog(_("User rejected guild invite."),
+ BY_SERVER);
+ break;
+
+ case 2:
+ guildTab->chatLog(_("User is now part of your guild."),
+ BY_SERVER);
+ break;
+
+ case 3:
+ guildTab->chatLog(_("Your guild is full."),
+ BY_SERVER);
+ break;
- // TODO
+ default:
+ guildTab->chatLog(_("Unknown guild invite response."),
+ BY_SERVER);
+ break;
+ }
}
break;
- case SMSG_GUILD_POSITION_INFO:
+ case SMSG_GUILD_LEAVE:
+ msg.readString(24); // Name
+ msg.readString(40); // Message
+ break;
+
+ case SMSG_GUILD_EXPULSION:
+ msg.readString(24); // Name (of expulsed?)
+ msg.readString(40); // Message
+ msg.skip(24); // unused ("dummy")
+ break;
+
+ case SMSG_GUILD_EXPULSION_LIST:
{
- int guildId = msg.readInt32();
- int emblem = msg.readInt32();
- int posMode = msg.readInt32();
- msg.readInt32(); // Unused
- msg.readInt8(); // Unused
- std::string guildName = msg.readString(24);
+ int length = msg.readInt16();
+ int count = (length - 4) / 88;
- logger->log("Guild position info: %d %d %d %s\n", guildId,
- emblem, posMode, guildName.c_str());
+ for (int i = 0; i < count; i++)
+ {
+ msg.readString(24); // Name (of expulsed?)
+ msg.readString(24); // 'Acc' (name of expulser?)
+ msg.readString(24); // Message
+ }
+ }
+ break;
+
+ case SMSG_GUILD_MESSAGE:
+ {
+ int msgLength = msg.readInt16() - 4;
+ if (msgLength <= 0)
+ {
+ return;
+ }
+ guildTab->chatLog(msg.readString(msgLength));
}
break;
+
+ case SMSG_GUILD_SKILL_UP:
+ msg.readInt16(); // Skill ID
+ msg.readInt16(); // Level
+ msg.readInt16(); // SP
+ msg.readInt16(); // 'Range'
+ msg.readInt8(); // unused? (always 1)
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE:
+ msg.readInt32(); // Account ID
+ msg.readString(24); // Name
+ break;
+
+ case SMSG_GUILD_REQ_ALLIANCE_ACK:
+ msg.readInt32(); // Flag
+ break;
+
+ case SMSG_GUILD_DEL_ALLIANCE:
+ msg.readInt32(); // Guild ID
+ msg.readInt32(); // Flag
+ break;
+
+ case SMSG_GUILD_OPPOSITION_ACK:
+ msg.readInt8(); // Flag
+ break;
+
+ case SMSG_GUILD_BROKEN:
+ msg.readInt32(); // Flag
+ break;
}
}
@@ -136,7 +398,7 @@ void GuildHandler::create(const std::string &name)
void GuildHandler::invite(int guildId, const std::string &name)
{
- // TODO
+ // TODO?
}
void GuildHandler::invite(int guildId, Player *player)
@@ -165,13 +427,13 @@ void GuildHandler::leave(int guildId)
msg.writeString("", 30); // Message
}
-void GuildHandler::kick(GuildMember member)
+void GuildHandler::kick(GuildMember *member, std::string reason)
{
MessageOut msg(CMSG_GUILD_EXPULSION);
- msg.writeInt32(member.getGuild()->getId());
- msg.writeInt32(member.getID()); // Account ID
+ msg.writeInt32(member->getGuild()->getId());
+ msg.writeInt32(member->getID()); // Account ID
msg.writeInt32(0); // Char ID
- msg.writeString("", 40); // Message
+ msg.writeString(reason, 40); // Message
}
void GuildHandler::chat(int guildId, const std::string &text)
@@ -183,10 +445,18 @@ void GuildHandler::chat(int guildId, const std::string &text)
void GuildHandler::memberList(int guildId)
{
- // TODO
+ // TODO four types of info requests:
+ // 0 = basic info + alliance info
+ // 1 = position name list + member list
+ // 2 = position name list + position info list
+ // 3 = skill info
+ // 4 = expulsion list
+
+ MessageOut msg(CMSG_GUILD_REQUEST_INFO);
+ msg.writeInt32(1); // Request member list
}
-void GuildHandler::changeMemberPostion(GuildMember member, int level)
+void GuildHandler::changeMemberPostion(GuildMember *member, int level)
{
// TODO
}
diff --git a/src/net/ea/guildhandler.h b/src/net/ea/guildhandler.h
index 8b1753c7..df43a2a1 100644
--- a/src/net/ea/guildhandler.h
+++ b/src/net/ea/guildhandler.h
@@ -32,6 +32,8 @@ class GuildHandler : public Net::GuildHandler, public MessageHandler
public:
GuildHandler();
+ ~GuildHandler();
+
void handleMessage(Net::MessageIn &msg);
void create(const std::string &name);
@@ -44,13 +46,13 @@ class GuildHandler : public Net::GuildHandler, public MessageHandler
void leave(int guildId);
- void kick(GuildMember member);
+ void kick(GuildMember *member, std::string reason = "");
void chat(int guildId, const std::string &text);
void memberList(int guildId);
- void changeMemberPostion(GuildMember member, int level);
+ void changeMemberPostion(GuildMember *member, int level);
void requestAlliance(int guildId, int otherGuildId);
diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp
index 1a0e296c..60288ed3 100644
--- a/src/net/ea/inventoryhandler.cpp
+++ b/src/net/ea/inventoryhandler.cpp
@@ -107,6 +107,20 @@ InventoryHandler::InventoryHandler()
};
handledMessages = _messages;
inventoryHandler = this;
+
+ mStorage = 0;
+ mStorageWindow = 0;
+}
+
+InventoryHandler::~InventoryHandler()
+{
+ if (mStorageWindow)
+ {
+ mStorageWindow->close();
+ mStorageWindow = 0;
+ }
+
+ delete mStorage;
}
void InventoryHandler::handleMessage(Net::MessageIn &msg)
@@ -115,7 +129,6 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
int index, amount, itemId, equipType, arrow;
int identified, cards[4], itemType;
Inventory *inventory = player_node->getInventory();
- Inventory *storage = player_node->getStorage();
switch (msg.getId())
{
@@ -129,6 +142,10 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
inventory->clear();
}
+ else
+ {
+ mInventoryItems.clear();
+ }
msg.readInt16(); // length
number = (msg.getLength() - 4) / 18;
@@ -157,20 +174,15 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
if (msg.getId() == SMSG_PLAYER_INVENTORY)
{
- inventory->setItem(index, itemId, amount, false);
-
// Trick because arrows are not considered equipment
- if (arrow & 0x8000)
- {
- if (Item *item = inventory->getItem(index))
- item->setEquipment(true);
- }
+ bool isEquipment = arrow & 0x8000;
- //const Item *item = inventory->getItem(index);
+ inventory->setItem(index, itemId, amount, isEquipment);
}
else
{
- storage->setItem(index, itemId, amount, false);
+ mInventoryItems.push_back(InventoryItem(index, itemId,
+ amount, false));
}
}
break;
@@ -201,7 +213,8 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
cards[0], cards[1], cards[2], cards[3]);
}
- storage->setItem(index, itemId, amount, false);
+ mInventoryItems.push_back(InventoryItem(index, itemId, amount,
+ false));
}
break;
@@ -280,15 +293,27 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
* server. It always comes after the two SMSG_PLAYER_STORAGE_...
* packets that update storage contents.
*/
- player_node->setInStorage(true);
- msg.readInt16(); // Storage capacity
- msg.readInt16(); // Used count
+ {
+ msg.readInt16(); // Used count
+ int size = msg.readInt16(); // Max size
+
+ if (!mStorage)
+ mStorage = new Inventory(size);
+
+ InventoryItems::iterator it = mInventoryItems.begin();
+ InventoryItems::iterator it_end = mInventoryItems.end();
+ for (; it != it_end; it++)
+ mStorage->setItem((*it).slot, (*it).id, (*it).quantity,
+ (*it).equip);
+ mInventoryItems.clear();
+
+ if (!mStorageWindow)
+ mStorageWindow = new StorageWindow(mStorage);
+ }
break;
case SMSG_PLAYER_STORAGE_ADD:
- /*
- * Move an item into storage
- */
+ // Move an item into storage
index = msg.readInt16() - STORAGE_OFFSET;
amount = msg.readInt32();
itemId = msg.readInt16();
@@ -298,37 +323,38 @@ void InventoryHandler::handleMessage(Net::MessageIn &msg)
for (int i = 0; i < 4; i++)
cards[i] = msg.readInt16();
- if (Item *item = storage->getItem(index))
+ if (Item *item = mStorage->getItem(index))
{
item->setId(itemId);
item->increaseQuantity(amount);
}
else
{
- storage->setItem(index, itemId, amount, false);
+ mStorage->setItem(index, itemId, amount, false);
}
break;
case SMSG_PLAYER_STORAGE_REMOVE:
- /*
- * Move an item out of storage
- */
+ // Move an item out of storage
index = msg.readInt16() - STORAGE_OFFSET;
amount = msg.readInt16();
- if (Item *item = storage->getItem(index))
+ if (Item *item = mStorage->getItem(index))
{
item->increaseQuantity(-amount);
if (item->getQuantity() == 0)
- storage->removeItemAt(index);
+ mStorage->removeItemAt(index);
}
break;
case SMSG_PLAYER_STORAGE_CLOSE:
- /*
- * Storage access has been closed
- */
- storage->clear();
- player_node->setInStorage(false);
+ // Storage access has been closed
+
+ // Storage window deletes itself
+ mStorageWindow = 0;
+
+ mStorage->clear();
+ delete mStorage;
+ mStorage = 0;
break;
case SMSG_PLAYER_EQUIPMENT:
@@ -482,9 +508,9 @@ size_t InventoryHandler::getSize(StorageType type) const
case INVENTORY:
return 100;
case STORAGE:
- return 300;
+ return 0;
case GUILD_STORAGE:
- return 1000;
+ return 0;
default:
return 0;
}
diff --git a/src/net/ea/inventoryhandler.h b/src/net/ea/inventoryhandler.h
index a2d0d388..2699e584 100644
--- a/src/net/ea/inventoryhandler.h
+++ b/src/net/ea/inventoryhandler.h
@@ -26,11 +26,15 @@
#include "inventory.h"
#include "localplayer.h"
+#include "gui/storagewindow.h"
+
#include "net/inventoryhandler.h"
#include "net/net.h"
#include "net/ea/messagehandler.h"
+#include <list>
+
namespace EAthena {
class EquipBackend : public Equipment::Backend {
@@ -89,11 +93,35 @@ class EquipBackend : public Equipment::Backend {
int mEquipment[EQUIPMENT_SIZE];
};
+/**
+ * Used to cache storage data until we get size data for it.
+ */
+class InventoryItem
+{
+ public:
+ int slot;
+ int id;
+ int quantity;
+ bool equip;
+
+ InventoryItem(int slot, int id, int quantity, bool equip)
+ {
+ this->slot = slot;
+ this->id = id;
+ this->quantity = quantity;
+ this->equip = equip;
+ }
+};
+
+typedef std::list<InventoryItem> InventoryItems;
+
class InventoryHandler : public MessageHandler, public Net::InventoryHandler
{
public:
InventoryHandler();
+ ~InventoryHandler();
+
void handleMessage(Net::MessageIn &msg);
void equipItem(const Item *item);
@@ -121,6 +149,9 @@ class InventoryHandler : public MessageHandler, public Net::InventoryHandler
private:
EquipBackend mEquips;
+ InventoryItems mInventoryItems;
+ Inventory *mStorage;
+ StorageWindow *mStorageWindow;
};
} // namespace EAthena
diff --git a/src/net/ea/partyhandler.cpp b/src/net/ea/partyhandler.cpp
index 157898dc..922e59d1 100644
--- a/src/net/ea/partyhandler.cpp
+++ b/src/net/ea/partyhandler.cpp
@@ -68,11 +68,8 @@ PartyHandler::PartyHandler():
PartyHandler::~PartyHandler()
{
- if (partyTab)
- {
- delete partyTab;
- partyTab = 0;
- }
+ delete partyTab;
+ partyTab = 0;
}
void PartyHandler::handleMessage(Net::MessageIn &msg)
@@ -104,11 +101,12 @@ void PartyHandler::handleMessage(Net::MessageIn &msg)
bool leader = msg.readInt8() == 0;
bool online = msg.readInt8() == 0;
- PartyMember *member = new PartyMember(PARTY_ID, id, nick);
+ PartyMember *member = eaParty->addMember(id, nick);
member->setLeader(leader);
member->setOnline(online);
- eaParty->addMember(member);
}
+
+ player_node->setParty(eaParty);
}
break;
case SMSG_PARTY_INVITE_RESPONSE:
@@ -270,6 +268,13 @@ void PartyHandler::handleMessage(Net::MessageIn &msg)
m->setHp(hp);
m->setMaxHp(maxhp);
}
+
+ // The server only sends this when the member is in range, so
+ // lets make sure they get the party hilight.
+ if (Being *b = beingManager->findBeing(id))
+ {
+ static_cast<Player*>(b)->setParty(eaParty);
+ }
}
break;
case SMSG_PARTY_UPDATE_COORDS:
diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp
index 46919488..89e55f92 100644
--- a/src/net/ea/playerhandler.cpp
+++ b/src/net/ea/playerhandler.cpp
@@ -86,8 +86,6 @@ namespace {
NpcDialog::closeAll();
SellDialog::closeAll();
- if (storageWindow->isVisible())
- storageWindow->close();
viewport->closePopupMenu();
}
} deathListener;
diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h
index 85b4cc8a..75683944 100644
--- a/src/net/guildhandler.h
+++ b/src/net/guildhandler.h
@@ -42,13 +42,13 @@ class GuildHandler
virtual void leave(int guildId) = 0;
- virtual void kick(GuildMember member) = 0;
+ virtual void kick(GuildMember *member, std::string reason = "") = 0;
virtual void chat(int guildId, const std::string &text) = 0;
virtual void memberList(int guildId) = 0;
- virtual void changeMemberPostion(GuildMember member, int level) = 0;
+ virtual void changeMemberPostion(GuildMember *member, int level) = 0;
virtual void requestAlliance(int guildId, int otherGuildId) = 0;
diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h
index e3d0064d..9d0a5bc8 100644
--- a/src/net/inventoryhandler.h
+++ b/src/net/inventoryhandler.h
@@ -61,6 +61,7 @@ class InventoryHandler
virtual void moveItem(StorageType source, int slot, int amount,
StorageType destination) = 0;
+ // TODO: fix/remove me
virtual size_t getSize(StorageType type) const = 0;
virtual ~InventoryHandler() {}
diff --git a/src/net/manaserv/guildhandler.cpp b/src/net/manaserv/guildhandler.cpp
index 0dfe8cde..253efb01 100644
--- a/src/net/manaserv/guildhandler.cpp
+++ b/src/net/manaserv/guildhandler.cpp
@@ -131,9 +131,8 @@ void GuildHandler::handleMessage(Net::MessageIn &msg)
online = msg.readInt8();
if (name != "")
{
- member = new GuildMember(guildId, name);
+ member = guild->addMember(name);
member->setOnline(online);
- guild->addMember(member);
}
}
}
@@ -153,9 +152,8 @@ void GuildHandler::handleMessage(Net::MessageIn &msg)
switch(eventId)
{
case GUILD_EVENT_NEW_PLAYER:
- member = new GuildMember(guildId, name);
+ member = guild->addMember(name);
member->setOnline(true);
- guild->addMember(member);
break;
case GUILD_EVENT_LEAVING_PLAYER:
@@ -296,7 +294,7 @@ void GuildHandler::leave(int guildId)
chatServerConnection->send(msg);
}
-void GuildHandler::kick(GuildMember member)
+void GuildHandler::kick(GuildMember *member, std::string reason)
{
// TODO
}
@@ -313,7 +311,7 @@ void GuildHandler::memberList(int guildId)
chatServerConnection->send(msg);
}
-void GuildHandler::changeMemberPostion(GuildMember member, int level)
+void GuildHandler::changeMemberPostion(GuildMember *member, int level)
{
/*MessageOut msg(PCMSG_GUILD_PROMOTE_MEMBER);
msg.writeInt16(guildId);
diff --git a/src/net/manaserv/guildhandler.h b/src/net/manaserv/guildhandler.h
index 8fdd40a4..1b6d51b5 100644
--- a/src/net/manaserv/guildhandler.h
+++ b/src/net/manaserv/guildhandler.h
@@ -45,13 +45,13 @@ public:
void leave(int guildId);
- void kick(GuildMember member);
+ void kick(GuildMember *member, std::string reason = "");
void chat(int guildId, const std::string &text);
void memberList(int guildId);
- void changeMemberPostion(GuildMember member, int level);
+ void changeMemberPostion(GuildMember *member, int level);
void requestAlliance(int guildId, int otherGuildId);
diff --git a/src/net/manaserv/partyhandler.cpp b/src/net/manaserv/partyhandler.cpp
index 9ec8d2b8..ec153fa8 100644
--- a/src/net/manaserv/partyhandler.cpp
+++ b/src/net/manaserv/partyhandler.cpp
@@ -109,8 +109,7 @@ void PartyHandler::handleMessage(Net::MessageIn &msg)
if (id == player_node->getId())
player_node->setParty(mParty);
- PartyMember *member = new PartyMember(PARTY_ID, id, name);
- mParty->addMember(member);
+ mParty->addMember(id, name);
} break;
case CPMSG_PARTY_MEMBER_LEFT:
diff --git a/src/party.cpp b/src/party.cpp
index a8e18b2d..e01aab50 100644
--- a/src/party.cpp
+++ b/src/party.cpp
@@ -23,32 +23,19 @@
#include "beingmanager.h"
#include "player.h"
-PartyMember::PartyMember(int partyId, int id, const std::string &name):
- Avatar(name), mId(id), mLeader(false)
+PartyMember::PartyMember(Party *party, int id, const std::string &name):
+ Avatar(name), mId(id), mParty(party), mLeader(false)
{
- mParty = Party::getParty(partyId);
-
- if (beingManager)
- {
- Player *player = dynamic_cast<Player*>(beingManager->findBeing(id));
- if (player)
- {
- player->setParty(mParty);
- }
- }
}
-PartyMember::PartyMember(int PartyId, int id):
- mId(id), mLeader(false)
+PartyMember::PartyMember(Party *party, int id):
+ mId(id), mParty(party), mLeader(false)
{
- mParty = Party::getParty(PartyId);
+}
- if (beingManager)
- {
- Player *player = dynamic_cast<Player*>(beingManager->findBeing(id));
- if (player)
- player->setParty(mParty);
- }
+PartyMember::PartyMember(Party *party, const std::string &name):
+ Avatar(name), mParty(party), mLeader(false)
+{
}
Party::PartyMap Party::parties;
@@ -59,19 +46,49 @@ Party::Party(short id):
{
parties[id] = this;
}
+PartyMember *Party::addMember(int id, const std::string &name)
+{
+ PartyMember *m;
+ if ((m = getMember(id)))
+ {
+ return m;
+ }
+
+ m = new PartyMember(this, id, name);
-void Party::addMember(PartyMember *member)
+ mMembers.push_back(m);
+
+ return m;
+}
+
+PartyMember *Party::addMember(int id)
{
- if (member->mParty > 0 && member->mParty != this)
+ PartyMember *m;
+ if ((m = getMember(id)))
{
- throw "Member in another Party!";
+ return m;
}
- if (!isMember(member))
+ m = new PartyMember(this, id);
+
+ mMembers.push_back(m);
+
+ return m;
+}
+
+PartyMember *Party::addMember(const std::string &name)
+{
+ PartyMember *m;
+ if ((m = getMember(name)))
{
- mMembers.push_back(member);
- member->mParty = this;
+ return m;
}
+
+ m = new PartyMember(this, name);
+
+ mMembers.push_back(m);
+
+ return m;
}
PartyMember *Party::getMember(int id)
@@ -230,7 +247,7 @@ bool Party::isMember(const std::string &name) const
return false;
}
-const void Party::getNames(std::vector<std::string> &names) const
+void Party::getNames(std::vector<std::string> &names) const
{
names.clear();
MemberList::const_iterator it = mMembers.begin(),
diff --git a/src/party.h b/src/party.h
index e109d7b6..d50ed7b0 100644
--- a/src/party.h
+++ b/src/party.h
@@ -34,12 +34,6 @@ class Party;
class PartyMember : public Avatar
{
public:
- PartyMember(int partyId, int id, const std::string &name);
-
- PartyMember(int partyId, int id);
-
- PartyMember(int partyId, const std::string &name);
-
int getID() const { return mId; }
void setID(int id) { mId = id; }
@@ -53,6 +47,12 @@ public:
protected:
friend class Party;
+ PartyMember(Party *party, int id, const std::string &name);
+
+ PartyMember(Party *party, int id);
+
+ PartyMember(Party *party, const std::string &name);
+
int mId;
Party *mParty;
bool mLeader;
@@ -73,7 +73,17 @@ public:
/**
* Adds member to the list.
*/
- void addMember(PartyMember *member);
+ PartyMember *addMember(int id, const std::string &name);
+
+ /**
+ * Adds member to the list.
+ */
+ PartyMember *addMember(int id);
+
+ /**
+ * Adds member to the list.
+ */
+ PartyMember *addMember(const std::string &name);
/**
* Find a member by ID.
@@ -153,7 +163,7 @@ public:
bool isMember(const std::string &name) const;
- const void getNames(std::vector<std::string> &names) const;
+ void getNames(std::vector<std::string> &names) const;
static Party *getParty(int id);
diff --git a/src/player.cpp b/src/player.cpp
index 5eb0163f..4fc5d523 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -220,6 +220,7 @@ void Player::setSpriteColor(unsigned int slot, const std::string &color)
void Player::addGuild(Guild *guild)
{
mGuilds[guild->getId()] = guild;
+ guild->addMember(mId, mName);
if (this == player_node && socialWindow)
{
@@ -234,6 +235,7 @@ void Player::removeGuild(int id)
socialWindow->removeTab(mGuilds[id]);
}
+ mGuilds[id]->removeMember(mId);
mGuilds.erase(id);
}
@@ -264,6 +266,27 @@ Guild *Player::getGuild(int id) const
return NULL;
}
+const std::map<int, Guild*> &Player::getGuilds() const
+{
+ return mGuilds;
+}
+
+void Player::clearGuilds()
+{
+ std::map<int, Guild*>::const_iterator itr, itr_end = mGuilds.end();
+ for (itr = mGuilds.begin(); itr != itr_end; ++itr)
+ {
+ Guild *guild = itr->second;
+
+ if (this == player_node && socialWindow)
+ socialWindow->removeTab(guild);
+
+ guild->removeMember(mId);
+ }
+
+ mGuilds.clear();
+}
+
void Player::setParty(Party *party)
{
if (party == mParty)
@@ -272,6 +295,16 @@ void Player::setParty(Party *party)
Party *old = mParty;
mParty = party;
+ if (old)
+ {
+ party->removeMember(mId);
+ }
+
+ if (party)
+ {
+ party->addMember(mId, mName);
+ }
+
updateColors();
if (this == player_node && socialWindow)
diff --git a/src/player.h b/src/player.h
index 170bcb72..670f6d84 100644
--- a/src/player.h
+++ b/src/player.h
@@ -105,6 +105,16 @@ class Player : public Being
Guild *getGuild(int id) const;
/**
+ * Returns all guilds the player is in.
+ */
+ const std::map<int, Guild*> &getGuilds() const;
+
+ /**
+ * Removes all guilds the player is in.
+ */
+ void clearGuilds();
+
+ /**
* Get number of guilds the player belongs to.
*/
short getNumberOfGuilds() const { return mGuilds.size(); }
diff --git a/src/utils/copynpaste.cpp b/src/utils/copynpaste.cpp
new file mode 100644
index 00000000..31aa7bf9
--- /dev/null
+++ b/src/utils/copynpaste.cpp
@@ -0,0 +1,319 @@
+/*
+ * Retrieve string pasted depending on OS mechanisms.
+ * Copyright (C) 2001-2010 Wormux Team
+ *
+ * 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/>.
+ */
+
+/*
+ * IMPORTANT!
+ *
+ * This code was taken from Wormux svn trunk at Feb 25 2010. Please don't
+ * make any unnecessary modifications, and try to sync up modifications
+ * when possible.
+ */
+
+#ifdef _MSC_VER
+# include "msvc/config.h"
+#elif defined(HAVE_CONFIG_H)
+# include "config.h"
+#endif
+
+#include <SDL_syswm.h>
+#include "copynpaste.h"
+
+#ifdef WIN32
+bool RetrieveBuffer(std::string& text, std::string::size_type& pos)
+{
+ bool ret = false;
+
+ if (!OpenClipboard(NULL))
+ return false;
+
+ HANDLE h = GetClipboardData(CF_UNICODETEXT);
+ if (h)
+ {
+ LPCWSTR data = (LPCWSTR)GlobalLock(h);
+
+ if (data)
+ {
+ int len = WideCharToMultiByte(CP_UTF8, 0, data, -1, NULL, 0, NULL, NULL);
+ if (len > 0)
+ {
+ // Convert from UTF-16 to UTF-8
+ void *temp = malloc(len);
+ if (WideCharToMultiByte(CP_UTF8, 0, data, -1, (LPSTR)temp, len, NULL, NULL))
+ {
+ text.insert(pos, (char*)temp);
+ pos += len-1;
+ }
+ free(temp);
+ ret = true;
+ }
+ }
+ GlobalUnlock(h);
+ }
+ else
+ {
+ h = GetClipboardData(CF_TEXT);
+
+ if (h)
+ {
+ const char *data = (char*)GlobalLock(h);
+ if (data)
+ {
+ text.insert(pos, data);
+ pos += strlen(data);
+ ret = true;
+ }
+ GlobalUnlock(h);
+ }
+ }
+
+ CloseClipboard();
+ return ret;
+}
+#elif defined(__APPLE__)
+
+#ifdef Status
+#undef Status
+#endif
+
+#include <Carbon/Carbon.h>
+
+// Sorry for the very long code, all nicer OS X APIs are coded in Objective C and not C!
+// Also it does very thorough error handling
+bool GetDataFromPasteboard( PasteboardRef inPasteboard, char* flavorText /* out */, const int bufSize )
+{
+ OSStatus err = noErr;
+ PasteboardSyncFlags syncFlags;
+ ItemCount itemCount;
+
+ syncFlags = PasteboardSynchronize( inPasteboard );
+
+ //require_action( syncFlags & kPasteboardModified, PasteboardOutOfSync,
+ // err = badPasteboardSyncErr );
+
+ err = PasteboardGetItemCount( inPasteboard, &itemCount );
+ require_noerr( err, CantGetPasteboardItemCount );
+
+ for (UInt32 itemIndex = 1; itemIndex <= itemCount; itemIndex++)
+ {
+ PasteboardItemID itemID;
+ CFArrayRef flavorTypeArray;
+ CFIndex flavorCount;
+
+ err = PasteboardGetItemIdentifier( inPasteboard, itemIndex, &itemID );
+ require_noerr( err, CantGetPasteboardItemIdentifier );
+
+ err = PasteboardCopyItemFlavors( inPasteboard, itemID, &flavorTypeArray );
+ require_noerr( err, CantCopyPasteboardItemFlavors );
+
+ flavorCount = CFArrayGetCount( flavorTypeArray );
+
+ for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++)
+ {
+ CFStringRef flavorType;
+ CFDataRef flavorData;
+ CFIndex flavorDataSize;
+ flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
+
+ // we're only interested by text...
+ if (UTTypeConformsTo(flavorType, CFSTR("public.utf8-plain-text")))
+ {
+ err = PasteboardCopyItemFlavorData( inPasteboard, itemID,
+ flavorType, &flavorData );
+ require_noerr( err, CantCopyFlavorData );
+ flavorDataSize = CFDataGetLength( flavorData );
+ flavorDataSize = (flavorDataSize<254) ? flavorDataSize : 254;
+
+ if (flavorDataSize+2 > bufSize)
+ {
+ fprintf(stderr, "Cannot copy clipboard, contents is too big!\n");
+ return false;
+ }
+
+ for (short dataIndex = 0; dataIndex <= flavorDataSize; dataIndex++)
+ {
+ char byte = *(CFDataGetBytePtr( flavorData ) + dataIndex);
+ flavorText[dataIndex] = byte;
+ }
+
+ flavorText[flavorDataSize] = '\0';
+ flavorText[flavorDataSize+1] = '\n';
+
+ CFRelease (flavorData);
+ return true;
+ }
+
+ continue;
+ CantCopyFlavorData: fprintf(stderr, "Cannot copy clipboard, CantCopyFlavorData!\n");
+ }
+
+ CFRelease (flavorTypeArray);
+ continue;
+
+ CantCopyPasteboardItemFlavors: fprintf(stderr, "Cannot copy clipboard, CantCopyPasteboardItemFlavors!\n"); continue;
+ CantGetPasteboardItemIdentifier: fprintf(stderr, "Cannot copy clipboard, CantGetPasteboardItemIdentifier!\n"); continue;
+ }
+ fprintf(stderr, "Cannot copy clipboard, found no acceptable flavour!\n");
+ return false;
+
+ CantGetPasteboardItemCount: fprintf(stderr, "Cannot copy clipboard, CantGetPasteboardItemCount!\n"); return false;
+ //PasteboardOutOfSync: fprintf(stderr, "Cannot copy clipboard, PasteboardOutOfSync!\n"); return false;
+}
+
+bool getClipBoard(char* text /* out */, const int bufSize )
+{
+ OSStatus err = noErr;
+
+ PasteboardRef theClipboard;
+ err = PasteboardCreate( kPasteboardClipboard, &theClipboard );
+ require_noerr( err, PasteboardCreateFailed );
+
+ if (!GetDataFromPasteboard(theClipboard, text, bufSize))
+ {
+ fprintf(stderr, "Cannot copy clipboard, GetDataFromPasteboardFailed!\n");
+ return false;
+ }
+
+ CFRelease(theClipboard);
+
+ return true;
+
+ // ---- error handling
+ PasteboardCreateFailed: fprintf(stderr, "Cannot copy clipboard, PasteboardCreateFailed!\n");
+ CFRelease(theClipboard);
+ return false;
+}
+
+bool RetrieveBuffer(std::string& text, std::string::size_type& pos)
+{
+ const int bufSize = 512;
+ char buffer[bufSize];
+
+ if (getClipBoard(buffer, bufSize))
+ {
+ text = buffer;
+ pos += strlen(buffer);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+#elif USE_X11
+static char* getSelection(Display *dpy, Window us, Atom selection)
+{
+ int max_events = 50;
+ Window owner = XGetSelectionOwner (dpy, selection);
+ int ret;
+
+ //printf("XConvertSelection on %s\n", XGetAtomName(dpy, selection));
+ if (owner == None)
+ {
+ //printf("No owner\n");
+ return NULL;
+ }
+ XConvertSelection(dpy, selection, XA_STRING, XA_PRIMARY, us, CurrentTime);
+ XFlush(dpy);
+
+ while (max_events--)
+ {
+ XEvent e;
+
+ XNextEvent(dpy, &e);
+ if(e.type == SelectionNotify)
+ {
+ //printf("Received %s\n", XGetAtomName(dpy, e.xselection.selection));
+ if(e.xselection.property == None)
+ {
+ //printf("Couldn't convert\n");
+ return NULL;
+ }
+
+ long unsigned len, left, dummy;
+ int format;
+ Atom type;
+ unsigned char *data = NULL;
+
+ XGetWindowProperty(dpy, us, e.xselection.property, 0, 0, False,
+ AnyPropertyType, &type, &format, &len, &left, &data);
+ if (left < 1)
+ return NULL;
+
+ ret = XGetWindowProperty(dpy, us, e.xselection.property, 0, left, False,
+ AnyPropertyType, &type, &format, &len, &dummy, &data);
+ if (ret != Success)
+ {
+ //printf("Failed to get property: %p on %lu\n", data, len);
+ return NULL;
+ }
+
+ //printf(">>> Got %s: len=%lu left=%lu (event %i)\n", data, len, left, 50-max_events);
+ return (char*)data;
+ }
+ }
+ printf("Timeout\n");
+ return NULL;
+}
+
+bool RetrieveBuffer(std::string& text, std::string::size_type& pos)
+{
+ SDL_SysWMinfo info;
+
+ //printf("Retrieving buffer...\n");
+ SDL_VERSION(&info.version);
+ if ( SDL_GetWMInfo(&info) )
+ {
+ Display *dpy = info.info.x11.display;
+ Window us = info.info.x11.window;
+ char *data = NULL;
+
+ if (!data)
+ {
+ data = getSelection(dpy, us, XA_PRIMARY);
+ }
+ if (!data)
+ {
+ data = getSelection(dpy, us, XA_SECONDARY);
+ }
+ if (!data)
+ {
+ Atom XA_CLIPBOARD = XInternAtom(dpy, "CLIPBOARD", 0);
+ data = getSelection(dpy, us, XA_CLIPBOARD);
+ }
+ if (data)
+ {
+ // check cursor position
+ if (pos > text.size()) {
+ pos = text.size();
+ }
+
+ text.insert(pos, data);
+ pos += strlen(data);
+ XFree(data);
+
+ return true;
+ }
+ }
+ return false;
+}
+#else
+bool RetrieveBuffer(std::string&, std::string::size_type&) { return false; }
+#endif
diff --git a/src/utils/copynpaste.h b/src/utils/copynpaste.h
new file mode 100644
index 00000000..1a7c81d0
--- /dev/null
+++ b/src/utils/copynpaste.h
@@ -0,0 +1,33 @@
+/*
+ * Retrieve string pasted depending on OS mechanisms.
+ * Copyright (C) 2001-2010 Wormux Team
+ *
+ * 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 <string>
+
+/**
+ * Attempts to retrieve text from the clipboard buffer and inserts it in
+ * \a text at position \pos. The characters are encoded in utf-8.
+ *
+ * Implemented for Windows, X11 and Mac OS X.
+ *
+ * @return <code>true</code> when successful or <code>false</code> when there
+ * was a problem retrieving the clipboard buffer.
+ */
+bool RetrieveBuffer(std::string& text, std::string::size_type& pos);
+
diff --git a/src/utils/mkdir.cpp b/src/utils/mkdir.cpp
new file mode 100644
index 00000000..223abe71
--- /dev/null
+++ b/src/utils/mkdir.cpp
@@ -0,0 +1,101 @@
+/*
+ * The Mana Client
+ * Copyright (C) 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 <climits>
+#include <cstring>
+#include <cerrno>
+
+#if defined WIN32
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#endif
+
+#ifdef _MKDIR_TEST_
+// compile with -D_MKDIR_TEST_ to get a standalone binary
+#include <cstdio>
+#include <cstdlib>
+#endif
+
+#include "mkdir.h"
+
+int mkdir_r(const char *pathname) {
+ char tmp[PATH_MAX];
+ char *p;
+
+ if (strlen(pathname) >= PATH_MAX-2)
+ return -1;
+
+ strncpy(tmp, pathname, sizeof(tmp)-1);
+ tmp[PATH_MAX-1] = '\0';
+
+ int len=strlen(tmp);
+
+ // terminate the pathname with '/'
+ if (tmp[len-1] != '/') {
+ tmp[len] = '/';
+ tmp[len+1] = '\0';
+ }
+
+ for (p=tmp; *p; p++) {
+#if defined WIN32
+ if (*p == '/' || *p == '\\')
+#else
+ if (*p == '/')
+#endif
+ {
+ *p = '\0';
+ // ignore a slash at the beginning of a path
+ if (strlen(tmp) == 0){
+ *p = '/';
+ continue;
+ }
+#if defined WIN32
+ if (!CreateDirectory(tmp, 0) &&
+ GetLastError() != ERROR_ALREADY_EXISTS)
+#else
+ if (mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) &&
+ errno != EEXIST)
+#endif
+ {
+#if defined WIN32
+ // hack, hack. just assume that x: might be a drive
+ // letter, and try again
+ if (!(strlen(tmp) == 2 &&
+ !strcmp(tmp + 1, ":")))
+#endif
+ return -1;
+ }
+
+#ifdef _MKDIR_TEST_
+ printf("%s\n", tmp);
+#endif
+ *p = '/';
+ }
+ }
+ return 0;
+}
+
+#ifdef _MKDIR_TEST_
+int main(int argc, char** argv) {
+ if (argc < 2) exit(1);
+ mkdir_r(argv[1]);
+}
+#endif
diff --git a/src/utils/mkdir.h b/src/utils/mkdir.h
new file mode 100644
index 00000000..9369b4e7
--- /dev/null
+++ b/src/utils/mkdir.h
@@ -0,0 +1,26 @@
+/*
+ * The Mana Client
+ * Copyright (C) 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/>.
+ */
+
+#ifndef _MKDIR_H
+#define _MKDIR_H
+
+int mkdir_r(const char *pathname);
+
+#endif
diff --git a/src/utils/specialfolder.cpp b/src/utils/specialfolder.cpp
new file mode 100644
index 00000000..64607716
--- /dev/null
+++ b/src/utils/specialfolder.cpp
@@ -0,0 +1,78 @@
+/*
+ * The Mana Client
+ * Copyright (C) 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/>.
+ */
+
+#ifdef WIN32
+#include "specialfolder.h"
+#include <windows.h>
+
+#ifdef _SPECIALFOLDERLOCATION_TEST_
+// compile with -D_SPECIALFOLDERLOCATION_TEST_ to get a standalone
+// binary for testing
+#include <iostream>
+#endif
+
+/*
+ * Retrieve the pathname of special folders on win32, or an empty string
+ * on error / if the folder does not exist.
+ * See http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx for
+ * a list of folder ids
+ */
+std::string getSpecialFolderLocation(int folderId)
+{
+ std::string ret;
+ LPITEMIDLIST pItemIdList;
+ LPMALLOC pMalloc;
+ char szPath[_MAX_PATH];
+
+ // get the item ID list for folderId
+ HRESULT hr = SHGetSpecialFolderLocation(NULL, folderId, &pItemIdList);
+ if (hr != S_OK)
+ return ret;
+
+ // convert the ID list into a file system path
+ if (SHGetPathFromIDList(pItemIdList, szPath) == FALSE)
+ return ret;
+
+ // get the IMalloc pointer and free all resources we used
+ hr = SHGetMalloc(&pMalloc);
+ pMalloc->Free(pItemIdList);
+ pMalloc->Release();
+
+ ret = szPath;
+ return ret;
+}
+
+#ifdef _SPECIALFOLDERLOCATION_TEST_
+int main()
+{
+ std::cout << "APPDATA " << getSpecialFolderLocation(CSIDL_APPDATA)
+ << std::endl;
+ std::cout << "DESKTOP " << getSpecialFolderLocation(CSIDL_DESKTOP)
+ << std::endl;
+ std::cout << "LOCAL_APPDATA "
+ << getSpecialFolderLocation(CSIDL_LOCAL_APPDATA)
+ << std::endl;
+ std::cout << "MYPICTURES " << getSpecialFolderLocation(CSIDL_MYPICTURES)
+ << std::endl;
+ std::cout << "PERSONAL " << getSpecialFolderLocation(CSIDL_PERSONAL)
+ << std::endl;
+}
+#endif
+#endif
diff --git a/src/utils/specialfolder.h b/src/utils/specialfolder.h
new file mode 100644
index 00000000..c2c2e0be
--- /dev/null
+++ b/src/utils/specialfolder.h
@@ -0,0 +1,30 @@
+/*
+ * The Mana Client
+ * Copyright (C) 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/>.
+ */
+
+#ifndef _SPECIALFOLDER_H
+#define _SPECIALFOLDER_H
+
+#ifdef WIN32
+#include <shlobj.h>
+#include <string>
+std::string getSpecialFolderLocation(int folderId);
+#endif
+
+#endif