File: DifficultySettingsManager.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (286 lines) | stat: -rw-r--r-- 8,920 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#include "DifficultySettingsManager.h"

#include "ieclass.h"
#include "itextstream.h"
#include "entitylib.h"
#include "gamelib.h"
#include "maplib.h"
#include "string/string.h"
#include "registry/registry.h"
#include "DifficultyEntity.h"
#include "DifficultyEntityFinder.h"

namespace difficulty {

DifficultySettingsPtr DifficultySettingsManager::getSettings(int level) {
    for (std::size_t i = 0; i < _settings.size(); i++) {
        if (_settings[i]->getLevel() == level) {
            return _settings[i];
        }
    }
    return DifficultySettingsPtr();
}

void DifficultySettingsManager::loadSettings() {
    loadDefaultSettings();
    loadMapSettings();
    loadDifficultyNames();
}

void DifficultySettingsManager::loadDefaultSettings() {
    // Try to lookup the given entityDef
    IEntityClassPtr eclass = GlobalEntityClassManager().findClass(
        game::current::getValue<std::string>(GKEY_DIFFICULTY_ENTITYDEF_DEFAULT)
    );

    if (eclass == NULL) {
        rError() << "Could not find default difficulty settings entityDef.\n";
        return;
    }

    // greebo: Setup the default difficulty levels using the found entityDef
    int numLevels = game::current::getValue<int>(GKEY_DIFFICULTY_LEVELS);
    for (int i = 0; i < numLevels; i++) {
        // Allocate a new settings object
        DifficultySettingsPtr settings(new DifficultySettings(i));

        // Parse the settings from the given default settings entityDef
        settings->parseFromEntityDef(eclass);

        // Store the settings object in the local list
        _settings.push_back(settings);
    }
}

void DifficultySettingsManager::loadMapSettings()
{
    // Construct a helper walker
    DifficultyEntityFinder finder;
    GlobalSceneGraph().root()->traverse(finder);

    const DifficultyEntityFinder::EntityList& found = finder.getEntities();

    // Pop all entities into each difficulty setting
    for (DifficultyEntityFinder::EntityList::const_iterator ent = found.begin();
         ent != found.end(); ent++)
    {
        for (std::size_t i = 0; i < _settings.size(); i++) {
            _settings[i]->parseFromMapEntity(*ent);
        }
    }
}

namespace
{

// Class to manage mod-specific difficulty names (which are probably string
// table entries like "#str_000300") and their English translations (we
// maintain these translations separately in the .game file since we don't have
// any support for parsing the mod-specific translation data).
class ModDifficultyNames
{
    // Lookup table of strings and their translations
    std::map<std::string, std::string> _transStrings;

    // Mod-specific entity giving default difficulty names
    IEntityClassPtr _menuEclass;

public:

    // Construct and initialise
    ModDifficultyNames()
    : _menuEclass(
        GlobalEntityClassManager().findClass(
            game::current::getValue<std::string>(GKEY_DIFFICULTY_ENTITYDEF_MENU)
        )
    )
    {
        // Load translations from xml nodes in the .game file
        xml::NodeList transNodes = game::current::getNodes(
            GKEY_DIFFICULTY_ENTITYDEF_MENU + "/string"
        );
        for (auto xmlNode: transNodes)
        {
            _transStrings[xmlNode.getAttributeValue("id")] = xmlNode.getContent();
        }
    }

    // Return the translated name for the difficulty level identified by the
    // given key (e.g. the translated name for "diff0Default" might be "Easy").
    std::string getNameForKey(const std::string& nameKey) const
    {
        if (_menuEclass)
        {
            std::string rawName = _menuEclass->getAttributeValue(nameKey);
            if (!rawName.empty())
            {
                // Look for a translation, otherwise use the raw name
                auto found = _transStrings.find(rawName);
                if (found != _transStrings.end())
                    return found->second;
                else
                    return rawName;
            }
        }

        return "";
    }
};

// Return key for a difficulty level name
std::string diffNameKeyForLevel(int level)
{
    return "diff" + std::to_string(level) + "default";
}

}

void DifficultySettingsManager::loadDifficultyNames()
{
    // Load mod-specific difficulty names first if possible
    ModDifficultyNames diffNames;

    // Look up a name for each difficulty level, starting with a map-specific
    // name set on the worldspawn, then falling back to mod default names and
    // then autogenerated names.
    int numLevels = game::current::getValue<int>(GKEY_DIFFICULTY_LEVELS);

    for (int i = 0; i < numLevels; i++)
    {
        std::string nameKey = diffNameKeyForLevel(i);

        // Load the mod default, get the translated value by the helper class
        std::string defaultName = diffNames.getNameForKey(nameKey);

        if (!defaultName.empty())
        {
            // Use the (hopefully translated) mod-specific default name
            _difficultyNames.push_back(defaultName);
        }
        else
        {
            // Fall back to a non-empty default
            _difficultyNames.push_back(std::to_string(i));
        }
    }

    // Store the current set as default for later change detection
    _defaultDifficultyNames = _difficultyNames;

    // Locate the worldspawn entity to find any map-specific names
    Entity* worldspawn = map::current::getWorldspawn();

    if (worldspawn != nullptr)
    {
        assert(static_cast<int>(_difficultyNames.size()) == numLevels);

        for (int i = 0; i < numLevels; i++)
        {
            std::string nameKey = diffNameKeyForLevel(i);

            // First, try to find a map-specific name
            std::string name = worldspawn->getKeyValue(nameKey);

            if (!name.empty())
            {
                // Found a setting on worldspawn, take it
                _difficultyNames[i] = name;
            }
        }
    }
}

void DifficultySettingsManager::saveSettings()
{
    // Locates all difficulty entities
    DifficultyEntityFinder finder;
    GlobalSceneGraph().root()->traverse(finder);

    // Copy the list from the finder to a local list
    DifficultyEntityFinder::EntityList entities = finder.getEntities();

    if (entities.empty())
    {
        // Create a new difficulty entity
        std::string eclassName = game::current::getValue<std::string>(GKEY_DIFFICULTY_ENTITYDEF_MAP);
        IEntityClassPtr diffEclass = GlobalEntityClassManager().findClass(eclassName);

        if (diffEclass == NULL) {
            rError() << "[Diff]: Cannot create difficulty entity!\n";
            return;
        }

        // Create and insert a new entity node into the scenegraph root
        IEntityNodePtr entNode = GlobalEntityModule().createEntity(diffEclass);
        GlobalSceneGraph().root()->addChildNode(entNode);

        // Add the entity to the list
        entities.push_back(&entNode->getEntity());
    }

    // Clear all difficulty-spawnargs from existing entities
    for (DifficultyEntityFinder::EntityList::const_iterator i = entities.begin();
         i != entities.end(); i++)
    {
        // Construct a difficulty entity using the raw Entity* pointer
        DifficultyEntity diffEnt(*i);
        // Clear the difficulty-related spawnargs from the entity
        diffEnt.clear();
    }

    // Take the first entity
    DifficultyEntity diffEnt(*entities.begin());

    // Cycle through all settings objects and issue save call
    for (std::size_t i = 0; i < _settings.size(); i++) {
        _settings[i]->saveToEntity(diffEnt);
    }

    // Write the changed difficulty (and only those) names into the worldspawn
    Entity* worldspawn = map::current::getWorldspawn(true);

    if (!worldspawn)
    {
        throw std::logic_error("DifficultySettingsManager::saveSettings():"
            " could not find or create worldspawn"
        );
    }

    for (std::size_t i = 0; i < _difficultyNames.size(); ++i)
    {
        // Compare each value to the default set
        if (_defaultDifficultyNames.size() > i && _defaultDifficultyNames[i] != _difficultyNames[i])
        {
            worldspawn->setKeyValue(diffNameKeyForLevel(i), _difficultyNames[i]);
        }
        else
        {
            // Remove the corresponding spawnarg from the worldspawn if there's no override
            worldspawn->setKeyValue(diffNameKeyForLevel(i), "");
        }
    }
}

std::string DifficultySettingsManager::getDifficultyName(int level)
{
    if (level < 0 || level >= static_cast<int>(_difficultyNames.size()))
    {
        return "";
    }

    return _difficultyNames[level];
}

void DifficultySettingsManager::setDifficultyName(int level, const std::string& name)
{
    if (level < 0 || level >= static_cast<int>(_difficultyNames.size()))
    {
        throw std::logic_error(
            "Invalid difficulty level (" + std::to_string(level) + ")"
        );
    }

    _difficultyNames[level] = name;
}

} // namespace difficulty