diff options
Diffstat (limited to 'src')
119 files changed, 2250 insertions, 695 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 65f5a0f44..67943d365 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -343,6 +343,8 @@ SET(SRCS gui/widgets/tabs/setup_colors.h gui/widgets/tabs/setup_joystick.cpp gui/widgets/tabs/setup_joystick.h + gui/widgets/tabs/setup_mods.cpp + gui/widgets/tabs/setup_mods.h gui/widgets/tabs/setup_other.cpp gui/widgets/tabs/setup_other.h gui/widgets/tabs/setup_theme.cpp @@ -474,8 +476,12 @@ SET(SRCS resources/iteminfo.cpp resources/db/mapdb.cpp resources/db/mapdb.h + resources/db/moddb.cpp + resources/db/moddb.h resources/mapreader.cpp resources/mapreader.h + resources/modinfo.cpp + resources/modinfo.h resources/db/monsterdb.cpp resources/db/monsterdb.h resources/db/npcdb.cpp diff --git a/src/Makefile.am b/src/Makefile.am index c7ee06128..7d6abb875 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -478,6 +478,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ gui/widgets/tabs/setup_colors.h \ gui/widgets/tabs/setup_joystick.cpp \ gui/widgets/tabs/setup_joystick.h \ + gui/widgets/tabs/setup_mods.cpp \ + gui/widgets/tabs/setup_mods.h \ gui/widgets/tabs/setup_other.cpp \ gui/widgets/tabs/setup_other.h \ gui/widgets/tabs/setup_theme.cpp \ @@ -611,8 +613,12 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \ resources/iteminfo.cpp \ resources/db/mapdb.cpp \ resources/db/mapdb.h \ + resources/db/moddb.cpp \ + resources/db/moddb.h \ resources/mapreader.cpp \ resources/mapreader.h \ + resources/modinfo.cpp \ + resources/modinfo.h \ resources/db/monsterdb.cpp \ resources/db/monsterdb.h \ resources/db/npcdb.cpp \ diff --git a/src/actionmanager.cpp b/src/actionmanager.cpp index 9152dfe7a..cc04e60a1 100644 --- a/src/actionmanager.cpp +++ b/src/actionmanager.cpp @@ -87,28 +87,36 @@ extern QuitDialog *quitDialog; namespace ActionManager { -impHandler(moveUp) +static bool closeMoveNpcDialog(bool focus) { - if (NpcDialog *const dialog = NpcDialog::getActive()) + NpcDialog *const dialog = NpcDialog::getActive(); + if (dialog) { - dialog->refocus(); - return false; + if (dialog->isCloseState()) + { + dialog->closeDialog(); + return true; + } + else if (focus) + { + dialog->refocus(); + } } + return false; +} + +impHandler(moveUp) +{ if (inputManager.isActionActive(Input::KEY_EMOTE)) return directUp(event); - return false; + return closeMoveNpcDialog(false); } impHandler(moveDown) { - if (NpcDialog *const dialog = NpcDialog::getActive()) - { - dialog->refocus(); - return false; - } if (inputManager.isActionActive(Input::KEY_EMOTE)) return directDown(event); - return false; + return closeMoveNpcDialog(false); } impHandler(moveLeft) @@ -122,7 +130,7 @@ impHandler(moveLeft) } if (inputManager.isActionActive(Input::KEY_EMOTE)) return directLeft(event); - return false; + return closeMoveNpcDialog(false); } impHandler(moveRight) @@ -136,14 +144,14 @@ impHandler(moveRight) } if (inputManager.isActionActive(Input::KEY_EMOTE)) return directRight(event); - return false; + return closeMoveNpcDialog(false); } impHandler(moveForward) { if (inputManager.isActionActive(Input::KEY_EMOTE)) return directRight(event); - return false; + return closeMoveNpcDialog(false); } impHandler(emote) diff --git a/src/being/being.cpp b/src/being/being.cpp index e155dc886..aa2b875af 100644 --- a/src/being/being.cpp +++ b/src/being/being.cpp @@ -151,7 +151,7 @@ Being::Being(const int id, const Type type, const uint16_t subtype, mSpriteHide(new int[20]), mSpriteDraw(new int[20]), mComment(), - mPet(nullptr), + mPets(), mOwner(nullptr), mSpecialParticle(nullptr), mX(0), @@ -176,8 +176,8 @@ Being::Being(const int id, const Type type, const uint16_t subtype, mCriticalHit(0), mPvpRank(0), mNumber(100), - mPetId(0), mLook(0), + mUsageCounter(1), mHairColor(0), mErased(false), mEnemy(false), @@ -242,14 +242,18 @@ Being::~Being() mAnimationEffect = nullptr; if (mOwner) - mOwner->setPet(nullptr); - if (mPet) + mOwner->unassignPet(this); + FOR_EACH (std::vector<Being*>::iterator, it, mPets) { - mPet->setOwner(nullptr); - actorManager->erase(mPet); - delete mPet; - mPet = nullptr; + Being *pet = *it; + if (pet) + { + pet->setOwner(nullptr); + actorManager->erase(pet); + delete pet; + } } + mPets.clear(); removeAllItemsParticles(); } @@ -913,7 +917,7 @@ const Guild *Being::getGuild(const std::string &guildName) const { FOR_EACH (GuildsMapCIter, itr, mGuilds) { - Guild *const guild = itr->second; + const Guild *const guild = itr->second; if (guild && guild->getName() == guildName) return guild; } @@ -1609,8 +1613,12 @@ void Being::logic() actorManager->destroy(this); } - if (mPet) - mPet->petLogic(); + FOR_EACH (std::vector<Being*>::iterator, it, mPets) + { + Being *const pet = *it; + if (pet) + pet->petLogic(); + } const SoundInfo *const sound = mNextSound.sound; if (sound) @@ -1655,9 +1663,9 @@ void Being::petLogic() setAction(Being::STAND, 0); fixPetSpawnPos(dstX, dstY); setTileCoords(dstX, dstY); - Net::getPetHandler()->spawn(mOwner, dstX, dstY); + Net::getPetHandler()->spawn(mOwner, mId, dstX, dstY); } - else if (divX > followDist || divY > followDist) + else if (!followDist || divX > followDist || divY > followDist) { if (!dist) { @@ -1708,7 +1716,7 @@ void Being::petLogic() if (mX != dstX || mY != dstY) { setPath(mMap->findPath(mX, mY, dstX, dstY, walkMask)); - Net::getPetHandler()->move(mOwner, mX, mY, dstX, dstY); + Net::getPetHandler()->move(mOwner, mId, mX, mY, dstX, dstY); return; } } @@ -1806,7 +1814,9 @@ void Being::drawSpeech(const int offsetX, const int offsetY) mSpeechBubble->setPosition(px - (mSpeechBubble->getWidth() / 2), py - getHeight() - (mSpeechBubble->getHeight())); mSpeechBubble->setVisible(true); +#ifdef USE_INTERNALGUICHAN mSpeechBubble->requestMoveToBackground(); +#endif } else if (mSpeechTime > 0 && speech == TEXT_OVERHEAD) { @@ -2121,11 +2131,11 @@ void Being::setSprite(const unsigned int slot, const int id, if (id1) { const ItemInfo &info = ItemDB::get(id1); - if (mMap) + if (mMap && mType == PLAYER) { const int pet = info.getPet(); if (pet) - removePet(); + removePet(pet); } removeItemParticles(id1); } @@ -3207,40 +3217,77 @@ void Being::addPet(const int id) if (!actorManager || !config.getBoolValue("usepets")) return; - removePet(); + Being *const pet = findChildPet(id); + if (pet) + { + pet->incUsage(); + return; + } + Being *const being = actorManager->createBeing( id, ActorSprite::PET, 0); if (being) { being->setOwner(this); - mPetId = id; - mPet = being; + mPets.push_back(being); int dstX = mX; int dstY = mY; being->fixPetSpawnPos(dstX, dstY); being->setTileCoords(dstX, dstY); - Net::getPetHandler()->spawn(this, dstX, dstY); + Net::getPetHandler()->spawn(this, being->mId, dstX, dstY); } } -void Being::removePet() +Being *Being::findChildPet(const int id) +{ + FOR_EACH (std::vector<Being*>::iterator, it, mPets) + { + Being *const pet = *it; + if (pet && pet->mId == id) + return pet; + } + return nullptr; +} + +void Being::removePet(const int id) { if (!actorManager) return; - mPetId = 0; - if (mPet) + FOR_EACH (std::vector<Being*>::iterator, it, mPets) { - mPet->setOwner(nullptr); - actorManager->erase(mPet); - delete mPet; - mPet = nullptr; + Being *const pet = *it; + if (pet && pet->mId == id) + { + if (!pet->decUsage()) + { + pet->setOwner(nullptr); + actorManager->erase(pet); + mPets.erase(it); + delete pet; + } + } + } +} + +void Being::removeAllPets() +{ + FOR_EACH (std::vector<Being*>::iterator, it, mPets) + { + Being *const pet = *it; + if (pet) + { + pet->setOwner(nullptr); + actorManager->erase(pet); + delete pet; + } } + mPets.clear(); } void Being::updatePets() { - removePet(); + removeAllPets(); FOR_EACH (std::vector<int>::const_iterator, it, mSpriteIDs) { const int id = *it; @@ -3249,8 +3296,17 @@ void Being::updatePets() const ItemInfo &info = ItemDB::get(id); const int pet = info.getPet(); if (pet) - { addPet(pet); + } +} + +void Being::unassignPet(const Being *const pet1) +{ + FOR_EACH (std::vector<Being*>::iterator, it, mPets) + { + if (*it == pet1) + { + mPets.erase(it); return; } } diff --git a/src/being/being.h b/src/being/being.h index 634c26d23..7314fe1f1 100644 --- a/src/being/being.h +++ b/src/being/being.h @@ -877,21 +877,27 @@ class Being : public ActorSprite, public ConfigListener void addPet(const int id); - void removePet(); + void removePet(const int id); void updatePets(); void fixPetSpawnPos(int &dstX, int &dstY) const; - Being *getPet() - { return mPet; } + const std::vector<Being*> &getPets() const + { return mPets; } - void setPet(Being *const pet) - { mPet = pet; } + Being *getFirstPet() + { return mPets.empty() ? nullptr : mPets[0]; } void setOwner(Being *const owner) { mOwner = owner; } + void unassignPet(const Being *const pet); + + void removeAllPets(); + + Being *findChildPet(const int id); + void playSfx(const SoundInfo &sound, Being *const being, const bool main, const int x, const int y) const; @@ -918,6 +924,12 @@ class Being : public ActorSprite, public ConfigListener void recreateItemParticles(); + void incUsage() + { mUsageCounter ++; } + + int decUsage() + { return --mUsageCounter; } + protected: /** * Updates name's location. @@ -1029,7 +1041,7 @@ class Being : public ActorSprite, public ConfigListener int *mSpriteHide; int *mSpriteDraw; std::string mComment; - Being *mPet; + std::vector<Being*> mPets; Being *mOwner; Particle *mSpecialParticle; @@ -1076,8 +1088,8 @@ class Being : public ActorSprite, public ConfigListener int mCriticalHit; unsigned int mPvpRank; unsigned int mNumber; - int mPetId; int mLook; + int mUsageCounter; unsigned char mHairColor; bool mErased; bool mEnemy; diff --git a/src/client.cpp b/src/client.cpp index 43c474dab..289b4df0b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -98,6 +98,7 @@ #include "resources/db/sounddb.h" #include "resources/db/itemdb.h" #include "resources/db/mapdb.h" +#include "resources/db/moddb.h" #include "resources/db/monsterdb.h" #include "resources/db/npcdb.h" #ifdef MANASERV_SUPPORT @@ -106,6 +107,7 @@ #include "resources/db/palettedb.h" #include "resources/db/petdb.h" +#include "utils/base64.h" #include "utils/cpu.h" #include "utils/files.h" #include "utils/fuzzer.h" @@ -357,12 +359,16 @@ void Client::gameInit() SDL_EventState(SDL_USEREVENT, SDL_IGNORE); #ifdef WIN32 + extractDataDir(); + mountDataDir(); + setIcon(); + initGraphics(); +#else setIcon(); -#endif - initGraphics(); extractDataDir(); mountDataDir(); +#endif if (mOptions.dataPath.empty() && !branding.getStringValue("dataPath").empty()) @@ -413,6 +419,10 @@ void Client::gameInit() Joystick::init(); createWindows(); + keyboard.update(); + if (joystick) + joystick->update(); + // Initialize default server mCurrentServer.hostname = mOptions.serverName; mCurrentServer.port = mOptions.serverPort; @@ -828,6 +838,7 @@ void Client::gameClear() PaletteDB::unload(); PETDB::unload(); StatusEffect::unload(); + ModDB::unload(); if (Net::getLoginHandler()) Net::getLoginHandler()->clearWorlds(); @@ -1170,6 +1181,12 @@ int Client::gameExec() Net::getPartyHandler()->clear(); if (chatLogger) chatLogger->clear(); + if (!mOptions.dataPath.empty()) + UpdaterWindow::unloadMods(mOptions.dataPath); + else + UpdaterWindow::unloadMods(mOldUpdates); + if (!mOptions.skipUpdate) + UpdaterWindow::unloadMods(mOldUpdates + "/fix/"); } mOldState = mState; @@ -1366,6 +1383,7 @@ int Client::gameExec() { mState = STATE_LOAD_DATA; mOldUpdates = ""; + UpdaterWindow::loadDirMods(mOptions.dataPath); } else if (loginData.updateType & LoginData::Upd_Skip) { @@ -1450,6 +1468,7 @@ int Client::gameExec() NPCDB::load(); PETDB::load(); EmoteDB::load(); +// ModDB::load(); StatusEffect::load(); Units::loadUnits(); @@ -1713,6 +1732,8 @@ int Client::gameExec() mServerName.clear(); serverConfig.write(); serverConfig.unload(); + if (setupWindow) + setupWindow->externalUnload(); mState = STATE_CHOOSE_SERVER; BLOCK_END("Client::gameExec STATE_SWITCH_SERVER") @@ -2279,40 +2300,39 @@ void Client::initScreenshotDir() } else if (mScreenshotDir.empty()) { + mScreenshotDir = decodeBase64String( + config.getStringValue("screenshotDirectory2")); + if (mScreenshotDir.empty()) + { #ifdef __ANDROID__ - mScreenshotDir = getSdStoragePath() + std::string("/images"); + mScreenshotDir = getSdStoragePath() + std::string("/images"); - if (mkdir_r(mScreenshotDir.c_str())) - { - // TRANSLATORS: directory creation error - logger->log(strprintf( - _("Error: %s doesn't exist and can't be created! " - "Exiting."), mScreenshotDir.c_str())); - } + if (mkdir_r(mScreenshotDir.c_str())) + { + // TRANSLATORS: directory creation error + logger->log(strprintf( + _("Error: %s doesn't exist and can't be created! " + "Exiting."), mScreenshotDir.c_str())); + } #else - const std::string configScreenshotDir = - config.getStringValue("screenshotDirectory"); - if (!configScreenshotDir.empty()) - mScreenshotDir = configScreenshotDir; - else - mScreenshotDir = getDesktopDir(); + mScreenshotDir = getPicturesDir(); #endif - -// config.setValue("screenshotDirectory", mScreenshotDir); - logger->log("screenshotDirectory: " + mScreenshotDir); - - if (config.getBoolValue("useScreenshotDirectorySuffix")) - { - const std::string configScreenshotSuffix = - branding.getValue("screenshots", "ManaPlus"); - - if (!configScreenshotSuffix.empty()) + if (config.getBoolValue("useScreenshotDirectorySuffix")) { - mScreenshotDir.append(dirSeparator).append( - configScreenshotSuffix); + const std::string configScreenshotSuffix = + branding.getValue("screenshots", "ManaPlus"); + + if (!configScreenshotSuffix.empty()) + { + mScreenshotDir.append(dirSeparator).append( + configScreenshotSuffix); + } } + config.setValue("screenshotDirectory2", + encodeBase64String(mScreenshotDir)); } } + logger->log("screenshotDirectory: " + mScreenshotDir); } void Client::accountLogin(LoginData *const data) const @@ -2353,7 +2373,9 @@ void Client::storeSafeParameters() const std::string particleFont; std::string helpFont; std::string secureFont; + std::string npcFont; std::string japanFont; + std::string chinaFont; bool showBackground; bool enableMumble; bool enableMapReduce; @@ -2377,7 +2399,9 @@ void Client::storeSafeParameters() const particleFont = config.getStringValue("particleFont"); helpFont = config.getStringValue("helpFont"); secureFont = config.getStringValue("secureFont"); + npcFont = config.getStringValue("npcFont"); japanFont = config.getStringValue("japanFont"); + chinaFont = config.getStringValue("chinaFont"); showBackground = config.getBoolValue("showBackground"); enableMumble = config.getBoolValue("enableMumble"); @@ -2397,7 +2421,9 @@ void Client::storeSafeParameters() const config.setValue("particleFont", "fonts/dejavusans.ttf"); config.setValue("helpFont", "fonts/dejavusansmono.ttf"); config.setValue("secureFont", "fonts/dejavusansmono.ttf"); + config.setValue("npcFont", "fonts/dejavusans.ttf"); config.setValue("japanFont", "fonts/mplus-1p-regular.ttf"); + config.setValue("chinaFont", "fonts/wqy-microhei.ttf"); config.setValue("showBackground", false); config.setValue("enableMumble", false); config.setValue("enableMapReduce", false); @@ -2437,7 +2463,9 @@ void Client::storeSafeParameters() const config.setValue("particleFont", particleFont); config.setValue("helpFont", helpFont); config.setValue("secureFont", secureFont); + config.setValue("npcFont", npcFont); config.setValue("japanFont", japanFont); + config.setValue("chinaFont", chinaFont); config.setValue("showBackground", showBackground); config.setValue("enableMumble", enableMumble); config.setValue("enableMapReduce", enableMapReduce); @@ -2967,7 +2995,10 @@ void Client::setIcon() #ifdef WIN32 static SDL_SysWMinfo pInfo; - SDL::getWindowWMInfo(mainGraphics->getWindow(), &pInfo); + if (mainGraphics) + SDL::getWindowWMInfo(mainGraphics->getWindow(), &pInfo); + else + SDL::getWindowWMInfo(nullptr, &pInfo); // Attempt to load icon from .ico file HICON icon = (HICON) LoadImage(nullptr, iconFile.c_str(), IMAGE_ICON, 64, 64, LR_LOADFROMFILE); @@ -3002,6 +3033,12 @@ bool Client::isKeyboardVisible() const #endif } +void Client::reloadWallpaper() +{ + if (mDesktop) + mDesktop->reloadWallpaper(); +} + #ifdef ANDROID #ifdef USE_SDL2 void Client::extractAssets() diff --git a/src/client.h b/src/client.h index b89996e26..c166b438b 100644 --- a/src/client.h +++ b/src/client.h @@ -321,6 +321,8 @@ public: void updateScreenKeyboard(const int height) { mKeyboardHeight = height; } + void reloadWallpaper(); + Window *openErrorDialog(const std::string &header, const std::string &message, const bool modal); diff --git a/src/commands.cpp b/src/commands.cpp index 827eebe92..07e0414d5 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -726,7 +726,8 @@ impHandler1(emote) impHandler1(emotePet) { - Net::getPetHandler()->emote(static_cast<uint8_t>(atoi(args.c_str()))); + // need use actual pet id + Net::getPetHandler()->emote(static_cast<uint8_t>(atoi(args.c_str())), 0); } impHandler1(away) @@ -871,7 +872,7 @@ impHandler0(dirs) impHandler2(info) { - if (!tab || !player_node || !tmwServerVersion > 0) + if (!tab || !player_node || tmwServerVersion > 0) return; switch (tab->getType()) @@ -1261,7 +1262,7 @@ impHandler1(talkRaw) impHandler1(talkPet) { // in future probably need add channel detection - if (player_node->getPet()) + if (!player_node->getPets().empty()) Net::getChatHandler()->talkPet(args, GENERAL_CHANNEL); else Net::getChatHandler()->talk(args, GENERAL_CHANNEL); diff --git a/src/configuration.cpp b/src/configuration.cpp index cb7adf8f1..ca4542155 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -666,7 +666,7 @@ bool Configuration::resetBoolValue(const std::string &key) } -void ConfigurationObject::initFromXML(const XmlNodePtr parent_node) +void ConfigurationObject::initFromXML(const XmlNodePtrConst parent_node) { clear(); @@ -729,7 +729,7 @@ void Configuration::init(const std::string &filename, const bool useResManager) return; } - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "configuration")) { @@ -749,7 +749,7 @@ void Configuration::reInit() return; } - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "configuration")) { diff --git a/src/configuration.h b/src/configuration.h index 71f59d2fd..322d4c934 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -208,7 +208,7 @@ class ConfigurationObject protected: ConfigurationObject(); - virtual void initFromXML(const XmlNodePtr parent_node); + virtual void initFromXML(const XmlNodePtrConst parent_node); virtual void writeToXML(const XmlTextWriterPtr writer); void deleteList(const std::string &name); diff --git a/src/defaults.cpp b/src/defaults.cpp index 8aa64fcec..9362473fd 100644 --- a/src/defaults.cpp +++ b/src/defaults.cpp @@ -152,7 +152,7 @@ DefaultsData* getConfigDefaults() AddDEF("lastCharacter", ""); AddDEF("altfpslimit", 5); AddDEF("updatehost", ""); - AddDEF("screenshotDirectory", ""); + AddDEF("screenshotDirectory2", ""); AddDEF("useScreenshotDirectorySuffix", true); AddDEF("screenshotDirectorySuffix", ""); AddDEF("EnableSync", false); @@ -188,6 +188,7 @@ DefaultsData* getConfigDefaults() AddDEF("helpFont", "fonts/dejavusansmono.ttf"); AddDEF("secureFont", "fonts/dejavusansmono.ttf"); AddDEF("japanFont", "fonts/mplus-1p-regular.ttf"); + AddDEF("chinaFont", "fonts/wqy-microhei.ttf"); AddDEF("npcFont", "fonts/dejavusans.ttf"); AddDEF("showBackground", true); AddDEF("enableTradeTab", true); @@ -430,6 +431,7 @@ DefaultsData* getBrandingDefaults() AddDEF("secureFont", "fonts/dejavusansmono.ttf"); AddDEF("npcFont", "fonts/dejavusans.ttf"); AddDEF("japanFont", "fonts/mplus-1p-regular.ttf"); + AddDEF("chinaFont", "fonts/wqy-microhei.ttf"); AddDEF("guiPath", "graphics/gui/"); AddDEF("guiThemePath", "themes/"); @@ -473,27 +475,61 @@ DefaultsData* getPathsDefaults() AddDEF("help", "help/"); AddDEF("statusEffectsFile", "status-effects.xml"); + AddDEF("statusEffectsPatchFile", "status-effects_patch.xml"); + AddDEF("statusEffectsPatchDir", "status-effects.d"); AddDEF("effectsFile", "effects.xml"); + AddDEF("effectsPatchFile", "effects_patch.xml"); + AddDEF("effectsPatchDir", "effects.d"); AddDEF("unitsFile", "units.xml"); + AddDEF("unitsPatchFile", "units_patch.xml"); + AddDEF("unitsPatchDir", "units.d"); AddDEF("featuresFile", "features.xml"); AddDEF("questsFile", "quests.xml"); - AddDEF("skillsFile", "ea-skills.xml"); - AddDEF("skillsFile2", "skills.xml"); + AddDEF("questsPatchFile", "quests_patch.xml"); + AddDEF("questsPatchDir", "quests.d"); + AddDEF("skillsFile", "skills.xml"); + AddDEF("skillsPatchFile", "skills_patch.xml"); + AddDEF("skillsPatchDir", "skills.d"); + AddDEF("skillsFile2", "ea-skills.xml"); AddDEF("equipmentWindowFile", "equipmentwindow.xml"); AddDEF("emotesFile", "emotes.xml"); + AddDEF("emotesPatchFile", "emotes_patch.xml"); + AddDEF("emotesPatchDir", "emotes.d"); AddDEF("hairColorFile", "hair.xml"); - AddDEF("hairColorFile2", "colors.xml"); + AddDEF("hairColorPatchFile", "hair_patch.xml"); + AddDEF("hairColorPatchDir", "hair.d"); AddDEF("itemColorsFile", "itemcolors.xml"); + AddDEF("itemColorsPatchFile", "itemcolors_patch.xml"); + AddDEF("itemColorsPatchDir", "itemcolors.d"); AddDEF("charCreationFile", "charcreation.xml"); AddDEF("soundsFile", "sounds.xml"); + AddDEF("soundsPatchFile", "sounds_patch.xml"); + AddDEF("soundsPatchDir", "sounds.d"); AddDEF("itemsFile", "items.xml"); + AddDEF("itemsPatchFile", "items_patch.xml"); + AddDEF("itemsPatchDir", "items.d"); AddDEF("avatarsFile", "avatars.xml"); + AddDEF("avatarsPatchFile", "avatars_patch.xml"); + AddDEF("avatarsPatchDir", "avatars.d"); + AddDEF("modsFile", "mods.xml"); + AddDEF("modsPatchFile", "mods_patch.xml"); + AddDEF("modsPatchDir", "mods.d"); AddDEF("npcsFile", "npcs.xml"); + AddDEF("npcsPatchFile", "npcs_patch.xml"); + AddDEF("npcsPatchDir", "npcs.d"); AddDEF("petsFile", "pets.xml"); + AddDEF("petsPatchFile", "pets_patch.xml"); + AddDEF("petsPatchDir", "pets.d"); AddDEF("monstersFile", "monsters.xml"); + AddDEF("monstersPatchFile", "monsters_patch.xml"); + AddDEF("monstersPatchDir", "monsters.d"); AddDEF("mapsRemapFile", "maps/remap.xml"); AddDEF("mapsFile", "maps.xml"); + AddDEF("mapsPatchFile", "maps_patch.xml"); + AddDEF("mapsPatchDir", "maps.d"); AddDEF("deadMessagesFile", "deadmessages.xml"); + AddDEF("deadMessagesPatchFile", "deadmessages_patch.xml"); + AddDEF("deadMessagesPatchDir", "deadmessages.d"); return configData; } diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp index 6790cf61f..6bf32ecb9 100644 --- a/src/effectmanager.cpp +++ b/src/effectmanager.cpp @@ -30,28 +30,40 @@ #include "particle/particle.h" +#include "resources/beingcommon.h" + #include "debug.h" EffectManager::EffectManager() : mEffects() { - XML::Document doc(paths.getStringValue("effectsFile")); - const XmlNodePtr root = doc.rootNode(); + logger->log1("Effects are now loading"); + loadXmlFile(paths.getStringValue("effectsFile")); + loadXmlFile(paths.getStringValue("effectsPatchFile")); + loadXmlDir("effectsPatchDir", loadXmlFile); +} + +void EffectManager::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst root = doc.rootNode(); if (!root || !xmlNameEqual(root, "being-effects")) { - logger->log("Error loading being effects file: " - + paths.getStringValue("effectsFile")); + logger->log("Error loading being effects file: " + fileName); return; } - else - { - logger->log1("Effects are now loading"); - } for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "effect")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(node, "effect")) { mEffects.push_back(EffectDescription( XML::getProperty(node, "id", -1), diff --git a/src/effectmanager.h b/src/effectmanager.h index 57181a750..f31027269 100644 --- a/src/effectmanager.h +++ b/src/effectmanager.h @@ -57,6 +57,8 @@ class EffectManager final ~EffectManager(); + void loadXmlFile(const std::string &fileName); + /** * Triggers a effect with the id, at * the specified being. diff --git a/src/game.cpp b/src/game.cpp index d17407b51..91a9038c8 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -65,6 +65,7 @@ #include "gui/windows/killstats.h" #include "gui/windows/minimap.h" #include "gui/windows/ministatuswindow.h" +#include "gui/windows/npcdialog.h" #include "gui/windows/okdialog.h" #include "gui/windows/outfitwindow.h" #include "gui/windows/setup.h" @@ -487,9 +488,12 @@ Game::~Game() bool Game::createScreenshot() { + if (!mainGraphics) + return false; + SDL_Surface *screenshot = nullptr; - if (!config.getBoolValue("showip")) + if (!config.getBoolValue("showip") && gui) { mainGraphics->setSecure(true); mainGraphics->prepareScreenshot(); @@ -511,7 +515,6 @@ bool Game::createScreenshot() bool Game::saveScreenshot(SDL_Surface *const screenshot) { std::string screenshotDirectory = client->getScreenshotDirectory(); - if (mkdir_r(screenshotDirectory.c_str()) != 0) { logger->log("Directory %s doesn't exist and can't be created! " @@ -526,31 +529,51 @@ bool Game::saveScreenshot(SDL_Surface *const screenshot) std::fstream testExists; bool found = false; static unsigned int screenshotCount = 0; + + time_t rawtime; + char buffer [100]; + time(&rawtime); + struct tm *const timeinfo = localtime(&rawtime); + strftime(buffer, 99, "%Y-%m-%d_%H-%M-%S", timeinfo); + + const std::string serverName = client->getServerName(); + std::string screenShortStr; + if (serverName.empty()) + { + screenShortStr = strprintf("%s_Screenshot_%s_", + branding.getValue("appName", "ManaPlus").c_str(), + buffer); + } + else + { + screenShortStr = strprintf("%s_Screenshot_%s_%s_", + branding.getValue("appName", "ManaPlus").c_str(), + serverName.c_str(), buffer); + } + do { screenshotCount++; - filenameSuffix.str(""); filename.str(""); filename << screenshotDirectory << "/"; - filenameSuffix << branding.getValue("appName", "ManaPlus") - << "_Screenshot_" << screenshotCount << ".png"; - filename << filenameSuffix.str(); + filename << screenShortStr << screenshotCount << ".png"; testExists.open(filename.str().c_str(), std::ios::in); found = !testExists.is_open(); testExists.close(); } while (!found); - const bool success = ImageWriter::writePNG(screenshot, filename.str()); - + const std::string fileNameStr = filename.str(); + const bool success = ImageWriter::writePNG(screenshot, fileNameStr); if (success) { - std::stringstream chatlogentry; - // TRANSLATORS: save file message - chatlogentry << strprintf(_("Screenshot saved as %s"), - filenameSuffix.str().c_str()); if (localChatTab) - localChatTab->chatLog(chatlogentry.str(), BY_SERVER); + { + // TRANSLATORS: save file message + std::string str = strprintf(_("Screenshot saved as %s"), + fileNameStr.c_str()); + localChatTab->chatLog(str, BY_SERVER); + } } else { @@ -564,7 +587,6 @@ bool Game::saveScreenshot(SDL_Surface *const screenshot) } MSDL_FreeSurface(screenshot); - return success; } @@ -803,12 +825,15 @@ void Game::handleMove() return; // Moving player around - if (player_node->isAlive() && !PlayerInfo::isTalking() - && chatWindow && !chatWindow->isInputFocused() - && !InventoryWindow::isAnyInputFocused() && !quitDialog) + if (player_node->isAlive() + && chatWindow + && !chatWindow->isInputFocused() + && !InventoryWindow::isAnyInputFocused() + && !quitDialog) { - // Get the state of the keyboard keys - keyboard.refreshActiveKeys(); + NpcDialog *const dialog = NpcDialog::getActive(); + if (dialog) + return; // Ignore input if either "ignore" key is pressed // Stops the character moving about if the user's window manager diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index db3faa314..a8c31469a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -49,6 +49,7 @@ #include "resources/resourcemanager.h" #include "utils/langs.h" +#include "utils/timer.h" #include <guichan/exception.hpp> @@ -107,6 +108,7 @@ Gui::Gui() : mFocusListeners(), mForegroundColor(Theme::getThemeColor(Theme::TEXT)), mForegroundColor2(Theme::getThemeColor(Theme::TEXT_OUTLINE)), + mTime(0), mCustomCursor(false), mDoubleClick(true) { @@ -137,6 +139,8 @@ void Gui::postInit(Graphics *const graphics) const StringVect langs = getLang(); const bool isJapan = (!langs.empty() && langs[0].size() > 3 && langs[0].substr(0, 3) == "ja_"); + const bool isChinese = (!langs.empty() && langs[0].size() > 3 + && langs[0].substr(0, 3) == "zh_"); // Set global font const int fontSize = config.getIntValue("fontSize"); @@ -147,7 +151,12 @@ void Gui::postInit(Graphics *const graphics) if (fontFile.empty()) fontFile = branding.getStringValue("japanFont"); } - + else if (isChinese) + { + fontFile = config.getValue("chinaFont", ""); + if (fontFile.empty()) + fontFile = branding.getStringValue("chinaFont"); + } if (fontFile.empty()) fontFile = branding.getStringValue("font"); @@ -161,17 +170,21 @@ void Gui::postInit(Graphics *const graphics) .append("': ").append(e.getMessage())); } + // Set particle font fontFile = config.getValue("particleFont", ""); - if (isJapan) { fontFile = config.getValue("japanFont", ""); if (fontFile.empty()) fontFile = branding.getStringValue("japanFont"); } - - + else if (isChinese) + { + fontFile = config.getValue("chinaFont", ""); + if (fontFile.empty()) + fontFile = branding.getStringValue("chinaFont"); + } if (fontFile.empty()) fontFile = branding.getStringValue("particleFont"); @@ -186,6 +199,7 @@ void Gui::postInit(Graphics *const graphics) .append("': ").append(e.getMessage())); } + // Set bold font fontFile = config.getValue("boldFont", ""); if (fontFile.empty()) @@ -201,6 +215,7 @@ void Gui::postInit(Graphics *const graphics) .append("': ").append(e.getMessage())); } + // Set help font fontFile = config.getValue("helpFont", ""); if (fontFile.empty()) @@ -216,6 +231,7 @@ void Gui::postInit(Graphics *const graphics) .append("': ").append(e.getMessage())); } + // Set secure font fontFile = config.getValue("secureFont", ""); if (fontFile.empty()) @@ -231,9 +247,22 @@ void Gui::postInit(Graphics *const graphics) .append("': ").append(e.getMessage())); } + // Set npc font - fontFile = config.getValue("npcFont", ""); const int npcFontSize = config.getIntValue("npcfontSize"); + fontFile = config.getValue("npcFont", ""); + if (isJapan) + { + fontFile = config.getValue("japanFont", ""); + if (fontFile.empty()) + fontFile = branding.getStringValue("japanFont"); + } + else if (isChinese) + { + fontFile = config.getValue("chinaFont", ""); + if (fontFile.empty()) + fontFile = branding.getStringValue("chinaFont"); + } if (fontFile.empty()) fontFile = branding.getStringValue("npcFont"); @@ -339,6 +368,14 @@ void Gui::slowLogic() mNpcFont->slowLogic(5); if (windowContainer) windowContainer->slowLogic(); + + const int time = cur_time; + if (mTime != time) + { + logger->flush(); + mTime = time; + } + BLOCK_END("Gui::slowLogic") } diff --git a/src/gui/gui.h b/src/gui/gui.h index 56895b699..7c34fc5cf 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -197,6 +197,7 @@ class Gui final : public gcn::Gui FocusListenerList mFocusListeners; gcn::Color mForegroundColor; gcn::Color mForegroundColor2; + int mTime; bool mCustomCursor; /**< Show custom cursor */ bool mDoubleClick; }; diff --git a/src/gui/popups/popupmenu.cpp b/src/gui/popups/popupmenu.cpp index b1f7b8c8a..52cf1d5d6 100644 --- a/src/gui/popups/popupmenu.cpp +++ b/src/gui/popups/popupmenu.cpp @@ -294,8 +294,7 @@ void PopupMenu::showPopup(const int x, const int y, const Being *const being) case ActorSprite::PORTAL: case ActorSprite::PET: default: - /* Other beings aren't interesting... */ - return; + break; } // TRANSLATORS: popup menu item // TRANSLATORS: add being name to chat diff --git a/src/gui/theme.cpp b/src/gui/theme.cpp index 45c6db5ba..44f2ca24e 100644 --- a/src/gui/theme.cpp +++ b/src/gui/theme.cpp @@ -33,6 +33,7 @@ #include "resources/resourcemanager.h" #include "utils/dtor.h" +#include "utils/files.h" #include "utils/physfstools.h" #include <algorithm> @@ -649,37 +650,13 @@ bool Theme::tryThemePath(const std::string &themeName) void Theme::fillSkinsList(StringVect &list) { - char **skins = PhysFs::enumerateFiles( - branding.getStringValue("guiThemePath").c_str()); - - for (char **i = skins; *i; i++) - { - if (PhysFs::isDirectory(( - branding.getStringValue("guiThemePath") + *i).c_str())) - { - list.push_back(*i); - } - } - - PhysFs::freeList(skins); + Files::getDirs(branding.getStringValue("guiThemePath"), list); } void Theme::fillFontsList(StringVect &list) { PHYSFS_permitSymbolicLinks(1); - char **fonts = PhysFs::enumerateFiles( - branding.getStringValue("fontsPath").c_str()); - - for (char **i = fonts; *i; i++) - { - if (!PhysFs::isDirectory(( - branding.getStringValue("fontsPath") + *i).c_str())) - { - list.push_back(*i); - } - } - - PhysFs::freeList(fonts); + Files::getFiles(branding.getStringValue("fontsPath"), list); PHYSFS_permitSymbolicLinks(0); } @@ -1095,7 +1072,7 @@ void Theme::loadColors(std::string file) file.append("/colors.xml"); XML::Document doc(resolveThemePath(file)); - const XmlNodePtr root = doc.rootNode(); + const XmlNodePtrConst root = doc.rootNode(); if (!root || !xmlNameEqual(root, "colors")) { @@ -1283,7 +1260,7 @@ ThemeInfo *Theme::loadInfo(const std::string &themeName) } logger->log("loading: " + path); XML::Document doc(path); - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "info")) return nullptr; @@ -1308,8 +1285,12 @@ ThemeInfo *Theme::loadInfo(const std::string &themeName) readValue(helpFont); else if (xmlNameEqual(infoNode, "secureFont")) readValue(secureFont); + else if (xmlNameEqual(infoNode, "npcFont")) + readValue(npcFont); else if (xmlNameEqual(infoNode, "japanFont")) readValue(japanFont); + else if (xmlNameEqual(infoNode, "chinaFont")) + readValue(chinaFont); else if (xmlNameEqual(infoNode, "fontSize")) readIntValue(fontSize); else if (xmlNameEqual(infoNode, "npcfontSize")) diff --git a/src/gui/theme.h b/src/gui/theme.h index 95511dc59..910964bdf 100644 --- a/src/gui/theme.h +++ b/src/gui/theme.h @@ -53,7 +53,9 @@ struct ThemeInfo final particleFont(), helpFont(), secureFont(), + npcFont(), japanFont(), + chinaFont(), fontSize(0), npcfontSize(0), guiAlpha(0.0F) @@ -66,7 +68,9 @@ struct ThemeInfo final std::string particleFont; std::string helpFont; std::string secureFont; + std::string npcFont; std::string japanFont; + std::string chinaFont; int fontSize; int npcfontSize; float guiAlpha; diff --git a/src/gui/widgets/colorpage.cpp b/src/gui/widgets/colorpage.cpp index ee5a37c3c..11dd1f07e 100644 --- a/src/gui/widgets/colorpage.cpp +++ b/src/gui/widgets/colorpage.cpp @@ -29,10 +29,10 @@ ColorPage::ColorPage(const Widget2 *const widget, gcn::ListModel *const listModel, const std::string &skin) : - ListBox(widget, listModel, skin), - mItemPadding(mSkin ? mSkin->getOption("itemPadding") : 1), - mRowHeight(13) + ListBox(widget, listModel, skin) { + mItemPadding = mSkin ? mSkin->getOption("itemPadding") : 1; + mRowHeight = 13; const gcn::Font *const font = getFont(); if (font) mRowHeight = font->getHeight() + 2 * mItemPadding; diff --git a/src/gui/widgets/colorpage.h b/src/gui/widgets/colorpage.h index 06699cbbd..c1c90eddf 100644 --- a/src/gui/widgets/colorpage.h +++ b/src/gui/widgets/colorpage.h @@ -41,10 +41,6 @@ class ColorPage final : public ListBox void resetAction(); void adjustSize() override final; - - private: - int mItemPadding; - unsigned int mRowHeight; }; #endif // GUI_WIDGETS_COLORPAGE_H diff --git a/src/gui/widgets/container.cpp b/src/gui/widgets/container.cpp index 4b0fc60f7..e37bd5103 100644 --- a/src/gui/widgets/container.cpp +++ b/src/gui/widgets/container.cpp @@ -35,7 +35,11 @@ Container::~Container() { if (gui) gui->removeDragged(this); + removeControls(); +} +void Container::removeControls() +{ while (!mWidgets.empty()) delete mWidgets.front(); } diff --git a/src/gui/widgets/container.h b/src/gui/widgets/container.h index efcdb70a7..2983a7433 100644 --- a/src/gui/widgets/container.h +++ b/src/gui/widgets/container.h @@ -49,6 +49,8 @@ class Container : public gcn::Container, virtual ~Container(); bool safeRemove(gcn::Widget *const widget); + + void removeControls(); }; #endif // GUI_WIDGETS_CONTAINER_H diff --git a/src/gui/widgets/desktop.cpp b/src/gui/widgets/desktop.cpp index 8ccfcb982..32c8a4988 100644 --- a/src/gui/widgets/desktop.cpp +++ b/src/gui/widgets/desktop.cpp @@ -159,9 +159,10 @@ void Desktop::setBestFittingWallpaper() if (nWallPaper) { + ResourceManager *const resman = ResourceManager::getInstance(); if (mWallpaper) { - mWallpaper->decRef(); + resman->decRefDelete(mWallpaper); mWallpaper = nullptr; } @@ -174,13 +175,13 @@ void Desktop::setBestFittingWallpaper() || nWallPaper->getHeight() != height)) { // We rescale to obtain a fullscreen wallpaper... - Image *const newRsclWlPpr = ResourceManager::getInstance() - ->getRescaled(nWallPaper, width, height); + Image *const newRsclWlPpr = resman->getRescaled( + nWallPaper, width, height); if (newRsclWlPpr) { + resman->decRefDelete(nWallPaper); // We replace the resource in the resource manager - nWallPaper->decRef(); mWallpaper = newRsclWlPpr; } else diff --git a/src/gui/widgets/progressbar.cpp b/src/gui/widgets/progressbar.cpp index 97c29bd8c..aecb323f8 100644 --- a/src/gui/widgets/progressbar.cpp +++ b/src/gui/widgets/progressbar.cpp @@ -46,10 +46,7 @@ ProgressBar::ProgressBar(const Widget2 *const widget, float progress, mSkin(nullptr), mProgress(progress), mProgressToGo(progress), - mBackgroundColor(Theme::getProgressColor(backColor >= 0 - ? backColor : 0, mProgress)), - mBackgroundColorToGo(mBackgroundColor), - mForegroundColor2(getThemeColor(Theme::PROGRESS_BAR_OUTLINE)), + mBackgroundColorToGo(), mText(), mVertexes(new ImageCollection), mProgressPalette(backColor), @@ -60,6 +57,11 @@ ProgressBar::ProgressBar(const Widget2 *const widget, float progress, mSmoothColorChange(true), mRedraw(true) { + mBackgroundColor = Theme::getProgressColor(backColor >= 0 + ? backColor : 0, mProgress); + mBackgroundColorToGo = mBackgroundColor; + mForegroundColor2 = getThemeColor(Theme::PROGRESS_BAR_OUTLINE); + // The progress value is directly set at load time: if (mProgress > 1.0F || mProgress < 0.0F) mProgress = 1.0F; diff --git a/src/gui/widgets/progressbar.h b/src/gui/widgets/progressbar.h index 9e503526c..eefabe83a 100644 --- a/src/gui/widgets/progressbar.h +++ b/src/gui/widgets/progressbar.h @@ -144,9 +144,7 @@ class ProgressBar final : public gcn::Widget, float mProgress; float mProgressToGo; - gcn::Color mBackgroundColor; gcn::Color mBackgroundColorToGo; - gcn::Color mForegroundColor2; std::string mText; ImageCollection *mVertexes; diff --git a/src/gui/widgets/setupitem.cpp b/src/gui/widgets/setupitem.cpp index 09717bc8b..6ce8ce102 100644 --- a/src/gui/widgets/setupitem.cpp +++ b/src/gui/widgets/setupitem.cpp @@ -37,6 +37,7 @@ #include "gui/widgets/sliderlist.h" #include "gui/widgets/vertcontainer.h" +#include "utils/base64.h" #include "utils/gettext.h" #include "utils/mathutils.h" @@ -105,6 +106,9 @@ Configuration *SetupItem::getConfig() const void SetupItem::load() { + if (mKeyName.empty()) + return; + const Configuration *const cfg = getConfig(); if (mUseDefault) { @@ -133,8 +137,11 @@ void SetupItem::load() } } -void SetupItem::save() const +void SetupItem::save() { + if (mKeyName.empty()) + return; + Configuration *const cfg = getConfig(); cfg->setValue(mKeyName, mValue); } @@ -178,6 +185,10 @@ void SetupItem::externalUpdated(const std::string &eventName A_UNUSED) toWidget(); } +void SetupItem::externalUnloaded(const std::string &eventName A_UNUSED) +{ +} + void SetupItem::fixFirstItemSize(gcn::Widget *const widget) { const int maxSize = mParent->getPreferredFirstItemSize(); @@ -257,13 +268,15 @@ SetupItemTextField::SetupItemTextField(const std::string &restrict text, const std::string &restrict keyName, SetupTabScroll *restrict const parent, const std::string &restrict eventName, - const bool mainConfig) : + const bool mainConfig, + const bool useBase64) : SetupItem(text, description, keyName, parent, eventName, mainConfig), mHorizont(nullptr), mLabel(nullptr), mTextField(nullptr), mButton(nullptr), - mEditDialog(nullptr) + mEditDialog(nullptr), + mUseBase64(useBase64) { mValueType = VSTR; createControls(); @@ -275,13 +288,15 @@ SetupItemTextField::SetupItemTextField(const std::string &restrict text, SetupTabScroll *restrict const parent, const std::string &restrict eventName, const std::string &restrict def, - const bool mainConfig) : + const bool mainConfig, + const bool useBase64) : SetupItem(text, description, keyName, parent, eventName, def, mainConfig), mHorizont(nullptr), mLabel(nullptr), mTextField(nullptr), mButton(nullptr), - mEditDialog(nullptr) + mEditDialog(nullptr), + mUseBase64(useBase64) { mValueType = VSTR; createControls(); @@ -296,9 +311,26 @@ SetupItemTextField::~SetupItemTextField() mButton = nullptr; } +void SetupItemTextField::save() +{ + if (mUseBase64) + { + std::string normalValue = mValue; + mValue = encodeBase64String(mValue); + SetupItem::save(); + mValue = normalValue; + } + else + { + SetupItem::save(); + } +} + void SetupItemTextField::createControls() { load(); + if (mUseBase64) + mValue = decodeBase64String(mValue); mHorizont = new HorizontContainer(this, 32, 2); mLabel = new Label(this, mText); diff --git a/src/gui/widgets/setupitem.h b/src/gui/widgets/setupitem.h index 561265729..91547733f 100644 --- a/src/gui/widgets/setupitem.h +++ b/src/gui/widgets/setupitem.h @@ -63,7 +63,7 @@ class SetupItem : public gcn::ActionListener, void load(); - void save() const; + virtual void save(); virtual void fromWidget() = 0; @@ -89,6 +89,8 @@ class SetupItem : public gcn::ActionListener, virtual void externalUpdated(const std::string &eventName); + virtual void externalUnloaded(const std::string &eventName); + bool isMainConfig() const A_WARN_UNUSED { return mMainConfig; } @@ -96,6 +98,15 @@ class SetupItem : public gcn::ActionListener, void rereadValue(); + void setValue(const std::string str) + { mValue = str; } + + std::string getValue() const + { return mValue; } + + std::string getEventName() const + { return mEventName; } + protected: SetupItem(const std::string &restrict text, const std::string &restrict description, @@ -177,7 +188,8 @@ class SetupItemTextField final : public SetupItem const std::string &restrict keyName, SetupTabScroll *restrict const parent, const std::string &restrict eventName, - const bool mainConfig = true); + const bool mainConfig = true, + const bool useBase64 = false); SetupItemTextField(const std::string &restrict text, const std::string &restrict description, @@ -185,7 +197,8 @@ class SetupItemTextField final : public SetupItem SetupTabScroll *restrict const parent, const std::string &restrict eventName, const std::string &restrict def, - const bool mainConfig = true); + const bool mainConfig = true, + const bool useBase64 = false); A_DELETE_COPY(SetupItemTextField) @@ -201,12 +214,18 @@ class SetupItemTextField final : public SetupItem void apply(const std::string &eventName) override final; + void save() override final; + + void setUseBase64(const bool b) + { mUseBase64 = b; } + protected: HorizontContainer *mHorizont; Label *mLabel; TextField *mTextField; Button *mButton; EditDialog *mEditDialog; + bool mUseBase64; }; class SetupItemIntTextField final : public SetupItem diff --git a/src/gui/widgets/shoplistbox.cpp b/src/gui/widgets/shoplistbox.cpp index 9b368a10c..58f9ec8eb 100644 --- a/src/gui/widgets/shoplistbox.cpp +++ b/src/gui/widgets/shoplistbox.cpp @@ -42,20 +42,18 @@ const int ITEM_ICON_SIZE = 32; -float ShopListBox::mAlpha = 1.0; - ShopListBox::ShopListBox(const Widget2 *const widget, gcn::ListModel *const listModel) : ListBox(widget, listModel, "shoplistbox.xml"), mPlayerMoney(0), mShopItems(nullptr), mItemPopup(new ItemPopup), - mRowHeight(getFont()->getHeight()), mBackgroundColor(getThemeColor(Theme::BACKGROUND)), mWarningColor(getThemeColor(Theme::SHOP_WARNING)), mPriceCheck(true), mProtectItems(false) { + mRowHeight = getFont()->getHeight(); mItemPopup->postInit(); mHighlightColor = getThemeColor(Theme::HIGHLIGHT); mForegroundColor = getThemeColor(Theme::LISTBOX); @@ -68,12 +66,12 @@ ShopListBox::ShopListBox(const Widget2 *const widget, mPlayerMoney(0), mShopItems(shopListModel), mItemPopup(new ItemPopup), - mRowHeight(std::max(getFont()->getHeight(), ITEM_ICON_SIZE)), mBackgroundColor(getThemeColor(Theme::BACKGROUND)), mWarningColor(getThemeColor(Theme::SHOP_WARNING)), mPriceCheck(true), mProtectItems(false) { + mRowHeight = std::max(getFont()->getHeight(), ITEM_ICON_SIZE); mItemPopup->postInit(); mHighlightColor = getThemeColor(Theme::HIGHLIGHT); mForegroundColor = getThemeColor(Theme::LISTBOX); diff --git a/src/gui/widgets/shoplistbox.h b/src/gui/widgets/shoplistbox.h index 5cbc66158..095d187eb 100644 --- a/src/gui/widgets/shoplistbox.h +++ b/src/gui/widgets/shoplistbox.h @@ -59,12 +59,6 @@ class ShopListBox final : public ListBox void draw(gcn::Graphics *graphics) override final; /** - * Returns the height of a row. - */ - unsigned int getRowHeight() const override final A_WARN_UNUSED - { return mRowHeight; } - - /** * gives information about the current player's money */ void setPlayersMoney(const int money); @@ -100,15 +94,11 @@ class ShopListBox final : public ListBox ItemPopup *mItemPopup; - unsigned int mRowHeight; /**< Row Height */ - gcn::Color mBackgroundColor; gcn::Color mWarningColor; bool mPriceCheck; bool mProtectItems; - - static float mAlpha; }; #endif // GUI_WIDGETS_SHOPLISTBOX_H diff --git a/src/gui/widgets/tabs/setup_mods.cpp b/src/gui/widgets/tabs/setup_mods.cpp new file mode 100644 index 000000000..ada0ef686 --- /dev/null +++ b/src/gui/widgets/tabs/setup_mods.cpp @@ -0,0 +1,126 @@ +/* + * The ManaPlus Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 Andrei Karas + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "gui/widgets/tabs/setup_mods.h" + +#include "configuration.h" + +#include "gui/widgets/layouthelper.h" +#include "gui/widgets/setupitem.h" +#include "gui/widgets/scrollarea.h" + +#include "resources/db/moddb.h" + +#include "utils/gettext.h" + +#include "debug.h" + +Setup_Mods::Setup_Mods(const Widget2 *const widget) : + SetupTabScroll(widget) +{ + // TRANSLATORS: mods tab in settings + setName(_("Mods")); + + LayoutHelper h(this); + ContainerPlacer place = h.getPlacer(0, 0); + place(0, 0, mScroll, 10, 10); + + setDimension(gcn::Rectangle(0, 0, 550, 350)); +} + +Setup_Mods::~Setup_Mods() +{ +} + +void Setup_Mods::apply() +{ + SetupTabScroll::apply(); + saveMods(); +} + +void Setup_Mods::externalUpdated() +{ + clear(); + loadMods(); +} + +void Setup_Mods::loadMods() +{ + std::string modsString = serverConfig.getValue("mods", ""); + std::set<std::string> modsList; + splitToStringSet(modsList, modsString, '|'); + + const ModInfos &mods = ModDB::getAll(); + if (mods.empty()) + { + // TRANSLATORS: settings label + new SetupItemLabel(_("No mods present"), "", this, false); + return; + } + + FOR_EACH (ModInfoCIterator, it, mods) + { + const ModInfo *const info = (*it).second; + if (!info) + continue; + + std::string name = info->getName(); + replaceAll(name, "|", ""); + SetupItem *const item = new SetupItemCheckBox( + info->getDescription(), "", "", this, name); + if (modsList.find(name) != modsList.end()) + item->setValue("1"); + else + item->setValue("0"); + item->toWidget(); + } +} + +void Setup_Mods::saveMods() const +{ + const ModInfos &mods = ModDB::getAll(); + if (mods.empty()) + return; + + std::string modsString; + const std::set<SetupItem*> &modsList = getAllItems(); + FOR_EACH (std::set<SetupItem*>::const_iterator, it, modsList) + { + const SetupItem *const item = *it; + if (!item) + continue; + const std::string val = item->getValue(); + if (val == "1") + { + const std::string key = item->getEventName(); + if (!modsString.empty()) + modsString.append("|"); + modsString.append(key); + } + } + serverConfig.setValue("mods", modsString); +} + +void Setup_Mods::externalUnloaded() +{ + clear(); +} diff --git a/src/gui/widgets/tabs/setup_mods.h b/src/gui/widgets/tabs/setup_mods.h new file mode 100644 index 000000000..c385aca08 --- /dev/null +++ b/src/gui/widgets/tabs/setup_mods.h @@ -0,0 +1,50 @@ +/* + * The ManaPlus Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 Andrei Karas + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus 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 GUI_WIDGETS_TABS_SETUP_MODS_H +#define GUI_WIDGETS_TABS_SETUP_MODS_H + +#include "gui/widgets/tabs/setuptabscroll.h" + +class Setup_Mods final : public SetupTabScroll +{ + public: + explicit Setup_Mods(const Widget2 *const widget); + + A_DELETE_COPY(Setup_Mods) + + ~Setup_Mods(); + + void apply() override final; + + void externalUpdated() override final; + + void externalUnloaded() override final; + + void loadMods(); + + void saveMods() const; + + protected: +}; + +#endif // GUI_WIDGETS_TABS_SETUP_MODS_H diff --git a/src/gui/widgets/tabs/setup_other.cpp b/src/gui/widgets/tabs/setup_other.cpp index 7dea0021f..9f3c30dec 100644 --- a/src/gui/widgets/tabs/setup_other.cpp +++ b/src/gui/widgets/tabs/setup_other.cpp @@ -371,6 +371,12 @@ Setup_Other::Setup_Other(const Widget2 *const widget) : "", "usefbo", this, "usefboEvent"); #endif +#ifndef WIN32 + // TRANSLATORS: settings option + new SetupItemTextField(_("Screenshot directory"), "", + "screenshotDirectory2", this, "screenshotDirectory2Event", true, true); +#endif + // TRANSLATORS: settings option new SetupItemIntTextField(_("Network delay between sub servers"), "", "networksleep", this, "networksleepEvent", 0, 10000); diff --git a/src/gui/widgets/tabs/setup_theme.cpp b/src/gui/widgets/tabs/setup_theme.cpp index c80ec3aac..d7cc4b4df 100644 --- a/src/gui/widgets/tabs/setup_theme.cpp +++ b/src/gui/widgets/tabs/setup_theme.cpp @@ -46,7 +46,9 @@ const char* ACTION_BOLD_FONT = "bold font"; const char* ACTION_PARTICLE_FONT = "particle font"; const char* ACTION_HELP_FONT = "help font"; const char* ACTION_SECURE_FONT = "secure font"; -const char* ACTION_JAPAN_FONT = "japan font"; +const char* ACTION_NPC_FONT = "npc font"; +const char* ACTION_JAPAN_FONT = "japanese font"; +const char* ACTION_CHINA_FONT = "chinese font"; const char* ACTION_INFO = "info"; class ThemesModel final : public NamesModel @@ -258,9 +260,17 @@ Setup_Theme::Setup_Theme(const Widget2 *const widget) : mSecureFontDropDown(new DropDown(this, mFontsModel)), mSecureFont(config.getStringValue("secureFont")), // TRANSLATORS: theme settings label + mNpcFontLabel(new Label(this, _("Npc font"))), + mNpcFontDropDown(new DropDown(this, mFontsModel)), + mNpcFont(config.getStringValue("npcFont")), + // TRANSLATORS: theme settings label mJapanFontLabel(new Label(this, _("Japanese font"))), mJapanFontDropDown(new DropDown(this, mFontsModel)), mJapanFont(config.getStringValue("japanFont")), + // TRANSLATORS: theme settings label + mChinaFontLabel(new Label(this, _("Chinese font"))), + mChinaFontDropDown(new DropDown(this, mFontsModel)), + mChinaFont(config.getStringValue("chinaFont")), mFontSizeListModel(new FontSizeChoiceListModel), // TRANSLATORS: theme settings label mFontSizeLabel(new Label(this, _("Font size"))), @@ -292,8 +302,12 @@ Setup_Theme::Setup_Theme(const Widget2 *const widget) : mHelpFontDropDown->addActionListener(this); mSecureFontDropDown->setActionEventId(ACTION_SECURE_FONT); mSecureFontDropDown->addActionListener(this); + mNpcFontDropDown->setActionEventId(ACTION_NPC_FONT); + mNpcFontDropDown->addActionListener(this); mJapanFontDropDown->setActionEventId(ACTION_JAPAN_FONT); mJapanFontDropDown->addActionListener(this); + mChinaFontDropDown->setActionEventId(ACTION_CHINA_FONT); + mChinaFontDropDown->addActionListener(this); mFontSizeDropDown->setSelected(mFontSize - 9); mFontSizeDropDown->adjustHeight(); mNpcFontSizeDropDown->setSelected(mNpcFontSize - 9); @@ -325,8 +339,12 @@ Setup_Theme::Setup_Theme(const Widget2 *const widget) : config.getStringValue("helpFont"))); mSecureFontDropDown->setSelectedString(getFileName( config.getStringValue("secureFont"))); + mNpcFontDropDown->setSelectedString(getFileName( + config.getStringValue("npcFont"))); mJapanFontDropDown->setSelectedString(getFileName( config.getStringValue("japanFont"))); + mChinaFontDropDown->setSelectedString(getFileName( + config.getStringValue("chinaFont"))); updateInfo(); @@ -343,7 +361,9 @@ Setup_Theme::Setup_Theme(const Widget2 *const widget) : place(0, 6, mParticleFontLabel, 5); place(0, 7, mHelpFontLabel, 5); place(0, 8, mSecureFontLabel, 5); - place(0, 9, mJapanFontLabel, 5); + place(0, 9, mNpcFontLabel, 5); + place(0, 10, mJapanFontLabel, 5); + place(0, 11, mChinaFontLabel, 5); place(6, 0, mThemeDropDown, 10); place(6, 1, mLangDropDown, 10); @@ -354,7 +374,9 @@ Setup_Theme::Setup_Theme(const Widget2 *const widget) : place(6, 6, mParticleFontDropDown, 10); place(6, 7, mHelpFontDropDown, 10); place(6, 8, mSecureFontDropDown, 10); - place(6, 9, mJapanFontDropDown, 10); + place(6, 9, mNpcFontDropDown, 10); + place(6, 10, mJapanFontDropDown, 10); + place(6, 11, mChinaFontDropDown, 10); place(17, 0, mInfoButton, 1); @@ -450,10 +472,18 @@ void Setup_Theme::action(const gcn::ActionEvent &event) { mSecureFont = mSecureFontDropDown->getSelectedString(); } + else if (eventId == ACTION_NPC_FONT) + { + mNpcFont = mNpcFontDropDown->getSelectedString(); + } else if (eventId == ACTION_JAPAN_FONT) { mJapanFont = mJapanFontDropDown->getSelectedString(); } + else if (eventId == ACTION_CHINA_FONT) + { + mChinaFont = mChinaFontDropDown->getSelectedString(); + } else if (eventId == ACTION_INFO) { // TRANSLATORS: theme info dialog header @@ -471,7 +501,9 @@ void Setup_Theme::cancel() mParticleFont = getFileName(config.getStringValue("particleFont")); mHelpFont = getFileName(config.getStringValue("helpFont")); mSecureFont = getFileName(config.getStringValue("secureFont")); + mNpcFont = getFileName(config.getStringValue("npcFont")); mJapanFont = getFileName(config.getStringValue("japanFont")); + mChinaFont = getFileName(config.getStringValue("chinaFont")); } #define updateField(name1, name2) if (!mInfo->name1.empty()) \ @@ -494,7 +526,9 @@ void Setup_Theme::apply() updateField(particleFont, mParticleFont); updateField(helpFont, mHelpFont); updateField(secureFont, mSecureFont); + updateField(npcFont, mNpcFont); updateField(japanFont, mJapanFont); + updateField(chinaFont, mChinaFont); if (mInfo->fontSize) { const int size = mInfo->fontSize - 9; @@ -517,7 +551,10 @@ void Setup_Theme::apply() || config.getValue("particleFont", "dejavusans.ttf") != mParticleFont || config.getValue("helpFont", "dejavusansmono.ttf") != mHelpFont || config.getValue("secureFont", "dejavusansmono.ttf") != mSecureFont + || config.getValue("npcFont", "dejavusans.ttf") != mNpcFont || config.getValue("japanFont", "mplus-1p-regular.ttf") != mJapanFont + || config.getValue("chinaFont", "fonts/wqy-microhei.ttf") + != mChinaFont || config.getIntValue("fontSize") != static_cast<int>(mFontSizeDropDown->getSelected()) + 9 || config.getIntValue("npcfontSize") @@ -528,7 +565,9 @@ void Setup_Theme::apply() config.setValue("particleFont", "fonts/" + getFileName(mParticleFont)); config.setValue("helpFont", "fonts/" + getFileName(mHelpFont)); config.setValue("secureFont", "fonts/" + getFileName(mSecureFont)); + config.setValue("npcFont", "fonts/" + getFileName(mNpcFont)); config.setValue("japanFont", "fonts/" + getFileName(mJapanFont)); + config.setValue("chinaFont", "fonts/" + getFileName(mChinaFont)); config.setValue("fontSize", mFontSizeDropDown->getSelected() + 9); config.setValue("npcfontSize", mNpcFontSizeDropDown->getSelected() + 9); diff --git a/src/gui/widgets/tabs/setup_theme.h b/src/gui/widgets/tabs/setup_theme.h index daf0a6f72..a7f3ad101 100644 --- a/src/gui/widgets/tabs/setup_theme.h +++ b/src/gui/widgets/tabs/setup_theme.h @@ -84,10 +84,18 @@ class Setup_Theme final : public SetupTab DropDown *mSecureFontDropDown; std::string mSecureFont; + Label *mNpcFontLabel; + DropDown *mNpcFontDropDown; + std::string mNpcFont; + Label *mJapanFontLabel; DropDown *mJapanFontDropDown; std::string mJapanFont; + Label *mChinaFontLabel; + DropDown *mChinaFontDropDown; + std::string mChinaFont; + FontSizeChoiceListModel *mFontSizeListModel; Label *mFontSizeLabel; int mFontSize; diff --git a/src/gui/widgets/tabs/setuptab.cpp b/src/gui/widgets/tabs/setuptab.cpp index ab015f9a2..c445e2ad7 100644 --- a/src/gui/widgets/tabs/setuptab.cpp +++ b/src/gui/widgets/tabs/setuptab.cpp @@ -37,3 +37,7 @@ SetupTab::SetupTab(const Widget2 *const widget) : void SetupTab::externalUpdated() { } + +void SetupTab::externalUnloaded() +{ +} diff --git a/src/gui/widgets/tabs/setuptab.h b/src/gui/widgets/tabs/setuptab.h index 9d1362faf..388b0a988 100644 --- a/src/gui/widgets/tabs/setuptab.h +++ b/src/gui/widgets/tabs/setuptab.h @@ -57,6 +57,8 @@ class SetupTab : public Container, virtual void externalUpdated(); + virtual void externalUnloaded(); + protected: explicit SetupTab(const Widget2 *const widget); diff --git a/src/gui/widgets/tabs/setuptabscroll.cpp b/src/gui/widgets/tabs/setuptabscroll.cpp index f01d691b6..659ef5824 100644 --- a/src/gui/widgets/tabs/setuptabscroll.cpp +++ b/src/gui/widgets/tabs/setuptabscroll.cpp @@ -45,6 +45,11 @@ SetupTabScroll::~SetupTabScroll() delete mContainer; mContainer = nullptr; + removeItems(); +} + +void SetupTabScroll::removeItems() +{ std::set<SetupItem*>::iterator it = mAllItems.begin(); const std::set<SetupItem*>::iterator it_end = mAllItems.end(); while (it != it_end) @@ -53,6 +58,15 @@ SetupTabScroll::~SetupTabScroll() ++ it; } mAllItems.clear(); + + mItems.clear(); +} + +void SetupTabScroll::clear() +{ + removeItems(); + mContainer->removeControls(); + mContainer->clear(); } void SetupTabScroll::addControl(SetupItem *const widget) @@ -120,6 +134,18 @@ void SetupTabScroll::externalUpdated() } } +void SetupTabScroll::externalUnloaded() +{ + for (std::map<std::string, SetupItem*>::const_iterator + iter = mItems.begin(), iter_end = mItems.end(); + iter != iter_end; ++ iter) + { + SetupItem *const widget = (*iter).second; + if (widget && !widget->isMainConfig()) + widget->externalUnloaded((*iter).first); + } +} + void SetupTabScroll::widgetResized(const gcn::Event &event A_UNUSED) { mScroll->setWidth(getWidth() - 12); diff --git a/src/gui/widgets/tabs/setuptabscroll.h b/src/gui/widgets/tabs/setuptabscroll.h index 578fba861..e3d495690 100644 --- a/src/gui/widgets/tabs/setuptabscroll.h +++ b/src/gui/widgets/tabs/setuptabscroll.h @@ -54,6 +54,8 @@ class SetupTabScroll : public SetupTab virtual void externalUpdated() override; + virtual void externalUnloaded() override; + virtual void action(const gcn::ActionEvent &event A_UNUSED) override final { } @@ -65,7 +67,14 @@ class SetupTabScroll : public SetupTab void reread(const std::string &name); + void clear(); + + const std::set<SetupItem*> &getAllItems() const + { return mAllItems; } + protected: + void removeItems(); + VertContainer *mContainer; ScrollArea *mScroll; std::map<std::string, SetupItem*> mItems; diff --git a/src/gui/widgets/textfield.cpp b/src/gui/widgets/textfield.cpp index ddee77233..07fde23cd 100644 --- a/src/gui/widgets/textfield.cpp +++ b/src/gui/widgets/textfield.cpp @@ -24,11 +24,8 @@ #include "client.h" -#if defined ANDROID || defined USE_SDL2 #include "input/inputmanager.h" -#endif -#include "input/keydata.h" #include "input/keyevent.h" #include "gui/sdlinput.h" @@ -275,71 +272,7 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) mLastEventPaste = 0; bool consumed(false); - switch (val) - { - case 2: // Ctrl+b - { - moveCaretBack(); - consumed = true; - break; - } - - case 6: // Ctrl+f - { - moveCaretForward(); - consumed = true; - break; - } - - case 4: // Ctrl+d - { - caretDelete(); - consumed = true; - break; - } - - case 8: // Ctrl+h - deleteCharLeft(mText, &mCaretPosition); - consumed = true; - break; - - case 5: // Ctrl+e - mCaretPosition = static_cast<int>(mText.size()); - consumed = true; - break; - - case 11: // Ctrl+k - mText = mText.substr(0, mCaretPosition); - consumed = true; - break; - - case 21: // Ctrl+u - caretDeleteToStart(); - consumed = true; - break; - - case 3: // Ctrl+c - handleCopy(); - consumed = true; - break; - - case 22: // Control code 22, SYNCHRONOUS IDLE, sent on Ctrl+v - // hack to prevent paste key sticking - if (mLastEventPaste && mLastEventPaste > cur_time) - break; - handlePaste(); - mLastEventPaste = cur_time + 2; - consumed = true; - break; - - case 23: // Ctrl+w - caretDeleteWord(); - consumed = true; - break; - - default: - break; - } + handleSDLKeys(val, consumed); if (consumed) { @@ -352,7 +285,39 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) } #endif - const int action = static_cast<KeyEvent*>(&keyEvent)->getActionId(); + if (consumed) + { + keyEvent.consume(); + } + else + { + const int action = static_cast<KeyEvent*>(&keyEvent)->getActionId(); + if (!inputManager.isActionActive(static_cast<int>( + Input::KEY_GUI_CTRL))) + { + if (!handleNormalKeys(action, consumed)) + { + if (consumed) + keyEvent.consume(); + return; + } + } + else + { + handleCtrlKeys(action, consumed); + } + } + + if (mSendAlwaysEvents) + distributeActionEvent(); + + if (consumed) + keyEvent.consume(); + fixScroll(); +} + +bool TextField::handleNormalKeys(const int action, bool &consumed) +{ switch (action) { case Input::KEY_GUI_LEFT: @@ -407,9 +372,9 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) case Input::KEY_GUI_SELECT2: distributeActionEvent(); - keyEvent.consume(); + consumed = true; fixScroll(); - return; + return false; case Input::KEY_GUI_HOME: mCaretPosition = 0; @@ -423,99 +388,169 @@ void TextField::keyPressed(gcn::KeyEvent &keyEvent) case Input::KEY_GUI_TAB: if (mLoseFocusOnTab) - return; + return false; consumed = true; break; default: break; } + return true; +} - if (mSendAlwaysEvents) - distributeActionEvent(); - - if (consumed) +void TextField::handleCtrlKeys(const int action, bool &consumed) +{ + switch (action) { - keyEvent.consume(); - } + case Input::KEY_GUI_LEFT: + { + moveCaretWordBack(); + consumed = true; + break; + } + case Input::KEY_GUI_RIGHT: + { + moveCaretWordForward(); + consumed = true; + break; + } #ifdef USE_SDL2 - else + case Input::KEY_GUI_B: + { + moveCaretBack(); + consumed = true; + break; + } + case Input::KEY_GUI_C: + { + handleCopy(); + consumed = true; + break; + } + case Input::KEY_GUI_D: + { + caretDelete(); + consumed = true; + break; + } + case Input::KEY_GUI_E: + { + mCaretPosition = static_cast<int>(mText.size()); + consumed = true; + break; + } + case Input::KEY_GUI_F: + { + moveCaretBack(); + consumed = true; + break; + } + case Input::KEY_GUI_H: + { + deleteCharLeft(mText, &mCaretPosition); + consumed = true; + break; + } + case Input::KEY_GUI_U: + { + caretDeleteToStart(); + consumed = true; + break; + } + case Input::KEY_GUI_K: + { + mText = mText.substr(0, mCaretPosition); + consumed = true; + break; + } + case Input::KEY_GUI_V: + { + handlePaste(); + consumed = true; + break; + } + case Input::KEY_GUI_W: + { + caretDeleteWord(); + consumed = true; + break; + } +#endif + default: + break; + } +} + +#ifndef USE_SDL2 +void TextField::handleSDLKeys(const int val, bool &consumed) +{ + switch (val) { - if (inputManager.isActionActive(static_cast<int>(Input::KEY_GUI_CTRL))) + case 2: // Ctrl+b { - switch (action) - { - case Input::KEY_GUI_B: - { - moveCaretBack(); - consumed = true; - break; - } - case Input::KEY_GUI_C: - { - handleCopy(); - consumed = true; - break; - } - case Input::KEY_GUI_D: - { - caretDelete(); - consumed = true; - break; - } - case Input::KEY_GUI_E: - { - mCaretPosition = static_cast<int>(mText.size()); - consumed = true; - break; - } - case Input::KEY_GUI_F: - { - moveCaretBack(); - consumed = true; - break; - } - case Input::KEY_GUI_H: - { - deleteCharLeft(mText, &mCaretPosition); - consumed = true; - break; - } - case Input::KEY_GUI_U: - { - caretDeleteToStart(); - consumed = true; - break; - } - case Input::KEY_GUI_K: - { - mText = mText.substr(0, mCaretPosition); - consumed = true; - break; - } - case Input::KEY_GUI_V: - { - handlePaste(); - consumed = true; - break; - } - case Input::KEY_GUI_W: - { - caretDeleteWord(); - consumed = true; - break; - } + moveCaretBack(); + consumed = true; + break; + } - default: - break; - } + case 6: // Ctrl+f + { + moveCaretForward(); + consumed = true; + break; } + + case 4: // Ctrl+d + { + caretDelete(); + consumed = true; + break; + } + + case 8: // Ctrl+h + deleteCharLeft(mText, &mCaretPosition); + consumed = true; + break; + + case 5: // Ctrl+e + mCaretPosition = static_cast<int>(mText.size()); + consumed = true; + break; + + case 11: // Ctrl+k + mText = mText.substr(0, mCaretPosition); + consumed = true; + break; + + case 21: // Ctrl+u + caretDeleteToStart(); + consumed = true; + break; + + case 3: // Ctrl+c + handleCopy(); + consumed = true; + break; + + case 22: // Control code 22, SYNCHRONOUS IDLE, sent on Ctrl+v + // hack to prevent paste key sticking + if (mLastEventPaste && mLastEventPaste > cur_time) + break; + handlePaste(); + mLastEventPaste = cur_time + 2; + consumed = true; + break; + + case 23: // Ctrl+w + caretDeleteWord(); + consumed = true; + break; + + default: + break; } - if (consumed) - keyEvent.consume(); -#endif - fixScroll(); } +#endif void TextField::moveCaretBack() { @@ -571,6 +606,45 @@ void TextField::caretDeleteToStart() } } +void TextField::moveCaretWordBack() +{ + const unsigned int oldCaret = mCaretPosition; + while (mCaretPosition > 0) + { + if (!isWordSeparator(mText[mCaretPosition - 1])) + break; + mCaretPosition --; + } + if (oldCaret != mCaretPosition) + return; + while (mCaretPosition > 0) + { + if (isWordSeparator(mText[mCaretPosition - 1])) + break; + mCaretPosition --; + } +} + +void TextField::moveCaretWordForward() +{ + const unsigned sz = static_cast<unsigned>(mText.size()); + const unsigned int oldCaret = mCaretPosition; + while (mCaretPosition < sz) + { + if (!isWordSeparator(mText[mCaretPosition])) + break; + mCaretPosition ++; + } + if (oldCaret != mCaretPosition) + return; + while (mCaretPosition < sz) + { + if (isWordSeparator(mText[mCaretPosition])) + break; + mCaretPosition ++; + } +} + void TextField::caretDeleteWord() { while (mCaretPosition > 0) diff --git a/src/gui/widgets/textfield.h b/src/gui/widgets/textfield.h index 675e9761d..abff9cf2a 100644 --- a/src/gui/widgets/textfield.h +++ b/src/gui/widgets/textfield.h @@ -131,6 +131,10 @@ class TextField : public gcn::TextField, void moveCaretForward(); + void moveCaretWordBack(); + + void moveCaretWordForward(); + void caretDelete(); void caretDeleteToStart(); @@ -146,6 +150,14 @@ class TextField : public gcn::TextField, bool mSendAlwaysEvents; + bool handleNormalKeys(const int action, bool &consumed); + + void handleCtrlKeys(const int action, bool &consumed); + +#ifndef USE_SDL2 + void handleSDLKeys(const int val, bool &consumed); +#endif + static Skin *mSkin; private: diff --git a/src/gui/windows/chatwindow.cpp b/src/gui/windows/chatwindow.cpp index 015e58b61..e1afb6e5b 100644 --- a/src/gui/windows/chatwindow.cpp +++ b/src/gui/windows/chatwindow.cpp @@ -1240,6 +1240,39 @@ WhisperTab *ChatWindow::getWhisperTab(const std::string &nick) const return ret; } + +#define changeColor(fun) \ + { \ + msg = removeColors(msg); \ + int skip = 0; \ + const size_t sz = msg.length(); \ + for (size_t f = 0; f < sz; f ++) \ + { \ + if (skip > 0) \ + { \ + newMsg += msg.at(f); \ + skip --; \ + continue; \ + } \ + const unsigned char ch = static_cast<unsigned char>(msg.at(f)); \ + if (f + 2 < sz && msg.substr(f, 2) == "%%") \ + { \ + newMsg += msg.at(f); \ + skip = 2; \ + } \ + else if (ch > 0xc0 || ch < 0x80) \ + { \ + newMsg += "##" + toString(fun) + msg.at(f); \ + if (mRainbowColor > 9) \ + mRainbowColor = 0; \ + } \ + else \ + { \ + newMsg += msg.at(f); \ + } \ + } \ + } + std::string ChatWindow::addColors(std::string &msg) { // default color or chat command @@ -1256,31 +1289,13 @@ std::string ChatWindow::addColors(std::string &msg) switch (mChatColor) { case 11: - msg = removeColors(msg); - for (unsigned int f = 0; f < msg.length(); f ++) - { - newMsg += "##" + toString(mRainbowColor++) + msg.at(f); - if (mRainbowColor > 9) - mRainbowColor = 0; - } + changeColor(mRainbowColor++) return newMsg; case 12: - msg = removeColors(msg); - for (unsigned int f = 0; f < msg.length(); f ++) - { - newMsg += "##" + toString(cMap[mRainbowColor++]) + msg.at(f); - if (mRainbowColor > 9) - mRainbowColor = 0; - } + changeColor(cMap[mRainbowColor++]) return newMsg; case 13: - msg = removeColors(msg); - for (unsigned int f = 0; f < msg.length(); f ++) - { - newMsg += "##" + toString(cMap[9-mRainbowColor++]) + msg.at(f); - if (mRainbowColor > 9) - mRainbowColor = 0; - } + changeColor(cMap[9-mRainbowColor++]) return newMsg; default: break; @@ -1290,6 +1305,8 @@ std::string ChatWindow::addColors(std::string &msg) return std::string("##").append(toString(mChatColor - 1)).append(msg); } +#undef changeColor + void ChatWindow::autoComplete() { const int caretPos = mChatInput->getCaretPosition(); @@ -1577,7 +1594,7 @@ void ChatWindow::localPetSay(const std::string &nick, const std::string &text) Being *pet = nullptr; if (being) { - pet = being->getPet(); + pet = being->getFirstPet(); if (pet) pet->setSpeech(text, GENERAL_CHANNEL); } @@ -1601,7 +1618,7 @@ void ChatWindow::localPetEmote(const std::string &nick, const uint8_t emoteId) nick, ActorSprite::PLAYER); if (being) { - Being *const pet = being->getPet(); + Being *const pet = being->getFirstPet(); if (pet) pet->setEmote(emoteId, 0); } diff --git a/src/gui/windows/editdialog.cpp b/src/gui/windows/editdialog.cpp index 502b71ecb..5c002212f 100644 --- a/src/gui/windows/editdialog.cpp +++ b/src/gui/windows/editdialog.cpp @@ -35,9 +35,9 @@ EditDialog::EditDialog(const std::string &restrict title, Window(title, modal, parent, "edit.xml"), gcn::ActionListener(), mEventOk(eventOk), - mTextField(new TextField(this)), - mDefaultWidth(width) + mTextField(new TextField(this)) { + mDefaultWidth = width; mTextField->setText(msg); } diff --git a/src/gui/windows/editdialog.h b/src/gui/windows/editdialog.h index 74242ed52..fa4a02bf7 100644 --- a/src/gui/windows/editdialog.h +++ b/src/gui/windows/editdialog.h @@ -66,7 +66,6 @@ class EditDialog final : public Window, public gcn::ActionListener private: std::string mEventOk; TextField *mTextField; - int mDefaultWidth; }; #endif // GUI_WINDOWS_EDITDIALOG_H diff --git a/src/gui/windows/equipmentwindow.h b/src/gui/windows/equipmentwindow.h index 1877d1309..d291a5f81 100644 --- a/src/gui/windows/equipmentwindow.h +++ b/src/gui/windows/equipmentwindow.h @@ -115,7 +115,7 @@ class EquipmentWindow final : public Window, public gcn::ActionListener void addBox(const int idx, int x, int y, const int imageIndex); - void loadWindow(const XmlNodePtr windowNode); + void loadWindow(const XmlNodePtrConst windowNode); void loadPlayerBox(const XmlNodePtr playerBoxNode); diff --git a/src/gui/windows/ministatuswindow.cpp b/src/gui/windows/ministatuswindow.cpp index cfc8f1e12..b5832c7b7 100644 --- a/src/gui/windows/ministatuswindow.cpp +++ b/src/gui/windows/ministatuswindow.cpp @@ -485,7 +485,7 @@ void MiniStatusWindow::loadBars() if (mStatusBar) mStatusBar->setVisible(false); if (mJobBar) - mJobBar->setVisible(false); + mJobBar->setVisible(true); return; } diff --git a/src/gui/windows/npcdialog.h b/src/gui/windows/npcdialog.h index af68594cd..5e679d7d1 100644 --- a/src/gui/windows/npcdialog.h +++ b/src/gui/windows/npcdialog.h @@ -212,6 +212,9 @@ class NpcDialog final : public Window, void mousePressed(gcn::MouseEvent &event); + int isCloseState() const + { return mActionState == NPC_ACTION_CLOSE; } + static void copyToClipboard(const int npcId, const int x, const int y); static NpcDialogs mNpcDialogs; diff --git a/src/gui/windows/questswindow.cpp b/src/gui/windows/questswindow.cpp index 0cd04dbc1..e5f599f0f 100644 --- a/src/gui/windows/questswindow.cpp +++ b/src/gui/windows/questswindow.cpp @@ -41,6 +41,8 @@ #include "utils/translation/podict.h" +#include "resources/beingcommon.h" + #include "debug.h" enum QuestType @@ -181,7 +183,9 @@ QuestsWindow::QuestsWindow() : loadWindowState(); enableVisibleSound(true); - loadXml(); + loadXmlFile(paths.getStringValue("questsFile")); + loadXmlFile(paths.getStringValue("questsPatchFile")); + loadXmlDir("questsPatchDir", loadXmlFile); } QuestsWindow::~QuestsWindow() @@ -218,16 +222,23 @@ QuestsWindow::~QuestsWindow() } } -void QuestsWindow::loadXml() +void QuestsWindow::loadXmlFile(const std::string &fileName) { - XML::Document doc(paths.getStringValue("questsFile")); - const XmlNodePtr root = doc.rootNode(); + XML::Document doc(fileName); + const XmlNodePtrConst root = doc.rootNode(); if (!root) return; for_each_xml_child_node(varNode, root) { - if (xmlNameEqual(varNode, "var")) + if (xmlNameEqual(varNode, "include")) + { + const std::string name = XML::getProperty(varNode, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(varNode, "var")) { const int id = XML::getProperty(varNode, "id", 0); if (id < 0) diff --git a/src/gui/windows/questswindow.h b/src/gui/windows/questswindow.h index 5b008e93e..7ecc86c84 100644 --- a/src/gui/windows/questswindow.h +++ b/src/gui/windows/questswindow.h @@ -72,7 +72,7 @@ class QuestsWindow final : public Window, void addEffect(Being *const being); private: - void loadXml(); + void loadXmlFile(const std::string &fileName); void loadQuest(const int var, const XmlNodePtr node); diff --git a/src/gui/windows/serverdialog.cpp b/src/gui/windows/serverdialog.cpp index 971f3c5a5..2a1ec8314 100644 --- a/src/gui/windows/serverdialog.cpp +++ b/src/gui/windows/serverdialog.cpp @@ -394,7 +394,10 @@ void ServerDialog::connectToSelectedServer() if (!LoginDialog::savedPasswordKey.empty()) { if (mServerInfo->hostname != LoginDialog::savedPasswordKey) + { LoginDialog::savedPassword.clear(); + client->reloadWallpaper(); + } } config.setValue("usePersistentIP", diff --git a/src/gui/windows/setup.cpp b/src/gui/windows/setup.cpp index 995e6beac..d0ecc2d6d 100644 --- a/src/gui/windows/setup.cpp +++ b/src/gui/windows/setup.cpp @@ -32,13 +32,14 @@ #include "gui/widgets/tabs/setup_audio.h" #include "gui/widgets/tabs/setup_chat.h" #include "gui/widgets/tabs/setup_colors.h" +#include "gui/widgets/tabs/setup_input.h" #include "gui/widgets/tabs/setup_joystick.h" +#include "gui/widgets/tabs/setup_mods.h" #include "gui/widgets/tabs/setup_other.h" -#include "gui/widgets/tabs/setup_theme.h" -#include "gui/widgets/tabs/setup_input.h" #include "gui/widgets/tabs/setup_perfomance.h" #include "gui/widgets/tabs/setup_players.h" #include "gui/widgets/tabs/setup_relations.h" +#include "gui/widgets/tabs/setup_theme.h" #include "gui/widgets/tabs/setup_touch.h" #include "gui/widgets/tabs/setup_video.h" #include "gui/widgets/tabs/setup_visual.h" @@ -58,6 +59,7 @@ Setup::Setup() : Window(_("Setup"), false, nullptr, "setup.xml"), gcn::ActionListener(), mTabs(), + mModsTab(nullptr), mWindowsToReset(), mButtons(), mResetWindows(nullptr), @@ -201,6 +203,10 @@ void Setup::setInGame(const bool inGame) void Setup::externalUpdate() { + unloadModTab(); + mModsTab = new Setup_Mods(this); + mTabs.push_back(mModsTab); + mPanel->addTab(mModsTab->getName(), mModsTab); FOR_EACH (std::list<SetupTab*>::const_iterator, it, mTabs) { if (*it) @@ -208,6 +214,28 @@ void Setup::externalUpdate() } } +void Setup::unloadModTab() +{ + if (mModsTab) + { + mTabs.remove(mModsTab); + Tab *const tab = mPanel->getTab(mModsTab->getName()); + mPanel->removeTab(tab); + delete mModsTab; + mModsTab = nullptr; + } +} + +void Setup::externalUnload() +{ + FOR_EACH (std::list<SetupTab*>::const_iterator, it, mTabs) + { + if (*it) + (*it)->externalUnloaded(); + } + unloadModTab(); +} + void Setup::registerWindowForReset(Window *const window) { mWindowsToReset.push_back(window); diff --git a/src/gui/windows/setup.h b/src/gui/windows/setup.h index 1b484b726..4c9ab2fb7 100644 --- a/src/gui/windows/setup.h +++ b/src/gui/windows/setup.h @@ -57,6 +57,8 @@ class Setup final : public Window, public gcn::ActionListener void externalUpdate(); + void externalUnload(); + void registerWindowForReset(Window *const window); void clearWindowsForReset() @@ -71,7 +73,10 @@ class Setup final : public Window, public gcn::ActionListener void widgetResized(const gcn::Event &event) override final; private: + void unloadModTab(); + std::list<SetupTab*> mTabs; + SetupTab *mModsTab; std::list<Window*> mWindowsToReset; std::vector<Button*> mButtons; Button *mResetWindows; diff --git a/src/gui/windows/skilldialog.cpp b/src/gui/windows/skilldialog.cpp index fc44fdf65..197c60a90 100644 --- a/src/gui/windows/skilldialog.cpp +++ b/src/gui/windows/skilldialog.cpp @@ -51,6 +51,8 @@ #include "utils/dtor.h" #include "utils/gettext.h" +#include "resources/beingcommon.h" + #include <guichan/font.hpp> #include "debug.h" @@ -62,14 +64,14 @@ class SkillListBox final : public ListBox ListBox(widget, model, "skilllistbox.xml"), mModel(model), mPopup(new TextPopup), - mHighlightColor(getThemeColor(Theme::HIGHLIGHT)), mTextColor(getThemeColor(Theme::TEXT)), mTextColor2(getThemeColor(Theme::TEXT_OUTLINE)), mTextPadding(mSkin ? mSkin->getOption("textPadding", 34) : 34), mSpacing(mSkin ? mSkin->getOption("spacing", 0) : 0), - mRowHeight(getFont()->getHeight() * 2 + mSpacing + 2 * mPadding), mSkillClicked(false) { + mRowHeight = getFont()->getHeight() * 2 + mSpacing + 2 * mPadding; + mHighlightColor = getThemeColor(Theme::HIGHLIGHT); mPopup->postInit(); if (mRowHeight < 34) @@ -231,12 +233,10 @@ class SkillListBox final : public ListBox private: SkillModel *mModel; TextPopup *mPopup; - gcn::Color mHighlightColor; gcn::Color mTextColor; gcn::Color mTextColor2; int mTextPadding; int mSpacing; - int mRowHeight; bool mSkillClicked; }; @@ -429,75 +429,41 @@ void SkillDialog::clearSkills() void SkillDialog::loadSkills() { clearSkills(); + loadXmlFile(paths.getStringValue("skillsFile")); + if (mSkills.empty()) + loadXmlFile(paths.getStringValue("skillsFile2")); + loadXmlFile(paths.getStringValue("skillsPatchFile")); + loadXmlDir("skillsPatchDir", loadXmlFile); - XML::Document doc(paths.getStringValue("skillsFile")); - XML::Document doc2(paths.getStringValue("skillsFile2")); - XmlNodePtr root = doc.rootNode(); + update(); +} - int setCount = 0; - std::string setName; - ScrollArea *scroll; - SkillListBox *listbox; - SkillTab *tab; +void SkillDialog::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + XmlNodePtrConst root = doc.rootNode(); - if (!root || !xmlNameEqual(root, "skills")) - root = doc2.rootNode(); + int setCount = 0; if (!root || !xmlNameEqual(root, "skills")) { - logger->log("Error loading skills"); - -#ifdef MANASERV_SUPPORT - if (Net::getNetworkType() != ServerInfo::MANASERV) -#endif - { - SkillModel *const model = new SkillModel(); - if (!mDefaultModel) - mDefaultModel = model; - - SkillInfo *const skill = new SkillInfo; - skill->id = 1; - // TRANSLATORS: skills dialog default skills tab - skill->data->name = _("basic"); - skill->data->description.clear(); - // TRANSLATORS: skills dialog default skill name - skill->data->dispName = _("basic, 1"); - skill->data->shortName = "bas"; - skill->data->setIcon(""); - skill->modifiable = true; - skill->visible = true; - skill->model = model; - skill->update(); - - model->addSkill(skill); - mSkills[1] = skill; - - model->updateVisibilities(); - - listbox = new SkillListBox(this, model); - listbox->postInit(); - listbox->setActionEventId("sel"); - listbox->addActionListener(this); - scroll = new ScrollArea(listbox, false); - scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); - scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); - - tab = new SkillTab(this, "Skills", listbox); - mDeleteTabs.push_back(tab); - - mTabs->addTab(tab, scroll); - - update(); - } + logger->log("Error loading skills: " + fileName); return; } for_each_xml_child_node(set, root) { - if (xmlNameEqual(set, "set")) + if (xmlNameEqual(set, "include")) + { + const std::string name = XML::getProperty(set, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(set, "set")) { setCount++; - setName = XML::getProperty(set, "name", + const std::string setName = XML::getProperty(set, "name", // TRANSLATORS: skills dialog default skill tab strprintf(_("Skill Set %d"), setCount)); @@ -575,20 +541,19 @@ void SkillDialog::loadSkills() model->updateVisibilities(); // possible leak listbox, scroll - listbox = new SkillListBox(this, model); + SkillListBox *const listbox = new SkillListBox(this, model); listbox->setActionEventId("sel"); listbox->addActionListener(this); - scroll = new ScrollArea(listbox, false); + ScrollArea *const scroll = new ScrollArea(listbox, false); scroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER); scroll->setVerticalScrollPolicy(ScrollArea::SHOW_ALWAYS); - tab = new SkillTab(this, setName, listbox); + SkillTab *const tab = new SkillTab(this, setName, listbox); mDeleteTabs.push_back(tab); mTabs->addTab(tab, scroll); } } - update(); } bool SkillDialog::updateSkill(const int id, const int range, diff --git a/src/gui/windows/skilldialog.h b/src/gui/windows/skilldialog.h index 600ea4d34..4a6182d38 100644 --- a/src/gui/windows/skilldialog.h +++ b/src/gui/windows/skilldialog.h @@ -69,6 +69,8 @@ class SkillDialog final : public Window, public gcn::ActionListener */ void update(); + void loadXmlFile(const std::string &fileName); + void clearSkills(); void loadSkills(); diff --git a/src/gui/windows/updaterwindow.cpp b/src/gui/windows/updaterwindow.cpp index 099142cbe..584f8e90e 100644 --- a/src/gui/windows/updaterwindow.cpp +++ b/src/gui/windows/updaterwindow.cpp @@ -39,6 +39,8 @@ #include "resources/resourcemanager.h" +#include "resources/db/moddb.h" + #include "utils/gettext.h" #include "utils/mkdir.h" #include "utils/paths.h" @@ -58,11 +60,12 @@ const std::string updateServer2 /** * Load the given file into a vector of updateFiles. */ -static std::vector<UpdateFile> loadXMLFile(const std::string &fileName) +static std::vector<UpdateFile> loadXMLFile(const std::string &fileName, + const bool loadMods) { std::vector<UpdateFile> files; XML::Document doc(fileName, false); - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "updates")) { @@ -72,10 +75,8 @@ static std::vector<UpdateFile> loadXMLFile(const std::string &fileName) for_each_xml_child_node(fileNode, rootNode) { - if (!xmlNameEqual(fileNode, "update")) - continue; - - if (XML::getProperty(fileNode, "group", "default") != "default") + const bool isMod = xmlNameEqual(fileNode, "mod"); + if (!xmlNameEqual(fileNode, "update") && !isMod) continue; UpdateFile file; @@ -83,6 +84,10 @@ static std::vector<UpdateFile> loadXMLFile(const std::string &fileName) file.hash = XML::getProperty(fileNode, "hash", ""); file.type = XML::getProperty(fileNode, "type", "data"); file.desc = XML::getProperty(fileNode, "description", ""); + file.group = XML::getProperty(fileNode, "group", ""); + if (!file.group.empty() && (!isMod || !loadMods)) + continue; + const std::string version = XML::getProperty( fileNode, "version", ""); if (!version.empty()) @@ -128,6 +133,7 @@ static std::vector<UpdateFile> loadTxtFile(const std::string &fileName) thisFile.name = name; thisFile.hash = hash; thisFile.type = "data"; + thisFile.group = ""; thisFile.required = true; thisFile.desc.clear(); @@ -558,7 +564,7 @@ void UpdaterWindow::loadUpdates() if (mUpdateFiles.empty()) { // updates not downloaded mUpdateFiles = loadXMLFile(std::string(mUpdatesDir).append( - "/").append(xmlUpdateFile)); + "/").append(xmlUpdateFile), false); if (mUpdateFiles.empty()) { logger->log("Warning this server does not have a" @@ -573,18 +579,22 @@ void UpdaterWindow::loadUpdates() const unsigned sz = static_cast<unsigned>(mUpdateFiles.size()); for (mUpdateIndex = 0; mUpdateIndex < sz; mUpdateIndex++) { - UpdaterWindow::addUpdateFile(resman, mUpdatesDir, fixPath, - mUpdateFiles[mUpdateIndex].name, false); + const UpdateFile &file = mUpdateFiles[mUpdateIndex]; + if (!file.group.empty()) + continue; + UpdaterWindow::addUpdateFile(resman, mUpdatesDir, + fixPath, file.name, false); } loadManaPlusUpdates(mUpdatesDir, resman); + loadMods(mUpdatesDir, resman, mUpdateFiles); } void UpdaterWindow::loadLocalUpdates(const std::string &dir) { const ResourceManager *const resman = ResourceManager::getInstance(); - std::vector<UpdateFile> updateFiles - = loadXMLFile(std::string(dir).append("/").append(xmlUpdateFile)); + std::vector<UpdateFile> updateFiles = loadXMLFile( + std::string(dir).append("/").append(xmlUpdateFile), false); if (updateFiles.empty()) { @@ -599,17 +609,21 @@ void UpdaterWindow::loadLocalUpdates(const std::string &dir) for (unsigned int updateIndex = 0, sz = static_cast<unsigned int>( updateFiles.size()); updateIndex < sz; updateIndex ++) { - UpdaterWindow::addUpdateFile(resman, dir, fixPath, - updateFiles[updateIndex].name, false); + const UpdateFile &file = updateFiles[updateIndex]; + if (!file.group.empty()) + continue; + UpdaterWindow::addUpdateFile(resman, dir, + fixPath, file.name, false); } loadManaPlusUpdates(dir, resman); + loadMods(dir, resman, updateFiles); } void UpdaterWindow::unloadUpdates(const std::string &dir) { const ResourceManager *const resman = ResourceManager::getInstance(); - std::vector<UpdateFile> updateFiles - = loadXMLFile(std::string(dir).append("/").append(xmlUpdateFile)); + std::vector<UpdateFile> updateFiles = loadXMLFile( + std::string(dir).append("/").append(xmlUpdateFile), true); if (updateFiles.empty()) { @@ -631,19 +645,23 @@ void UpdaterWindow::loadManaPlusUpdates(const std::string &dir, const ResourceManager *const resman) { std::string fixPath = dir + "/fix"; - std::vector<UpdateFile> updateFiles - = loadXMLFile(std::string(fixPath).append("/").append(xmlUpdateFile)); + std::vector<UpdateFile> updateFiles = loadXMLFile( + std::string(fixPath).append("/").append(xmlUpdateFile), false); for (unsigned int updateIndex = 0, sz = static_cast<unsigned int>( updateFiles.size()); updateIndex < sz; updateIndex ++) { - std::string name = updateFiles[updateIndex].name; + const UpdateFile &file = updateFiles[updateIndex]; + if (!file.group.empty()) + continue; + const std::string name = file.name; if (strStartWith(name, "manaplus_")) { struct stat statbuf; - std::string file = std::string(fixPath).append("/").append(name); - if (!stat(file.c_str(), &statbuf)) - resman->addToSearchPath(file, false); + std::string fileName = std::string(fixPath).append( + "/").append(name); + if (!stat(fileName.c_str(), &statbuf)) + resman->addToSearchPath(fileName, false); } } } @@ -652,8 +670,8 @@ void UpdaterWindow::unloadManaPlusUpdates(const std::string &dir, const ResourceManager *const resman) { const std::string fixPath = dir + "/fix"; - const std::vector<UpdateFile> updateFiles - = loadXMLFile(std::string(fixPath).append("/").append(xmlUpdateFile)); + const std::vector<UpdateFile> updateFiles = loadXMLFile( + std::string(fixPath).append("/").append(xmlUpdateFile), true); for (unsigned int updateIndex = 0, sz = static_cast<unsigned int>( updateFiles.size()); updateIndex < sz; updateIndex ++) @@ -780,7 +798,7 @@ void UpdaterWindow::logic() if (mCurrentFile == xmlUpdateFile) { mUpdateFiles = loadXMLFile(std::string(mUpdatesDir).append( - "/").append(xmlUpdateFile)); + "/").append(xmlUpdateFile), true); if (mUpdateFiles.empty()) { @@ -869,7 +887,7 @@ void UpdaterWindow::logic() if (mCurrentFile == xmlUpdateFile) { mTempUpdateFiles = loadXMLFile(std::string( - mUpdatesDir).append("/").append(xmlUpdateFile)); + mUpdatesDir).append("/").append(xmlUpdateFile), true); } mUpdateIndexOffset = mUpdateIndex; mUpdateIndex = 0; @@ -956,13 +974,9 @@ void UpdaterWindow::handleLink(const std::string &link, gcn::MouseEvent *event A_UNUSED) { if (strStartWith(link, "http://") || strStartWith(link, "https://")) - { openBrowser(link); - } else if (link == "news") - { loadFile("news"); - } } void UpdaterWindow::loadFile(std::string file) @@ -977,3 +991,104 @@ void UpdaterWindow::loadFile(std::string file) for (size_t i = 0, sz = lines.size(); i < sz; ++i) mBrowserBox->addRow(lines[i]); } + +void UpdaterWindow::loadMods(const std::string &dir, + const ResourceManager *const resman, + const std::vector<UpdateFile> &updateFiles) +{ + ModDB::load(); + std::string modsString = serverConfig.getValue("mods", ""); + std::set<std::string> modsList; + splitToStringSet(modsList, modsString, '|'); + + const std::string fixPath = dir + "/fix"; + for (unsigned int updateIndex = 0, sz = static_cast<unsigned int>( + updateFiles.size()); updateIndex < sz; updateIndex ++) + { + const UpdateFile &file = updateFiles[updateIndex]; + if (file.group.empty()) + continue; + const std::set<std::string>::const_iterator + it = modsList.find(file.group); + if (it != modsList.end()) + { + UpdaterWindow::addUpdateFile(resman, dir, + fixPath, file.name, false); + } + } + + std::vector<UpdateFile> updateFiles2 = loadXMLFile( + std::string(fixPath).append("/").append(xmlUpdateFile), true); + + for (unsigned int updateIndex = 0, sz = static_cast<unsigned int>( + updateFiles2.size()); updateIndex < sz; updateIndex ++) + { + const UpdateFile &file = updateFiles2[updateIndex]; + if (file.group.empty()) + continue; + std::string name = file.name; + if (strStartWith(name, "manaplus_")) + { + const std::set<std::string>::const_iterator + it = modsList.find(file.group); + if (it != modsList.end()) + { + struct stat statbuf; + std::string fileName = std::string(fixPath).append( + "/").append(name); + if (!stat(fileName.c_str(), &statbuf)) + resman->addToSearchPath(fileName, false); + } + } + } + + loadDirMods(dir + "/local/"); +} + +void UpdaterWindow::loadDirMods(const std::string &dir) +{ + ModDB::load(); + const ResourceManager *const resman = ResourceManager::getInstance(); + const ModInfos &mods = ModDB::getAll(); + + std::string modsString = serverConfig.getValue("mods", ""); + StringVect modsList; + splitToStringVector(modsList, modsString, '|'); + FOR_EACH (StringVectCIter, it, modsList) + { + const std::string &name = *it; + const ModInfoCIterator modIt = mods.find(name); + if (modIt == mods.end()) + continue; + const ModInfo *const mod = (*modIt).second; + if (mod) + { + const std::string localDir = mod->getLocalDir(); + if (!localDir.empty()) + resman->addToSearchPath(dir + "/" + localDir, false); + } + } +} + +void UpdaterWindow::unloadMods(const std::string &dir) +{ + const ResourceManager *const resman = ResourceManager::getInstance(); + const ModInfos &mods = ModDB::getAll(); + std::string modsString = serverConfig.getValue("mods", ""); + StringVect modsList; + splitToStringVector(modsList, modsString, '|'); + FOR_EACH (StringVectCIter, it, modsList) + { + const std::string &name = *it; + const ModInfoCIterator modIt = mods.find(name); + if (modIt == mods.end()) + continue; + const ModInfo *const mod = (*modIt).second; + if (mod) + { + const std::string localDir = mod->getLocalDir(); + if (!localDir.empty()) + resman->removeFromSearchPath(dir + "/" + localDir); + } + } +} diff --git a/src/gui/windows/updaterwindow.h b/src/gui/windows/updaterwindow.h index 433ab82ad..bddd3ef9e 100644 --- a/src/gui/windows/updaterwindow.h +++ b/src/gui/windows/updaterwindow.h @@ -51,6 +51,7 @@ struct UpdateFile final hash(), type(), desc(), + group(), required(false) { } @@ -58,6 +59,7 @@ struct UpdateFile final std::string hash; std::string type; std::string desc; + std::string group; bool required; }; @@ -151,6 +153,14 @@ class UpdaterWindow final : public Window, static unsigned long getFileHash(const std::string &filePath); + static void loadMods(const std::string &dir, + const ResourceManager *const resman, + const std::vector<UpdateFile> &updateFiles); + + static void loadDirMods(const std::string &dir); + + static void unloadMods(const std::string &dir); + private: void download(); diff --git a/src/input/inputmanager.cpp b/src/input/inputmanager.cpp index c0d3a1d0f..090629aea 100644 --- a/src/input/inputmanager.cpp +++ b/src/input/inputmanager.cpp @@ -24,11 +24,11 @@ #include "game.h" #include "touchmanager.h" -#include "being/localplayer.h" - #include "input/joystick.h" #include "input/keyboardconfig.h" #include "input/keyboarddata.h" +#include "being/localplayer.h" +#include "being/playerinfo.h" #ifdef USE_SDL2 #include "input/multitouchmanager.h" #endif @@ -38,10 +38,12 @@ #include "gui/widgets/tabs/setup_input.h" +#include "gui/windows/buydialog.h" #include "gui/windows/chatwindow.h" #include "gui/windows/inventorywindow.h" #include "gui/windows/npcdialog.h" #include "gui/windows/npcpostdialog.h" +#include "gui/windows/selldialog.h" #include "gui/windows/setup.h" #include "gui/windows/textdialog.h" #include "gui/windows/tradewindow.h" @@ -332,7 +334,20 @@ void InputManager::callbackNewKey() mSetupInput->newKeyCallback(mNewKeyIndex); } -bool InputManager::isActionActive(const int index) +bool InputManager::isActionActive(const int index) const +{ + if (!isActionActive0(index)) + return false; + + const KeyData &key = keyData[index]; +// logger->log("isActionActive mask=%d, condition=%d, index=%d", +// mMask, key.condition, index); + if ((key.condition & mMask) != key.condition) + return false; + return true; +} + +bool InputManager::isActionActive0(const int index) const { if (keyboard.isActionActive(index)) return true; @@ -524,6 +539,7 @@ bool InputManager::handleEvent(const SDL_Event &event) { case SDL_KEYDOWN: { + keyboard.refreshActiveKeys(); updateConditionMask(); if (handleAssignKey(event, INPUT_KEYBOARD)) return true; @@ -551,6 +567,7 @@ bool InputManager::handleEvent(const SDL_Event &event) } case SDL_KEYUP: { + keyboard.refreshActiveKeys(); updateConditionMask(); keyboard.handleDeActicateKey(event); break; @@ -655,6 +672,9 @@ void InputManager::updateConditionMask() mMask |= COND_NOINPUT; } + if (!BuyDialog::isActive() && !SellDialog::isActive()) + mMask |= COND_NOBUYSELL; + if (!player_node || !player_node->getAway()) mMask |= COND_NOAWAY; @@ -670,12 +690,18 @@ void InputManager::updateConditionMask() const NpcDialog *const dialog = NpcDialog::getActive(); if (!dialog || !dialog->isTextInputFocused()) mMask |= COND_NONPCINPUT; + if (!dialog || dialog->isCloseState()) + { + mMask |= COND_NONPCDIALOG; + if (!InventoryWindow::isStorageActive()) + mMask |= COND_NOTALKING; + } if (!player_node || !player_node->getDisableGameModifiers()) mMask |= COND_EMODS; - if (!isActionActive(Input::KEY_STOP_ATTACK) - && !isActionActive(Input::KEY_UNTARGET)) + if (!isActionActive0(Input::KEY_STOP_ATTACK) + && !isActionActive0(Input::KEY_UNTARGET)) { mMask |= COND_NOTARGET; } @@ -688,12 +714,12 @@ void InputManager::updateConditionMask() bool InputManager::checkKey(const KeyData *const key) const { -// logger->log("mask=%d, condition=%d", mMask, key->condition); + // logger->log("checkKey mask=%d, condition=%d", mMask, key->condition); if (!key || (key->condition & mMask) != key->condition) return false; return (key->modKeyIndex == Input::KEY_NO_VALUE - || isActionActive(key->modKeyIndex)); + || isActionActive0(key->modKeyIndex)); } bool InputManager::invokeKey(const KeyData *const key, const int keyNum) diff --git a/src/input/inputmanager.h b/src/input/inputmanager.h index 1a65220c6..ce662611a 100644 --- a/src/input/inputmanager.h +++ b/src/input/inputmanager.h @@ -84,10 +84,16 @@ enum KeyCondition // pressed COND_NOFOLLOW = 1024, // follow mode disabled COND_INGAME = 2048, // game must be started + COND_NOBUYSELL = 4096, // no active buy or sell dialogs + COND_NONPCDIALOG = 8192, // no active npc dialog or + // dialog almost closed + COND_NOTALKING = 16384, // player have no opened + // dialogs what prevent moving COND_SHORTCUT = 2 + 4 + 16 + 512 + 2048, // flags for shortcut keys COND_SHORTCUT0 = 2 + 4 + 16 + 512, // flags for shortcut keys COND_GAME = 2 + 4 + 8 + 16 + 64 + 2048, // main game key - COND_GAME2 = 2 + 8 + 16 + 64 + 2048 + COND_GAME2 = 2 + 8 + 16 + 64 + 2048, + COND_ARROWKEYS = 2 + 8 + 16 + 64 + 2048 + 4096 + 16384 }; class InputManager final @@ -130,7 +136,7 @@ class InputManager final void unassignKey(); - static bool isActionActive(const int index) A_WARN_UNUSED; + bool isActionActive(const int index) const A_WARN_UNUSED; /** * Set the index of the new key to be assigned. @@ -174,6 +180,8 @@ class InputManager final void executeAction(const int keyNum); protected: + bool isActionActive0(const int index) const A_WARN_UNUSED; + Setup_Input *mSetupInput; /**< Reference to setup window */ int mNewKeyIndex; /**< Index of new key to be assigned */ diff --git a/src/input/joystick.cpp b/src/input/joystick.cpp index 3e52d6ffe..7343ec00c 100644 --- a/src/input/joystick.cpp +++ b/src/input/joystick.cpp @@ -345,7 +345,6 @@ void Joystick::handleRepeat(const int time) { bool repeat(false); const int key = (*it).first; - int &keyTime = (*it).second; if (key >= 0 && key < mButtonsNumber) { if (mActiveButtons[key]) @@ -353,6 +352,7 @@ void Joystick::handleRepeat(const int time) } if (repeat) { + int &keyTime = (*it).second; if (time > keyTime && abs(time - keyTime) > SDL_DEFAULT_REPEAT_DELAY * 10) { diff --git a/src/input/keyboardconfig.cpp b/src/input/keyboardconfig.cpp index a5e3674ed..6cf524842 100644 --- a/src/input/keyboardconfig.cpp +++ b/src/input/keyboardconfig.cpp @@ -251,7 +251,6 @@ void KeyboardConfig::handleRepeat(const int time) { bool repeat(false); const int key = (*it).first; - int &keyTime = (*it).second; if (key >= 0) { if (mActiveKeys && mActiveKeys[key]) @@ -264,6 +263,7 @@ void KeyboardConfig::handleRepeat(const int time) } if (repeat) { + int &keyTime = (*it).second; if (time > keyTime && abs(time - keyTime) > static_cast<signed>(mRepeatTime)) { diff --git a/src/input/keyboarddata.h b/src/input/keyboarddata.h index 5dd9e84a3..da0d22fee 100644 --- a/src/input/keyboarddata.h +++ b/src/input/keyboarddata.h @@ -40,28 +40,28 @@ static const KeyData keyData[Input::KEY_TOTAL] = { Input::GRP_DEFAULT, &ActionManager::moveUp, Input::KEY_NO_VALUE, 50, - COND_GAME2}, + COND_ARROWKEYS}, {"keyMoveDown", INPUT_KEYBOARD, SDLK_DOWN, INPUT_UNKNOWN, Input::KEY_NO_VALUE, Input::GRP_DEFAULT, &ActionManager::moveDown, Input::KEY_NO_VALUE, 50, - COND_GAME2}, + COND_ARROWKEYS}, {"keyMoveLeft", INPUT_KEYBOARD, SDLK_LEFT, INPUT_UNKNOWN, Input::KEY_NO_VALUE, Input::GRP_DEFAULT, &ActionManager::moveLeft, Input::KEY_NO_VALUE, 50, - COND_GAME}, + COND_ARROWKEYS}, {"keyMoveRight", INPUT_KEYBOARD, SDLK_RIGHT, INPUT_UNKNOWN, Input::KEY_NO_VALUE, Input::GRP_DEFAULT, &ActionManager::moveRight, Input::KEY_NO_VALUE, 50, - COND_GAME}, + COND_ARROWKEYS}, {"keyAttack", INPUT_KEYBOARD, SDLK_LCTRL, INPUT_UNKNOWN, Input::KEY_NO_VALUE, @@ -187,7 +187,7 @@ static const KeyData keyData[Input::KEY_TOTAL] = { Input::GRP_DEFAULT, &ActionManager::screenshot, Input::KEY_NO_VALUE, 50, - COND_GAME | COND_NOTARGET}, + COND_NOTARGET}, {"keyTrade", INPUT_KEYBOARD, SDLK_r, INPUT_UNKNOWN, Input::KEY_NO_VALUE, @@ -1974,7 +1974,7 @@ static const KeyData keyData[Input::KEY_TOTAL] = { Input::GRP_DEFAULT, &ActionManager::moveForward, Input::KEY_NO_VALUE, 50, - COND_GAME}, + COND_ARROWKEYS}, {"keyGUICtrl", INPUT_KEYBOARD, SDLK_LCTRL, INPUT_UNKNOWN, Input::KEY_NO_VALUE, diff --git a/src/logger.cpp b/src/logger.cpp index bac895954..c430aefec 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -64,6 +64,9 @@ Logger *logger = nullptr; // Log object Logger::Logger() : mLogFile(), + mDelayedLog(), + mMutex(SDL_CreateMutex()), + mThreadLocked(false), mLogToStandardOut(true), mDebugLog(false) { @@ -73,6 +76,7 @@ Logger::~Logger() { if (mLogFile.is_open()) mLogFile.close(); + SDL_DestroyMutex(mMutex); } void Logger::setLogFile(const std::string &logFilename) @@ -169,6 +173,61 @@ void Logger::log(const char *const log_text, ...) delete [] buf; } +void Logger::log_r(const char *const log_text, ...) +{ + SDL_mutexP(mMutex); + + unsigned size = 1024; + if (strlen(log_text) * 3 > size) + size = static_cast<unsigned>(strlen(log_text) * 3); + + char* buf = new char[size + 1]; + va_list ap; + + // Use a temporary buffer to fill in the variables + va_start(ap, log_text); + vsnprintf(buf, size, log_text, ap); + buf[size] = 0; + va_end(ap); + + // Get the current system time + timeval tv; + gettimeofday(&tv, nullptr); + + // Print the log entry + std::stringstream timeStr; + DATESTREAM + LOG_ANDROID(buf) + + if (mLogFile.is_open()) + { + timeStr << buf << std::endl; + mThreadLocked = true; + mDelayedLog.push_back(timeStr.str()); + mThreadLocked = false; + } + + if (mLogToStandardOut) + std::cout << timeStr.str() << buf << std::endl; + + // Delete temporary buffer + delete [] buf; + + SDL_mutexV(mMutex); +} + +void Logger::flush() +{ + if (!mThreadLocked) + { + SDL_mutexP(mMutex); + FOR_EACH (std::vector<std::string>::const_iterator, it, mDelayedLog) + mLogFile << *it; + mDelayedLog.clear(); + SDL_mutexV(mMutex); + } +} + // here string must be safe for any usage void Logger::safeError(const std::string &error_text) { diff --git a/src/logger.h b/src/logger.h index 0941a0e63..c68277ce5 100644 --- a/src/logger.h +++ b/src/logger.h @@ -24,7 +24,11 @@ #define LOGGER_H #include "main.h" + +#include <SDL_thread.h> + #include <fstream> +#include <vector> #include "localconsts.h" @@ -79,6 +83,19 @@ class Logger final ; /** + * Enters a message in the log (thread safe). + */ + void log_r(const char *const log_text, ...) +#ifdef __GNUC__ +#ifdef __OpenBSD__ + __attribute__((__format__(printf, 2, 3))) +#else + __attribute__((__format__(gnu_printf, 2, 3))) +#endif +#endif + ; + + /** * Enters a message in the log. The message will be timestamped. */ void log1(const char *const log_text); @@ -88,6 +105,8 @@ class Logger final */ void log(const std::string &str); + void flush(); + #ifdef ENABLEDEBUGLOG /** * Enters debug message in the log. The message will be timestamped. @@ -113,6 +132,9 @@ class Logger final private: std::ofstream mLogFile; + std::vector<std::string> mDelayedLog; + SDL_mutex *mMutex; + volatile bool mThreadLocked; bool mLogToStandardOut; bool mDebugLog; }; diff --git a/src/main.h b/src/main.h index b88c8b795..a0fbf3c35 100644 --- a/src/main.h +++ b/src/main.h @@ -45,8 +45,8 @@ * different interfaces, which have different implementations for each server. */ -#define SMALL_VERSION "1.4.1.4" -#define CHECK_VERSION "01.04.01.04" +#define SMALL_VERSION "1.4.1.18" +#define CHECK_VERSION "01.04.01.18" #ifdef HAVE_CONFIG_H #include "../config.h" diff --git a/src/net/download.cpp b/src/net/download.cpp index e9b6dd6d7..d986fa232 100644 --- a/src/net/download.cpp +++ b/src/net/download.cpp @@ -302,7 +302,7 @@ int Download::downloadThread(void *ptr) { if (d->mError) { - logger->log("curl error %d: %s host: %s", + logger->log_r("curl error %d: %s host: %s", res, d->mError, d->mUrl.c_str()); } break; @@ -347,7 +347,7 @@ int Download::downloadThread(void *ptr) // Remove the corrupted file ::remove(d->mFileName.c_str()); - logger->log("Checksum for file %s failed: (%lx/%lx)", + logger->log_r("Checksum for file %s failed: (%lx/%lx)", d->mFileName.c_str(), adler, d->mAdler); attempts++; diff --git a/src/net/ea/network.cpp b/src/net/ea/network.cpp index 4ca79b87e..c5ed9de57 100644 --- a/src/net/ea/network.cpp +++ b/src/net/ea/network.cpp @@ -201,10 +201,10 @@ bool Network::realConnect() _("Unable to resolve host \"")).append( mServer.hostname).append("\"."); setError(errorMessage); - logger->log("TcpNet::ResolveHost: %s", errorMessage.c_str()); + logger->log_r("TcpNet::ResolveHost: %s", errorMessage.c_str()); return false; } - logger->log("using alt host name: %s", mServer.althostname.c_str()); + logger->log_r("using alt host name: %s", mServer.althostname.c_str()); } mState = CONNECTING; @@ -212,12 +212,12 @@ bool Network::realConnect() mSocket = TcpNet::open(&ipAddress); if (!mSocket) { - logger->log("Error in TcpNet::open(): %s", TcpNet::getError()); + logger->log_r("Error in TcpNet::open(): %s", TcpNet::getError()); setError(TcpNet::getError()); return false; } - logger->log("Network::Started session with %s:%i", + logger->log_r("Network::Started session with %s:%i", ipToString(ipAddress.host), ipAddress.port); mState = CONNECTED; @@ -248,7 +248,7 @@ void Network::receive() switch (numReady) { case -1: - logger->log1("Error: TcpNet::checkSockets"); + logger->log_r("Error: TcpNet::checkSockets"); break; // FALLTHROUGH case 0: @@ -272,7 +272,7 @@ void Network::receive() { // We got disconnected mState = IDLE; - logger->log1("Disconnected."); + logger->log_r("Disconnected."); } else if (ret < 0) { @@ -315,14 +315,14 @@ void Network::receive() } if (TcpNet::delSocket(set, mSocket) == -1) - logger->log("Error in TcpNet::delSocket(): %s", TcpNet::getError()); + logger->log_r("Error in TcpNet::delSocket(): %s", TcpNet::getError()); TcpNet::freeSocketSet(set); } void Network::setError(const std::string &error) { - logger->log("Network error: %s", error.c_str()); + logger->log_r("Network error: %s", error.c_str()); mError = error; mState = NET_ERROR; } diff --git a/src/net/pethandler.h b/src/net/pethandler.h index 05816acc4..7b5666396 100644 --- a/src/net/pethandler.h +++ b/src/net/pethandler.h @@ -33,13 +33,15 @@ class PetHandler { } virtual void move(const Being *const being, + const int petId, const int x1, const int y1, const int x2, const int y2) const = 0; virtual void spawn(const Being *const being, + const int petId, const int x, const int y) const = 0; - virtual void emote(const uint8_t emoteId) const = 0; + virtual void emote(const uint8_t emoteId, const int petId) const = 0; }; } // namespace Net diff --git a/src/net/sdltcpnet.cpp b/src/net/sdltcpnet.cpp index c07ba569b..7530e0f53 100644 --- a/src/net/sdltcpnet.cpp +++ b/src/net/sdltcpnet.cpp @@ -106,14 +106,14 @@ TcpNet::Socket TcpNet::open(IPaddress *const ip) if (setsockopt(hack->channel, IPPROTO_TCP, TCP_THIN_LINEAR_TIMEOUTS, &val, sizeof(val))) { - logger->log1("error on set TCP_THIN_LINEAR_TIMEOUTS"); + logger->log_r("error on set TCP_THIN_LINEAR_TIMEOUTS"); } #endif #ifdef TCP_THIN_DUPACK if (setsockopt(hack->channel, IPPROTO_TCP, TCP_THIN_DUPACK, &val, sizeof(val))) { - logger->log1("error on set TCP_THIN_DUPACK"); + logger->log_r("error on set TCP_THIN_DUPACK"); } #endif } diff --git a/src/net/tmwa/pethandler.cpp b/src/net/tmwa/pethandler.cpp index 2a3e56e0e..6b9e5adb7 100644 --- a/src/net/tmwa/pethandler.cpp +++ b/src/net/tmwa/pethandler.cpp @@ -52,17 +52,19 @@ void PetHandler::handleMessage(Net::MessageIn &msg A_UNUSED) } void PetHandler::move(const Being *const being A_UNUSED, + const int petId A_UNUSED, const int x1 A_UNUSED, const int y1 A_UNUSED, const int x2 A_UNUSED, const int y2 A_UNUSED) const { } void PetHandler::spawn(const Being *const being A_UNUSED, + const int petId A_UNUSED, const int x A_UNUSED, const int y A_UNUSED) const { } -void PetHandler::emote(const uint8_t emoteId) const +void PetHandler::emote(const uint8_t emoteId, const int petId A_UNUSED) const { Net::getChatHandler()->talk("\302\202\302e" + toString( static_cast<int>(emoteId)), GENERAL_CHANNEL); diff --git a/src/net/tmwa/pethandler.h b/src/net/tmwa/pethandler.h index 81536e0b1..30eff59b8 100644 --- a/src/net/tmwa/pethandler.h +++ b/src/net/tmwa/pethandler.h @@ -38,13 +38,16 @@ class PetHandler final : public MessageHandler, public Net::PetHandler void handleMessage(Net::MessageIn &msg) override final; void move(const Being *const being, + const int petId, const int x1, const int y1, const int x2, const int y2) const override final; void spawn(const Being *const being, + const int petId, const int x, const int y) const override final; - void emote(const uint8_t emoteId) const override final; + void emote(const uint8_t emoteId, + const int petId) const override final; }; } // namespace TmwAthena diff --git a/src/particle/animationparticle.cpp b/src/particle/animationparticle.cpp index d7f31ca1e..4381ceb8a 100644 --- a/src/particle/animationparticle.cpp +++ b/src/particle/animationparticle.cpp @@ -32,7 +32,7 @@ AnimationParticle::AnimationParticle(Animation *const animation) : { } -AnimationParticle::AnimationParticle(XmlNodePtr const animationNode, +AnimationParticle::AnimationParticle(XmlNodePtrConst animationNode, const std::string& dyePalettes): ImageParticle(nullptr), mAnimation(new SimpleAnimation(animationNode, dyePalettes)) diff --git a/src/particle/animationparticle.h b/src/particle/animationparticle.h index da4d8f6cd..8b9df63e4 100644 --- a/src/particle/animationparticle.h +++ b/src/particle/animationparticle.h @@ -35,7 +35,7 @@ class AnimationParticle final : public ImageParticle public: explicit AnimationParticle(Animation *const animation); - explicit AnimationParticle(XmlNodePtr const animationNode, + explicit AnimationParticle(XmlNodePtrConst animationNode, const std::string& dyePalettes = std::string()); diff --git a/src/particle/particle.cpp b/src/particle/particle.cpp index 576b292fa..a9cc9993f 100644 --- a/src/particle/particle.cpp +++ b/src/particle/particle.cpp @@ -290,7 +290,7 @@ Particle *Particle::addEffect(const std::string &particleEffectFile, const std::string dyePalettes = (pos != std::string::npos) ? particleEffectFile.substr(pos + 1) : ""; XML::Document doc(particleEffectFile.substr(0, pos)); - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "effect")) { diff --git a/src/particle/particleemitter.cpp b/src/particle/particleemitter.cpp index 13f9d8d41..27f01d4e1 100644 --- a/src/particle/particleemitter.cpp +++ b/src/particle/particleemitter.cpp @@ -43,7 +43,7 @@ static const float DEG_RAD_FACTOR = 0.017453293F; typedef std::vector<ImageSet*>::const_iterator ImageSetVectorCIter; typedef std::list<ParticleEmitter>::const_iterator ParticleEmitterListCIter; -ParticleEmitter::ParticleEmitter(const XmlNodePtr emitterNode, +ParticleEmitter::ParticleEmitter(const XmlNodePtrConst emitterNode, Particle *const target, Map *const map, const int rotation, const std::string& dyePalettes) : @@ -425,7 +425,7 @@ ParticleEmitter::ParticleEmitter(const ParticleEmitter &o) *this = o; } -ImageSet *ParticleEmitter::getImageSet(XmlNodePtr node) +ImageSet *ParticleEmitter::getImageSet(XmlNodePtrConst node) { ResourceManager *const resman = ResourceManager::getInstance(); ImageSet *imageset = nullptr; @@ -525,7 +525,7 @@ ParticleEmitter::~ParticleEmitter() } template <typename T> ParticleEmitterProp<T> -ParticleEmitter::readParticleEmitterProp(XmlNodePtr propertyNode, T def) +ParticleEmitter::readParticleEmitterProp(XmlNodePtrConst propertyNode, T def) { ParticleEmitterProp<T> retval; diff --git a/src/particle/particleemitter.h b/src/particle/particleemitter.h index bf0e05ae5..9e149034f 100644 --- a/src/particle/particleemitter.h +++ b/src/particle/particleemitter.h @@ -43,8 +43,10 @@ class Particle; class ParticleEmitter final { public: - ParticleEmitter(const XmlNodePtr emitterNode, Particle *const target, - Map *const map, const int rotation = 0, + ParticleEmitter(const XmlNodePtrConst emitterNode, + Particle *const target, + Map *const map, + const int rotation = 0, const std::string& dyePalettes = std::string()); /** @@ -82,9 +84,9 @@ class ParticleEmitter final private: template <typename T> ParticleEmitterProp<T> - readParticleEmitterProp(XmlNodePtr propertyNode, T def); + readParticleEmitterProp(XmlNodePtrConst propertyNode, T def); - ImageSet *getImageSet(XmlNodePtr node); + ImageSet *getImageSet(XmlNodePtrConst node); /** * initial position of particles: diff --git a/src/resources/beingcommon.cpp b/src/resources/beingcommon.cpp index 174a6edfd..7b7a57d9e 100644 --- a/src/resources/beingcommon.cpp +++ b/src/resources/beingcommon.cpp @@ -20,11 +20,17 @@ #include "resources/beingcommon.h" +#include "utils/files.h" +#include "utils/stringutils.h" + #include "resources/beinginfo.h" +#include <algorithm> + #include "debug.h" -void BeingCommon::readBasicAttributes(BeingInfo *const info, XmlNodePtr node, +void BeingCommon::readBasicAttributes(BeingInfo *const info, + XmlNodePtrConst node, const std::string &hoverCursor) { info->setTargetCursorSize(XML::getProperty(node, @@ -42,3 +48,17 @@ void BeingCommon::readBasicAttributes(BeingInfo *const info, XmlNodePtr node, info->setHpBarOffsetX(XML::getProperty(node, "hpBarOffsetX", 0)); info->setHpBarOffsetY(XML::getProperty(node, "hpBarOffsetY", 0)); } + +void BeingCommon::getIncludeFiles(const std::string &dir, StringVect &list) +{ + const std::string path = dir + "/"; + StringVect tempList; + Files::getFilesWithDir(path, tempList); + FOR_EACH (StringVectCIter, it, tempList) + { + const std::string &str = *it; + if (findLast(str, ".xml")) + list.push_back(str); + } + std::sort(list.begin(), list.end()); +} diff --git a/src/resources/beingcommon.h b/src/resources/beingcommon.h index 54c9fc35d..6124de5a1 100644 --- a/src/resources/beingcommon.h +++ b/src/resources/beingcommon.h @@ -23,14 +23,26 @@ #include "utils/xml.h" +#include "utils/stringvector.h" + #include "localconsts.h" class BeingInfo; +#define loadXmlDir(name, function) \ + StringVect listVect; \ + BeingCommon::getIncludeFiles(paths.getStringValue( \ + name), listVect); \ + FOR_EACH (StringVectCIter, itVect, listVect) \ + function(*itVect); + namespace BeingCommon { - void readBasicAttributes(BeingInfo *const info, XmlNodePtr node, + void readBasicAttributes(BeingInfo *const info, + XmlNodePtrConst node, const std::string &hoverCursor); + + void getIncludeFiles(const std::string &dir, StringVect &list); } #endif // RESOURCES_BEINGCOMMON_H diff --git a/src/resources/db/avatardb.cpp b/src/resources/db/avatardb.cpp index 8ff58efc5..e51a8d468 100644 --- a/src/resources/db/avatardb.cpp +++ b/src/resources/db/avatardb.cpp @@ -24,6 +24,7 @@ #include "logger.h" +#include "resources/beingcommon.h" #include "resources/beinginfo.h" #include "utils/dtor.h" @@ -43,24 +44,43 @@ void AvatarDB::load() { if (mLoaded) unload(); + loadXmlFile(paths.getStringValue("avatarsFile")); + loadXmlFile(paths.getStringValue("avatarsPatchFile")); + loadXmlDir("avatarsPatchDir", loadXmlFile); +} - XML::Document doc(paths.getStringValue("avatarsFile")); - const XmlNodePtr rootNode = doc.rootNode(); +void AvatarDB::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "avatars")) { logger->log("Avatars Database: Error while loading %s!", - paths.getStringValue("avatarsFile").c_str()); + fileName.c_str()); mLoaded = true; return; } for_each_xml_child_node(avatarNode, rootNode) { + if (xmlNameEqual(avatarNode, "include")) + { + const std::string name = XML::getProperty(avatarNode, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + if (!xmlNameEqual(avatarNode, "avatar")) continue; - BeingInfo *const currentInfo = new BeingInfo; + const int id = XML::getProperty(avatarNode, "id", 0); + BeingInfo *currentInfo = nullptr; + if (mAvatarInfos.find(id) != mAvatarInfos.end()) + currentInfo = mAvatarInfos[id]; + if (!currentInfo) + currentInfo = new BeingInfo; currentInfo->setName(XML::langProperty( // TRANSLATORS: unknown info name @@ -97,8 +117,7 @@ void AvatarDB::load() } } currentInfo->setDisplay(display); - - mAvatarInfos[XML::getProperty(avatarNode, "id", 0)] = currentInfo; + mAvatarInfos[id] = currentInfo; } mLoaded = true; diff --git a/src/resources/db/avatardb.h b/src/resources/db/avatardb.h index 0b4894c9c..ba2d8ad47 100644 --- a/src/resources/db/avatardb.h +++ b/src/resources/db/avatardb.h @@ -23,6 +23,8 @@ #ifndef RESOURCES_DB_AVATARDB_H #define RESOURCES_DB_AVATARDB_H +#include <string> + #include "localconsts.h" class BeingInfo; @@ -34,6 +36,8 @@ namespace AvatarDB void unload(); BeingInfo *get(const int id) A_WARN_UNUSED; -} + + void loadXmlFile(const std::string &fileName); +} // namespace AvatarDB #endif // RESOURCES_DB_AVATARDB_H diff --git a/src/resources/db/chardb.cpp b/src/resources/db/chardb.cpp index 80ddedc37..2d4e2fee2 100644 --- a/src/resources/db/chardb.cpp +++ b/src/resources/db/chardb.cpp @@ -48,7 +48,7 @@ void CharDB::load() XML::Document *doc = new XML::Document( paths.getStringValue("charCreationFile")); - const XmlNodePtr root = doc->rootNode(); + const XmlNodePtrConst root = doc->rootNode(); if (!root || !xmlNameEqual(root, "chars")) { diff --git a/src/resources/db/colordb.cpp b/src/resources/db/colordb.cpp index 759226e84..630c1c902 100644 --- a/src/resources/db/colordb.cpp +++ b/src/resources/db/colordb.cpp @@ -26,6 +26,8 @@ #include "utils/xml.h" +#include "resources/beingcommon.h" + #include "debug.h" namespace @@ -43,56 +45,60 @@ void ColorDB::load() if (mLoaded) unload(); - loadHair(); + std::map<int, ItemColor> colors; + ColorListsIterator it = mColorLists.find("hair"); + if (it != mColorLists.end()) + colors = it->second; + loadHair(paths.getStringValue("hairColorFile"), colors); + loadHair(paths.getStringValue("hairColorPatchFile"), colors); + StringVect list; + BeingCommon::getIncludeFiles(paths.getStringValue( + "hairColorPatchDir"), list); + FOR_EACH (StringVectCIter, it2, list) + loadHair(*it2, colors); + + mColorLists["hair"] = colors; + if (serverVersion >= 1) - loadColorLists(); + { + loadColorLists(paths.getStringValue("itemColorsFile")); + loadColorLists(paths.getStringValue("itemColorsPatchFile")); + loadXmlDir("itemColorsPatchDir", loadColorLists); + } - const ColorListsIterator it = mColorLists.find("hair"); + it = mColorLists.find("hair"); if (it != mColorLists.end()) mHairColorsSize = static_cast<int>((*it).second.size()); else mHairColorsSize = 0; + mLoaded = true; } -void ColorDB::loadHair() +void ColorDB::loadHair(const std::string &fileName, + std::map<int, ItemColor> &colors) { - std::map <int, ItemColor> colors; - const ColorListsIterator it = mColorLists.find("hair"); - - if (it != mColorLists.end()) - colors = it->second; - - XML::Document *doc = new XML::Document( - paths.getStringValue("hairColorFile")); - XmlNodePtr root = doc->rootNode(); - bool hairXml = true; + XML::Document *doc = new XML::Document(fileName); + const XmlNodePtrConst root = doc->rootNode(); if (!root || !xmlNameEqual(root, "colors")) { - logger->log("Trying to fall back on " - + paths.getStringValue("hairColorFile2")); - - hairXml = false; - - delete doc; - doc = new XML::Document(paths.getStringValue("hairColorFile2")); - root = doc->rootNode(); - - if (!root || !xmlNameEqual(root, "colors")) - { - logger->log1("ColorDB: Failed to find any color files."); + logger->log("ColorDB: Failed to find hair colors file."); + if (colors.find(0) == colors.end()) colors[0] = ItemColor(0, "", ""); - mLoaded = true; - - delete doc; - - return; - } + delete doc; + return; } for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "color")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadHair(name, colors); + continue; + } + else if (xmlNameEqual(node, "color")) { const int id = XML::getProperty(node, "id", 0); @@ -100,21 +106,17 @@ void ColorDB::loadHair() logger->log("ColorDB: Redefinition of dye ID %d", id); colors[id] = ItemColor(id, XML::langProperty(node, "name", ""), - XML::getProperty(node, hairXml ? "value" : "dye", "#FFFFFF")); + XML::getProperty(node, "value", "#FFFFFF")); } } delete doc; - - mColorLists["hair"] = colors; - mLoaded = true; } -void ColorDB::loadColorLists() +void ColorDB::loadColorLists(const std::string &fileName) { - XML::Document *doc = new XML::Document( - paths.getStringValue("itemColorsFile")); - const XmlNodePtr root = doc->rootNode(); + XML::Document *doc = new XML::Document(fileName); + const XmlNodePtrConst root = doc->rootNode(); if (!root) { delete doc; @@ -123,7 +125,14 @@ void ColorDB::loadColorLists() for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "list")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadColorLists(name); + continue; + } + else if (xmlNameEqual(node, "list")) { const std::string name = XML::getProperty(node, "name", ""); if (name.empty()) diff --git a/src/resources/db/colordb.h b/src/resources/db/colordb.h index e466102f9..d1fc1a420 100644 --- a/src/resources/db/colordb.h +++ b/src/resources/db/colordb.h @@ -62,9 +62,10 @@ namespace ColorDB /** * Loads the color data from <code>colors.xml</code>. */ - void loadHair(); + void loadHair(const std::string &fileName, + std::map<int, ItemColor> &colors); - void loadColorLists(); + void loadColorLists(const std::string &fileName); /** * Clear the color data diff --git a/src/resources/db/deaddb.cpp b/src/resources/db/deaddb.cpp index ba4efc8a8..e66e87e6a 100644 --- a/src/resources/db/deaddb.cpp +++ b/src/resources/db/deaddb.cpp @@ -25,6 +25,8 @@ #include "utils/translation/podict.h" +#include "resources/beingcommon.h" + #include "debug.h" namespace @@ -38,9 +40,16 @@ void DeadDB::load() if (mLoaded) unload(); - XML::Document *doc = new XML::Document( - paths.getStringValue("deadMessagesFile")); - const XmlNodePtr root = doc->rootNode(); + loadXmlFile(paths.getStringValue("deadMessagesFile")); + loadXmlFile(paths.getStringValue("deadMessagesPatchFile")); + loadXmlDir("deadMessagesPatchDir", loadXmlFile); + mLoaded = true; +} + +void DeadDB::loadXmlFile(const std::string &fileName) +{ + XML::Document *doc = new XML::Document(fileName); + const XmlNodePtrConst root = doc->rootNode(); if (!root || !xmlNameEqual(root, "messages")) { @@ -52,7 +61,14 @@ void DeadDB::load() for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "message")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(node, "message")) { const char *const data = reinterpret_cast<const char*>( xmlNodeGetContent(node)); @@ -63,7 +79,6 @@ void DeadDB::load() } delete doc; - mLoaded = true; } void DeadDB::unload() diff --git a/src/resources/db/deaddb.h b/src/resources/db/deaddb.h index 1e47e9c3b..06ed14722 100644 --- a/src/resources/db/deaddb.h +++ b/src/resources/db/deaddb.h @@ -33,6 +33,8 @@ namespace DeadDB */ void load(); + void loadXmlFile(const std::string &fileName); + /** * Clear the chars data */ diff --git a/src/resources/db/emotedb.cpp b/src/resources/db/emotedb.cpp index 7b1bb1ce2..594bcce5d 100644 --- a/src/resources/db/emotedb.cpp +++ b/src/resources/db/emotedb.cpp @@ -27,6 +27,8 @@ #include "configuration.h" +#include "resources/beingcommon.h" + #include "debug.h" namespace @@ -43,8 +45,6 @@ void EmoteDB::load() if (mLoaded) unload(); - mLastEmote = 0; - EmoteSprite *const unknownSprite = new EmoteSprite; unknownSprite->sprite = AnimatedSprite::load( paths.getStringValue("spriteErrorFile")); @@ -53,21 +53,41 @@ void EmoteDB::load() logger->log1("Initializing emote database..."); - XML::Document doc(paths.getStringValue("emotesFile")); - XmlNodePtr rootNode = doc.rootNode(); + mLastEmote = 0; + loadXmlFile(paths.getStringValue("emotesFile")); + loadXmlFile(paths.getStringValue("emotesPatchFile")); + loadXmlDir("emotesPatchDir", loadXmlFile); + loadSpecialXmlFile("graphics/sprites/manaplus_emotes.xml"); + + mLoaded = true; +} + +void EmoteDB::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "emotes")) { logger->log("Emote Database: Error while loading %s!", - paths.getStringValue("emotesFile").c_str()); + fileName.c_str()); return; } // iterate <emote>s for_each_xml_child_node(emoteNode, rootNode) { - if (!xmlNameEqual(emoteNode, "emote")) + if (xmlNameEqual(emoteNode, "include")) + { + const std::string name = XML::getProperty(emoteNode, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (!xmlNameEqual(emoteNode, "emote")) + { continue; + } const int id = XML::getProperty(emoteNode, "id", -1); // skip hight images @@ -80,8 +100,11 @@ void EmoteDB::load() paths.getStringValue("emotesFile").c_str()); continue; } - - EmoteInfo *const currentInfo = new EmoteInfo; + EmoteInfo *currentInfo = nullptr; + if (mEmoteInfos.find(id) != mEmoteInfos.end()) + currentInfo = mEmoteInfos[id]; + else + currentInfo = new EmoteInfo; currentInfo->time = XML::getProperty(emoteNode, "time", 500); for_each_xml_child_node(spriteNode, emoteNode) @@ -111,9 +134,12 @@ void EmoteDB::load() if (id > mLastEmote) mLastEmote = id; } +} - XML::Document doc2("graphics/sprites/manaplus_emotes.xml"); - rootNode = doc2.rootNode(); +void EmoteDB::loadSpecialXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "emotes")) { @@ -125,8 +151,17 @@ void EmoteDB::load() // iterate <emote>s for_each_xml_child_node(emoteNode, rootNode) { - if (!xmlNameEqual(emoteNode, "emote")) + if (xmlNameEqual(emoteNode, "include")) + { + const std::string name = XML::getProperty(emoteNode, "name", ""); + if (!name.empty()) + loadSpecialXmlFile(name); continue; + } + else if (!xmlNameEqual(emoteNode, "emote")) + { + continue; + } const int id = XML::getProperty(emoteNode, "id", -1); if (id == -1) @@ -137,7 +172,11 @@ void EmoteDB::load() } const int altId = XML::getProperty(emoteNode, "altid", -1); - EmoteInfo *const currentInfo = new EmoteInfo; + EmoteInfo *currentInfo = nullptr; + if (mEmoteInfos.find(id) != mEmoteInfos.end()) + currentInfo = mEmoteInfos[id]; + if (!currentInfo) + currentInfo = new EmoteInfo; currentInfo->time = XML::getProperty(emoteNode, "time", 500); for_each_xml_child_node(spriteNode, emoteNode) @@ -170,8 +209,6 @@ void EmoteDB::load() if (id > mLastEmote) mLastEmote = id; } - - mLoaded = true; } void EmoteDB::unload() diff --git a/src/resources/db/emotedb.h b/src/resources/db/emotedb.h index a7dc40422..f00bfbb47 100644 --- a/src/resources/db/emotedb.h +++ b/src/resources/db/emotedb.h @@ -69,6 +69,10 @@ namespace EmoteDB { void load(); + void loadXmlFile(const std::string &fileName); + + void loadSpecialXmlFile(const std::string &fileName); + void unload(); const EmoteInfo *get(const int id, diff --git a/src/resources/db/itemdb.cpp b/src/resources/db/itemdb.cpp index 4c702ed33..abc164493 100644 --- a/src/resources/db/itemdb.cpp +++ b/src/resources/db/itemdb.cpp @@ -25,6 +25,7 @@ #include "configuration.h" #include "logger.h" +#include "resources/beingcommon.h" #include "resources/iteminfo.h" #include "utils/dtor.h" @@ -47,13 +48,16 @@ namespace extern int serverVersion; // Forward declarations -static void loadSpriteRef(ItemInfo *const itemInfo, const XmlNodePtr node); -static void loadSoundRef(ItemInfo *const itemInfo, const XmlNodePtr node); +static void loadSpriteRef(ItemInfo *const itemInfo, + const XmlNodePtr node); +static void loadSoundRef(ItemInfo *const itemInfo, + const XmlNodePtr node); static void loadFloorSprite(SpriteDisplay *const display, - const XmlNodePtr node); + const XmlNodePtrConst node); static void loadReplaceSprite(ItemInfo *const itemInfo, const XmlNodePtr replaceNode); -static void loadOrderSprite(ItemInfo *const itemInfo, const XmlNodePtr node, +static void loadOrderSprite(ItemInfo *const itemInfo, + const XmlNodePtr node, const bool drawAfter); static int parseSpriteName(const std::string &name); static int parseDirectionName(const std::string &name); @@ -210,12 +214,18 @@ void ItemDB::load() mUnknown->setSprite(errFile, GENDER_OTHER, 0); mUnknown->addTag(mTags["All"]); loadXmlFile(paths.getStringValue("itemsFile"), tagNum); + loadXmlFile(paths.getStringValue("itemsPatchFile"), tagNum); + + StringVect list; + BeingCommon::getIncludeFiles(paths.getStringValue("itemsPatchDir"), list); + FOR_EACH (StringVectCIter, it, list) + loadXmlFile(*it, tagNum); } void ItemDB::loadXmlFile(const std::string &fileName, int &tagNum) { XML::Document doc(fileName); - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "items")) { @@ -237,6 +247,7 @@ void ItemDB::loadXmlFile(const std::string &fileName, int &tagNum) continue; const int id = XML::getProperty(node, "id", 0); + ItemInfo *itemInfo = nullptr; if (id == 0) { @@ -247,7 +258,10 @@ void ItemDB::loadXmlFile(const std::string &fileName, int &tagNum) else if (mItemInfos.find(id) != mItemInfos.end()) { logger->log("ItemDB: Redefinition of item ID %d", id); + itemInfo = mItemInfos[id]; } + if (!itemInfo) + itemInfo = new ItemInfo; const std::string typeStr = XML::getProperty(node, "type", "other"); const int weight = XML::getProperty(node, "weight", 0); @@ -310,7 +324,6 @@ void ItemDB::loadXmlFile(const std::string &fileName, int &tagNum) else display.floor = image; - ItemInfo *const itemInfo = new ItemInfo; itemInfo->setId(id); // TRANSLATORS: item info name itemInfo->setName(name.empty() ? _("unnamed") : name); @@ -485,18 +498,7 @@ void ItemDB::loadXmlFile(const std::string &fileName, int &tagNum) if (!name.empty()) { temp = normalize(name); - - const NamedItemInfos::const_iterator - itr = mNamedItemInfos.find(temp); - if (itr == mNamedItemInfos.end()) - { - mNamedItemInfos[temp] = itemInfo; - } - else - { - logger->log("ItemDB: Duplicate name of item found item %d", - id); - } + mNamedItemInfos[temp] = itemInfo; } if (!attackAction.empty()) @@ -758,7 +760,8 @@ void loadSoundRef(ItemInfo *const itemInfo, const XmlNodePtr node) } } -void loadFloorSprite(SpriteDisplay *const display, const XmlNodePtr floorNode) +void loadFloorSprite(SpriteDisplay *const display, + const XmlNodePtrConst floorNode) { for_each_xml_child_node(spriteNode, floorNode) { @@ -779,7 +782,8 @@ void loadFloorSprite(SpriteDisplay *const display, const XmlNodePtr floorNode) } } -void loadReplaceSprite(ItemInfo *const itemInfo, const XmlNodePtr replaceNode) +void loadReplaceSprite(ItemInfo *const itemInfo, + const XmlNodePtr replaceNode) { const std::string removeSprite = XML::getProperty( replaceNode, "sprite", ""); @@ -910,7 +914,8 @@ void loadReplaceSprite(ItemInfo *const itemInfo, const XmlNodePtr replaceNode) } } -void loadOrderSprite(ItemInfo *const itemInfo, const XmlNodePtr node, +void loadOrderSprite(ItemInfo *const itemInfo, + const XmlNodePtr node, const bool drawAfter) { const int sprite = parseSpriteName(XML::getProperty(node, "name", "")); diff --git a/src/resources/db/mapdb.cpp b/src/resources/db/mapdb.cpp index 15c80f2ae..20d108f13 100644 --- a/src/resources/db/mapdb.cpp +++ b/src/resources/db/mapdb.cpp @@ -24,6 +24,8 @@ #include "configuration.h" #include "logger.h" +#include "resources/beingcommon.h" + #include "debug.h" namespace @@ -36,8 +38,8 @@ namespace namespace MapDB { - void readMap(XmlNodePtr node); - void readAtlas(XmlNodePtr node); + void readMap(XmlNodePtrConst node); + void readAtlas(XmlNodePtrConst node); } void MapDB::load() @@ -46,7 +48,9 @@ void MapDB::load() unload(); loadRemap(); - loadInfo(); + loadInfo(paths.getStringValue("mapsFile")); + loadInfo(paths.getStringValue("mapsPatchFile")); + loadXmlDir("mapsPatchDir", loadInfo); mLoaded = true; } @@ -55,7 +59,7 @@ void MapDB::loadRemap() XML::Document *const doc = new XML::Document( paths.getStringValue("mapsRemapFile")); - const XmlNodePtr root = doc->rootNode(); + const XmlNodePtrConst root = doc->rootNode(); if (!root) { delete doc; @@ -81,7 +85,7 @@ void MapDB::loadRemap() delete doc; } -void MapDB::readMap(XmlNodePtr node) +void MapDB::readMap(XmlNodePtrConst node) { const std::string map = XML::getProperty(node, "name", ""); if (map.empty()) @@ -99,7 +103,7 @@ void MapDB::readMap(XmlNodePtr node) } } -void MapDB::readAtlas(XmlNodePtr node) +void MapDB::readAtlas(XmlNodePtrConst node) { const std::string atlas = XML::getProperty(node, "name", ""); if (atlas.empty()) @@ -125,10 +129,10 @@ void MapDB::readAtlas(XmlNodePtr node) } } -void MapDB::loadInfo() +void MapDB::loadInfo(const std::string &fileName) { - XML::Document *doc = new XML::Document(paths.getStringValue("mapsFile")); - const XmlNodePtr root = doc->rootNode(); + XML::Document *doc = new XML::Document(fileName); + const XmlNodePtrConst root = doc->rootNode(); if (!root) { delete doc; @@ -138,9 +142,20 @@ void MapDB::loadInfo() for_each_xml_child_node(node, root) { if (xmlNameEqual(node, "map")) + { readMap(node); + } else if (xmlNameEqual(node, "atlas")) + { readAtlas(node); + } + else if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadInfo(name); + continue; + } } delete doc; } diff --git a/src/resources/db/mapdb.h b/src/resources/db/mapdb.h index 527d15ba4..c2fbb3137 100644 --- a/src/resources/db/mapdb.h +++ b/src/resources/db/mapdb.h @@ -52,7 +52,7 @@ namespace MapDB void loadRemap(); - void loadInfo(); + void loadInfo(const std::string &fileName); /** * Clear the remap data diff --git a/src/resources/db/moddb.cpp b/src/resources/db/moddb.cpp new file mode 100644 index 000000000..4cdc96b3d --- /dev/null +++ b/src/resources/db/moddb.cpp @@ -0,0 +1,116 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus 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 "resources/db/moddb.h" + +#include "configuration.h" +#include "logger.h" + +#include "resources/beingcommon.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" + +#include "debug.h" + +namespace +{ + ModInfos mModInfos; + bool mLoaded = false; +} + +void ModDB::load() +{ + if (mLoaded) + unload(); + loadXmlFile(paths.getStringValue("modsFile")); + loadXmlFile(paths.getStringValue("modsPatchFile")); + loadXmlDir("modsPatchDir", loadXmlFile); + mLoaded = true; +} + +void ModDB::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst rootNode = doc.rootNode(); + + if (!rootNode || !xmlNameEqual(rootNode, "mods")) + { + logger->log("Mods Database: Error while loading %s!", + fileName.c_str()); + return; + } + + for_each_xml_child_node(modNode, rootNode) + { + if (xmlNameEqual(modNode, "include")) + { + const std::string name = XML::getProperty(modNode, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + + if (!xmlNameEqual(modNode, "mod")) + continue; + + const std::string name = XML::langProperty( + // TRANSLATORS: unknown info name + modNode, "name", _("unnamed")); + ModInfo *currentInfo = nullptr; + if (mModInfos.find(name) != mModInfos.end()) + currentInfo = mModInfos[name]; + if (!currentInfo) + currentInfo = new ModInfo; + + currentInfo->setName(name); + currentInfo->setDescription(XML::langProperty( + modNode, "description", "")); + currentInfo->setHelp(XML::getProperty( + modNode, "help", "")); + currentInfo->setLocalDir(XML::getProperty( + modNode, "localdir", "")); + + mModInfos[name] = currentInfo; + } +} + +void ModDB::unload() +{ + delete_all(mModInfos); + mModInfos.clear(); + mLoaded = false; +} + +ModInfo *ModDB::get(const std::string &name) +{ + ModInfoIterator i = mModInfos.find(name); + if (i == mModInfos.end()) + return nullptr; + else + return i->second; +} + +const ModInfos &ModDB::getAll() +{ + return mModInfos; +} diff --git a/src/resources/db/moddb.h b/src/resources/db/moddb.h new file mode 100644 index 000000000..f1bf1b25d --- /dev/null +++ b/src/resources/db/moddb.h @@ -0,0 +1,47 @@ +/* + * The ManaPlus Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus 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 RESOURCES_DB_MODDB_H +#define RESOURCES_DB_MODDB_H + +#include "resources/modinfo.h" + +#include <string> + +#include "localconsts.h" + +class ModInfo; + +namespace ModDB +{ + void load(); + + void unload(); + + ModInfo *get(const std::string &name) A_WARN_UNUSED; + + void loadXmlFile(const std::string &fileName); + + const ModInfos &getAll(); +} // namespace ModDB + +#endif // RESOURCES_DB_MODDB_H diff --git a/src/resources/db/monsterdb.cpp b/src/resources/db/monsterdb.cpp index d53bb4b69..9b9112f0f 100644 --- a/src/resources/db/monsterdb.cpp +++ b/src/resources/db/monsterdb.cpp @@ -49,6 +49,9 @@ void MonsterDB::load() logger->log1("Initializing monster database..."); loadXmlFile(paths.getStringValue("monstersFile")); + loadXmlFile(paths.getStringValue("monstersPatchFile")); + loadXmlDir("monstersPatchDir", loadXmlFile); + mLoaded = true; } @@ -87,7 +90,15 @@ void MonsterDB::loadXmlFile(const std::string &fileName) if (!xmlNameEqual(monsterNode, "monster")) continue; - BeingInfo *const currentInfo = new BeingInfo; + const int id = XML::getProperty(monsterNode, "id", 0); + BeingInfo *currentInfo = nullptr; + if (mMonsterInfos.find(id + offset) != mMonsterInfos.end()) + { + logger->log("MonsterDB: Redefinition of monster ID %d", id); + currentInfo = mMonsterInfos[id + offset]; + } + if (!currentInfo) + currentInfo = new BeingInfo; currentInfo->setWalkMask(Map::BLOCKMASK_WALL | Map::BLOCKMASK_CHARACTER | Map::BLOCKMASK_MONSTER); @@ -197,7 +208,7 @@ void MonsterDB::loadXmlFile(const std::string &fileName) } else if (xmlNameEqual(spriteNode, "attack")) { - const int id = XML::getProperty(spriteNode, "id", 0); + const int attackId = XML::getProperty(spriteNode, "id", 0); const int effectId = XML::getProperty( spriteNode, "effect-id", paths.getIntValue("effectId")); const int hitEffectId = XML::getProperty(spriteNode, @@ -218,7 +229,7 @@ void MonsterDB::loadXmlFile(const std::string &fileName) const std::string missileParticle = XML::getProperty( spriteNode, "missile-particle", ""); - currentInfo->addAttack(id, spriteAction, skySpriteAction, + currentInfo->addAttack(attackId, spriteAction, skySpriteAction, waterSpriteAction, effectId, hitEffectId, criticalHitEffectId, missEffectId, missileParticle); } @@ -233,8 +244,7 @@ void MonsterDB::loadXmlFile(const std::string &fileName) } currentInfo->setDisplay(display); - mMonsterInfos[XML::getProperty( - monsterNode, "id", 0) + offset] = currentInfo; + mMonsterInfos[id + offset] = currentInfo; } } diff --git a/src/resources/db/monsterdb.h b/src/resources/db/monsterdb.h index a4be8213e..9de5bde3a 100644 --- a/src/resources/db/monsterdb.h +++ b/src/resources/db/monsterdb.h @@ -41,6 +41,6 @@ namespace MonsterDB void loadXmlFile(const std::string &fileName); BeingInfo *get(const int id) A_WARN_UNUSED; -} +} // namespace MonsterDB #endif // RESOURCES_DB_MONSTERDB_H diff --git a/src/resources/db/npcdb.cpp b/src/resources/db/npcdb.cpp index a7bc2121a..359c86c4a 100644 --- a/src/resources/db/npcdb.cpp +++ b/src/resources/db/npcdb.cpp @@ -48,13 +48,16 @@ void NPCDB::load() logger->log1("Initializing NPC database..."); loadXmlFile(paths.getStringValue("npcsFile")); + loadXmlFile(paths.getStringValue("npcsPatchFile")); + loadXmlDir("npcsPatchDir", loadXmlFile); + mLoaded = true; } void NPCDB::loadXmlFile(const std::string &fileName) { XML::Document doc(fileName); - const XmlNodePtr rootNode = doc.rootNode(); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "npcs")) { @@ -79,14 +82,20 @@ void NPCDB::loadXmlFile(const std::string &fileName) continue; const int id = XML::getProperty(npcNode, "id", 0); + BeingInfo *currentInfo = nullptr; if (id == 0) { logger->log("NPC Database: NPC with missing ID in %s!", paths.getStringValue("npcsFile").c_str()); continue; } - - BeingInfo *const currentInfo = new BeingInfo; + else if (mNPCInfos.find(id) != mNPCInfos.end()) + { + logger->log("NpcDB: Redefinition of npc ID %d", id); + currentInfo = mNPCInfos[id]; + } + if (!currentInfo) + currentInfo = new BeingInfo; currentInfo->setTargetSelection(XML::getBoolProperty(npcNode, "targetSelection", true)); diff --git a/src/resources/db/petdb.cpp b/src/resources/db/petdb.cpp index c235200a6..c9f3bdbeb 100644 --- a/src/resources/db/petdb.cpp +++ b/src/resources/db/petdb.cpp @@ -47,33 +47,52 @@ void PETDB::load() unload(); logger->log1("Initializing PET database..."); + loadXmlFile(paths.getStringValue("petsFile")); + loadXmlFile(paths.getStringValue("petsPatchFile")); + loadXmlDir("petsPatchDir", loadXmlFile); + mLoaded = true; +} - XML::Document doc(paths.getStringValue("petsFile")); - const XmlNodePtr rootNode = doc.rootNode(); +void PETDB::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "pets")) { logger->log("PET Database: Error while loading %s!", - paths.getStringValue("petsFile").c_str()); - mLoaded = true; + fileName.c_str()); return; } // iterate <pet>s for_each_xml_child_node(petNode, rootNode) { - if (!xmlNameEqual(petNode, "pet")) + if (xmlNameEqual(petNode, "include")) + { + const std::string name = XML::getProperty(petNode, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (!xmlNameEqual(petNode, "pet")) + { continue; + } - const int id = XML::getProperty(petNode, "id", 0); - if (id == 0) + const int id = XML::getProperty(petNode, "id", -1); + if (id == -1) { logger->log("PET Database: PET with missing ID in %s!", paths.getStringValue("petsFile").c_str()); continue; } - BeingInfo *const currentInfo = new BeingInfo; + BeingInfo *currentInfo = nullptr; + if (mPETInfos.find(id) != mPETInfos.end()) + currentInfo = mPETInfos[id]; + if (!currentInfo) + currentInfo = new BeingInfo; currentInfo->setName(XML::langProperty(petNode, // TRANSLATORS: unknown info name @@ -151,8 +170,6 @@ void PETDB::load() mPETInfos[id] = currentInfo; } - - mLoaded = true; } void PETDB::unload() diff --git a/src/resources/db/petdb.h b/src/resources/db/petdb.h index e3ed9a7da..17ef7b287 100644 --- a/src/resources/db/petdb.h +++ b/src/resources/db/petdb.h @@ -23,6 +23,8 @@ #ifndef RESOURCES_DB_PETDB_H #define RESOURCES_DB_PETDB_H +#include <string> + #include "localconsts.h" class BeingInfo; @@ -31,9 +33,11 @@ namespace PETDB { void load(); + void loadXmlFile(const std::string &fileName); + void unload(); BeingInfo *get(const int id) A_WARN_UNUSED; -} +} // namespace PETDB #endif // RESOURCES_DB_PETDB_H diff --git a/src/resources/db/sounddb.cpp b/src/resources/db/sounddb.cpp index 639156909..17ff4bc71 100644 --- a/src/resources/db/sounddb.cpp +++ b/src/resources/db/sounddb.cpp @@ -26,6 +26,8 @@ #include "utils/xml.h" +#include "resources/beingcommon.h" + #include "debug.h" namespace @@ -37,9 +39,15 @@ namespace void SoundDB::load() { unload(); + loadXmlFile(paths.getStringValue("soundsFile")); + loadXmlFile(paths.getStringValue("soundsPatchFile")); + loadXmlDir("soundsPatchDir", loadXmlFile); +} - XML::Document *doc = new XML::Document(paths.getStringValue("soundsFile")); - const XmlNodePtr root = doc->rootNode(); +void SoundDB::loadXmlFile(const std::string &fileName) +{ + XML::Document *doc = new XML::Document(fileName); + const XmlNodePtrConst root = doc->rootNode(); if (!root || !xmlNameEqual(root, "sounds")) { @@ -49,7 +57,14 @@ void SoundDB::load() for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "sound")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(node, "sound")) { const std::string name = XML::getProperty(node, "name", ""); const int id = NotifyManager::getIndexBySound(name); diff --git a/src/resources/db/sounddb.h b/src/resources/db/sounddb.h index 3c421ca62..eca7286a2 100644 --- a/src/resources/db/sounddb.h +++ b/src/resources/db/sounddb.h @@ -29,9 +29,11 @@ namespace SoundDB { void load(); + void loadXmlFile(const std::string &fileName); + void unload(); std::string &getSound(const int id); -} +} // namespace SoundDB #endif // RESOURCES_DB_SOUNDDB_H diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index 9caa14945..bcafe4bb7 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -227,7 +227,7 @@ Map *MapReader::readMap(const std::string &restrict filename, XML::Document doc(reinterpret_cast<char*>(inflated), inflatedSize); free(inflated); - XmlNodePtr node = doc.rootNode(); + XmlNodePtrConst node = doc.rootNode(); // Parse the inflated map data if (node) @@ -256,7 +256,7 @@ Map *MapReader::readMap(const std::string &restrict filename, return map; } -Map *MapReader::readMap(XmlNodePtr node, const std::string &path) +Map *MapReader::readMap(XmlNodePtrConst node, const std::string &path) { if (!node) return nullptr; @@ -405,7 +405,8 @@ Map *MapReader::readMap(XmlNodePtr node, const std::string &path) return map; } -void MapReader::readProperties(const XmlNodePtr node, Properties *const props) +void MapReader::readProperties(const XmlNodePtrConst node, + Properties *const props) { if (!node || !props) return; @@ -511,7 +512,8 @@ inline static void setTile(Map *const map, MapLayer *const layer, } \ } \ -bool MapReader::readBase64Layer(const XmlNodePtr childNode, Map *const map, +bool MapReader::readBase64Layer(const XmlNodePtrConst childNode, + Map *const map, MapLayer *const layer, const MapLayer::Type &layerType, MapHeights *const heights, @@ -528,7 +530,7 @@ bool MapReader::readBase64Layer(const XmlNodePtr childNode, Map *const map, } // Read base64 encoded map file - XmlNodePtr dataChild = childNode->xmlChildrenNode; + XmlNodePtrConst dataChild = childNode->xmlChildrenNode; if (!dataChild) return true; @@ -613,14 +615,15 @@ bool MapReader::readBase64Layer(const XmlNodePtr childNode, Map *const map, return true; } -bool MapReader::readCsvLayer(const XmlNodePtr childNode, Map *const map, +bool MapReader::readCsvLayer(const XmlNodePtrConst childNode, + Map *const map, MapLayer *const layer, const MapLayer::Type &layerType, MapHeights *const heights, int &restrict x, int &restrict y, const int w, const int h) { - XmlNodePtr dataChild = childNode->xmlChildrenNode; + XmlNodePtrConst dataChild = childNode->xmlChildrenNode; if (!dataChild) return true; @@ -803,7 +806,8 @@ void MapReader::readLayer(const XmlNodePtr node, Map *const map) } } -Tileset *MapReader::readTileset(XmlNodePtr node, const std::string &path, +Tileset *MapReader::readTileset(XmlNodePtr node, + const std::string &path, Map *const map) { if (!map) diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 3573fcc89..4f84f1a1a 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -52,7 +52,7 @@ class MapReader final * Read an XML map from a parsed XML tree. The path is used to find the * location of referenced tileset images. */ - static Map *readMap(XmlNodePtr node, + static Map *readMap(XmlNodePtrConst node, const std::string &path) A_WARN_UNUSED; static Map *createEmptyMap(const std::string &restrict filename, @@ -67,7 +67,7 @@ class MapReader final * @param props The Properties instance to which the properties will * be assigned. */ - static void readProperties(const XmlNodePtr node, + static void readProperties(const XmlNodePtrConst node, Properties *const props); /** @@ -75,7 +75,8 @@ class MapReader final */ static void readLayer(const XmlNodePtr node, Map *const map); - static bool readBase64Layer(const XmlNodePtr childNode, Map *const map, + static bool readBase64Layer(const XmlNodePtrConst childNode, + Map *const map, MapLayer *const layer, const MapLayer::Type &layerType, MapHeights *const heights, @@ -83,7 +84,8 @@ class MapReader final int &restrict x, int &restrict y, const int w, const int h); - static bool readCsvLayer(const XmlNodePtr childNode, Map *const map, + static bool readCsvLayer(const XmlNodePtrConst childNode, + Map *const map, MapLayer *const layer, const MapLayer::Type &layerType, MapHeights *const heights, @@ -93,7 +95,8 @@ class MapReader final /** * Reads a tile set. */ - static Tileset *readTileset(XmlNodePtr node, const std::string &path, + static Tileset *readTileset(XmlNodePtr node, + const std::string &path, Map *const map) A_WARN_UNUSED; static void updateMusic(Map *const map); diff --git a/src/resources/modinfo.cpp b/src/resources/modinfo.cpp new file mode 100644 index 000000000..2774343ee --- /dev/null +++ b/src/resources/modinfo.cpp @@ -0,0 +1,34 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus 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 "resources/modinfo.h" + +#include "utils/gettext.h" + +#include "debug.h" + +ModInfo::ModInfo() : + // TRANSLATORS: being info default name + mName(_("unnamed")), + mDescription(), + mHelp(), + mLocalDir() +{ +} diff --git a/src/resources/modinfo.h b/src/resources/modinfo.h new file mode 100644 index 000000000..b621e45d8 --- /dev/null +++ b/src/resources/modinfo.h @@ -0,0 +1,71 @@ +/* + * The ManaPlus Client + * Copyright (C) 2011-2014 The ManaPlus Developers + * + * This file is part of The ManaPlus 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 RESOURCES_MODINFO_H +#define RESOURCES_MODINFO_H + +#include <string> +#include <map> + +#include "localconsts.h" + +class ModInfo final +{ + public: + ModInfo(); + + A_DELETE_COPY(ModInfo) + + void setName(const std::string &name) + { mName = name; } + + const std::string &getName() const A_WARN_UNUSED + { return mName; } + + void setDescription(const std::string &text) + { mDescription = text; } + + const std::string &getDescription() const A_WARN_UNUSED + { return mDescription; } + + void setHelp(const std::string &text) + { mHelp = text; } + + const std::string &getHelp() const A_WARN_UNUSED + { return mHelp; } + + void setLocalDir(const std::string &text) + { mLocalDir = text; } + + const std::string &getLocalDir() const A_WARN_UNUSED + { return mLocalDir; } + + private: + std::string mName; + std::string mDescription; + std::string mHelp; + std::string mLocalDir; +}; + +typedef std::map<std::string, ModInfo*> ModInfos; +typedef ModInfos::iterator ModInfoIterator; +typedef ModInfos::const_iterator ModInfoCIterator; + +#endif // RESOURCES_MODINFO_H diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index 00fe37c75..6b87e6cdf 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -887,6 +887,36 @@ void ResourceManager::moveToDeleted(Resource *const res) } } +void ResourceManager::decRefDelete(Resource *const res) +{ + if (!res) + return; + + const int count = res->getRefCount(); + if (count == 1) + { + logResource(res); + + ResourceIterator resIter = mResources.find(res->mIdPath); + if (resIter != mResources.end() && resIter->second == res) + { + mResources.erase(resIter); + } + else + { + resIter = mOrphanedResources.find(res->mIdPath); + if (resIter != mOrphanedResources.end() && resIter->second == res) + mOrphanedResources.erase(resIter); + } + + delete res; + } + else + { + res->decRef(); + } +} + ResourceManager *ResourceManager::getInstance() { // Create a new instance if necessary. diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index 83c0036f2..72cad5988 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -240,6 +240,8 @@ class ResourceManager final void clearDeleted(const bool full = true); + void decRefDelete(Resource *const res); + static void logResource(const Resource *const res); /** diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 9970063c8..e37aa1aa8 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -85,7 +85,7 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, palettes = animationFile.substr(pos + 1); XML::Document doc(animationFile.substr(0, pos)); - XmlNodePtr rootNode = doc.rootNode(); + XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "sprite")) { @@ -225,7 +225,8 @@ void SpriteDef::loadImageSet(const XmlNodePtr node, mImageSets[name] = imageSet; } -void SpriteDef::loadAction(const XmlNodePtr node, const int variant_offset) +void SpriteDef::loadAction(const XmlNodePtr node, + const int variant_offset) { const std::string actionName = XML::getProperty(node, "name", ""); const std::string imageSetName = XML::getProperty(node, "imageset", ""); diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index fe84096c3..ecedde507 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -185,18 +185,21 @@ class SpriteDef final : public Resource /** * Loads a sprite element. */ - void loadSprite(const XmlNodePtr spriteNode, const int variant, + void loadSprite(const XmlNodePtr spriteNode, + const int variant, const std::string &palettes = ""); /** * Loads an imageset element. */ - void loadImageSet(const XmlNodePtr node, const std::string &palettes); + void loadImageSet(const XmlNodePtr node, + const std::string &palettes); /** * Loads an action element. */ - void loadAction(const XmlNodePtr node, const int variant_offset); + void loadAction(const XmlNodePtr node, + const int variant_offset); /** * Loads an animation element. diff --git a/src/statuseffect.cpp b/src/statuseffect.cpp index 018fe39ec..049c010b3 100644 --- a/src/statuseffect.cpp +++ b/src/statuseffect.cpp @@ -22,12 +22,13 @@ #include "statuseffect.h" +#include "configuration.h" #include "logger.h" #include "soundmanager.h" #include "gui/widgets/tabs/chattab.h" -#include "configuration.h" +#include "resources/beingcommon.h" #include <map> @@ -127,17 +128,34 @@ void StatusEffect::load() if (mLoaded) unload(); - XML::Document doc(paths.getStringValue("statusEffectsFile")); - const XmlNodePtr rootNode = doc.rootNode(); + loadXmlFile(paths.getStringValue("statusEffectsFile")); + loadXmlFile(paths.getStringValue("statusEffectsPatchFile")); + loadXmlDir("statusEffectsPatchDir", loadXmlFile); + + mLoaded = true; +} + +void StatusEffect::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst rootNode = doc.rootNode(); if (!rootNode || !xmlNameEqual(rootNode, "status-effects")) { - logger->log1("Error loading status effects file"); + logger->log("Error loading status effects file: " + fileName); return; } for_each_xml_child_node(node, rootNode) { + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + status_effect_map *the_map = nullptr; const int index = atoi(XML::getProperty(node, "id", "-1").c_str()); @@ -158,8 +176,12 @@ void StatusEffect::load() if (the_map) { - StatusEffect *const startEffect = new StatusEffect; - StatusEffect *const endEffect = new StatusEffect; + StatusEffect *startEffect = (*the_map)[1][index]; + StatusEffect *endEffect = (*the_map)[0][index]; + if (!startEffect) + startEffect = new StatusEffect; + if (!endEffect) + endEffect = new StatusEffect; startEffect->mMessage = XML::getProperty( node, "start-message", ""); @@ -182,7 +204,6 @@ void StatusEffect::load() (*the_map)[0][index] = endEffect; } } - mLoaded = true; } static void unloadMap(std::map<int, StatusEffect *> &map) diff --git a/src/statuseffect.h b/src/statuseffect.h index 7c36b48bf..6dda71cfc 100644 --- a/src/statuseffect.h +++ b/src/statuseffect.h @@ -105,6 +105,8 @@ public: static void load(); + static void loadXmlFile(const std::string &fileName); + static void unload(); private: static bool mLoaded; diff --git a/src/touchactions.cpp b/src/touchactions.cpp index 1b6abdf23..a79c14275 100644 --- a/src/touchactions.cpp +++ b/src/touchactions.cpp @@ -23,6 +23,8 @@ #include "mouseinput.h" #include "touchmanager.h" +#include "input/inputmanager.h" + #include "debug.h" bool padClicked(false); @@ -96,6 +98,7 @@ static void moveChar(int x, int y) touchManager.setActionActive(Input::KEY_MOVE_DOWN, false); touchManager.setActionActive(Input::KEY_MOVE_UP, false); } + inputManager.updateConditionMask(); } impHandler(padClick) diff --git a/src/units.cpp b/src/units.cpp index f1ba1ba39..f095d7bbf 100644 --- a/src/units.cpp +++ b/src/units.cpp @@ -28,6 +28,8 @@ #include "utils/stringutils.h" #include "utils/xml.h" +#include "resources/beingcommon.h" + #include <climits> #include <vector> @@ -102,8 +104,15 @@ void Units::loadUnits() units[UNIT_CURRENCY] = ud; } - XML::Document doc(paths.getStringValue("unitsFile")); - const XmlNodePtr root = doc.rootNode(); + loadXmlFile(paths.getStringValue("unitsFile")); + loadXmlFile(paths.getStringValue("unitsPatchFile")); + loadXmlDir("unitsPatchDir", loadXmlFile); +} + +void Units::loadXmlFile(const std::string &fileName) +{ + XML::Document doc(fileName); + const XmlNodePtrConst root = doc.rootNode(); if (!root || !xmlNameEqual(root, "units")) { @@ -114,7 +123,14 @@ void Units::loadUnits() for_each_xml_child_node(node, root) { - if (xmlNameEqual(node, "unit")) + if (xmlNameEqual(node, "include")) + { + const std::string name = XML::getProperty(node, "name", ""); + if (!name.empty()) + loadXmlFile(name); + continue; + } + else if (xmlNameEqual(node, "unit")) { UnitDescription ud; int level = 1; diff --git a/src/units.h b/src/units.h index 84ad177cd..18a3bd3a2 100644 --- a/src/units.h +++ b/src/units.h @@ -37,6 +37,8 @@ class Units final */ static void loadUnits(); + static void loadXmlFile(const std::string &fileName); + /** * Formats the given number in the correct currency format. */ diff --git a/src/utils/base64.cpp b/src/utils/base64.cpp index 280e71ff0..bd20496b3 100644 --- a/src/utils/base64.cpp +++ b/src/utils/base64.cpp @@ -174,3 +174,32 @@ unsigned char *php3_base64_decode(const unsigned char *restrict const string, result[k] = '\0'; return result; } + +std::string encodeBase64String(std::string value) +{ + int sz = 0; + const unsigned char *const str = reinterpret_cast<unsigned char*>( + const_cast<char*>(value.c_str())); + unsigned char *const buf = php3_base64_encode(str, value.size(), &sz); + if (!buf) + return std::string(); + + value = std::string(reinterpret_cast<char*>(buf), sz); + free(buf); + return value; +} + +std::string decodeBase64String(std::string value) +{ + int sz = 0; + const unsigned char *const str = reinterpret_cast<unsigned char*>( + const_cast<char*>(value.c_str())); + unsigned char *const buf = php3_base64_decode(str, value.size(), &sz); + + if (buf) + value = std::string(reinterpret_cast<char*>(buf), sz); + else + value.clear(); + free(buf); + return value; +} diff --git a/src/utils/base64.h b/src/utils/base64.h index 0e6546df5..4518a3e5a 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -30,11 +30,17 @@ #ifndef UTILS_BASE64_H #define UTILS_BASE64_H +#include <string> + #include "localconsts.h" -extern unsigned char *php3_base64_encode(const unsigned char *restrict, - int, int *restrict) A_WARN_UNUSED; -extern unsigned char *php3_base64_decode(const unsigned char *restrict, - int, int *restrict ) A_WARN_UNUSED; +unsigned char *php3_base64_encode(const unsigned char *restrict, + int, int *restrict) A_WARN_UNUSED; +unsigned char *php3_base64_decode(const unsigned char *restrict, + int, int *restrict ) A_WARN_UNUSED; + +std::string encodeBase64String(std::string value) A_WARN_UNUSED; + +std::string decodeBase64String(std::string value) A_WARN_UNUSED; #endif // UTILS_BASE64_H diff --git a/src/utils/files.cpp b/src/utils/files.cpp index 206a4d4bd..24ec9366e 100644 --- a/src/utils/files.cpp +++ b/src/utils/files.cpp @@ -22,10 +22,10 @@ #if defined(ANDROID) || defined(__native_client__) #include "resources/resourcemanager.h" -#include "utils/physfstools.h" #endif #include "utils/mkdir.h" +#include "utils/physfstools.h" #include "localconsts.h" @@ -196,3 +196,36 @@ int Files::copyFile(const std::string &restrict srcName, fclose(dstFile); return 0; } + +void Files::getFiles(const std::string &path, StringVect &list) +{ + char **fonts = PhysFs::enumerateFiles(path.c_str()); + for (char *const *i = fonts; *i; i++) + { + if (!PhysFs::isDirectory((path + *i).c_str())) + list.push_back(*i); + } + PhysFs::freeList(fonts); +} + +void Files::getDirs(const std::string &path, StringVect &list) +{ + char **fonts = PhysFs::enumerateFiles(path.c_str()); + for (char *const *i = fonts; *i; i++) + { + if (PhysFs::isDirectory((path + *i).c_str())) + list.push_back(*i); + } + PhysFs::freeList(fonts); +} + +void Files::getFilesWithDir(const std::string &path, StringVect &list) +{ + char **fonts = PhysFs::enumerateFiles(path.c_str()); + for (char *const *i = fonts; *i; i++) + { + if (!PhysFs::isDirectory((path + *i).c_str())) + list.push_back(path + *i); + } + PhysFs::freeList(fonts); +} diff --git a/src/utils/files.h b/src/utils/files.h index cc578464a..2046b8ac9 100644 --- a/src/utils/files.h +++ b/src/utils/files.h @@ -21,6 +21,8 @@ #ifndef UTILS_FILES_H #define UTILS_FILES_H +#include "utils/stringvector.h" + #include <string> #include "localconsts.h" @@ -52,6 +54,13 @@ namespace Files int copyFile(const std::string &restrict pFrom, const std::string &restrict pTo); + + void getFiles(const std::string &path, StringVect &list); + + void getDirs(const std::string &path, StringVect &list); + + void getFilesWithDir(const std::string &restrict path, + StringVect &restrict list); } // namespace Files #endif // UTILS_FILES_H diff --git a/src/utils/paths.cpp b/src/utils/paths.cpp index 4599efb16..a3f61bde3 100644 --- a/src/utils/paths.cpp +++ b/src/utils/paths.cpp @@ -151,7 +151,7 @@ std::string getSelfName() #endif -std::string getDesktopDir() +std::string getPicturesDir() { #ifdef WIN32 std::string dir = getSpecialFolderLocation(CSIDL_MYPICTURES); @@ -176,7 +176,7 @@ std::string getDesktopDir() FOR_EACH (StringVectCIter, it, arr) { std::string str = *it; - if (findCutFirst(str, "XDG_DESKTOP_DIR=\"")) + if (findCutFirst(str, "XDG_PICTURES_DIR=\"")) { str = str.substr(0, str.size() - 1); // use hack to replace $HOME var. diff --git a/src/utils/paths.h b/src/utils/paths.h index 8ed1fd3d9..a31716dcc 100644 --- a/src/utils/paths.h +++ b/src/utils/paths.h @@ -37,7 +37,7 @@ std::string removeLast(const std::string &str) A_WARN_UNUSED; std::string getSelfName() A_WARN_UNUSED; -std::string getDesktopDir() A_WARN_UNUSED; +std::string getPicturesDir() A_WARN_UNUSED; #ifdef ANDROID std::string getSdStoragePath() A_WARN_UNUSED; diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp index a2de26ee3..94fc18f98 100644 --- a/src/utils/xml.cpp +++ b/src/utils/xml.cpp @@ -140,7 +140,9 @@ namespace XML return mDoc ? xmlDocGetRootElement(mDoc) : nullptr; } - int getProperty(const XmlNodePtr node, const char *const name, int def) + int getProperty(const XmlNodePtr node, + const char *const name, + int def) { int &ret = def; @@ -154,8 +156,11 @@ namespace XML return ret; } - int getIntProperty(const XmlNodePtr node, const char *const name, int def, - const int min, const int max) + int getIntProperty(const XmlNodePtr node, + const char *const name, + int def, + const int min, + const int max) { int &ret = def; @@ -172,7 +177,8 @@ namespace XML return ret; } - double getFloatProperty(const XmlNodePtr node, const char *const name, + double getFloatProperty(const XmlNodePtr node, + const char *const name, double def) { double &ret = def; @@ -187,7 +193,8 @@ namespace XML return ret; } - std::string getProperty(const XmlNodePtr node, const char *const name, + std::string getProperty(const XmlNodePtr node, + const char *const name, const std::string &def) { xmlChar *const prop = XmlGetProp(node, name); @@ -201,7 +208,8 @@ namespace XML return def; } - std::string langProperty(const XmlNodePtr node, const char *const name, + std::string langProperty(const XmlNodePtr node, + const char *const name, const std::string &def) { std::string str = getProperty(node, name, def); @@ -211,7 +219,8 @@ namespace XML return translator->getStr(str); } - bool getBoolProperty(const XmlNodePtr node, const char *const name, + bool getBoolProperty(const XmlNodePtr node, + const char *const name, const bool def) { const xmlChar *const prop = XmlGetProp(node, name); diff --git a/src/utils/xml.h b/src/utils/xml.h index 0fa0ba078..5c1fb7605 100644 --- a/src/utils/xml.h +++ b/src/utils/xml.h @@ -31,6 +31,7 @@ #include "localconsts.h" #define XmlNodePtr xmlNodePtr +#define XmlNodePtrConst xmlNode *const #define XmlStrEqual(str1, str2) xmlStrEqual(str1, \ reinterpret_cast<const xmlChar*>(str2)) #define xmlNameEqual(node, str) xmlStrEqual((node)->name, \ @@ -96,37 +97,45 @@ namespace XML /** * Gets an floating point property from an XmlNodePtr. */ - double getFloatProperty(const XmlNodePtr node, const char *const name, + double getFloatProperty(const XmlNodePtr node, + const char *const name, double def) A_WARN_UNUSED; /** * Gets an integer property from an XmlNodePtr. */ - int getProperty(const XmlNodePtr node, const char *const name, + int getProperty(const XmlNodePtr node, + const char *const name, int def) A_WARN_UNUSED; /** * Gets an integer property from an XmlNodePtr. */ - int getIntProperty(const XmlNodePtr node, const char *const name, int def, - const int min, const int max) A_WARN_UNUSED; + int getIntProperty(const XmlNodePtr node, + const char *const name, + int def, + const int min, + const int max) A_WARN_UNUSED; /** * Gets a string property from an XmlNodePtr. */ - std::string getProperty(const XmlNodePtr node, const char *const name, + std::string getProperty(const XmlNodePtr node, + const char *const name, const std::string &def) A_WARN_UNUSED; /** * Gets a translated string property from an XmlNodePtr. */ - std::string langProperty(const XmlNodePtr node, const char *const name, + std::string langProperty(const XmlNodePtr node, + const char *const name, const std::string &def) A_WARN_UNUSED; /** * Gets a boolean property from an XmlNodePtr. */ - bool getBoolProperty(const XmlNodePtr node, const char *const name, + bool getBoolProperty(const XmlNodePtr node, + const char *const name, const bool def) A_WARN_UNUSED; /** |