GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/db/questdb.cpp Lines: 32 130 24.6 %
Date: 2017-11-29 Branches: 23 224 10.3 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2013-2017  The ManaPlus Developers
4
 *
5
 *  This file is part of The ManaPlus Client.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "resources/db/questdb.h"
22
23
#include "configuration.h"
24
#include "logger.h"
25
26
#include "utils/dtor.h"
27
#include "utils/gettext.h"
28
29
#include "utils/translation/podict.h"
30
31
#include "resources/beingcommon.h"
32
#include "resources/questeffect.h"
33
#include "resources/questitem.h"
34
35
#include "debug.h"
36
37
namespace
38
{
39
    // quest variables: var, (val1, val2, val3, time)
40
2
    NpcQuestVarMap mVars;
41
    // quests: var, quests
42
2
    std::map<int, STD_VECTOR<QuestItem*> > mQuests;
43
2
    STD_VECTOR<QuestEffect*> mAllEffects;
44
}  // namespace
45
46
2
void QuestDb::load()
47
{
48
2
    unload();
49
2
    logger->log1("Initializing quest database...");
50

10
    loadXmlFile(paths.getStringValue("questsFile"), SkipError_false);
51

10
    loadXmlFile(paths.getStringValue("questsPatchFile"), SkipError_true);
52



28
    loadXmlDir("questsPatchDir", loadXmlFile);
53
2
}
54
55
static void loadQuest(const int var,
56
                      XmlNodeConstPtr node)
57
{
58
    if (node == nullptr)
59
        return;
60
    QuestItem *const quest = new QuestItem;
61
    // TRANSLATORS: quests window quest name
62
    quest->name = XML::langProperty(node, "name", _("unknown"));
63
    quest->group = XML::getProperty(node, "group", "");
64
    std::string incompleteStr = XML::getProperty(node, "incomplete", "");
65
    std::string completeStr = XML::getProperty(node, "complete", "");
66
    if (incompleteStr.empty() && completeStr.empty())
67
    {
68
        logger->log("complete flags incorrect");
69
        delete quest;
70
        return;
71
    }
72
    splitToIntSet(quest->incomplete, incompleteStr, ',');
73
    splitToIntSet(quest->complete, completeStr, ',');
74
    if (quest->incomplete.empty() && quest->complete.empty())
75
    {
76
        logger->log("complete flags incorrect");
77
        delete quest;
78
        return;
79
    }
80
    if (quest->incomplete.empty() || quest->complete.empty())
81
        quest->broken = true;
82
83
    for_each_xml_child_node(dataNode, node)
84
    {
85
        if (!xmlTypeEqual(dataNode, XML_ELEMENT_NODE))
86
            continue;
87
        XmlChar *const data = reinterpret_cast<XmlChar*>(
88
            XmlNodeGetContent(dataNode));
89
        if (data == nullptr)
90
            continue;
91
        std::string str = translator->getStr(data);
92
        XmlFree(data);
93
94
        for (int f = 1; f < 100; f ++)
95
        {
96
            const std::string key = strprintf("text%d", f);
97
            const std::string val = XML::getProperty(dataNode,
98
                key.c_str(),
99
                "");
100
            if (val.empty())
101
                break;
102
            const std::string param = strprintf("{@@%d}", f);
103
            replaceAll(str, param, val);
104
        }
105
        replaceItemLinks(str);
106
        if (xmlNameEqual(dataNode, "text"))
107
        {
108
            quest->texts.push_back(QuestItemText(str,
109
                QuestType::TEXT,
110
                std::string(),
111
                std::string()));
112
        }
113
        else if (xmlNameEqual(dataNode, "name"))
114
        {
115
            quest->texts.push_back(QuestItemText(str,
116
                QuestType::NAME,
117
                std::string(),
118
                std::string()));
119
        }
120
        else if (xmlNameEqual(dataNode, "reward"))
121
        {
122
            quest->texts.push_back(QuestItemText(str,
123
                QuestType::REWARD,
124
                std::string(),
125
                std::string()));
126
        }
127
        else if (xmlNameEqual(dataNode, "questgiver") ||
128
                 xmlNameEqual(dataNode, "giver"))
129
        {
130
            quest->texts.push_back(QuestItemText(str,
131
                QuestType::GIVER,
132
                std::string(),
133
                std::string()));
134
        }
135
        else if (xmlNameEqual(dataNode, "coordinates"))
136
        {
137
            const std::string str1 = toString(XML::getIntProperty(
138
                dataNode, "x", 0, 1, 1000));
139
            const std::string str2 = toString(XML::getIntProperty(
140
                dataNode, "y", 0, 1, 1000));
141
            quest->texts.push_back(QuestItemText(str,
142
                QuestType::COORDINATES,
143
                str1,
144
                str2));
145
        }
146
        else if (xmlNameEqual(dataNode, "npc"))
147
        {
148
            quest->texts.push_back(QuestItemText(str,
149
                QuestType::NPC,
150
                std::string(),
151
                std::string()));
152
        }
153
    }
154
    quest->var = var;
155
    mQuests[var].push_back(quest);
156
}
157
158
static void loadEffect(const int var,
159
                       XmlNodeConstPtr node)
160
{
161
    QuestEffect *const effect = new QuestEffect;
162
    effect->map = XML::getProperty(node, "map", "");
163
    effect->id = fromInt(XML::getProperty(node, "npc", -1), BeingTypeId);
164
    effect->effectId = XML::getProperty(node, "effect", -1);
165
    const std::string values = XML::getProperty(node, "value", "");
166
    splitToIntSet(effect->values, values, ',');
167
168
    if (effect->map.empty() || effect->id == BeingTypeId_negOne
169
        || effect->effectId == -1 || values.empty())
170
    {
171
        delete effect;
172
        return;
173
    }
174
    effect->var = var;
175
    mAllEffects.push_back(effect);
176
}
177
178
4
void QuestDb::loadXmlFile(const std::string &fileName,
179
                          const SkipError skipError)
180
{
181
    XML::Document doc(fileName,
182
        UseVirtFs_true,
183
6
        skipError);
184
4
    XmlNodeConstPtrConst root = doc.rootNode();
185
4
    if (root == nullptr)
186
2
        return;
187
188
4
    for_each_xml_child_node(varNode, root)
189
    {
190

2
        if (xmlNameEqual(varNode, "include"))
191
        {
192
            const std::string name = XML::getProperty(varNode, "name", "");
193
            if (!name.empty())
194
                loadXmlFile(name, skipError);
195
            continue;
196
        }
197

2
        else if (xmlNameEqual(varNode, "var"))
198
        {
199
            const int id = XML::getProperty(varNode, "id", 0);
200
            if (id < 0)
201
                continue;
202
            mVars[id] = QuestVar();
203
            for_each_xml_child_node(questNode, varNode)
204
            {
205
                if (xmlNameEqual(questNode, "quest"))
206
                    loadQuest(id, questNode);
207
                else if (xmlNameEqual(questNode, "effect"))
208
                    loadEffect(id, questNode);
209
            }
210
        }
211
    }
212
}
213
214
215
4
void QuestDb::unload()
216
{
217
4
    logger->log1("Unloading quest database...");
218
    for (std::map<int, STD_VECTOR<QuestItem*> >::iterator it
219
8
         = mQuests.begin(), it_end = mQuests.end(); it != it_end; ++ it)
220
    {
221
        STD_VECTOR<QuestItem*> &quests = (*it).second;
222
        for (STD_VECTOR<QuestItem*>::iterator it2 = quests.begin(),
223
             it2_end = quests.end(); it2 != it2_end; ++ it2)
224
        {
225
            delete *it2;
226
        }
227
    }
228
4
    delete_all(mAllEffects);
229
4
    mAllEffects.clear();
230
4
    mQuests.clear();
231
4
}
232
233
2
NpcQuestVarMap *QuestDb::getVars()
234
{
235
2
    return &mVars;
236
}
237
238
2
std::map<int, STD_VECTOR<QuestItem*> > *QuestDb::getQuests()
239
{
240
2
    return &mQuests;
241
}
242
243
2
STD_VECTOR<QuestEffect*> *QuestDb::getAllEffects()
244
{
245
2
    return &mAllEffects;
246
}
247
248
std::string QuestDb::getName(const int id)
249
{
250
    std::map<int, STD_VECTOR<QuestItem*> >::const_iterator it =
251
        mQuests.find(id);
252
    if (it == mQuests.end())
253
    {
254
        // TRANSLATORS: quests window quest name
255
        return _("unknown");
256
    }
257
    const STD_VECTOR<QuestItem*> &items = (*it).second;
258
    if (items.empty())
259
    {
260
        // TRANSLATORS: quests window quest name
261
        return _("unknown");
262
    }
263
    return items[0]->name;
264

6
}