diff options
author | Bertram <bertram@cegetel.net> | 2010-03-03 23:36:37 +0100 |
---|---|---|
committer | Bertram <bertram@cegetel.net> | 2010-03-03 23:36:37 +0100 |
commit | 8cc31b582f372238ce6bd2c86888d312cf1fe5b2 (patch) | |
tree | 8db5f864348d08a05b8533c7ede58e76741a98f8 /src | |
parent | b1845e9e081df1fc77d9bcbed3ab95792d6ba682 (diff) | |
parent | d564943867452ad76e6d313a28870e640715dded (diff) | |
download | mana-client-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.gz mana-client-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.bz2 mana-client-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.tar.xz mana-client-8cc31b582f372238ce6bd2c86888d312cf1fe5b2.zip |
Merge branch 'master' of gitorious.org:mana/mana
Conflicts:
src/being.cpp
Diffstat (limited to 'src')
58 files changed, 1642 insertions, 320 deletions
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(¤tServer, - 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 |