GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/db/npcdialogdb.cpp Lines: 8 103 7.8 %
Date: 2021-03-17 Branches: 1 164 0.6 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2011-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/npcdialogdb.h"
23
24
#include "configuration.h"
25
26
#include "resources/beingcommon.h"
27
#include "resources/npcdialoginfo.h"
28
29
#include "utils/checkutils.h"
30
#include "utils/dtor.h"
31
32
#include "debug.h"
33
34
namespace
35
{
36
    bool mLoaded = false;
37
1
    NpcDialogDB::Dialogs mDialogs;
38
}  // namespace
39
40
void NpcDialogDB::load()
41
{
42
    if (mLoaded)
43
        unload();
44
45
    logger->log1("Loading npc dialog database...");
46
    loadXmlFile(paths.getStringValue("npcDialogsFile"), SkipError_false);
47
    loadXmlFile(paths.getStringValue("npcDialogsPatchFile"), SkipError_true);
48
    loadXmlDir("npcDialogsPatchDir", loadXmlFile)
49
50
    mLoaded = true;
51
}
52
53
static void loadNpcDialogMenu(NpcDialogInfo *const dialog,
54
                              XmlNodeConstPtrConst node)
55
{
56
    for_each_xml_child_node(childNode, node)
57
    {
58
        if (!xmlTypeEqual(childNode, XML_ELEMENT_NODE))
59
            continue;
60
61
        if (xmlNameEqual(childNode, "button"))
62
        {
63
            const std::string name = XML::getProperty(childNode, "name", "");
64
            const std::string value = XML::getProperty(childNode, "value", "");
65
            if (value.empty())
66
                continue;
67
68
            NpcButtonInfo *const button = new NpcButtonInfo;
69
            button->x = XML::getIntProperty(
70
                childNode, "x", 0, 0, 10000);
71
            button->y = XML::getIntProperty(
72
                childNode, "y", 0, 0, 10000);
73
            button->name = name;
74
            button->value = value;
75
            button->image = XML::getProperty(childNode, "image", "");
76
            if (button->name.empty() && button->image.empty())
77
            {
78
                reportAlways("Error: npc button without name or image")
79
                delete button;
80
                continue;
81
            }
82
            button->imageWidth = XML::getIntProperty(
83
                childNode, "imageWidth", 16, 1, 1000);
84
            button->imageHeight = XML::getIntProperty(
85
                childNode, "imageHeight", 16, 1, 1000);
86
            dialog->menu.buttons.push_back(button);
87
        }
88
        else if (xmlNameEqual(childNode, "image"))
89
        {
90
            const std::string image = XML::getProperty(childNode, "image", "");
91
            if (image.empty())
92
            {
93
                reportAlways("Error: no image attribute found in image tag.")
94
                continue;
95
            }
96
            NpcImageInfo *const imageInfo = new NpcImageInfo;
97
            imageInfo->name = image;
98
            imageInfo->x = XML::getIntProperty(
99
                childNode, "x", 0, 0, 10000);
100
            imageInfo->y = XML::getIntProperty(
101
                childNode, "y", 0, 0, 10000);
102
            dialog->menu.images.push_back(imageInfo);
103
        }
104
        else if (xmlNameEqual(childNode, "text"))
105
        {
106
            const std::string text = XML::getProperty(childNode, "text", "");
107
            if (text.empty())
108
            {
109
                reportAlways("Error: no text attribute found in text tag.")
110
                continue;
111
            }
112
            NpcTextInfo *const textInfo = new NpcTextInfo;
113
            textInfo->text = text;
114
            textInfo->x = XML::getIntProperty(
115
                childNode, "x", 0, 0, 10000);
116
            textInfo->y = XML::getIntProperty(
117
                childNode, "y", 0, 0, 10000);
118
            textInfo->width = XML::getIntProperty(
119
                childNode, "width", 20, 10, 10000);
120
            textInfo->height = XML::getIntProperty(
121
                childNode, "height", 20, 10, 10000);
122
            dialog->menu.texts.push_back(textInfo);
123
        }
124
    }
125
}
126
127
static void loadNpcDialogInventory(NpcDialogInfo *const dialog,
128
                                   XmlNodePtrConst node)
129
{
130
    dialog->inventory.cell = XML::getProperty(node, "cell", "");
131
    dialog->inventory.columns = XML::getIntProperty(
132
        node, "columns", 10000, 1, 10000);
133
}
134
135
static void loadNpcDialog(NpcDialogInfo *const dialog,
136
                          XmlNodeConstPtrConst node)
137
{
138
    for_each_xml_child_node(childNode, node)
139
    {
140
        if (xmlNameEqual(childNode, "menu"))
141
        {
142
            loadNpcDialogMenu(dialog, childNode);
143
        }
144
        else if (xmlNameEqual(childNode, "inventory"))
145
        {
146
            loadNpcDialogInventory(dialog, childNode);
147
        }
148
    }
149
}
150
151
void NpcDialogDB::loadXmlFile(const std::string &fileName,
152
                              const SkipError skipError)
153
{
154
    XML::Document *const doc = new XML::Document(fileName,
155
        UseVirtFs_true,
156
        skipError);
157
158
    XmlNodeConstPtrConst root = doc->rootNode();
159
    if (root == nullptr)
160
    {
161
        delete doc;
162
        return;
163
    }
164
165
    for_each_xml_child_node(node, root)
166
    {
167
        if (xmlNameEqual(node, "dialog"))
168
        {
169
            const std::string name = XML::getProperty(node, "name", "");
170
            if (name.empty())
171
                continue;
172
173
            deleteDialog(name);
174
            NpcDialogInfo *const dialog = new NpcDialogInfo;
175
            dialog->name = name;
176
            dialog->hideText = XML::getBoolProperty(
177
                node, "hideText", false);
178
            mDialogs[name] = dialog;
179
            loadNpcDialog(dialog, node);
180
        }
181
        else if (xmlNameEqual(node, "include"))
182
        {
183
            const std::string name = XML::getProperty(node, "name", "");
184
            if (!name.empty())
185
                loadXmlFile(name, skipError);
186
            continue;
187
        }
188
    }
189
190
    delete doc;
191
}
192
193
void NpcDialogDB::deleteDialog(const std::string &name)
194
{
195
    const DialogsIter it = mDialogs.find(name);
196
    if (it == mDialogs.end())
197
        return;
198
199
    NpcDialogInfo *dialog = (*it).second;
200
    delete_all(dialog->menu.buttons);
201
    delete_all(dialog->menu.images);
202
    delete_all(dialog->menu.texts);
203
    mDialogs.erase(it);
204
    delete dialog;
205
}
206
207
215
void NpcDialogDB::unload()
208
{
209
215
    logger->log1("Unloading npc dialog database...");
210
211
430
    FOR_EACH (DialogsIter, it, mDialogs)
212
    {
213
        NpcDialogInfo *dialog = (*it).second;
214
        delete_all(dialog->menu.buttons);
215
        delete_all(dialog->menu.images);
216
        delete_all(dialog->menu.texts);
217
        delete dialog;
218
    }
219
215
    mDialogs.clear();
220
221
215
    mLoaded = false;
222
215
}
223
224
NpcDialogInfo *NpcDialogDB::getDialog(const std::string &name)
225
{
226
    const DialogsIter it = mDialogs.find(name);
227
    if (it == mDialogs.end())
228
        return nullptr;
229
    return (*it).second;
230
2
}