GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/actormanager.cpp Lines: 136 981 13.9 %
Date: 2021-03-17 Branches: 137 1424 9.6 %

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-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
#include "actormanager.h"
25
26
#include "game.h"
27
#include "guild.h"
28
#include "configuration.h"
29
#include "settings.h"
30
31
#include "being/localplayer.h"
32
#include "being/playerrelations.h"
33
34
#include "gui/sdlinput.h"
35
#include "gui/viewport.h"
36
37
#include "gui/widgets/tabs/chat/chattab.h"
38
39
#include "gui/windows/equipmentwindow.h"
40
#include "gui/windows/socialwindow.h"
41
#include "gui/windows/questswindow.h"
42
43
#include "fs/files.h"
44
45
#include "input/inputmanager.h"
46
47
#include "utils/checkutils.h"
48
#include "utils/foreach.h"
49
#include "utils/mathutils.h"
50
#include "utils/gettext.h"
51
52
#include "net/beinghandler.h"
53
#include "net/charserverhandler.h"
54
#include "net/packetlimiter.h"
55
#include "net/playerhandler.h"
56
#include "net/serverfeatures.h"
57
58
#include "resources/chatobject.h"
59
#include "resources/iteminfo.h"
60
61
#include "resources/map/map.h"
62
63
#include "resources/db/itemdb.h"
64
65
#ifdef TMWA_SUPPORT
66
#include "being/playerinfo.h"
67
68
#include "gui/windows/chatwindow.h"
69
70
#include "net/net.h"
71
#endif  // TMWA_SUPPORT
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
5
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

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

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

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

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

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

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

20
    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




235
    mIgnorePickupItemsSet()
242
{
243

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

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

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

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

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

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

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

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

20
    config.addListener("visiblenamespos", this);
252
253
5
    loadAttackList();
254
5
}
255
256
125
ActorManager::~ActorManager()
257
{
258
5
    config.removeListeners(this);
259
    CHECKLISTENERS
260
5
    storeAttackList();
261
5
    clear();
262
10
}
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(std::string()) !=
722
        mPickupItemsSet.end();
723
    if (!serverBuggy)
724
    {
725
        for_actorsm
726
        {
727
// disabled for performance
728
//            if (reportTrue(*it == nullptr))
729
//                continue;
730
731
            if ((*it)->getType() == ActorType::FloorItem
732
                && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2)
733
                && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2))
734
            {
735
                FloorItem *const item = static_cast<FloorItem*>(*it);
736
                if (allowAll)
737
                {
738
                    if (mIgnorePickupItemsSet.find(item->getName())
739
                        == mIgnorePickupItemsSet.end())
740
                    {
741
                        if (localPlayer->pickUp(item))
742
                            finded = true;
743
                    }
744
                }
745
                else
746
                {
747
                    if (mPickupItemsSet.find(item->getName())
748
                        != mPickupItemsSet.end())
749
                    {
750
                        if (localPlayer->pickUp(item))
751
                            finded = true;
752
                    }
753
                }
754
            }
755
        }
756
    }
757
    else if (PacketLimiter::checkPackets(PacketType::PACKET_PICKUP))
758
    {
759
        FloorItem *item = nullptr;
760
        unsigned cnt = 65535;
761
        for_actorsm
762
        {
763
// disabled for performance
764
//            if (reportTrue(*it == nullptr))
765
//                continue;
766
767
            if ((*it)->getType() == ActorType::FloorItem
768
                && ((*it)->getTileX() >= x1 && (*it)->getTileX() <= x2)
769
                && ((*it)->getTileY() >= y1 && (*it)->getTileY() <= y2))
770
            {
771
                FloorItem *const tempItem = static_cast<FloorItem*>(*it);
772
                if (tempItem->getPickupCount() < cnt)
773
                {
774
                    if (allowAll)
775
                    {
776
                        if (mIgnorePickupItemsSet.find(tempItem->getName())
777
                            == mIgnorePickupItemsSet.end())
778
                        {
779
                            item = tempItem;
780
                            cnt = item->getPickupCount();
781
                            if (cnt == 0)
782
                            {
783
                                item->incrementPickup();
784
                                localPlayer->pickUp(item);
785
                                return true;
786
                            }
787
                        }
788
                    }
789
                    else
790
                    {
791
                        if (mPickupItemsSet.find(tempItem->getName())
792
                            != mPickupItemsSet.end())
793
                        {
794
                            item = tempItem;
795
                            cnt = item->getPickupCount();
796
                            if (cnt == 0)
797
                            {
798
                                item->incrementPickup();
799
                                localPlayer->pickUp(item);
800
                                return true;
801
                            }
802
                        }
803
                    }
804
                }
805
            }
806
        }
807
        if ((item != nullptr) && localPlayer->pickUp(item))
808
            finded = true;
809
    }
810
    return finded;
811
}
812
813
bool ActorManager::pickUpNearest(const int x, const int y,
814
                                 int maxdist) const
815
{
816
    if (localPlayer == nullptr)
817
        return false;
818
819
    maxdist = maxdist * maxdist;
820
    FloorItem *closestItem = nullptr;
821
    int dist = 0;
822
    const bool allowAll = mPickupItemsSet.find(std::string()) !=
823
        mPickupItemsSet.end();
824
825
    for_actorsm
826
    {
827
// disabled for performance
828
//        if (reportTrue(*it == nullptr))
829
//            continue;
830
831
        if ((*it)->getType() == ActorType::FloorItem)
832
        {
833
            FloorItem *const item = static_cast<FloorItem*>(*it);
834
835
            const int d = (item->getTileX() - x) * (item->getTileX() - x)
836
                + (item->getTileY() - y) * (item->getTileY() - y);
837
838
            if ((d < dist || closestItem == nullptr) &&
839
                (!mTargetOnlyReachable || localPlayer->isReachable(
840
                item->getTileX(), item->getTileY(),
841
                false)))
842
            {
843
                if (allowAll)
844
                {
845
                    if (mIgnorePickupItemsSet.find(item->getName())
846
                        == mIgnorePickupItemsSet.end())
847
                    {
848
                        dist = d;
849
                        closestItem = item;
850
                    }
851
                }
852
                else
853
                {
854
                    if (mPickupItemsSet.find(item->getName())
855
                        != mPickupItemsSet.end())
856
                    {
857
                        dist = d;
858
                        closestItem = item;
859
                    }
860
                }
861
            }
862
        }
863
    }
864
    if ((closestItem != nullptr) && dist <= maxdist)
865
        return localPlayer->pickUp(closestItem);
866
867
    return false;
868
}
869
870
Being *ActorManager::findBeingByName(const std::string &name,
871
                                     const ActorTypeT type) const
872
{
873
    for_actorsm
874
    {
875
// disabled for performance
876
//        if (reportTrue(*it == nullptr))
877
//            continue;
878
879
        if ((*it)->getType() == ActorType::FloorItem
880
            || (*it)->getType() == ActorType::Portal)
881
        {
882
            continue;
883
        }
884
885
        Being *const being = static_cast<Being*>(*it);
886
        if (being->getName() == name &&
887
            (type == ActorType::Unknown || type == being->getType()))
888
        {
889
            return being;
890
        }
891
    }
892
    return nullptr;
893
}
894
895
Being *ActorManager::findNearestByName(const std::string &name,
896
                                       const ActorTypeT &type) const
897
{
898
    if (localPlayer == nullptr)
899
        return nullptr;
900
901
    int dist = 0;
902
    Being* closestBeing = nullptr;
903
    int x = localPlayer->getTileX();
904
    int 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
5
void ActorManager::clear()
1007
{
1008
5
    if (beingEquipmentWindow != nullptr)
1009
        beingEquipmentWindow->setBeing(nullptr);
1010
1011
5
    if (localPlayer != nullptr)
1012
    {
1013
5
        localPlayer->setTarget(nullptr);
1014
5
        localPlayer->unSetPickUpTarget();
1015
10
        mActors.erase(localPlayer);
1016
    }
1017
1018
15
    for_actors
1019
7
        delete *it;
1020
10
    mActors.clear();
1021
10
    mDeleteActors.clear();
1022
10
    mActorsIdMap.clear();
1023
1024
5
    if (localPlayer != nullptr)
1025
    {
1026
10
        mActors.insert(localPlayer);
1027
10
        mActorsIdMap[localPlayer->getId()] = localPlayer;
1028
    }
1029
1030
10
    mChars.clear();
1031
5
}
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
2
Being *ActorManager::findNearestLivingBeing(const Being *const aroundBeing,
1105
                                            const int maxDist,
1106
                                            const ActorTypeT type,
1107
                                            const AllowSort allowSort) const
1108
{
1109
2
    if (aroundBeing == nullptr)
1110
        return nullptr;
1111
1112
4
    return findNearestLivingBeing(aroundBeing,
1113
        maxDist,
1114
        type,
1115
2
        aroundBeing->getTileX(),
1116
2
        aroundBeing->getTileY(),
1117
        aroundBeing,
1118
2
        allowSort);
1119
}
1120
1121
2
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

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

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

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

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


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

4
        && type == ActorType::Monster;
1157
    const bool modActive = inputManager.isActionActive(
1158
2
        InputAction::STOP_ATTACK);
1159
1160
2
    bool ignoreDefault = false;
1161
2
    if (filtered)
1162
    {
1163
2
        attackMobs = mAttackMobsSet;
1164
2
        priorityMobs = mPriorityAttackMobsSet;
1165
2
        ignoreAttackMobs = mIgnoreAttackMobsSet;
1166
2
        attackMobsMap = mAttackMobsMap;
1167
2
        priorityMobsMap = mPriorityAttackMobsMap;
1168
1
        beingActorSorter.attackBeings = &attackMobsMap;
1169
1
        beingActorSorter.priorityBeings = &priorityMobsMap;
1170
1
        beingActorSorter.specialDistance = specialDistance;
1171
1
        beingActorSorter.attackRange = attackRange;
1172
4
        if (ignoreAttackMobs.find(std::string()) != ignoreAttackMobs.end())
1173
            ignoreDefault = true;
1174
4
        StringIntMapCIter itr = attackMobsMap.find(std::string());
1175
1
        if (itr != attackMobsMap.end())
1176
1
            defaultAttackIndex = (*itr).second;
1177
3
        itr = priorityMobsMap.find(std::string());
1178
1
        if (itr != priorityMobsMap.end())
1179
            defaultPriorityIndex = (*itr).second;
1180
    }
1181
1182
2
    if (cycleSelect)
1183
    {
1184
4
        STD_VECTOR<Being*> sortedBeings;
1185
1186
6
        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
2
        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
        IgnoreRecord_false,
1601
        TryRemoveColors_true);
1602
    debugChatTab->chatLog(header,
1603
        ChatMsgType::BY_SERVER,
1604
        IgnoreRecord_false,
1605
        TryRemoveColors_true);
1606
    for_actors
1607
    {
1608
// disabled for performance
1609
//        if (reportTrue(*it == nullptr))
1610
//            continue;
1611
1612
        if ((*it)->getType() == ActorType::FloorItem)
1613
            continue;
1614
1615
        const Being *const being = static_cast<const Being*>(*it);
1616
1617
        debugChatTab->chatLog(strprintf("%s (%d,%d) %d",
1618
            being->getName().c_str(), being->getTileX(), being->getTileY(),
1619
            toInt(being->getSubType(), int)),
1620
            ChatMsgType::BY_SERVER,
1621
            IgnoreRecord_false,
1622
            TryRemoveColors_true);
1623
        if (mActorsIdMap.find(being->getId()) == mActorsIdMap.end())
1624
        {
1625
            debugChatTab->chatLog("missing in id map: %s",
1626
                being->getName().c_str());
1627
        }
1628
    }
1629
    debugChatTab->chatLog("---------------------------------------",
1630
        ChatMsgType::BY_SERVER,
1631
        IgnoreRecord_false,
1632
        TryRemoveColors_true);
1633
    FOR_EACH (ActorSpritesMapConstIterator, itr, mActorsIdMap)
1634
    {
1635
        const ActorSprite *const actor = (*itr).second;
1636
        if (actor == nullptr)
1637
            continue;
1638
        if (actor->getId() != (*itr).first)
1639
            debugChatTab->chatLog("Actor with wrong key in map", "");
1640
1641
        bool found(false);
1642
1643
        for_actors
1644
        {
1645
// disabled for performance
1646
//            if (!*it)
1647
//                continue;
1648
1649
            if ((*it)->getId() == actor->getId())
1650
            {
1651
                found = true;
1652
                break;
1653
            }
1654
        }
1655
        if (!found)
1656
            debugChatTab->chatLog("Actor present in map but not in set", "");
1657
    }
1658
}
1659
1660
void ActorManager::printBeingsToChat(const STD_VECTOR<Being*> &beings,
1661
                                     const std::string &header)
1662
{
1663
    if (debugChatTab == nullptr)
1664
        return;
1665
1666
    debugChatTab->chatLog("---------------------------------------",
1667
        ChatMsgType::BY_SERVER,
1668
        IgnoreRecord_false,
1669
        TryRemoveColors_true);
1670
    debugChatTab->chatLog(header,
1671
        ChatMsgType::BY_SERVER,
1672
        IgnoreRecord_false,
1673
        TryRemoveColors_true);
1674
1675
    FOR_EACH (STD_VECTOR<Being*>::const_iterator, i, beings)
1676
    {
1677
        if (*i == nullptr)
1678
            continue;
1679
1680
        const Being *const being = *i;
1681
1682
        debugChatTab->chatLog(strprintf("%s (%d,%d) %d",
1683
            being->getName().c_str(), being->getTileX(), being->getTileY(),
1684
            toInt(being->getSubType(), int)),
1685
            ChatMsgType::BY_SERVER,
1686
            IgnoreRecord_false,
1687
            TryRemoveColors_true);
1688
    }
1689
    debugChatTab->chatLog("---------------------------------------",
1690
        ChatMsgType::BY_SERVER,
1691
        IgnoreRecord_false,
1692
        TryRemoveColors_true);
1693
}
1694
1695
4
void ActorManager::getPlayerNames(StringVect &names,
1696
                                  const NpcNames npcNames) const
1697
{
1698
4
    names.clear();
1699
1700
12
    for_actors
1701
    {
1702
// disabled for performance
1703
//      if (reportTrue(*it == nullptr))
1704
//          continue;
1705
1706

12
        if ((*it)->getType() == ActorType::FloorItem ||
1707
6
            (*it)->getType() == ActorType::Portal)
1708
        {
1709
            continue;
1710
        }
1711
1712
6
        const Being *const being = static_cast<const Being*>(*it);
1713

12
        if ((being->getType() == ActorType::Player ||
1714
            (being->getType() == ActorType::Npc &&
1715

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



60
    loadList("attackPriorityMobs", PriorityAttackMob)
2021



60
    loadList("attackMobs", AttackMob)
2022



60
    loadList("ignoreAttackMobs", IgnoreAttackMob)
2023
5
    if (!empty)
2024
    {
2025

20
        mAttackMobs.push_back("");
2026
25
        mAttackMobsSet.insert("");
2027
    }
2028
5
    empty = false;
2029
2030



60
    loadList("pickupItems", PickupItem)
2031



60
    loadList("ignorePickupItems", IgnorePickupItem)
2032
5
    if (!empty)
2033
    {
2034

20
        mPickupItems.push_back("");
2035
25
        mPickupItemsSet.insert("");
2036
    }
2037
2038
5
    rebuildAttackMobs();
2039
5
    rebuildPriorityAttackMobs();
2040
5
    rebuildPickupItems();
2041
5
}
2042
2043
5
void ActorManager::storeAttackList() const
2044
{
2045

25
    serverConfig.setValue("attackPriorityMobs", packList(mPriorityAttackMobs));
2046

25
    serverConfig.setValue("attackMobs", packList(mAttackMobs));
2047

25
    serverConfig.setValue("ignoreAttackMobs", packList(mIgnoreAttackMobs));
2048
2049

25
    serverConfig.setValue("pickupItems", packList(mPickupItems));
2050

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