GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/spellmanager.cpp Lines: 1 181 0.6 %
Date: 2021-03-17 Branches: 0 298 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2009  The Mana World Development Team
4
 *  Copyright (C) 2011-2019  The ManaPlus Developers
5
 *  Copyright (C) 2009-2021  Andrei Karas
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 "spellmanager.h"
24
25
#include "configuration.h"
26
#include "textcommand.h"
27
28
#include "being/localplayer.h"
29
#ifdef TMWA_SUPPORT
30
#include "being/playerinfo.h"
31
#endif  // TMWA_SUPPORT
32
33
#include "const/spells.h"
34
35
#include "gui/windows/chatwindow.h"
36
37
#include "net/playerhandler.h"
38
39
#include "resources/db/commandsdb.h"
40
41
#include "utils/dtor.h"
42
#include "utils/foreach.h"
43
44
#include <sstream>
45
46
#include "debug.h"
47
48
SpellManager *spellManager = nullptr;
49
50
SpellManager::SpellManager() :
51
    mSpells(),
52
    mSpellsVector()
53
{
54
    load();
55
}
56
57
SpellManager::~SpellManager()
58
{
59
    delete_all(mSpells);
60
    mSpells.clear();
61
    mSpellsVector.clear();
62
}
63
64
TextCommand* SpellManager::getSpell(const int spellId) const
65
{
66
    if (spellId < 0 || CAST_SIZE(spellId) >= mSpells.size())
67
        return nullptr;
68
69
    const std::map<unsigned int, TextCommand*>::const_iterator
70
        it = mSpells.find(spellId);
71
72
    return it != mSpells.end() ? (*it).second : nullptr;
73
}
74
75
const TextCommand* SpellManager::getSpellByItem(const int itemId) const
76
{
77
    return getSpell(itemId - SPELL_MIN_ID);
78
}
79
80
void SpellManager::fillSpells()
81
{
82
    CommandsDB::load();
83
84
    CommandsMap &commands = CommandsDB::getAll();
85
    FOR_EACH (CommandsMapIter, it, commands)
86
        addSpell((*it).second);
87
88
    for (unsigned f = 0; f < SPELL_SHORTCUT_ITEMS * SPELL_SHORTCUT_TABS; f++)
89
    {
90
        const std::map<unsigned int, TextCommand*>::const_iterator
91
            it = mSpells.find(f);
92
        if (it == mSpells.end())
93
            addSpell(new TextCommand(f));
94
    }
95
    CommandsDB::unload();
96
}
97
98
bool SpellManager::addSpell(TextCommand *const spell)
99
{
100
    if (spell == nullptr)
101
        return false;
102
103
    const int id = spell->getId();
104
    if (id < 0 || id >= CAST_S32(SPELL_SHORTCUT_ITEMS
105
        * SPELL_SHORTCUT_TABS))
106
    {
107
        delete spell;
108
        return false;
109
    }
110
    const std::map<unsigned int, TextCommand*>::const_iterator
111
        i = mSpells.find(spell->getId());
112
    if (i == mSpells.end())
113
    {
114
        mSpells[spell->getId()] = spell;
115
        mSpellsVector.push_back(spell);
116
        return true;
117
    }
118
    return false;
119
}
120
121
const STD_VECTOR<TextCommand*> &SpellManager::getAll() const
122
{
123
    return mSpellsVector;
124
}
125
126
void SpellManager::useItem(const int itemId) const
127
{
128
    invoke(itemId - SPELL_MIN_ID);
129
}
130
131
void SpellManager::invoke(const int spellId) const
132
{
133
    if (localPlayer == nullptr)
134
        return;
135
136
    const TextCommand *const spell = getSpell(spellId);
137
    if (spell == nullptr)
138
        return;
139
140
    if ((playerHandler == nullptr) || spell->getCommand().empty())
141
        return;
142
143
#ifdef TMWA_SUPPORT
144
    if (spell->getCommandType() == TextCommandType::Text ||
145
        (playerHandler->canUseMagic() &&
146
        PlayerInfo::getSkillLevel(CAST_S32(MagicSchool::SkillMagic))
147
        >= CAST_S32(spell->getBaseLvl()) &&
148
        PlayerInfo::getSkillLevel(CAST_S32(
149
        spell->getSchool())) >= CAST_S32(spell->getSchoolLvl())
150
        && PlayerInfo::getAttribute(Attributes::PLAYER_MP)
151
        >= CAST_S32(spell->getMana()))
152
        )
153
#endif  // TMWA_SUPPORT
154
    {
155
        const Being *const target = localPlayer->getTarget();
156
        if (spell->getTargetType() == CommandTarget::NoTarget)
157
        {
158
            invokeSpell(spell);
159
        }
160
#ifdef TMWA_SUPPORT
161
        if ((target != nullptr &&
162
            (target->getType() != ActorType::Monster ||
163
            spell->getCommandType() == TextCommandType::Text)) &&
164
            (spell->getTargetType() == CommandTarget::AllowTarget ||
165
            spell->getTargetType() == CommandTarget::NeedTarget))
166
#else  // TMWA_SUPPORT
167
168
        if (target != nullptr &&
169
            (spell->getTargetType() == CommandTarget::AllowTarget ||
170
            spell->getTargetType() == CommandTarget::NeedTarget))
171
#endif  // TMWA_SUPPORT
172
        {
173
            invokeSpell(spell, target);
174
        }
175
        else if (spell->getTargetType() == CommandTarget::AllowTarget)
176
        {
177
            invokeSpell(spell);
178
        }
179
    }
180
}
181
182
void SpellManager::invokeSpell(const TextCommand *const spell)
183
{
184
    if ((chatWindow == nullptr) || (spell == nullptr))
185
        return;
186
    chatWindow->localChatInput(parseCommand(spell->getCommand(), nullptr));
187
}
188
189
void SpellManager::invokeSpell(const TextCommand *const spell,
190
                               const Being *const target)
191
{
192
    if (chatWindow == nullptr ||
193
        spell == nullptr ||
194
        target == nullptr)
195
    {
196
        return;
197
    }
198
    chatWindow->localChatInput(parseCommand(spell->getCommand(), target));
199
}
200
201
void SpellManager::invokeCommand(const std::string &command,
202
                                 const Being *const target)
203
{
204
    if (chatWindow == nullptr)
205
        return;
206
    chatWindow->localChatInput(parseCommand(command, target));
207
}
208
209
std::string SpellManager::parseCommand(std::string command,
210
                                       const Being *const target)
211
{
212
    if (localPlayer == nullptr)
213
        return command;
214
215
    std::string name;
216
    std::string id;
217
    std::string name2;
218
219
    if (target != nullptr)
220
    {
221
        name = target->getName();
222
        name2 = name;
223
        id = toString(toInt(target->getId(), int));
224
    }
225
    else
226
    {
227
        name2 = localPlayer->getName();
228
    }
229
230
    bool found = false;
231
232
    size_t idx = command.find("<TARGET>");
233
    if (idx != std::string::npos)
234
    {
235
        found = true;
236
        command = replaceAll(command, "<TARGET>", name);
237
    }
238
    idx = command.find("<TARGETID>");
239
    if (idx != std::string::npos)
240
    {
241
        found = true;
242
        command = replaceAll(command, "<TARGETID>", id);
243
    }
244
    idx = command.find("<TARGETORSELF>");
245
    if (idx != std::string::npos)
246
    {
247
        found = true;
248
        command = replaceAll(command, "<TARGETORSELF>", name2);
249
    }
250
251
    if (!found && !name.empty())
252
        command.append(" ").append(name);
253
254
    return command;
255
}
256
257
TextCommand *SpellManager::createNewSpell() const
258
{
259
    return new TextCommand(CAST_U32(mSpellsVector.size()));
260
}
261
262
void SpellManager::load()
263
{
264
    const Configuration *cfg = &serverConfig;
265
266
    delete_all(mSpells);
267
    mSpells.clear();
268
    mSpellsVector.clear();
269
270
    if (cfg->getValue("commandShortcutFlags0", "").empty())
271
    {
272
        fillSpells();
273
        save();
274
        return;
275
    }
276
277
    for (unsigned i = 0; i < SPELL_SHORTCUT_ITEMS * SPELL_SHORTCUT_TABS; i++)
278
    {
279
        unsigned int targetType;
280
        unsigned int basicLvl;
281
        unsigned int school;
282
        unsigned int schoolLvl;
283
        unsigned int mana;
284
        unsigned int commandType;
285
286
        std::string flags =
287
            cfg->getValue("commandShortcutFlags" + toString(i), "");
288
        std::stringstream ss(flags);
289
        ss >> commandType;
290
        ss >> targetType;
291
        ss >> basicLvl;
292
        ss >> school;
293
        ss >> schoolLvl;
294
        ss >> mana;
295
296
        std::string cmd = cfg->getValue("commandShortcutCmd"
297
                                        + toString(i), "");
298
        std::string comment = cfg->getValue("commandShortcutComment"
299
                                        + toString(i), "");
300
        std::string symbol = cfg->getValue("commandShortcutSymbol"
301
                                           + toString(i), "");
302
        std::string icon = cfg->getValue("commandShortcutIcon"
303
                                         + toString(i), "");
304
305
#ifdef TMWA_SUPPORT
306
        if (static_cast<TextCommandTypeT>(commandType) ==
307
            TextCommandType::Magic)
308
        {
309
            addSpell(new TextCommand(i, symbol, cmd, comment,
310
                static_cast<CommandTargetT>(targetType), icon, basicLvl,
311
                static_cast<MagicSchoolT>(school), schoolLvl, mana));
312
        }
313
        else
314
#endif  // TMWA_SUPPORT
315
        {
316
            addSpell(new TextCommand(i, symbol, cmd, comment,
317
                static_cast<CommandTargetT>(targetType), icon));
318
        }
319
    }
320
}
321
322
#define setOrDel(str, method) \
323
    const std::string var##method = spell->method(); \
324
    if (!var##method.empty()) \
325
        serverConfig.setValue((str) + toString(i), var##method); \
326
    else \
327
        serverConfig.deleteKey((str) + toString(i))
328
329
void SpellManager::save() const
330
{
331
    for (unsigned i = 0; i < SPELL_SHORTCUT_ITEMS * SPELL_SHORTCUT_TABS; i++)
332
    {
333
        const TextCommand *const spell = mSpellsVector[i];
334
        if (spell != nullptr)
335
        {
336
            setOrDel("commandShortcutCmd", getCommand);
337
            setOrDel("commandShortcutComment", getComment);
338
            setOrDel("commandShortcutSymbol", getSymbol);
339
            setOrDel("commandShortcutIcon", getIcon);
340
            if (!spell->getCommand().empty() &&
341
                !spell->getSymbol().empty())
342
            {
343
#ifdef TMWA_SUPPORT
344
                serverConfig.setValue("commandShortcutFlags" + toString(i),
345
                    strprintf("%u %u %u %u %u %u",
346
                    CAST_U32(spell->getCommandType()),
347
                    CAST_U32(spell->getTargetType()),
348
                    spell->getBaseLvl(),
349
                    CAST_U32(spell->getSchool()),
350
                    spell->getSchoolLvl(),
351
                    CAST_U32(spell->getMana())));
352
#else  // TMWA_SUPPORT
353
354
                serverConfig.setValue("commandShortcutFlags" + toString(i),
355
                    strprintf("%u %u %u %u %u %u", 1U,
356
                    CAST_U32(spell->getTargetType()),
357
                    0U,
358
                    0U,
359
                    0U,
360
                    0U));
361
#endif  // TMWA_SUPPORT
362
            }
363
            else
364
            {
365
                serverConfig.deleteKey("commandShortcutFlags" + toString(i));
366
            }
367
        }
368
    }
369
}
370
371
#undef setOrDel
372
373
std::string SpellManager::autoComplete(const std::string &partName) const
374
{
375
    STD_VECTOR<TextCommand*>::const_iterator i = mSpellsVector.begin();
376
    const STD_VECTOR<TextCommand*>::const_iterator
377
        i_end = mSpellsVector.end();
378
    std::string newName;
379
    const TextCommand *newCommand = nullptr;
380
381
    while (i != i_end)
382
    {
383
        const TextCommand *const cmd = *i;
384
        const std::string line = cmd->getCommand();
385
386
        if (!line.empty())
387
        {
388
            const size_t pos = line.find(partName, 0);
389
            if (pos == 0)
390
            {
391
                if (!newName.empty())
392
                {
393
                    newName = findSameSubstring(line, newName);
394
                    newCommand = nullptr;
395
                }
396
                else
397
                {
398
                    newName = line;
399
                    newCommand = cmd;
400
                }
401
            }
402
        }
403
        ++i;
404
    }
405
    if (!newName.empty() &&
406
        (newCommand != nullptr) &&
407
        newCommand->getTargetType() == CommandTarget::NeedTarget)
408
    {
409
        return newName.append(" ");
410
    }
411
    return newName;
412
}
413
414
void SpellManager::swap(const int id1, const int id2)
415
{
416
    TextCommand *const spell1 = mSpells[id1];
417
    TextCommand *const spell2 = mSpells[id2];
418
    if ((spell1 == nullptr) || (spell2 == nullptr))
419
        return;
420
421
    // swap in map
422
    mSpells[id1] = spell2;
423
    mSpells[id2] = spell1;
424
425
    // swap id
426
    const int tmp = spell1->getId();
427
    spell1->setId(spell2->getId());
428
    spell2->setId(tmp);
429
430
    // swap in vector
431
    const size_t sz = SPELL_SHORTCUT_ITEMS * SPELL_SHORTCUT_TABS;
432
    for (size_t f = 0; f < sz; f++)
433
    {
434
        const TextCommand *const spellA = mSpellsVector[f];
435
        if (spellA == spell1)
436
        {
437
            for (size_t d = 0; d < sz; d++)
438
            {
439
                const TextCommand *const spellB = mSpellsVector[d];
440
                if (spellB == spell2)
441
                {
442
                    mSpellsVector[f] = spell2;
443
                    mSpellsVector[d] = spell1;
444
                    return;
445
                }
446
            }
447
        }
448
    }
449
2
}