GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/db/questdb.cpp Lines: 33 131 25.2 %
Date: 2021-03-17 Branches: 21 222 9.5 %

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

5
    loadXmlFile(paths.getStringValue("questsFile"), SkipError_false);
52

5
    loadXmlFile(paths.getStringValue("questsPatchFile"), SkipError_true);
53



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

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

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

3
}