GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/actormanager.cpp Lines: 137 1011 13.6 %
Date: 2017-11-29 Branches: 136 1328 10.2 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-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 "actormanager.h"
24
25
#include "game.h"
26
#include "configuration.h"
27
#include "settings.h"
28
29
#include "being/localplayer.h"
30
#include "being/playerrelations.h"
31
32
#include "gui/sdlinput.h"
33
#include "gui/viewport.h"
34
35
#include "gui/widgets/tabs/chat/chattab.h"
36
37
#include "gui/windows/equipmentwindow.h"
38
#include "gui/windows/socialwindow.h"
39
#include "gui/windows/questswindow.h"
40
41
#include "fs/files.h"
42
43
#include "input/inputmanager.h"
44
45
#include "utils/checkutils.h"
46
#include "utils/foreach.h"
47
#include "utils/mathutils.h"
48
#include "utils/gettext.h"
49
50
#include "net/beinghandler.h"
51
#include "net/charserverhandler.h"
52
#include "net/packetlimiter.h"
53
#include "net/playerhandler.h"
54
#include "net/serverfeatures.h"
55
56
#include "resources/chatobject.h"
57
#include "resources/iteminfo.h"
58
59
#include "resources/map/map.h"
60
61
#include "resources/db/itemdb.h"
62
63
#ifdef TMWA_SUPPORT
64
#include "being/playerinfo.h"
65
66
#include "gui/windows/chatwindow.h"
67
68
#include "net/net.h"
69
#endif  // TMWA_SUPPORT
70
71
#include <algorithm>
72
73
#include "debug.h"
74
75
#define for_actors for (ActorSpritesConstIterator it = mActors.begin(), \
76
    it_fend = mActors.end(); it != it_fend; ++it)
77
78
#define for_actorsm for (ActorSpritesIterator it = mActors.begin(), \
79
    it_fend = mActors.end(); it != it_fend; ++it)
80
81
ActorManager *actorManager = nullptr;
82
83
class FindBeingFunctor final
84
{
85
    public:
86
        A_DEFAULT_COPY(FindBeingFunctor)
87
88
        bool operator() (const ActorSprite *const actor) const
89
        {
90
            if ((actor == nullptr)
91
                || actor->getType() == ActorType::FloorItem
92
                || actor->getType() == ActorType::Portal)
93
            {
94
                return false;
95
            }
96
            const Being *const b = static_cast<const Being *>(actor);
97
98
            const unsigned other_y = y
99
                + ((b->getType() == ActorType::Npc) ? 1 : 0);
100
            const Vector &pos = b->getPixelPositionF();
101
            // +++ probably here need use int positions and not float?
102
            // but for now correct int positions only in Being
103
            return CAST_U32(pos.x) / mapTileSize == x &&
104
                (CAST_U32(pos.y) / mapTileSize == y
105
                || CAST_U32(pos.y) / mapTileSize == other_y) &&
106
                b->isAlive() && (type == ActorType::Unknown
107
                || b->getType() == type);
108
        }
109
110
        uint16_t x, y;
111
        ActorTypeT type;
112
} beingActorFinder;
113
114
class FindBeingEqualFunctor final
115
{
116
    public:
117
        A_DEFAULT_COPY(FindBeingEqualFunctor)
118
119
        bool operator() (const Being *const being) const
120
        {
121
            if ((being == nullptr) || (findBeing == nullptr))
122
                return false;
123
            return being->getId() == findBeing->getId();
124
        }
125
126
        Being *findBeing;
127
} beingEqualActorFinder;
128
129
class SortBeingFunctor final
130
{
131
    public:
132
        A_DEFAULT_COPY(SortBeingFunctor)
133
134
        bool operator() (const Being *const being1,
135
                         const Being *const being2) const
136
        {
137
            if ((being1 == nullptr) || (being2 == nullptr))
138
                return false;
139
140
            if (priorityBeings != nullptr)
141
            {
142
                int w1 = defaultPriorityIndex;
143
                int w2 = defaultPriorityIndex;
144
                const StringIntMapCIter it1 = priorityBeings->find(
145
                    being1->getName());
146
                const StringIntMapCIter it2 = priorityBeings->find(
147
                    being2->getName());
148
                if (it1 != priorityBeings->end())
149
                    w1 = (*it1).second;
150
                if (it2 != priorityBeings->end())
151
                    w2 = (*it2).second;
152
153
                if (w1 != w2)
154
                    return w1 < w2;
155
            }
156
            if (being1->getDistance() != being2->getDistance())
157
            {
158
                if (specialDistance && being1->getDistance() <= 2
159
                    && being2->getDistance() <= attackRange
160
                    && being2->getDistance() > 2)
161
                {
162
                    return false;
163
                }
164
                else if (specialDistance && being2->getDistance() <= 2
165
                         && being1->getDistance() <= attackRange
166
                         && being1->getDistance() > 2)
167
                {
168
                    return true;
169
                }
170
                return being1->getDistance() < being2->getDistance();
171
            }
172
173
            const int d1 = abs(being1->getTileX() - x)
174
                + abs(being1->getTileY() - y);
175
            const int d2 = abs(being2->getTileX() - x)
176
                + abs(being2->getTileY() - y);
177
178
            if (d1 != d2)
179
                return d1 < d2;
180
            if (attackBeings != nullptr)
181
            {
182
                int w1 = defaultAttackIndex;
183
                int w2 = defaultAttackIndex;
184
                const StringIntMapCIter it1 = attackBeings->find(
185
                    being1->getName());
186
                const StringIntMapCIter it2 = attackBeings->find(
187
                    being2->getName());
188
                if (it1 != attackBeings->end())
189
                    w1 = (*it1).second;
190
                if (it2 != attackBeings->end())
191
                    w2 = (*it2).second;
192
193
                if (w1 != w2)
194
                    return w1 < w2;
195
            }
196
197
            return being1->getName() < being2->getName();
198
        }
199
        StringIntMap *attackBeings;
200
        StringIntMap *priorityBeings;
201
        int x;
202
        int y;
203
        int defaultAttackIndex;
204
        int defaultPriorityIndex;
205
        int attackRange;
206
        bool specialDistance;
207
} beingActorSorter;
208
209
10
ActorManager::ActorManager() :
210
    mActors(),
211
    mDeleteActors(),
212
    mActorsIdMap(),
213
    mIdName(),
214
    mBlockedBeings(),
215
    mChars(),
216
    mMap(nullptr),
217
#ifdef TMWA_SUPPORT
218
    mSpellHeal1(serverConfig.getValue("spellHeal1", "#lum")),
219
    mSpellHeal2(serverConfig.getValue("spellHeal2", "#inma")),
220
    mSpellItenplz(serverConfig.getValue("spellItenplz", "#itenplz")),
221
#endif  // TMWA_SUPPORT
222

40
    mTargetDeadPlayers(config.getBoolValue("targetDeadPlayers")),
223

40
    mTargetOnlyReachable(config.getBoolValue("targetOnlyReachable")),
224

40
    mCyclePlayers(config.getBoolValue("cyclePlayers")),
225

40
    mCycleMonsters(config.getBoolValue("cycleMonsters")),
226

40
    mCycleNPC(config.getBoolValue("cycleNPC")),
227

40
    mExtMouseTargeting(config.getBoolValue("extMouseTargeting")),
228

40
    mEnableIdCollecting(config.getBoolValue("enableIdCollecting")),
229
    mPriorityAttackMobs(),
230
    mPriorityAttackMobsSet(),
231
    mPriorityAttackMobsMap(),
232
    mAttackMobs(),
233
    mAttackMobsSet(),
234
    mAttackMobsMap(),
235
    mIgnoreAttackMobs(),
236
    mIgnoreAttackMobsSet(),
237
    mPickupItems(),
238
    mPickupItemsSet(),
239
    mPickupItemsMap(),
240
    mIgnorePickupItems(),
241




470
    mIgnorePickupItemsSet()
242
{
243

40
    config.addListener("targetDeadPlayers", this);
244

40
    config.addListener("targetOnlyReachable", this);
245

40
    config.addListener("cyclePlayers", this);
246

40
    config.addListener("cycleMonsters", this);
247

40
    config.addListener("cycleNPC", this);
248

40
    config.addListener("extMouseTargeting", this);
249

40
    config.addListener("showBadges", this);
250

40
    config.addListener("enableIdCollecting", this);
251

40
    config.addListener("visiblenamespos", this);
252
253
10
    loadAttackList();
254
10
}
255
256
250
ActorManager::~ActorManager()
257
{
258
10
    config.removeListeners(this);
259
    CHECKLISTENERS
260
10
    storeAttackList();
261
10
    clear();
262
20
}
263
264
void ActorManager::setMap(Map *const map)
265
{
266
    mMap = map;
267
268
    if (localPlayer != nullptr)
269
        localPlayer->setMap(map);
270
}
271
272
void ActorManager::setPlayer(LocalPlayer *const player)
273
{
274
    localPlayer = player;
275
    mActors.insert(player);
276
    mActorsIdMap[player->getId()] = player;
277
    if (socialWindow != nullptr)
278
        socialWindow->updateAttackFilter();
279
    if (socialWindow != nullptr)
280
        socialWindow->updatePickupFilter();
281
}
282
283
Being *ActorManager::createBeing(const BeingId id,
284
                                 const ActorTypeT type,
285
                                 const BeingTypeId subtype)
286
{
287
    Being *const being = Being::createBeing(id,
288
        type,
289
        subtype,
290
        mMap);
291
292
    mActors.insert(being);
293
294
    mActorsIdMap[being->getId()] = being;
295
296
    switch (type)
297
    {
298
        case ActorType::Player:
299
        case ActorType::Mercenary:
300
        case ActorType::Pet:
301
        case ActorType::Homunculus:
302
        case ActorType::Npc:
303
            being->updateFromCache();
304
            if (beingHandler != nullptr)
305
                beingHandler->requestNameById(id);
306
            if (localPlayer != nullptr)
307
                localPlayer->checkNewName(being);
308
            break;
309
        case ActorType::Monster:
310
#ifdef TMWA_SUPPORT
311
            if (Net::getNetworkType() != ServerType::TMWATHENA)
312
#endif  // TMWA_SUPPORT
313
            {
314
                beingHandler->requestNameById(id);
315
            }
316
            break;
317
        case ActorType::Portal:
318
            if ((beingHandler != nullptr) &&
319
                (serverFeatures != nullptr) &&
320
                serverFeatures->haveServerWarpNames())
321
            {
322
                beingHandler->requestNameById(id);
323
            }
324
            break;
325
        case ActorType::Elemental:
326
            if (beingHandler != nullptr)
327
                beingHandler->requestNameById(id);
328
            break;
329
        case ActorType::SkillUnit:
330
            break;
331
        default:
332
        case ActorType::FloorItem:
333
        case ActorType::Avatar:
334
        case ActorType::Unknown:
335
            reportAlways("CreateBeing for unknown type %d", CAST_S32(type));
336
            break;
337
    }
338
339
    if (type == ActorType::Player)
340
    {
341
        if (socialWindow != nullptr)
342
            socialWindow->updateActiveList();
343
    }
344
    else if (type == ActorType::Npc)
345
    {
346
        if (questsWindow != nullptr)
347
            questsWindow->addEffect(being);
348
    }
349
    return being;
350
}
351
352
FloorItem *ActorManager::createItem(const BeingId id,
353
                                    const int itemId,
354
                                    const int x, const int y,
355
                                    const ItemTypeT itemType,
356
                                    const int amount,
357
                                    const int refine,
358
                                    const ItemColor color,
359
                                    const Identified identified,
360
                                    const Damaged damaged,
361
                                    const int subX, const int subY,
362
                                    const int *const cards)
363
{
364
    FloorItem *const floorItem = new FloorItem(id,
365
        itemId,
366
        x, y,
367
        itemType,
368
        amount,
369
        refine,
370
        color,
371
        identified,
372
        damaged,
373
        cards);
374
    floorItem->postInit(mMap, subX, subY);
375
376
    if (!checkForPickup(floorItem))
377
        floorItem->disableHightlight();
378
    mActors.insert(floorItem);
379
    mActorsIdMap[floorItem->getId()] = floorItem;
380
    return floorItem;
381
}
382
383
void ActorManager::destroy(ActorSprite *const actor)
384
{
385
    returnNullptrV(actor);
386
387
    if (actor == localPlayer)
388
        return;
389
390
    mDeleteActors.insert(actor);
391
}
392
393
void ActorManager::erase(ActorSprite *const actor)
394
{
395
    returnNullptrV(actor);
396
397
    if (actor == localPlayer)
398
        return;
399
400
    mActors.erase(actor);
401
    const ActorSpritesMapIterator it = mActorsIdMap.find(actor->getId());
402
    if (it != mActorsIdMap.end() && (*it).second == actor)
403
        mActorsIdMap.erase(it);
404
}
405
406
void ActorManager::undelete(const ActorSprite *const actor)
407
{
408
    returnNullptrV(actor);
409
410
    if (actor == localPlayer)
411
        return;
412
413
    FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors)
414
    {
415
        if (*it == actor)
416
        {
417
            mDeleteActors.erase(*it);
418
            return;
419
        }
420
    }
421
}
422
423
Being *ActorManager::findBeing(const BeingId id) const
424
{
425
    const ActorSpritesMapConstIterator it = mActorsIdMap.find(id);
426
    if (it != mActorsIdMap.end())
427
    {
428
        ActorSprite *const actor = (*it).second;
429
        if ((actor != nullptr) &&
430
            actor->getId() == id &&
431
            actor->getType() != ActorType::FloorItem)
432
        {
433
            return static_cast<Being*>(actor);
434
        }
435
    }
436
    return nullptr;
437
}
438
439
ActorSprite *ActorManager::findActor(const BeingId id) const
440
{
441
    const ActorSpritesMapConstIterator it = mActorsIdMap.find(id);
442
    if (it != mActorsIdMap.end())
443
    {
444
        ActorSprite *const actor = (*it).second;
445
        if ((actor != nullptr) &&
446
            actor->getId() == id)
447
        {
448
            return actor;
449
        }
450
    }
451
    return nullptr;
452
}
453
454
Being *ActorManager::findBeing(const int x, const int y,
455
                               const ActorTypeT type) const
456
{
457
    beingActorFinder.x = CAST_U16(x);
458
    beingActorFinder.y = CAST_U16(y);
459
    beingActorFinder.type = type;
460
461
    const ActorSpritesConstIterator it = std::find_if(
462
        mActors.begin(), mActors.end(), beingActorFinder);
463
464
    return (it == mActors.end()) ? nullptr : static_cast<Being*>(*it);
465
}
466
467
Being *ActorManager::findBeingByPixel(const int x, const int y,
468
                                      const AllPlayers allPlayers) const
469
{
470
    if (mMap == nullptr)
471
        return nullptr;
472
473
    const bool targetDead = mTargetDeadPlayers;
474
    const bool modActive = inputManager.isActionActive(
475
        InputAction::STOP_ATTACK);
476
477
    if (mExtMouseTargeting)
478
    {
479
        Being *tempBeing = nullptr;
480
        bool noBeing(false);
481
482
        for_actorsm
483
        {
484
// disabled for performance
485
//            if (reportTrue(*it == nullptr))
486
//                continue;
487
488
            if ((*it)->getType() == ActorType::Portal)
489
                continue;
490
491
            if ((*it)->getType() == ActorType::FloorItem)
492
            {
493
                if (!noBeing)
494
                {
495
                    const FloorItem *const floor
496
                        = static_cast<const FloorItem*>(*it);
497
                    const int px = floor->getPixelX();
498
                    const int py = floor->getPixelY();
499
                    if ((px - mapTileSize     <= x) &&
500
                        (px + mapTileSize     >  x) &&
501
                        (py - mapTileSize * 2 <= y) &&
502
                        (py + mapTileSize / 2 >  y))
503
                    {
504
                        noBeing = true;
505
                    }
506
                }
507
                continue;
508
            }
509
510
            Being *const being = static_cast<Being*>(*it);
511
512
            if (being->getInfo() != nullptr &&
513
                !(being->getInfo()->isTargetSelection() || modActive))
514
            {
515
                continue;
516
            }
517
518
            if ((being->mAction != BeingAction::DEAD ||
519
                (targetDead && being->getType() == ActorType::Player)) &&
520
                (allPlayers == AllPlayers_true || being != localPlayer))
521
            {
522
                const int px = being->getPixelX();
523
                const int py = being->getPixelY();
524
                if ((px - mapTileSize / 2 <= x) &&
525
                    (px + mapTileSize / 2 >  x) &&
526
                    (py - mapTileSize     <= y) &&
527
                    (py                   >  y))
528
                {
529
                    return being;
530
                }
531
                else if (!noBeing &&
532
                         (px - mapTileSize     <= x) &&
533
                         (px + mapTileSize     >  x) &&
534
                         (py - mapTileSize * 2 <= y) &&
535
                         (py + mapTileSize / 2 >  y))
536
                {
537
                    if (tempBeing != nullptr)
538
                        noBeing = true;
539
                    else
540
                        tempBeing = being;
541
                }
542
            }
543
        }
544
545
        if (noBeing)
546
            return nullptr;
547
        return tempBeing;
548
    }
549
    for_actorsm
550
    {
551
// disabled for performance
552
//            if (reportTrue(*it == nullptr))
553
//                continue;
554
555
        if ((*it)->getType() == ActorType::Portal ||
556
            (*it)->getType() == ActorType::FloorItem)
557
        {
558
            continue;
559
        }
560
561
        Being *const being = static_cast<Being*>(*it);
562
563
        if (being->getInfo() != nullptr &&
564
            !(being->getInfo()->isTargetSelection() || modActive))
565
        {
566
            continue;
567
        }
568
569
        const int px = being->getPixelX();
570
        const int py = being->getPixelY();
571
        if ((px - mapTileSize / 2 <= x) &&
572
            (px + mapTileSize / 2 >  x) &&
573
            (py - mapTileSize     <= y) &&
574
            (py                   >  y))
575
        {
576
            return being;
577
        }
578
    }
579
    return nullptr;
580
}
581
582
void ActorManager::findBeingsByPixel(STD_VECTOR<ActorSprite*> &beings,
583
                                     const int x, const int y,
584
                                     const AllPlayers allPlayers) const
585
{
586
    if (mMap == nullptr)
587
        return;
588
589
    const int xtol = mapTileSize / 2;
590
    const int uptol = mapTileSize;
591
    const bool modActive = inputManager.isActionActive(
592
        InputAction::STOP_ATTACK);
593
594
    for_actors
595
    {
596
        ActorSprite *const actor = *it;
597
598
// disabled for performance
599
//        if (reportTrue(actor == nullptr))
600
//            continue;
601
602
        const ActorTypeT actorType = actor->getType();
603
        switch (actorType)
604
        {
605
            default:
606
            case ActorType::Unknown:
607
            case ActorType::Avatar:
608
            case ActorType::Portal:
609
                break;
610
            case ActorType::FloorItem:
611
                if ((actor->getPixelX() - xtol <= x) &&
612
                    (actor->getPixelX() + xtol > x) &&
613
                    (actor->getPixelY() - uptol <= y) &&
614
                    (actor->getPixelY() > y))
615
                {
616
                    beings.push_back(actor);
617
                }
618
                break;
619
            case ActorType::Player:
620
            case ActorType::Npc:
621
            case ActorType::Monster:
622
            case ActorType::Pet:
623
            case ActorType::Mercenary:
624
            case ActorType::Homunculus:
625
            case ActorType::SkillUnit:
626
            case ActorType::Elemental:
627
            {
628
                const Being *const being = static_cast<const Being*>(*it);
629
                if (being == nullptr)
630
                    continue;
631
                if ((being->getInfo() != nullptr) &&
632
                    !(being->getInfo()->isTargetSelection() || modActive))
633
                {
634
                    continue;
635
                }
636
                if ((being->isAlive() ||
637
                    (mTargetDeadPlayers &&
638
                    actorType == ActorType::Player)) &&
639
                    (allPlayers == AllPlayers_true ||
640
                    being != localPlayer))
641
                {
642
                    if ((actor->getPixelX() - xtol <= x) &&
643
                        (actor->getPixelX() + xtol > x) &&
644
                        (actor->getPixelY() - uptol <= y) &&
645
                        (actor->getPixelY() > y))
646
                    {
647
                        beings.push_back(actor);
648
                    }
649
                }
650
                break;
651
            }
652
        }
653
    }
654
}
655
656
Being *ActorManager::findPortalByTile(const int x, const int y) const
657
{
658
    if (mMap == nullptr)
659
        return nullptr;
660
661
    for_actorsm
662
    {
663
// disabled for performance
664
//        if (reportTrue(*it == nullptr))
665
//            continue;
666
667
        if ((*it)->getType() != ActorType::Portal)
668
            continue;
669
670
        Being *const being = static_cast<Being*>(*it);
671
672
        if (being->getTileX() == x && being->getTileY() == y)
673
            return being;
674
    }
675
676
    return nullptr;
677
}
678
679
FloorItem *ActorManager::findItem(const BeingId id) const
680
{
681
    const ActorSpritesMapConstIterator it = mActorsIdMap.find(id);
682
    if (it != mActorsIdMap.end())
683
    {
684
        ActorSprite *const actor = (*it).second;
685
        returnNullptr(nullptr, actor);
686
        if (actor->getId() == id &&
687
            actor->getType() == ActorType::FloorItem)
688
        {
689
            return static_cast<FloorItem*>(actor);
690
        }
691
    }
692
    return nullptr;
693
}
694
695
FloorItem *ActorManager::findItem(const int x, const int y) const
696
{
697
    for_actorsm
698
    {
699
// disabled for performance
700
//        if (reportTrue(*it == nullptr))
701
//            continue;
702
703
        if ((*it)->getTileX() == x && (*it)->getTileY() == y &&
704
            (*it)->getType() == ActorType::FloorItem)
705
        {
706
            return static_cast<FloorItem*>(*it);
707
        }
708
    }
709
710
    return nullptr;
711
}
712
713
bool ActorManager::pickUpAll(const int x1, const int y1,
714
                             const int x2, const int y2,
715
                             const bool serverBuggy) const
716
{
717
    if (localPlayer == nullptr)
718
        return false;
719
720
    bool finded(false);
721
    const bool allowAll = mPickupItemsSet.find("") != mPickupItemsSet.end();
722
    if (!serverBuggy)
723
    {
724
        for_actorsm
725
        {
726
// disabled for performance
727
//            if (reportTrue(*it == nullptr))
728
//                continue;
729
730
            if ((*it)->getType() == ActorType::FloorItem
731
                && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2)
732
                && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2))
733
            {
734
                FloorItem *const item = static_cast<FloorItem*>(*it);
735
                if (allowAll)
736
                {
737
                    if (mIgnorePickupItemsSet.find(item->getName())
738
                        == mIgnorePickupItemsSet.end())
739
                    {
740
                        if (localPlayer->pickUp(item))
741
                            finded = true;
742
                    }
743
                }
744
                else
745
                {
746
                    if (mPickupItemsSet.find(item->getName())
747
                        != mPickupItemsSet.end())
748
                    {
749
                        if (localPlayer->pickUp(item))
750
                            finded = true;
751
                    }
752
                }
753
            }
754
        }
755
    }
756
    else if (PacketLimiter::checkPackets(PacketType::PACKET_PICKUP))
757
    {
758
        FloorItem *item = nullptr;
759
        unsigned cnt = 65535;
760
        for_actorsm
761
        {
762
// disabled for performance
763
//            if (reportTrue(*it == nullptr))
764
//                continue;
765
766
            if ((*it)->getType() == ActorType::FloorItem
767
                && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2)
768
                && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2))
769
            {
770
                FloorItem *const tempItem = static_cast<FloorItem*>(*it);
771
                if (tempItem->getPickupCount() < cnt)
772
                {
773
                    if (allowAll)
774
                    {
775
                        if (mIgnorePickupItemsSet.find(tempItem->getName())
776
                            == mIgnorePickupItemsSet.end())
777
                        {
778
                            item = tempItem;
779
                            cnt = item->getPickupCount();
780
                            if (cnt == 0)
781
                            {
782
                                item->incrementPickup();
783
                                localPlayer->pickUp(item);
784
                                return true;
785
                            }
786
                        }
787
                    }
788
                    else
789
                    {
790
                        if (mPickupItemsSet.find(tempItem->getName())
791
                            != mPickupItemsSet.end())
792
                        {
793
                            item = tempItem;
794
                            cnt = item->getPickupCount();
795
                            if (cnt == 0)
796
                            {
797
                                item->incrementPickup();
798
                                localPlayer->pickUp(item);
799
                                return true;
800
                            }
801
                        }
802
                    }
803
                }
804
            }
805
        }
806
        if ((item != nullptr) && localPlayer->pickUp(item))
807
            finded = true;
808
    }
809
    return finded;
810
}
811
812
bool ActorManager::pickUpNearest(const int x, const int y,
813
                                 int maxdist) const
814
{
815
    if (localPlayer == nullptr)
816
        return false;
817
818
    maxdist = maxdist * maxdist;
819
    FloorItem *closestItem = nullptr;
820
    int dist = 0;
821
    const bool allowAll = mPickupItemsSet.find("") != mPickupItemsSet.end();
822
823
    for_actorsm
824
    {
825
// disabled for performance
826
//        if (reportTrue(*it == nullptr))
827
//            continue;
828
829
        if ((*it)->getType() == ActorType::FloorItem)
830
        {
831
            FloorItem *const item = static_cast<FloorItem*>(*it);
832
833
            const int d = (item->getTileX() - x) * (item->getTileX() - x)
834
                + (item->getTileY() - y) * (item->getTileY() - y);
835
836
            if ((d < dist || closestItem == nullptr) &&
837
                (!mTargetOnlyReachable || localPlayer->isReachable(
838
                item->getTileX(), item->getTileY(),
839
                false)))
840
            {
841
                if (allowAll)
842
                {
843
                    if (mIgnorePickupItemsSet.find(item->getName())
844
                        == mIgnorePickupItemsSet.end())
845
                    {
846
                        dist = d;
847
                        closestItem = item;
848
                    }
849
                }
850
                else
851
                {
852
                    if (mPickupItemsSet.find(item->getName())
853
                        != mPickupItemsSet.end())
854
                    {
855
                        dist = d;
856
                        closestItem = item;
857
                    }
858
                }
859
            }
860
        }
861
    }
862
    if ((closestItem != nullptr) && dist <= maxdist)
863
        return localPlayer->pickUp(closestItem);
864
865
    return false;
866
}
867
868
Being *ActorManager::findBeingByName(const std::string &name,
869
                                     const ActorTypeT type) const
870
{
871
    for_actorsm
872
    {
873
// disabled for performance
874
//        if (reportTrue(*it == nullptr))
875
//            continue;
876
877
        if ((*it)->getType() == ActorType::FloorItem
878
            || (*it)->getType() == ActorType::Portal)
879
        {
880
            continue;
881
        }
882
883
        Being *const being = static_cast<Being*>(*it);
884
        if (being->getName() == name &&
885
            (type == ActorType::Unknown || type == being->getType()))
886
        {
887
            return being;
888
        }
889
    }
890
    return nullptr;
891
}
892
893
Being *ActorManager::findNearestByName(const std::string &name,
894
                                       const ActorTypeT &type) const
895
{
896
    if (localPlayer == nullptr)
897
        return nullptr;
898
899
    int dist = 0;
900
    Being* closestBeing = nullptr;
901
    int x, y;
902
903
    x = localPlayer->getTileX();
904
    y = localPlayer->getTileY();
905
906
    for_actorsm
907
    {
908
// disabled for performance
909
//        if (reportTrue(*it == nullptr))
910
//            continue;
911
912
        if ((*it)->getType() == ActorType::FloorItem
913
            || (*it)->getType() == ActorType::Portal)
914
        {
915
            continue;
916
        }
917
918
        Being *const being = static_cast<Being*>(*it);
919
920
        if ((being != nullptr) && being->getName() == name &&
921
            (type == ActorType::Unknown || type == being->getType()))
922
        {
923
            if (being->getType() == ActorType::Player)
924
            {
925
                return being;
926
            }
927
            const int d = (being->getTileX() - x) * (being->getTileX() - x)
928
                + (being->getTileY() - y) * (being->getTileY() - y);
929
930
            if (validateBeing(nullptr, being, type, nullptr, 50)
931
                && (d < dist || closestBeing == nullptr))
932
            {
933
                dist = d;
934
                closestBeing = being;
935
            }
936
        }
937
    }
938
    return closestBeing;
939
}
940
941
const ActorSprites &ActorManager::getAll() const
942
{
943
    return mActors;
944
}
945
946
void ActorManager::logic()
947
{
948
    BLOCK_START("ActorManager::logic")
949
    for_actors
950
    {
951
// disabled for performance
952
//        if (reportFalse(*it))
953
        (*it)->logic();
954
    }
955
956
    if (mDeleteActors.empty())
957
    {
958
        BLOCK_END("ActorManager::logic")
959
        return;
960
    }
961
962
    BLOCK_START("ActorManager::logic 1")
963
    FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors)
964
    {
965
        const ActorSprite *const actor = *it;
966
        const ActorTypeT &type = actor->getType();
967
        if (type == ActorType::Player)
968
        {
969
            const Being *const being = static_cast<const Being*>(actor);
970
            being->addToCache();
971
            if (beingEquipmentWindow != nullptr)
972
                beingEquipmentWindow->resetBeing(being);
973
        }
974
        if (localPlayer != nullptr)
975
        {
976
            if (localPlayer->getTarget() == actor)
977
                localPlayer->setTarget(nullptr);
978
            if (localPlayer->getPickUpTarget() == actor)
979
                localPlayer->unSetPickUpTarget();
980
        }
981
        if (viewport != nullptr)
982
            viewport->clearHover(*it);
983
    }
984
985
    FOR_EACH (ActorSpritesConstIterator, it, mDeleteActors)
986
    {
987
        ActorSprite *actor = *it;
988
        mActors.erase(actor);
989
990
        if (actor != nullptr)
991
        {
992
            const ActorSpritesMapIterator itr = mActorsIdMap.find(
993
                actor->getId());
994
            if (itr != mActorsIdMap.end() && (*itr).second == actor)
995
                mActorsIdMap.erase(itr);
996
997
            delete actor;
998
        }
999
    }
1000
1001
    mDeleteActors.clear();
1002
    BLOCK_END("ActorManager::logic 1")
1003
    BLOCK_END("ActorManager::logic")
1004
}
1005
1006
10
void ActorManager::clear()
1007
{
1008
10
    if (beingEquipmentWindow != nullptr)
1009
        beingEquipmentWindow->setBeing(nullptr);
1010
1011
10
    if (localPlayer != nullptr)
1012
    {
1013
10
        localPlayer->setTarget(nullptr);
1014
10
        localPlayer->unSetPickUpTarget();
1015
20
        mActors.erase(localPlayer);
1016
    }
1017
1018
30
    for_actors
1019
14
        delete *it;
1020
20
    mActors.clear();
1021
20
    mDeleteActors.clear();
1022
20
    mActorsIdMap.clear();
1023
1024
10
    if (localPlayer != nullptr)
1025
    {
1026
20
        mActors.insert(localPlayer);
1027
20
        mActorsIdMap[localPlayer->getId()] = localPlayer;
1028
    }
1029
1030
20
    mChars.clear();
1031
10
}
1032
1033
Being *ActorManager::findNearestPvpPlayer() const
1034
{
1035
    if (localPlayer == nullptr)
1036
        return nullptr;
1037
1038
    // don't attack players
1039
    if (settings.pvpAttackType == 3)
1040
        return nullptr;
1041
1042
    const Game *const game = Game::instance();
1043
    if (game == nullptr)
1044
        return nullptr;
1045
1046
    const Map *const map = game->getCurrentMap();
1047
    if (map == nullptr)
1048
        return nullptr;
1049
1050
    const int mapPvpMode = map->getPvpMode();
1051
    Being *target = nullptr;
1052
    int minDistSquared = 20000;
1053
1054
    for_actors
1055
    {
1056
        if ((*it)->getType() != ActorType::Player)
1057
            continue;
1058
1059
        Being *const being = static_cast<Being*>(*it);
1060
1061
        if (reportTrue(being == nullptr) ||
1062
            !being->isAlive() ||
1063
            localPlayer == being)
1064
        {
1065
            continue;
1066
        }
1067
1068
        const int teamId = being->getTeamId();
1069
        // this condition is very TMW-specific
1070
        if (!((mapPvpMode != 0) || (teamId != 0)))
1071
            continue;
1072
1073
        if (!LocalPlayer::checAttackPermissions(being))
1074
            continue;
1075
1076
        const int dx = being->getTileX() - localPlayer->getTileX();
1077
        const int dy = being->getTileY() - localPlayer->getTileY();
1078
        const int distSquared = dx * dx + dy * dy;
1079
        if (distSquared < minDistSquared)
1080
        {
1081
            minDistSquared = distSquared;
1082
            target = being;
1083
        }
1084
    }
1085
1086
    return target;
1087
}
1088
1089
1090
Being *ActorManager::findNearestLivingBeing(const int x, const int y,
1091
                                            const int maxTileDist,
1092
                                            const ActorTypeT type,
1093
                                            const Being *const excluded) const
1094
{
1095
    const int maxDist = maxTileDist * mapTileSize;
1096
1097
    return findNearestLivingBeing(nullptr, maxDist,
1098
        type,
1099
        x, y,
1100
        excluded,
1101
        AllowSort_true);
1102
}
1103
1104
4
Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing,
1105
                                            const int maxDist,
1106
                                            const ActorTypeT type,
1107
                                            const AllowSort allowSort) const
1108
{
1109
4
    if (aroundBeing == nullptr)
1110
        return nullptr;
1111
1112
8
    return findNearestLivingBeing(aroundBeing,
1113
        maxDist,
1114
        type,
1115
4
        aroundBeing->getTileX(),
1116
4
        aroundBeing->getTileY(),
1117
        aroundBeing,
1118
4
        allowSort);
1119
}
1120
1121
4
Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing,
1122
                                            int maxDist,
1123
                                            const ActorTypeT &type,
1124
                                            const int x, const int y,
1125
                                            const Being *const excluded,
1126
                                            const AllowSort allowSort) const
1127
{
1128

4
    if ((aroundBeing == nullptr) || (localPlayer == nullptr))
1129
        return nullptr;
1130
1131
4
    std::set<std::string> attackMobs;
1132
8
    std::set<std::string> priorityMobs;
1133
8
    std::set<std::string> ignoreAttackMobs;
1134
8
    StringIntMap attackMobsMap;
1135
8
    StringIntMap priorityMobsMap;
1136
4
    int defaultAttackIndex = 10000;
1137
4
    int defaultPriorityIndex = 10000;
1138
4
    const int attackRange = localPlayer->getAttackRange();
1139
1140
4
    bool specialDistance = false;
1141
4
    if (settings.moveToTargetType == 11
1142

4
        && localPlayer->getAttackRange() > 2)
1143
    {
1144
        specialDistance = true;
1145
    }
1146
1147
4
    maxDist = maxDist * maxDist;
1148
1149
4
    const bool cycleSelect = allowSort == AllowSort_true
1150

4
        && ((mCyclePlayers && type == ActorType::Player)
1151

2
        || (mCycleMonsters && type == ActorType::Monster)
1152
        || (mCycleNPC && type == ActorType::Npc));
1153
1154
    const bool filtered = allowSort == AllowSort_true
1155


16
        && config.getBoolValue("enableAttackFilter")
1156

8
        && type == ActorType::Monster;
1157
    const bool modActive = inputManager.isActionActive(
1158
4
        InputAction::STOP_ATTACK);
1159
1160
4
    bool ignoreDefault = false;
1161
4
    if (filtered)
1162
    {
1163
4
        attackMobs = mAttackMobsSet;
1164
4
        priorityMobs = mPriorityAttackMobsSet;
1165
4
        ignoreAttackMobs = mIgnoreAttackMobsSet;
1166
4
        attackMobsMap = mAttackMobsMap;
1167
4
        priorityMobsMap = mPriorityAttackMobsMap;
1168
2
        beingActorSorter.attackBeings = &attackMobsMap;
1169
2
        beingActorSorter.priorityBeings = &priorityMobsMap;
1170
2
        beingActorSorter.specialDistance = specialDistance;
1171
2
        beingActorSorter.attackRange = attackRange;
1172

10
        if (ignoreAttackMobs.find("") != ignoreAttackMobs.end())
1173
            ignoreDefault = true;
1174
12
        StringIntMapCIter itr = attackMobsMap.find("");
1175
2
        if (itr != attackMobsMap.end())
1176
2
            defaultAttackIndex = (*itr).second;
1177
10
        itr = priorityMobsMap.find("");
1178
2
        if (itr != priorityMobsMap.end())
1179
            defaultPriorityIndex = (*itr).second;
1180
    }
1181
1182
4
    if (cycleSelect)
1183
    {
1184
8
        STD_VECTOR<Being*> sortedBeings;
1185
1186
12
        FOR_EACH (ActorSprites::iterator, i, mActors)
1187
        {
1188
//  disabled for performance
1189
//            if (reportTrue(*i == nullptr))
1190
//                continue;
1191
1192
            if ((*i)->getType() == ActorType::FloorItem
1193
                || (*i)->getType() == ActorType::Portal)
1194
            {
1195
                continue;
1196
            }
1197
1198
            Being *const being = static_cast<Being*>(*i);
1199
1200
            if (filtered)
1201
            {
1202
                if (ignoreAttackMobs.find(being->getName())
1203
                    != ignoreAttackMobs.end())
1204
                {
1205
                    continue;
1206
                }
1207
                if (ignoreDefault && attackMobs.find(being->getName())
1208
                    == attackMobs.end() && priorityMobs.find(being->getName())
1209
                    == priorityMobs.end())
1210
                {
1211
                    continue;
1212
                }
1213
            }
1214
1215
            if ((being->getInfo() != nullptr)
1216
                && !(being->getInfo()->isTargetSelection() || modActive))
1217
            {
1218
                continue;
1219
            }
1220
1221
            if (validateBeing(aroundBeing, being, type, nullptr, maxDist))
1222
            {
1223
                if (being != excluded)
1224
                    sortedBeings.push_back(being);
1225
            }
1226
        }
1227
1228
        // no selectable beings
1229
4
        if (sortedBeings.empty())
1230
            return nullptr;
1231
1232
        beingActorSorter.x = x;
1233
        beingActorSorter.y = y;
1234
        if (filtered)
1235
        {
1236
            beingActorSorter.attackBeings = &attackMobsMap;
1237
            beingActorSorter.defaultAttackIndex = defaultAttackIndex;
1238
            beingActorSorter.priorityBeings = &priorityMobsMap;
1239
            beingActorSorter.defaultPriorityIndex = defaultPriorityIndex;
1240
        }
1241
        else
1242
        {
1243
            beingActorSorter.attackBeings = nullptr;
1244
            beingActorSorter.priorityBeings = nullptr;
1245
        }
1246
        std::sort(sortedBeings.begin(), sortedBeings.end(), beingActorSorter);
1247
        if (filtered)
1248
        {
1249
            beingActorSorter.attackBeings = nullptr;
1250
            beingActorSorter.priorityBeings = nullptr;
1251
        }
1252
1253
        if (localPlayer->getTarget() == nullptr)
1254
        {
1255
            Being *const target = sortedBeings.at(0);
1256
1257
            if (specialDistance && target->getType() == ActorType::Monster
1258
                && target->getDistance() <= 2)
1259
            {
1260
                return nullptr;
1261
            }
1262
            // if no selected being in vector, return first nearest being
1263
            return target;
1264
        }
1265
1266
        beingEqualActorFinder.findBeing = localPlayer->getTarget();
1267
        STD_VECTOR<Being*>::const_iterator i = std::find_if(
1268
            sortedBeings.begin(), sortedBeings.end(), beingEqualActorFinder);
1269
1270
        if (i == sortedBeings.end() || ++i == sortedBeings.end())
1271
        {
1272
            // if no selected being in vector, return first nearest being
1273
            return sortedBeings.at(0);
1274
        }
1275
1276
        // we find next being after target
1277
        return *i;
1278
    }
1279
1280
    int dist = 0;
1281
    int index = defaultPriorityIndex;
1282
    Being *closestBeing = nullptr;
1283
1284
    FOR_EACH (ActorSprites::iterator, i, mActors)
1285
    {
1286
//  disabled for performance
1287
//            if (reportTrue(*i == nullptr))
1288
//                continue;
1289
1290
        if ((*i)->getType() == ActorType::FloorItem ||
1291
            (*i)->getType() == ActorType::Portal)
1292
        {
1293
            continue;
1294
        }
1295
        Being *const being = static_cast<Being*>(*i);
1296
1297
        if (filtered)
1298
        {
1299
            if (ignoreAttackMobs.find(being->getName())
1300
                != ignoreAttackMobs.end())
1301
            {
1302
                continue;
1303
            }
1304
            if (ignoreDefault && attackMobs.find(being->getName())
1305
                == attackMobs.end() && priorityMobs.find(being->getName())
1306
                == priorityMobs.end())
1307
            {
1308
                continue;
1309
            }
1310
        }
1311
1312
        if ((being->getInfo() != nullptr)
1313
            && !(being->getInfo()->isTargetSelection() || modActive))
1314
        {
1315
            continue;
1316
        }
1317
1318
        const bool valid = validateBeing(aroundBeing, being,
1319
                                             type, excluded, 50);
1320
        int d = being->getDistance();
1321
        if (being->getType() != ActorType::Monster
1322
            || !mTargetOnlyReachable)
1323
        {   // if distance not calculated, use old distance
1324
            d = (being->getTileX() - x) * (being->getTileX() - x)
1325
                + (being->getTileY() - y) * (being->getTileY() - y);
1326
        }
1327
1328
        if (!valid)
1329
            continue;
1330
1331
        if (specialDistance && being->getDistance() <= 2
1332
            && being->getType() == type)
1333
        {
1334
            continue;
1335
        }
1336
1337
//            logger->log("being name:" + being->getName());
1338
//            logger->log("index:" + toString(index));
1339
//            logger->log("d:" + toString(d));
1340
1341
        if (!filtered && (d <= dist || (closestBeing == nullptr)))
1342
        {
1343
            dist = d;
1344
            closestBeing = being;
1345
        }
1346
        else if (filtered)
1347
        {
1348
            int w2 = defaultPriorityIndex;
1349
            if (closestBeing != nullptr)
1350
            {
1351
                const StringIntMapCIter it2 =  priorityMobsMap.find(
1352
                    being->getName());
1353
                if (it2 != priorityMobsMap.end())
1354
                    w2 = (*it2).second;
1355
1356
                if (w2 < index)
1357
                {
1358
                    dist = d;
1359
                    closestBeing = being;
1360
                    index = w2;
1361
                    continue;
1362
                }
1363
                if (w2 == index && d <= dist)
1364
                {
1365
                    dist = d;
1366
                    closestBeing = being;
1367
                    index = w2;
1368
                    continue;
1369
                }
1370
            }
1371
1372
            if (closestBeing == nullptr)
1373
            {
1374
                dist = d;
1375
                closestBeing = being;
1376
                const StringIntMapCIter it1 = priorityMobsMap.find(
1377
                    being->getName());
1378
                if (it1 != priorityMobsMap.end())
1379
                    index = (*it1).second;
1380
                else
1381
                    index = defaultPriorityIndex;
1382
            }
1383
        }
1384
    }
1385
    return (maxDist >= dist) ? closestBeing : nullptr;
1386
}
1387
1388
bool ActorManager::validateBeing(const Being *const aroundBeing,
1389
                                 Being *const being,
1390
                                 const ActorTypeT &type,
1391
                                 const Being* const excluded,
1392
                                 const int maxCost) const
1393
{
1394
    if (localPlayer == nullptr)
1395
        return false;
1396
    return (being != nullptr) && ((being->getType() == type
1397
        || type == ActorType::Unknown) && (being->isAlive()
1398
        || (mTargetDeadPlayers && type == ActorType::Player))
1399
        && being != aroundBeing) && being != excluded
1400
        && (type != ActorType::Monster || !mTargetOnlyReachable
1401
        || localPlayer->isReachable(being, maxCost));
1402
}
1403
1404
#ifdef TMWA_SUPPORT
1405
void ActorManager::healTarget() const
1406
{
1407
    if (localPlayer == nullptr)
1408
        return;
1409
1410
    heal(localPlayer->getTarget());
1411
}
1412
1413
void ActorManager::heal(const Being *const target) const
1414
{
1415
    if (Net::getNetworkType() != ServerType::TMWATHENA)
1416
        return;
1417
1418
    if (localPlayer == nullptr ||
1419
        chatWindow == nullptr ||
1420
        !localPlayer->isAlive() ||
1421
        !playerHandler->canUseMagic())
1422
    {
1423
        return;
1424
    }
1425
1426
    // self
1427
    if (target != nullptr &&
1428
        localPlayer->getName() == target->getName())
1429
    {
1430
        if (PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 6
1431
            && PlayerInfo::getAttribute(Attributes::PLAYER_HP)
1432
            != PlayerInfo::getAttribute(Attributes::PLAYER_MAX_HP))
1433
        {
1434
            if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1435
                return;
1436
            chatWindow->localChatInput(mSpellHeal1);
1437
        }
1438
    }
1439
    // magic levels < 2
1440
    else if (PlayerInfo::getSkillLevel(340) < 2 ||
1441
             PlayerInfo::getSkillLevel(341) < 2)
1442
    {
1443
        if (PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 6)
1444
        {
1445
            if (target != nullptr &&
1446
                target->getType() != ActorType::Monster)
1447
            {
1448
                if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1449
                    return;
1450
                chatWindow->localChatInput(mSpellHeal1 + " "
1451
                                           + target->getName());
1452
            }
1453
            else if (PlayerInfo::getAttribute(Attributes::PLAYER_HP)
1454
                     != PlayerInfo::getAttribute(Attributes::PLAYER_MAX_HP))
1455
            {
1456
                if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1457
                    return;
1458
                chatWindow->localChatInput(mSpellHeal1);
1459
            }
1460
        }
1461
    }
1462
    // magic level >= 2 and not self
1463
    else
1464
    {
1465
        // mp > 10 and target not monster
1466
        if (PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 10 &&
1467
            target != nullptr &&
1468
            target->getType() != ActorType::Monster)
1469
        {
1470
            // target not enemy
1471
            if (playerRelations.getRelation(target->getName()) !=
1472
                Relation::ENEMY2)
1473
            {
1474
                if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1475
                    return;
1476
                chatWindow->localChatInput(mSpellHeal2 + " "
1477
                    + target->getName());
1478
            }
1479
            // target enemy
1480
            else
1481
            {
1482
                if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1483
                    return;
1484
                chatWindow->localChatInput(mSpellHeal1);
1485
            }
1486
        }
1487
        // heal self if selected monster or selection empty
1488
        else if ((target == nullptr || target->getType() == ActorType::Monster)
1489
                 && PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 6
1490
                 && PlayerInfo::getAttribute(Attributes::PLAYER_HP)
1491
                 != PlayerInfo::getAttribute(Attributes::PLAYER_MAX_HP))
1492
        {
1493
            if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1494
                return;
1495
            chatWindow->localChatInput(mSpellHeal1);
1496
        }
1497
    }
1498
}
1499
#endif  // TMWA_SUPPORT
1500
1501
Being* ActorManager::findMostDamagedPlayer(const int maxTileDist) const
1502
{
1503
    if (localPlayer == nullptr)
1504
        return nullptr;
1505
1506
    int maxDamageTaken = 0;
1507
    Being *target = nullptr;
1508
1509
    for_actors
1510
    {
1511
        if ((*it)->getType() != ActorType::Player)
1512
            continue;
1513
1514
        Being *const being = static_cast<Being*>(*it);
1515
1516
        if ((being == nullptr) || !being->isAlive() ||  // don't heal dead
1517
            playerRelations.getRelation(being->getName()) ==
1518
            Relation::ENEMY2 ||                         // don't heal enemy
1519
            localPlayer == being)                       // don't heal self
1520
        {
1521
            continue;
1522
        }
1523
1524
        const int dx = being->getTileX() - localPlayer->getTileX();
1525
        const int dy = being->getTileY() - localPlayer->getTileY();
1526
        const int distance = fastSqrtInt(dx * dx + dy * dy);
1527
1528
        if (distance > maxTileDist)
1529
            continue;
1530
1531
        if (being->getDamageTaken() > maxDamageTaken)
1532
        {
1533
            maxDamageTaken = being->getDamageTaken();
1534
            target = being;
1535
        }
1536
    }
1537
1538
    return target;
1539
}
1540
1541
#ifdef TMWA_SUPPORT
1542
void ActorManager::itenplz() const
1543
{
1544
    if (Net::getNetworkType() != ServerType::TMWATHENA)
1545
        return;
1546
    if (localPlayer == nullptr ||
1547
        chatWindow == nullptr ||
1548
        !localPlayer->isAlive() ||
1549
        !playerHandler->canUseMagic())
1550
    {
1551
        return;
1552
    }
1553
1554
    if (!PacketLimiter::limitPackets(PacketType::PACKET_CHAT))
1555
        return;
1556
1557
    chatWindow->localChatInput(mSpellItenplz);
1558
}
1559
#endif  // TMWA_SUPPORT
1560
1561
bool ActorManager::hasActorSprite(const ActorSprite *const actor) const
1562
{
1563
    for_actors
1564
    {
1565
        if (actor == *it)
1566
            return true;
1567
    }
1568
1569
    return false;
1570
}
1571
1572
void ActorManager::addBlock(const BeingId id)
1573
{
1574
    mBlockedBeings.insert(id);
1575
}
1576
1577
void ActorManager::deleteBlock(const BeingId id)
1578
{
1579
    mBlockedBeings.erase(id);
1580
}
1581
1582
bool ActorManager::isBlocked(const BeingId id) const
1583
{
1584
    return mBlockedBeings.find(id) != mBlockedBeings.end();
1585
}
1586
1587
void ActorManager::printAllToChat()
1588
{
1589
    // TRANSLATORS: visible beings on map
1590
    printBeingsToChat(_("Visible on map"));
1591
}
1592
1593
void ActorManager::printBeingsToChat(const std::string &header) const
1594
{
1595
    if (debugChatTab == nullptr)
1596
        return;
1597
1598
    debugChatTab->chatLog("---------------------------------------",
1599
        ChatMsgType::BY_SERVER);
1600
    debugChatTab->chatLog(header, ChatMsgType::BY_SERVER);
1601
    for_actors
1602
    {
1603
// disabled for performance
1604
//        if (reportTrue(*it == nullptr))
1605
//            continue;
1606
1607
        if ((*it)->getType() == ActorType::FloorItem)
1608
            continue;
1609
1610
        const Being *const being = static_cast<const Being*>(*it);
1611
1612
        debugChatTab->chatLog(strprintf("%s (%d,%d) %d",
1613
            being->getName().c_str(), being->getTileX(), being->getTileY(),
1614
            toInt(being->getSubType(), int)), ChatMsgType::BY_SERVER);
1615
        if (mActorsIdMap.find(being->getId()) == mActorsIdMap.end())
1616
        {
1617
            debugChatTab->chatLog("missing in id map: %s",
1618
                being->getName().c_str());
1619
        }
1620
    }
1621
    debugChatTab->chatLog("---------------------------------------",
1622
        ChatMsgType::BY_SERVER);
1623
    FOR_EACH (ActorSpritesMapConstIterator, itr, mActorsIdMap)
1624
    {
1625
        const ActorSprite *const actor = (*itr).second;
1626
        if (actor == nullptr)
1627
            continue;
1628
        if (actor->getId() != (*itr).first)
1629
            debugChatTab->chatLog("Actor with wrong key in map", "");
1630
1631
        bool found(false);
1632
1633
        for_actors
1634
        {
1635
// disabled for performance
1636
//            if (!*it)
1637
//                continue;
1638
1639
            if ((*it)->getId() == actor->getId())
1640
            {
1641
                found = true;
1642
                break;
1643
            }
1644
        }
1645
        if (!found)
1646
            debugChatTab->chatLog("Actor present in map but not in set", "");
1647
    }
1648
}
1649
1650
void ActorManager::printBeingsToChat(const STD_VECTOR<Being*> &beings,
1651
                                     const std::string &header)
1652
{
1653
    if (debugChatTab == nullptr)
1654
        return;
1655
1656
    debugChatTab->chatLog("---------------------------------------",
1657
        ChatMsgType::BY_SERVER);
1658
    debugChatTab->chatLog(header, ChatMsgType::BY_SERVER);
1659
1660
    FOR_EACH (STD_VECTOR<Being*>::const_iterator, i, beings)
1661
    {
1662
        if (*i == nullptr)
1663
            continue;
1664
1665
        const Being *const being = *i;
1666
1667
        debugChatTab->chatLog(strprintf("%s (%d,%d) %d",
1668
            being->getName().c_str(), being->getTileX(), being->getTileY(),
1669
            toInt(being->getSubType(), int)), ChatMsgType::BY_SERVER);
1670
    }
1671
    debugChatTab->chatLog("---------------------------------------",
1672
        ChatMsgType::BY_SERVER);
1673
}
1674
1675
8
void ActorManager::getPlayerNames(StringVect &names,
1676
                                  const NpcNames npcNames) const
1677
{
1678
8
    names.clear();
1679
1680
24
    for_actors
1681
    {
1682
// disabled for performance
1683
//      if (reportTrue(*it == nullptr))
1684
//          continue;
1685
1686

24
        if ((*it)->getType() == ActorType::FloorItem ||
1687
12
            (*it)->getType() == ActorType::Portal)
1688
        {
1689
            continue;
1690
        }
1691
1692
12
        const Being *const being = static_cast<const Being*>(*it);
1693
12
        if ((being->getType() == ActorType::Player ||
1694
            (being->getType() == ActorType::Npc &&
1695

24
            npcNames == NpcNames_true)) &&
1696
12
            !being->getName().empty())
1697
        {
1698
6
            names.push_back(being->getName());
1699
        }
1700
    }
1701
8
}
1702
1703
void ActorManager::getMobNames(StringVect &names) const
1704
{
1705
    names.clear();
1706
1707
    for_actors
1708
    {
1709
// disabled for performance
1710
//      if (reportTrue(*it == nullptr))
1711
//          continue;
1712
1713
        if ((*it)->getType() == ActorType::FloorItem
1714
            || (*it)->getType() == ActorType::Portal)
1715
        {
1716
            continue;
1717
        }
1718
1719
        const Being *const being = static_cast<const Being*>(*it);
1720
        if (being->getType() == ActorType::Monster &&
1721
            !being->getName().empty())
1722
        {
1723
            names.push_back(being->getName());
1724
        }
1725
    }
1726
}
1727
1728
void ActorManager::updatePlayerNames() const
1729
{
1730
    for_actorsm
1731
    {
1732
// disabled for performance
1733
//      if (reportTrue(*it == nullptr))
1734
//          continue;
1735
1736
        if ((*it)->getType() == ActorType::FloorItem
1737
            || (*it)->getType() == ActorType::Portal)
1738
        {
1739
            continue;
1740
        }
1741
1742
        Being *const being = static_cast<Being*>(*it);
1743
        being->setGoodStatus(-1);
1744
        if (being->getType() == ActorType::Player && !being->getName().empty())
1745
            being->updateName();
1746
    }
1747
}
1748
1749
void ActorManager::updatePlayerColors() const
1750
{
1751
    for_actorsm
1752
    {
1753
// disabled for performance
1754
//      if (reportTrue(*it == nullptr))
1755
//          continue;
1756
1757
        if ((*it)->getType() == ActorType::FloorItem
1758
            || (*it)->getType() == ActorType::Portal)
1759
        {
1760
            continue;
1761
        }
1762
1763
        Being *const being = static_cast<Being*>(*it);
1764
        if (being->getType() == ActorType::Player && !being->getName().empty())
1765
            being->updateColors();
1766
    }
1767
}
1768
1769
void ActorManager::updatePlayerGuild() const
1770
{
1771
    for_actorsm
1772
    {
1773
// disabled for performance
1774
//      if (reportTrue(*it == nullptr))
1775
//          continue;
1776
1777
        if ((*it)->getType() == ActorType::FloorItem
1778
            || (*it)->getType() == ActorType::Portal)
1779
        {
1780
            continue;
1781
        }
1782
1783
        Being *const being = static_cast<Being*>(*it);
1784
        if (being->getType() == ActorType::Player && !being->getName().empty())
1785
            being->updateGuild();
1786
    }
1787
}
1788
1789
#ifdef TMWA_SUPPORT
1790
void ActorManager::parseLevels(std::string levels) const
1791
{
1792
    levels.append(", ");
1793
    size_t f = 0;
1794
    const std::string brkEnd("), ");
1795
    size_t pos = levels.find(brkEnd, f);
1796
1797
    while (pos != std::string::npos)
1798
    {
1799
        std::string part = levels.substr(f, pos - f);
1800
        if (part.empty())
1801
            break;
1802
        const size_t bktPos = part.rfind('(');
1803
        if (bktPos != std::string::npos)
1804
        {
1805
            Being *const being = findBeingByName(part.substr(0, bktPos),
1806
                ActorType::Player);
1807
            if (being != nullptr)
1808
            {
1809
                being->setLevel(atoi(part.substr(bktPos + 1).c_str()));
1810
                being->addToCache();
1811
            }
1812
        }
1813
        f = CAST_SIZE(pos + brkEnd.length());
1814
        pos = levels.find(brkEnd, f);
1815
    }
1816
    updatePlayerNames();
1817
}
1818
#endif  // TMWA_SUPPORT
1819
1820
void ActorManager::optionChanged(const std::string &name)
1821
{
1822
    if (name == "targetDeadPlayers")
1823
        mTargetDeadPlayers = config.getBoolValue("targetDeadPlayers");
1824
    else if (name == "targetOnlyReachable")
1825
        mTargetOnlyReachable = config.getBoolValue("targetOnlyReachable");
1826
    else if (name == "cyclePlayers")
1827
        mCyclePlayers = config.getBoolValue("cyclePlayers");
1828
    else if (name == "cycleMonsters")
1829
        mCycleMonsters = config.getBoolValue("cycleMonsters");
1830
    else if (name == "cycleNPC")
1831
        mCycleNPC = config.getBoolValue("cycleNPC");
1832
    else if (name == "extMouseTargeting")
1833
        mExtMouseTargeting = config.getBoolValue("extMouseTargeting");
1834
    else if (name == "showBadges")
1835
        updateBadges();
1836
    else if (name == "visiblenamespos")
1837
        updateBadges();
1838
    else if (name == "enableIdCollecting")
1839
        mEnableIdCollecting = config.getBoolValue("enableIdCollecting");
1840
}
1841
1842
void ActorManager::removeAttackMob(const std::string &name)
1843
{
1844
    mPriorityAttackMobs.remove(name);
1845
    mAttackMobs.remove(name);
1846
    mIgnoreAttackMobs.remove(name);
1847
    mPriorityAttackMobsSet.erase(name);
1848
    mAttackMobsSet.erase(name);
1849
    mIgnoreAttackMobsSet.erase(name);
1850
    rebuildAttackMobs();
1851
    rebuildPriorityAttackMobs();
1852
    storeAttackList();
1853
}
1854
1855
void ActorManager::removePickupItem(const std::string &name)
1856
{
1857
    mPickupItems.remove(name);
1858
    mPickupItemsSet.erase(name);
1859
    mIgnorePickupItems.remove(name);
1860
    mIgnorePickupItemsSet.erase(name);
1861
    rebuildPickupItems();
1862
    storeAttackList();
1863
}
1864
1865
#define addMobToList(name, mob) \
1866
{\
1867
    const int sz = get##mob##sSize();\
1868
    if (sz > 0)\
1869
    {\
1870
        const int idx = get##mob##Index("");\
1871
        if (idx + 1 == sz)\
1872
        {\
1873
            std::list<std::string>::iterator itr = m##mob##s.end();\
1874
            -- itr;\
1875
            m##mob##s.insert(itr, name);\
1876
        }\
1877
        else\
1878
        {\
1879
            m##mob##s.push_back(name);\
1880
        }\
1881
    }\
1882
    else\
1883
    {\
1884
        m##mob##s.push_back(name);\
1885
    }\
1886
    m##mob##sSet.insert(name);\
1887
    rebuild##mob##s();\
1888
}
1889
1890
#define rebuildMobsList(mob) \
1891
{\
1892
    m##mob##sMap.clear();\
1893
    std::list<std::string>::const_iterator i = m##mob##s.begin();\
1894
    int cnt = 0;\
1895
    while (i != m##mob##s.end())\
1896
    {\
1897
        m##mob##sMap[*i] = cnt;\
1898
        ++ i;\
1899
        ++ cnt;\
1900
    }\
1901
}
1902
1903
void ActorManager::addAttackMob(const std::string &name)
1904
{
1905
    addMobToList(name, AttackMob);
1906
    rebuildPriorityAttackMobs();
1907
    storeAttackList();
1908
}
1909
1910
void ActorManager::addPriorityAttackMob(const std::string &name)
1911
{
1912
    addMobToList(name, PriorityAttackMob);
1913
    storeAttackList();
1914
}
1915
1916
void ActorManager::addIgnoreAttackMob(const std::string &name)
1917
{
1918
    mIgnoreAttackMobs.push_back(name);
1919
    mIgnoreAttackMobsSet.insert(name);
1920
    rebuildAttackMobs();
1921
    rebuildPriorityAttackMobs();
1922
    storeAttackList();
1923
}
1924
1925
void ActorManager::addPickupItem(const std::string &name)
1926
{
1927
    addMobToList(name, PickupItem);
1928
    rebuildPickupItems();
1929
    storeAttackList();
1930
}
1931
1932
void ActorManager::addIgnorePickupItem(const std::string &name)
1933
{
1934
    mIgnorePickupItems.push_back(name);
1935
    mIgnorePickupItemsSet.insert(name);
1936
    rebuildPickupItems();
1937
    storeAttackList();
1938
}
1939
1940
10
void ActorManager::rebuildPriorityAttackMobs()
1941
{
1942
50
    rebuildMobsList(PriorityAttackMob);
1943
10
}
1944
1945
10
void ActorManager::rebuildAttackMobs()
1946
{
1947
80
    rebuildMobsList(AttackMob);
1948
10
}
1949
1950
10
void ActorManager::rebuildPickupItems()
1951
{
1952
80
    rebuildMobsList(PickupItem);
1953
10
}
1954
1955
int ActorManager::getIndexByName(const std::string &name,
1956
                                 const StringIntMap &map)
1957
{
1958
    const StringIntMapCIter i = map.find(name);
1959
    if (i == map.end())
1960
        return -1;
1961
1962
    return (*i).second;
1963
}
1964
1965
int ActorManager::getPriorityAttackMobIndex(const std::string &name) const
1966
{
1967
    return getIndexByName(name, mPriorityAttackMobsMap);
1968
}
1969
1970
int ActorManager::getAttackMobIndex(const std::string &name) const
1971
{
1972
    return getIndexByName(name, mAttackMobsMap);
1973
}
1974
1975
int ActorManager::getPickupItemIndex(const std::string &name) const
1976
{
1977
    return getIndexByName(name, mPickupItemsMap);
1978
}
1979
1980
#define loadList(key, mob) \
1981
{\
1982
    list = unpackList(serverConfig.getValue(key, ""));\
1983
    i = list.begin();\
1984
    i_end = list.end();\
1985
    while (i != i_end)\
1986
    {\
1987
        if ((*i).empty())\
1988
            empty = true;\
1989
        m##mob##s.push_back(*i);\
1990
        m##mob##sSet.insert(*i);\
1991
        ++ i;\
1992
    }\
1993
}
1994
1995
10
void ActorManager::loadAttackList()
1996
{
1997
10
    bool empty = false;
1998
20
    std::list<std::string> list;
1999
10
    std::list<std::string>::const_iterator i;
2000
10
    std::list<std::string>::const_iterator i_end;
2001
2002



120
    loadList("attackPriorityMobs", PriorityAttackMob);
2003



120
    loadList("attackMobs", AttackMob);
2004



120
    loadList("ignoreAttackMobs", IgnoreAttackMob);
2005
10
    if (!empty)
2006
    {
2007

40
        mAttackMobs.push_back("");
2008
50
        mAttackMobsSet.insert("");
2009
    }
2010
10
    empty = false;
2011
2012



120
    loadList("pickupItems", PickupItem);
2013



120
    loadList("ignorePickupItems", IgnorePickupItem);
2014
10
    if (!empty)
2015
    {
2016

40
        mPickupItems.push_back("");
2017
50
        mPickupItemsSet.insert("");
2018
    }
2019
2020
10
    rebuildAttackMobs();
2021
10
    rebuildPriorityAttackMobs();
2022
10
    rebuildPickupItems();
2023
10
}
2024
2025
10
void ActorManager::storeAttackList() const
2026
{
2027

50
    serverConfig.setValue("attackPriorityMobs", packList(mPriorityAttackMobs));
2028

50
    serverConfig.setValue("attackMobs", packList(mAttackMobs));
2029

50
    serverConfig.setValue("ignoreAttackMobs", packList(mIgnoreAttackMobs));
2030
2031

50
    serverConfig.setValue("pickupItems", packList(mPickupItems));
2032

50
    serverConfig.setValue("ignorePickupItems", packList(mIgnorePickupItems));
2033
10
}
2034
2035
bool ActorManager::checkForPickup(const FloorItem *const item) const
2036
{
2037
    if (mPickupItemsSet.find("") != mPickupItemsSet.end())
2038
    {
2039
        if (mIgnorePickupItemsSet.find(item->getName())
2040
            == mIgnorePickupItemsSet.end())
2041
        {
2042
            return true;
2043
        }
2044
    }
2045
    else if ((item != nullptr) && mPickupItemsSet.find(item->getName())
2046
             != mPickupItemsSet.end())
2047
    {
2048
        return true;
2049
    }
2050
    return false;
2051
}
2052
2053
bool ActorManager::checkDefaultPickup() const
2054
{
2055
    return mPickupItemsSet.find("") != mPickupItemsSet.end();
2056
}
2057
2058
void ActorManager::updateEffects(const std::map<BeingTypeId, int> &addEffects,
2059
                                 const std::set<BeingTypeId> &removeEffects)
2060
                                 const
2061
{
2062
    for_actorsm
2063
    {
2064
// disabled for performance
2065
//      if (reportTrue(*it == nullptr))
2066
//          continue;
2067
        if ((*it)->getType() != ActorType::Npc)
2068
            continue;
2069
        Being *const being = static_cast<Being*>(*it);
2070
        const BeingTypeId type = being->getSubType();
2071
        if (removeEffects.find(type) != removeEffects.end())
2072
            being->removeSpecialEffect();
2073
        const std::map<BeingTypeId, int>::const_iterator
2074
            idAdd = addEffects.find(type);
2075
        if (idAdd != addEffects.end())
2076
            being->addSpecialEffect((*idAdd).second);
2077
    }
2078
}
2079
2080
Being *ActorManager::cloneBeing(const Being *const srcBeing,
2081
                                const int dx, const int dy,
2082
                                const int id)
2083
{
2084
    returnNullptr(nullptr, srcBeing);
2085
    Being *const dstBeing = actorManager->createBeing(fromInt(
2086
        toInt(srcBeing->getId(), int) + id, BeingId),
2087
        ActorType::Player,
2088
        srcBeing->getSubType());
2089
    if (dstBeing == nullptr)
2090
        return nullptr;
2091
    dstBeing->setGender(srcBeing->getGender());
2092
    dstBeing->setAction(srcBeing->getCurrentAction(), 0);
2093
    dstBeing->setTileCoords(srcBeing->getTileX() + dx,
2094
        srcBeing->getTileY() + dy);
2095
    dstBeing->setName(srcBeing->getName());
2096
    dstBeing->setDirection(srcBeing->getDirection());
2097
    const int sz = CAST_S32(srcBeing->mSprites.size());
2098
    for (int slot = 0; slot < sz; slot ++)
2099
    {
2100
        const int spriteId = srcBeing->getSpriteID(slot);
2101
        const ItemColor color = srcBeing->getSpriteColor(slot);
2102
        dstBeing->setSpriteColorId(slot,
2103
            spriteId,
2104
            color);
2105
    }
2106
    const int hairSlot = charServerHandler->hairSprite();
2107
    const int hairStyle = -srcBeing->getSpriteID(hairSlot);
2108
    const ItemColor hairColor = srcBeing->getHairColor();
2109
    if (hairStyle != 0)
2110
    {
2111
        dstBeing->setSpriteColor(hairSlot,
2112
            hairStyle * -1,
2113
            ItemDB::get(-hairStyle).getDyeColorsString(hairColor));
2114
    }
2115
    else
2116
    {
2117
        dstBeing->unSetSprite(hairSlot);
2118
    }
2119
    dstBeing->setHairColor(hairColor);
2120
    return dstBeing;
2121
}
2122
2123
void ActorManager::updateBadges() const
2124
{
2125
    const BadgeDrawType::Type showBadges = static_cast<BadgeDrawType::Type>(
2126
        config.getIntValue("showBadges"));
2127
    Being::mShowBadges = showBadges;
2128
    Being::mVisibleNamePos = static_cast<VisibleNamePos::Type>(
2129
        config.getIntValue("visiblenamespos"));
2130
2131
    for_actors
2132
    {
2133
        ActorSprite *const actor = *it;
2134
        if (actor->getType() == ActorType::Player)
2135
        {
2136
            Being *const being = static_cast<Being*>(actor);
2137
            being->showBadges(showBadges != BadgeDrawType::Hide);
2138
        }
2139
    }
2140
}
2141
2142
void ActorManager::updateNameId(const std::string &name,
2143
                                const BeingId beingId)
2144
{
2145
    if (!mEnableIdCollecting)
2146
        return;
2147
    const int id = CAST_S32(beingId);
2148
    if ((id != 0) &&
2149
        (id < 2000000 ||
2150
        id >= 110000000))
2151
    {
2152
        return;
2153
    }
2154
2155
    if (mIdName.find(beingId) == mIdName.end() ||
2156
        mIdName[beingId].find(name) == mIdName[beingId].end())
2157
    {
2158
        mIdName[beingId].insert(name);
2159
        const std::string idStr = toString(id);
2160
        const std::string dateStr = getDateTimeString();
2161
        std::string dir;
2162
        if (beingId != BeingId_zero)
2163
        {
2164
            dir = pathJoin(settings.usersIdDir,
2165
                idStr,
2166
                stringToHexPath(name));
2167
            Files::saveTextFile(dir,
2168
                "info.txt",
2169
                (name + "\n").append(dateStr));
2170
        }
2171
2172
        dir = settings.usersDir;
2173
        dir.append(stringToHexPath(name));
2174
        Files::saveTextFile(dir,
2175
            "seen.txt",
2176
            (name + "\n").append(idStr).append("\n").append(dateStr));
2177
    }
2178
}
2179
2180
void ActorManager::updateSeenPlayers(const std::set<std::string>
2181
                                     &onlinePlayers)
2182
{
2183
    if (!mEnableIdCollecting)
2184
        return;
2185
2186
    FOR_EACH (std::set<std::string>::const_iterator, it, onlinePlayers)
2187
    {
2188
        const std::string name = *it;
2189
        if (findBeingByName(name, ActorType::Player) == nullptr)
2190
            updateNameId(name, BeingId_zero);
2191
    }
2192
}
2193
2194
std::string ActorManager::getSeenPlayerById(const BeingId id) const
2195
{
2196
    if (!mEnableIdCollecting)
2197
        return std::string();
2198
2199
    const IdNameMappingCIter it = mIdName.find(id);
2200
    if (it != mIdName.end())
2201
    {
2202
        if (!it->second.empty())
2203
            return *(it->second.begin());
2204
    }
2205
    return std::string();
2206
}
2207
2208
void ActorManager::removeRoom(const int chatId)
2209
{
2210
    for_actors
2211
    {
2212
        ActorSprite *const actor = *it;
2213
        if (actor->getType() == ActorType::Npc)
2214
        {
2215
            Being *const being = static_cast<Being*>(actor);
2216
            const ChatObject *const chat = being->getChat();
2217
            if ((chat != nullptr) && chat->chatId == chatId)
2218
            {
2219
                being->setChat(nullptr);
2220
            }
2221
        }
2222
    }
2223
}
2224
2225
void ActorManager::updateRoom(const ChatObject *const newChat)
2226
{
2227
    if (newChat == nullptr)
2228
        return;
2229
2230
    for_actors
2231
    {
2232
        const ActorSprite *const actor = *it;
2233
        if (actor->getType() == ActorType::Npc)
2234
        {
2235
            const Being *const being = static_cast<const Being*>(actor);
2236
            ChatObject *const chat = being->getChat();
2237
            if ((chat != nullptr) && chat->chatId == newChat->chatId)
2238
            {
2239
                chat->ownerId = newChat->ownerId;
2240
                chat->maxUsers = newChat->maxUsers;
2241
                chat->type = newChat->type;
2242
                chat->title = newChat->title;
2243
            }
2244
        }
2245
    }
2246
}
2247
2248
std::string ActorManager::findCharById(const int32_t id)
2249
{
2250
    const std::map<int32_t, std::string>::const_iterator it = mChars.find(id);
2251
    if (it == mChars.end())
2252
        return std::string();
2253
    return (*it).second;
2254
}
2255
2256
void ActorManager::addChar(const int32_t id,
2257
                           const std::string &name)
2258
{
2259
    mChars[id] = name;
2260
2261
    if (guiInput == nullptr)
2262
        return;
2263
2264
    guiInput->simulateMouseMove();
2265
4
}