summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/Makefile.am6
-rw-r--r--src/actionmanager.cpp36
-rw-r--r--src/being/being.cpp114
-rw-r--r--src/being/being.h26
-rw-r--r--src/client.cpp95
-rw-r--r--src/client.h2
-rw-r--r--src/commands.cpp7
-rw-r--r--src/configuration.cpp6
-rw-r--r--src/configuration.h2
-rw-r--r--src/defaults.cpp44
-rw-r--r--src/effectmanager.cpp30
-rw-r--r--src/effectmanager.h2
-rw-r--r--src/game.cpp63
-rw-r--r--src/gui/gui.cpp47
-rw-r--r--src/gui/gui.h1
-rw-r--r--src/gui/popups/popupmenu.cpp3
-rw-r--r--src/gui/theme.cpp37
-rw-r--r--src/gui/theme.h4
-rw-r--r--src/gui/widgets/colorpage.cpp6
-rw-r--r--src/gui/widgets/colorpage.h4
-rw-r--r--src/gui/widgets/container.cpp4
-rw-r--r--src/gui/widgets/container.h2
-rw-r--r--src/gui/widgets/desktop.cpp9
-rw-r--r--src/gui/widgets/progressbar.cpp10
-rw-r--r--src/gui/widgets/progressbar.h2
-rw-r--r--src/gui/widgets/setupitem.cpp42
-rw-r--r--src/gui/widgets/setupitem.h25
-rw-r--r--src/gui/widgets/shoplistbox.cpp6
-rw-r--r--src/gui/widgets/shoplistbox.h10
-rw-r--r--src/gui/widgets/tabs/setup_mods.cpp126
-rw-r--r--src/gui/widgets/tabs/setup_mods.h50
-rw-r--r--src/gui/widgets/tabs/setup_other.cpp6
-rw-r--r--src/gui/widgets/tabs/setup_theme.cpp45
-rw-r--r--src/gui/widgets/tabs/setup_theme.h8
-rw-r--r--src/gui/widgets/tabs/setuptab.cpp4
-rw-r--r--src/gui/widgets/tabs/setuptab.h2
-rw-r--r--src/gui/widgets/tabs/setuptabscroll.cpp26
-rw-r--r--src/gui/widgets/tabs/setuptabscroll.h9
-rw-r--r--src/gui/widgets/textfield.cpp372
-rw-r--r--src/gui/widgets/textfield.h12
-rw-r--r--src/gui/windows/chatwindow.cpp63
-rw-r--r--src/gui/windows/editdialog.cpp4
-rw-r--r--src/gui/windows/editdialog.h1
-rw-r--r--src/gui/windows/equipmentwindow.h2
-rw-r--r--src/gui/windows/ministatuswindow.cpp2
-rw-r--r--src/gui/windows/npcdialog.h3
-rw-r--r--src/gui/windows/questswindow.cpp21
-rw-r--r--src/gui/windows/questswindow.h2
-rw-r--r--src/gui/windows/serverdialog.cpp3
-rw-r--r--src/gui/windows/setup.cpp32
-rw-r--r--src/gui/windows/setup.h5
-rw-r--r--src/gui/windows/skilldialog.cpp93
-rw-r--r--src/gui/windows/skilldialog.h2
-rw-r--r--src/gui/windows/updaterwindow.cpp173
-rw-r--r--src/gui/windows/updaterwindow.h10
-rw-r--r--src/input/inputmanager.cpp40
-rw-r--r--src/input/inputmanager.h12
-rw-r--r--src/input/joystick.cpp2
-rw-r--r--src/input/keyboardconfig.cpp2
-rw-r--r--src/input/keyboarddata.h12
-rw-r--r--src/logger.cpp59
-rw-r--r--src/logger.h22
-rw-r--r--src/main.h4
-rw-r--r--src/net/download.cpp4
-rw-r--r--src/net/ea/network.cpp16
-rw-r--r--src/net/pethandler.h4
-rw-r--r--src/net/sdltcpnet.cpp4
-rw-r--r--src/net/tmwa/pethandler.cpp4
-rw-r--r--src/net/tmwa/pethandler.h5
-rw-r--r--src/particle/animationparticle.cpp2
-rw-r--r--src/particle/animationparticle.h2
-rw-r--r--src/particle/particle.cpp2
-rw-r--r--src/particle/particleemitter.cpp6
-rw-r--r--src/particle/particleemitter.h10
-rw-r--r--src/resources/beingcommon.cpp22
-rw-r--r--src/resources/beingcommon.h14
-rw-r--r--src/resources/db/avatardb.cpp31
-rw-r--r--src/resources/db/avatardb.h6
-rw-r--r--src/resources/db/chardb.cpp2
-rw-r--r--src/resources/db/colordb.cpp93
-rw-r--r--src/resources/db/colordb.h5
-rw-r--r--src/resources/db/deaddb.cpp25
-rw-r--r--src/resources/db/deaddb.h2
-rw-r--r--src/resources/db/emotedb.cpp65
-rw-r--r--src/resources/db/emotedb.h4
-rw-r--r--src/resources/db/itemdb.cpp47
-rw-r--r--src/resources/db/mapdb.cpp33
-rw-r--r--src/resources/db/mapdb.h2
-rw-r--r--src/resources/db/moddb.cpp116
-rw-r--r--src/resources/db/moddb.h47
-rw-r--r--src/resources/db/monsterdb.cpp20
-rw-r--r--src/resources/db/monsterdb.h2
-rw-r--r--src/resources/db/npcdb.cpp15
-rw-r--r--src/resources/db/petdb.cpp37
-rw-r--r--src/resources/db/petdb.h6
-rw-r--r--src/resources/db/sounddb.cpp21
-rw-r--r--src/resources/db/sounddb.h4
-rw-r--r--src/resources/mapreader.cpp20
-rw-r--r--src/resources/mapreader.h13
-rw-r--r--src/resources/modinfo.cpp34
-rw-r--r--src/resources/modinfo.h71
-rw-r--r--src/resources/resourcemanager.cpp30
-rw-r--r--src/resources/resourcemanager.h2
-rw-r--r--src/resources/spritedef.cpp5
-rw-r--r--src/resources/spritedef.h9
-rw-r--r--src/statuseffect.cpp35
-rw-r--r--src/statuseffect.h2
-rw-r--r--src/touchactions.cpp3
-rw-r--r--src/units.cpp22
-rw-r--r--src/units.h2
-rw-r--r--src/utils/base64.cpp29
-rw-r--r--src/utils/base64.h14
-rw-r--r--src/utils/files.cpp35
-rw-r--r--src/utils/files.h9
-rw-r--r--src/utils/paths.cpp4
-rw-r--r--src/utils/paths.h2
-rw-r--r--src/utils/xml.cpp23
-rw-r--r--src/utils/xml.h23
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;
/**