GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/db/npcdb.cpp Lines: 8 84 9.5 %
Date: 2017-11-29 Branches: 0 190 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2008-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "resources/db/npcdb.h"
24
25
#include "configuration.h"
26
27
#include "resources/beingcommon.h"
28
#include "resources/beinginfo.h"
29
30
#include "resources/db/unitsdb.h"
31
32
#include "resources/sprite/spritereference.h"
33
34
#include "utils/cast.h"
35
#include "utils/checkutils.h"
36
#include "utils/dtor.h"
37
#include "utils/gettext.h"
38
39
#include "debug.h"
40
41
namespace
42
{
43
2
    BeingInfos mNPCInfos;
44
    bool mLoaded = false;
45
}  // namespace
46
47
void NPCDB::load()
48
{
49
    if (mLoaded)
50
        unload();
51
52
    logger->log1("Initializing NPC database...");
53
54
    loadXmlFile(paths.getStringValue("npcsFile"), SkipError_false);
55
    loadXmlFile(paths.getStringValue("npcsPatchFile"), SkipError_true);
56
    loadXmlDir("npcsPatchDir", loadXmlFile);
57
58
    mLoaded = true;
59
}
60
61
void NPCDB::loadXmlFile(const std::string &fileName,
62
                        const SkipError skipError)
63
{
64
    XML::Document doc(fileName, UseVirtFs_true, skipError);
65
    XmlNodeConstPtrConst rootNode = doc.rootNode();
66
67
    if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "npcs"))
68
    {
69
        logger->log("NPC Database: Error while loading %s!",
70
            paths.getStringValue("npcsFile").c_str());
71
        mLoaded = true;
72
        return;
73
    }
74
75
    // iterate <npc>s
76
    for_each_xml_child_node(npcNode, rootNode)
77
    {
78
        if (xmlNameEqual(npcNode, "include"))
79
        {
80
            const std::string name = XML::getProperty(npcNode, "name", "");
81
            if (!name.empty())
82
                loadXmlFile(name, skipError);
83
            continue;
84
        }
85
86
        if (!xmlNameEqual(npcNode, "npc"))
87
            continue;
88
89
        const BeingTypeId id = fromInt(XML::getProperty(
90
            npcNode, "id", 0), BeingTypeId);
91
        BeingInfo *currentInfo = nullptr;
92
        if (id == BeingTypeId_zero)
93
        {
94
            reportAlways("NPC Database: NPC with missing ID in %s!",
95
                paths.getStringValue("npcsFile").c_str());
96
            continue;
97
        }
98
        else if (mNPCInfos.find(id) != mNPCInfos.end())
99
        {
100
            logger->log("NpcDB: Redefinition of npc ID %d", toInt(id, int));
101
            currentInfo = mNPCInfos[id];
102
        }
103
        if (currentInfo == nullptr)
104
            currentInfo = new BeingInfo;
105
106
        currentInfo->setTargetSelection(XML::getBoolProperty(npcNode,
107
            "targetSelection", true));
108
109
        BeingCommon::readBasicAttributes(currentInfo, npcNode, "talk");
110
        BeingCommon::readWalkingAttributes(currentInfo, npcNode, 0);
111
112
        currentInfo->setDeadSortOffsetY(XML::getProperty(npcNode,
113
            "deadSortOffsetY", 31));
114
115
        currentInfo->setAvatarId(fromInt(XML::getProperty(
116
            npcNode, "avatar", 0), BeingTypeId));
117
118
        currentInfo->setAllowDelete(XML::getBoolProperty(npcNode,
119
            "allowDelete", true));
120
121
        currentInfo->setAllowEquipment(XML::getBoolProperty(npcNode,
122
            "allowEquipment", false));
123
124
        const std::string currency = XML::getProperty(npcNode,
125
            "currency", "default");
126
        if (UnitsDb::existsCurrency(currency) == false)
127
        {
128
            reportAlways("Not found currency '%s' for npc %d",
129
                currency.c_str(),
130
                CAST_S32(id));
131
        }
132
        currentInfo->setCurrency(currency);
133
134
        SpriteDisplay display;
135
        for_each_xml_child_node(spriteNode, npcNode)
136
        {
137
            if (xmlNameEqual(spriteNode, "sprite"))
138
            {
139
                if (!XmlHaveChildContent(spriteNode))
140
                    continue;
141
142
                SpriteReference *const currentSprite = new SpriteReference;
143
                currentSprite->sprite = XmlChildContent(spriteNode);
144
                currentSprite->variant =
145
                    XML::getProperty(spriteNode, "variant", 0);
146
                display.sprites.push_back(currentSprite);
147
            }
148
            else if (xmlNameEqual(spriteNode, "particlefx"))
149
            {
150
                if (!XmlHaveChildContent(spriteNode))
151
                    continue;
152
153
                display.particles.push_back(XmlChildContent(spriteNode));
154
            }
155
            else if (xmlNameEqual(spriteNode, "menu"))
156
            {
157
                std::string name = XML::langProperty(spriteNode, "name", "");
158
                std::string command = XML::getProperty(spriteNode,
159
                    "command", "");
160
                currentInfo->addMenu(name, command);
161
            }
162
        }
163
164
        currentInfo->setDisplay(display);
165
        if (currentInfo->getMenu().empty())
166
        {
167
            // TRANSLATORS: npc context menu item
168
            currentInfo->addMenu(_("Talk"), "talk 'NAME'");
169
            // TRANSLATORS: npc context menu item
170
            currentInfo->addMenu(_("Buy"), "buy 'NAME'");
171
            // TRANSLATORS: npc context menu item
172
            currentInfo->addMenu(_("Sell"), "sell 'NAME'");
173
        }
174
        mNPCInfos[id] = currentInfo;
175
    }
176
}
177
178
384
void NPCDB::unload()
179
{
180
384
    logger->log1("Unloading NPC database...");
181
384
    delete_all(mNPCInfos);
182
384
    mNPCInfos.clear();
183
184
384
    mLoaded = false;
185
384
}
186
187
BeingInfo *NPCDB::get(const BeingTypeId id)
188
{
189
    const BeingInfoIterator i = mNPCInfos.find(id);
190
191
    if (i == mNPCInfos.end())
192
    {
193
        reportAlways("NPCDB: Warning, unknown NPC ID %d requested",
194
            toInt(id, int));
195
        return BeingInfo::unknown;
196
    }
197
    return i->second;
198
}
199
200
BeingTypeId NPCDB::getAvatarFor(const BeingTypeId id)
201
{
202
    const BeingInfo *const info = get(id);
203
    if (info == nullptr)
204
        return BeingTypeId_zero;
205
    return info->getAvatarId();
206
4
}