GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/progs/manaplus/actions/actions.cpp Lines: 1 774 0.1 %
Date: 2021-03-17 Branches: 0 947 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2012-2019  The ManaPlus Developers
4
 *  Copyright (C) 2019-2021  Andrei Karas
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "actions/actions.h"
23
24
#include "actormanager.h"
25
#include "configuration.h"
26
#include "game.h"
27
#ifdef USE_OPENGL
28
#include "graphicsmanager.h"
29
#endif  // USE_OPENGL
30
#include "main.h"
31
#include "spellmanager.h"
32
33
#include "actions/actiondef.h"
34
35
#include "being/localplayer.h"
36
#include "being/playerinfo.h"
37
38
#include "const/spells.h"
39
40
#include "const/resources/skill.h"
41
42
#include "fs/files.h"
43
44
#include "gui/gui.h"
45
#include "gui/popupmanager.h"
46
#include "gui/sdlinput.h"
47
#include "gui/windowmanager.h"
48
49
#include "gui/shortcut/dropshortcut.h"
50
#include "gui/shortcut/emoteshortcut.h"
51
#include "gui/shortcut/itemshortcut.h"
52
53
#include "gui/popups/popupmenu.h"
54
55
#include "gui/windows/buydialog.h"
56
#include "gui/windows/okdialog.h"
57
#include "gui/windows/tradewindow.h"
58
#include "gui/windows/quitdialog.h"
59
#include "gui/windows/buyselldialog.h"
60
#include "gui/windows/chatwindow.h"
61
#include "gui/windows/helpwindow.h"
62
#include "gui/windows/inventorywindow.h"
63
#include "gui/windows/itemamountwindow.h"
64
#include "gui/windows/npcdialog.h"
65
#include "gui/windows/outfitwindow.h"
66
#include "gui/windows/setupwindow.h"
67
#include "gui/windows/shopwindow.h"
68
#include "gui/windows/shortcutwindow.h"
69
#include "gui/windows/skilldialog.h"
70
#include "gui/windows/whoisonline.h"
71
72
#include "gui/widgets/createwidget.h"
73
74
#include "gui/widgets/tabs/chat/chattab.h"
75
76
#include "input/inputactionoperators.h"
77
78
#if defined USE_OPENGL
79
#include "render/normalopenglgraphics.h"
80
#endif  // USE_OPENGL
81
82
#include "net/adminhandler.h"
83
#include "net/beinghandler.h"
84
#include "net/buyingstorehandler.h"
85
#include "net/buysellhandler.h"
86
#include "net/chathandler.h"
87
#include "net/download.h"
88
#include "net/homunculushandler.h"
89
#include "net/gamehandler.h"
90
#include "net/inventoryhandler.h"
91
#include "net/ipc.h"
92
#include "net/mercenaryhandler.h"
93
#include "net/net.h"
94
#include "net/npchandler.h"
95
#include "net/serverfeatures.h"
96
#include "net/uploadcharinfo.h"
97
#include "net/tradehandler.h"
98
#include "net/vendinghandler.h"
99
100
#include "resources/iteminfo.h"
101
#include "resources/memorymanager.h"
102
103
#include "resources/resourcemanager/resourcemanager.h"
104
105
#include "utils/chatutils.h"
106
#include "utils/foreach.h"
107
#include "utils/gettext.h"
108
#include "utils/parameters.h"
109
#include "utils/timer.h"
110
111
#ifdef TMWA_SUPPORT
112
#include "net/playerhandler.h"
113
114
#include "utils/mathutils.h"
115
#endif  // TMWA_SUPPORT
116
117
PRAGMA48(GCC diagnostic push)
118
PRAGMA48(GCC diagnostic ignored "-Wshadow")
119
#ifdef ANDROID
120
#ifndef USE_SDL2
121
#include <SDL_screenkeyboard.h>
122
#endif  // USE_OPENGL
123
#endif  // ANDROID
124
PRAGMA48(GCC diagnostic pop)
125
126
#include <sstream>
127
128
#include "debug.h"
129
130
extern std::string tradePartnerName;
131
extern QuitDialog *quitDialog;
132
extern time_t start_time;
133
extern char **environ;
134
135
namespace Actions
136
{
137
138
static int uploadUpdate(void *ptr,
139
                        const DownloadStatusT status,
140
                        size_t total A_UNUSED,
141
                        const size_t remaining A_UNUSED) A_NONNULL(1);
142
143
static int uploadUpdate(void *ptr,
144
                        const DownloadStatusT status,
145
                        size_t total A_UNUSED,
146
                        const size_t remaining A_UNUSED)
147
{
148
    if (status == DownloadStatus::Idle || status == DownloadStatus::Starting)
149
        return 0;
150
151
    UploadChatInfo *const info = reinterpret_cast<UploadChatInfo*>(ptr);
152
    if (info == nullptr)
153
        return 0;
154
155
    if (status == DownloadStatus::Complete)
156
    {
157
        std::string str = Net::Download::getUploadResponse();
158
        const size_t sz = str.size();
159
        if (sz > 0)
160
        {
161
            if (str[sz - 1] == '\n')
162
                str = str.substr(0, sz - 1);
163
            str.append(info->addStr);
164
            ChatTab *const tab = info->tab;
165
            if (chatWindow != nullptr &&
166
                (tab == nullptr || chatWindow->isTabPresent(tab)))
167
            {
168
                str = strprintf("%s [@@%s |%s@@]",
169
                    info->text.c_str(), str.c_str(), str.c_str());
170
                outStringNormal(tab, str, str);
171
            }
172
            else
173
            {
174
                CREATEWIDGET(OkDialog,
175
                    // TRANSLATORS: file uploaded message
176
                    _("File uploaded"),
177
                    str,
178
                    // TRANSLATORS: ok dialog button
179
                    _("OK"),
180
                    DialogType::OK,
181
                    Modal_true,
182
                    ShowCenter_false,
183
                    nullptr,
184
                    260);
185
            }
186
        }
187
    }
188
//    delete2(info->upload)
189
    info->upload = nullptr;
190
    delete info;
191
    return 0;
192
}
193
194
static void uploadFile(const std::string &str,
195
                       const std::string &fileName,
196
                       const std::string &addStr,
197
                       ChatTab *const tab)
198
{
199
    UploadChatInfo *const info = new UploadChatInfo;
200
    Net::Download *const upload = new Net::Download(info,
201
        "http://ix.io",
202
        &uploadUpdate,
203
        false, true, false);
204
    info->upload = upload;
205
    info->text = str;
206
    info->addStr = addStr;
207
    info->tab = tab;
208
    upload->setFile(fileName, -1);
209
    upload->start();
210
}
211
212
static Being *findBeing(const std::string &name, const bool npc)
213
{
214
    if ((localPlayer == nullptr) || (actorManager == nullptr))
215
        return nullptr;
216
217
    Being *being = nullptr;
218
219
    if (name.empty())
220
    {
221
        being = localPlayer->getTarget();
222
    }
223
    else
224
    {
225
        being = actorManager->findBeingByName(
226
            name, ActorType::Unknown);
227
    }
228
    if ((being == nullptr) && npc)
229
    {
230
        being = actorManager->findNearestLivingBeing(
231
            localPlayer, 1, ActorType::Npc, AllowSort_true);
232
        if (being != nullptr)
233
        {
234
            if (abs(being->getTileX() - localPlayer->getTileX()) > 1
235
                || abs(being->getTileY() - localPlayer->getTileY()) > 1)
236
            {
237
                being = nullptr;
238
            }
239
        }
240
    }
241
    if ((being == nullptr) && npc)
242
    {
243
        being = actorManager->findNearestLivingBeing(
244
            localPlayer, 1, ActorType::Player, AllowSort_true);
245
        if (being != nullptr)
246
        {
247
            if (abs(being->getTileX() - localPlayer->getTileX()) > 1
248
                || abs(being->getTileY() - localPlayer->getTileY()) > 1)
249
            {
250
                being = nullptr;
251
            }
252
        }
253
    }
254
    return being;
255
}
256
257
static Item *getItemByInvIndex(const int index,
258
                               const InventoryTypeT invType)
259
{
260
    const Inventory *inv = nullptr;
261
    switch (invType)
262
    {
263
        case InventoryType::Storage:
264
            inv = PlayerInfo::getStorageInventory();
265
            break;
266
267
        case InventoryType::Inventory:
268
            inv = PlayerInfo::getInventory();
269
            break;
270
        case InventoryType::Trade:
271
        case InventoryType::Npc:
272
        case InventoryType::Cart:
273
        case InventoryType::Vending:
274
        case InventoryType::MailEdit:
275
        case InventoryType::MailView:
276
        case InventoryType::Craft:
277
        case InventoryType::TypeEnd:
278
        default:
279
            break;
280
    }
281
    if (inv != nullptr)
282
        return inv->getItem(index);
283
    return nullptr;
284
}
285
286
static int getAmountFromEvent(const InputEvent &event,
287
                              Item *&item0,
288
                              const InventoryTypeT invType)
289
{
290
    Item *const item = getItemByInvIndex(atoi(event.args.c_str()),
291
        invType);
292
    item0 = item;
293
    if (item == nullptr)
294
        return 0;
295
296
    std::string str = event.args;
297
    removeToken(str, " ");
298
299
    if (str.empty())
300
        return 0;
301
302
    int amount = 0;
303
    if (str[0] == '-')
304
    {
305
        if (str.size() > 1)
306
        {
307
            amount = item->getQuantity() - atoi(str.substr(1).c_str());
308
            if (amount <= 0 || amount > item->getQuantity())
309
                amount = item->getQuantity();
310
        }
311
    }
312
    else if (str == "/")
313
    {
314
        amount = item->getQuantity() / 2;
315
    }
316
    else if (str == "all")
317
    {
318
        amount = item->getQuantity();
319
    }
320
    else
321
    {
322
        amount = atoi(str.c_str());
323
    }
324
    return amount;
325
}
326
327
impHandler(emote)
328
{
329
    const int emotion = 1 + (event.action - InputAction::EMOTE_1);
330
    if (emotion > 0)
331
    {
332
        if (emoteShortcut != nullptr)
333
            emoteShortcut->useEmotePlayer(emotion);
334
        if (Game::instance() != nullptr)
335
            Game::instance()->setValidSpeed();
336
        return true;
337
    }
338
339
    return false;
340
}
341
342
impHandler(outfit)
343
{
344
    if (inputManager.isActionActive(InputAction::WEAR_OUTFIT))
345
    {
346
        const int num = event.action - InputAction::OUTFIT_1;
347
        if ((outfitWindow != nullptr) && num >= 0)
348
        {
349
            outfitWindow->wearOutfit(num,
350
                true,
351
                false);
352
            if (Game::instance() != nullptr)
353
                Game::instance()->setValidSpeed();
354
            return true;
355
        }
356
    }
357
    else if (inputManager.isActionActive(InputAction::COPY_OUTFIT))
358
    {
359
        const int num = event.action - InputAction::OUTFIT_1;
360
        if ((outfitWindow != nullptr) && num >= 0)
361
        {
362
            outfitWindow->copyOutfit(num);
363
            if (Game::instance() != nullptr)
364
                Game::instance()->setValidSpeed();
365
            return true;
366
        }
367
    }
368
369
    return false;
370
}
371
372
impHandler0(mouseClick)
373
{
374
    if ((guiInput == nullptr) || (gui == nullptr))
375
        return false;
376
377
    int mouseX;
378
    int mouseY;
379
    Gui::getMouseState(mouseX, mouseY);
380
    guiInput->simulateMouseClick(mouseX, mouseY, MouseButton::RIGHT);
381
    return true;
382
}
383
384
impHandler0(ok)
385
{
386
    // Close the Browser if opened
387
    if ((helpWindow != nullptr) && helpWindow->isWindowVisible())
388
    {
389
        helpWindow->setVisible(Visible_false);
390
        return true;
391
    }
392
    // Close the config window, cancelling changes if opened
393
    else if ((setupWindow != nullptr) && setupWindow->isWindowVisible())
394
    {
395
        setupWindow->action(ActionEvent(nullptr, "cancel"));
396
        return true;
397
    }
398
    else if (NpcDialog *const dialog = NpcDialog::getActive())
399
    {
400
        dialog->action(ActionEvent(nullptr, "ok"));
401
        return true;
402
    }
403
    else if (popupMenu->isPopupVisible())
404
    {
405
        popupMenu->select();
406
    }
407
    return false;
408
}
409
410
impHandler(shortcut)
411
{
412
    if (itemShortcutWindow != nullptr)
413
    {
414
        const int num = itemShortcutWindow->getTabIndex();
415
        if (num >= 0 && num < CAST_S32(SHORTCUT_TABS))
416
        {
417
            if (itemShortcut[num] != nullptr)
418
            {
419
                itemShortcut[num]->useItem(event.action
420
                    - InputAction::SHORTCUT_1);
421
            }
422
        }
423
        return true;
424
    }
425
    return false;
426
}
427
428
impHandler0(quit)
429
{
430
    if (Game::instance() == nullptr)
431
        return false;
432
    if (PopupManager::isPopupMenuVisible())
433
    {
434
        PopupManager::closePopupMenu();
435
        return true;
436
    }
437
    else if (quitDialog == nullptr)
438
    {
439
        CREATEWIDGETV(quitDialog, QuitDialog,
440
            &quitDialog);
441
        quitDialog->requestMoveToTop();
442
        return true;
443
    }
444
    return false;
445
}
446
447
impHandler0(dropItem0)
448
{
449
    if (dropShortcut != nullptr)
450
    {
451
        dropShortcut->dropFirst();
452
        return true;
453
    }
454
    return false;
455
}
456
457
impHandler0(dropItem)
458
{
459
    if (dropShortcut != nullptr)
460
    {
461
        dropShortcut->dropItems(1);
462
        return true;
463
    }
464
    return false;
465
}
466
467
impHandler(dropItemId)
468
{
469
    const Inventory *const inv = PlayerInfo::getInventory();
470
    if (inv == nullptr)
471
        return false;
472
473
    // +++ ignoring item color for now
474
    Item *const item = inv->findItem(atoi(event.args.c_str()),
475
        ItemColor_one);
476
477
    if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId()))
478
    {
479
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop,
480
            inventoryWindow,
481
            item,
482
            0,
483
            0);
484
    }
485
    return true;
486
}
487
488
impHandler(dropItemInv)
489
{
490
    Item *const item = getItemByInvIndex(atoi(event.args.c_str()),
491
        InventoryType::Inventory);
492
    if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId()))
493
    {
494
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop,
495
            inventoryWindow,
496
            item,
497
            0,
498
            0);
499
    }
500
    return true;
501
}
502
503
impHandler(dropItemIdAll)
504
{
505
    const Inventory *const inv = PlayerInfo::getInventory();
506
    if (inv == nullptr)
507
        return false;
508
509
    // +++ ignoring item color for now
510
    Item *const item = inv->findItem(atoi(event.args.c_str()),
511
        ItemColor_one);
512
513
    if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId()))
514
        PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true);
515
    return true;
516
}
517
518
impHandler(dropItemInvAll)
519
{
520
    Item *const item = getItemByInvIndex(atoi(event.args.c_str()),
521
        InventoryType::Inventory);
522
    if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId()))
523
        PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true);
524
    return true;
525
}
526
527
#ifdef TMWA_SUPPORT
528
impHandler(heal)
529
{
530
    if (Net::getNetworkType() != ServerType::TMWATHENA)
531
        return false;
532
    if (actorManager != nullptr &&
533
        localPlayer != nullptr)
534
    {
535
        std::string args = event.args;
536
537
        if (!args.empty())
538
        {
539
            const Being *being = nullptr;
540
            if (args[0] == ':')
541
            {
542
                being = actorManager->findBeing(fromInt(atoi(
543
                    args.substr(1).c_str()), BeingId));
544
                if (being != nullptr && being->getType() == ActorType::Monster)
545
                    being = nullptr;
546
            }
547
            else
548
            {
549
                being = actorManager->findBeingByName(args, ActorType::Player);
550
            }
551
            if (being != nullptr)
552
                actorManager->heal(being);
553
        }
554
        else
555
        {
556
            Being *target = localPlayer->getTarget();
557
            if (inputManager.isActionActive(InputAction::STOP_ATTACK))
558
            {
559
                if (target == nullptr ||
560
                    target->getType() != ActorType::Player)
561
                {
562
                    target = actorManager->findNearestLivingBeing(
563
                        localPlayer, 10, ActorType::Player, AllowSort_true);
564
                }
565
            }
566
            else
567
            {
568
                if (target == nullptr)
569
                    target = localPlayer;
570
            }
571
            actorManager->heal(target);
572
        }
573
574
        if (Game::instance() != nullptr)
575
            Game::instance()->setValidSpeed();
576
        return true;
577
    }
578
    return false;
579
}
580
#else  // TMWA_SUPPORT
581
582
impHandler0(heal)
583
{
584
    return false;
585
}
586
#endif  // TMWA_SUPPORT
587
588
impHandler0(healmd)
589
{
590
#ifdef TMWA_SUPPORT
591
    if (Net::getNetworkType() != ServerType::TMWATHENA)
592
        return false;
593
    if (actorManager != nullptr)
594
    {
595
        const int matk = PlayerInfo::getStatEffective(Attributes::PLAYER_MATK);
596
        int maxHealingRadius;
597
598
        // magic levels < 2
599
        if (PlayerInfo::getSkillLevel(340) < 2
600
            || PlayerInfo::getSkillLevel(341) < 2)
601
        {
602
            maxHealingRadius = matk / 100 + 1;
603
        }
604
        else
605
        {
606
            maxHealingRadius = (12 * fastSqrtInt(matk) + matk) / 100 + 1;
607
        }
608
609
        Being *target = actorManager->findMostDamagedPlayer(maxHealingRadius);
610
        if (target != nullptr)
611
            actorManager->heal(target);
612
613
        if (Game::instance() != nullptr)
614
            Game::instance()->setValidSpeed();
615
        return true;
616
    }
617
#endif  // TMWA_SUPPORT
618
619
    return false;
620
}
621
622
impHandler0(itenplz)
623
{
624
#ifdef TMWA_SUPPORT
625
    if (Net::getNetworkType() != ServerType::TMWATHENA)
626
        return false;
627
    if (actorManager != nullptr)
628
    {
629
        if (playerHandler != nullptr &&
630
            playerHandler->canUseMagic() &&
631
            PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 3)
632
        {
633
            actorManager->itenplz();
634
        }
635
        return true;
636
    }
637
#endif  // TMWA_SUPPORT
638
639
    return false;
640
}
641
642
impHandler0(setHome)
643
{
644
    if (localPlayer != nullptr)
645
    {
646
        localPlayer->setHome();
647
        return true;
648
    }
649
    return false;
650
}
651
652
impHandler0(magicAttack)
653
{
654
#ifdef TMWA_SUPPORT
655
    if (Net::getNetworkType() != ServerType::TMWATHENA)
656
        return false;
657
    if (localPlayer != nullptr)
658
    {
659
        localPlayer->magicAttack();
660
        return true;
661
    }
662
#endif  // TMWA_SUPPORT
663
664
    return false;
665
}
666
667
impHandler0(copyEquippedToOutfit)
668
{
669
    if (outfitWindow != nullptr)
670
    {
671
        outfitWindow->copyFromEquiped();
672
        return true;
673
    }
674
    return false;
675
}
676
677
impHandler(pickup)
678
{
679
    if (localPlayer == nullptr)
680
        return false;
681
682
    const std::string args = event.args;
683
    if (args.empty())
684
    {
685
        localPlayer->pickUpItems(0);
686
    }
687
    else
688
    {
689
        FloorItem *const item = actorManager->findItem(fromInt(
690
            atoi(args.c_str()), BeingId));
691
        if (item != nullptr)
692
            localPlayer->pickUp(item);
693
    }
694
    return true;
695
}
696
697
static void doSit()
698
{
699
    if (inputManager.isActionActive(InputAction::EMOTE))
700
        localPlayer->updateSit();
701
    else
702
        localPlayer->toggleSit();
703
}
704
705
impHandler0(sit)
706
{
707
    if (localPlayer != nullptr)
708
    {
709
        doSit();
710
        return true;
711
    }
712
    return false;
713
}
714
715
impHandler(screenshot)
716
{
717
    Game::createScreenshot(event.args);
718
    return true;
719
}
720
721
impHandler0(ignoreInput)
722
{
723
    return true;
724
}
725
726
impHandler(buy)
727
{
728
    if (serverFeatures == nullptr)
729
        return false;
730
    const std::string args = event.args;
731
    Being *being = findBeing(args, false);
732
    if ((being == nullptr) && Net::getNetworkType() == ServerType::TMWATHENA)
733
    {
734
        if (whoIsOnline != nullptr)
735
        {
736
            const std::set<std::string> &players =
737
                whoIsOnline->getOnlineNicks();
738
            if (players.find(args) != players.end())
739
            {
740
                if (buySellHandler != nullptr)
741
                    buySellHandler->requestSellList(args);
742
                return true;
743
            }
744
        }
745
        return false;
746
    }
747
748
    if (being == nullptr)
749
        being = findBeing(args, true);
750
751
    if (being == nullptr)
752
        return false;
753
754
    if (being->getType() == ActorType::Npc)
755
    {
756
        if (npcHandler != nullptr)
757
            npcHandler->buy(being);
758
        return true;
759
    }
760
    else if (being->getType() == ActorType::Player)
761
    {
762
        if (vendingHandler != nullptr &&
763
            Net::getNetworkType() != ServerType::TMWATHENA)
764
        {
765
            vendingHandler->open(being);
766
        }
767
        else if (buySellHandler != nullptr)
768
        {
769
            buySellHandler->requestSellList(being->getName());
770
        }
771
        return true;
772
    }
773
    return false;
774
}
775
776
impHandler(sell)
777
{
778
    if (serverFeatures == nullptr)
779
        return false;
780
781
    const std::string args = event.args;
782
    Being *being = findBeing(args, false);
783
    if (being == nullptr &&
784
        Net::getNetworkType() == ServerType::TMWATHENA)
785
    {
786
        if (whoIsOnline != nullptr)
787
        {
788
            const std::set<std::string> &players =
789
                whoIsOnline->getOnlineNicks();
790
            if (players.find(args) != players.end())
791
            {
792
                if (buySellHandler != nullptr)
793
                    buySellHandler->requestBuyList(args);
794
                return true;
795
            }
796
        }
797
        return false;
798
    }
799
800
    if (being == nullptr)
801
        being = findBeing(args, true);
802
803
    if (being == nullptr)
804
        return false;
805
806
    if (being->getType() == ActorType::Npc)
807
    {
808
        if (npcHandler != nullptr)
809
            npcHandler->sell(being->getId());
810
        return true;
811
    }
812
    else if (being->getType() == ActorType::Player)
813
    {
814
        if ((buyingStoreHandler != nullptr) &&
815
            Net::getNetworkType() != ServerType::TMWATHENA)
816
        {
817
            buyingStoreHandler->open(being);
818
        }
819
        else if (buySellHandler != nullptr)
820
        {
821
            buySellHandler->requestBuyList(being->getName());
822
        }
823
        return true;
824
    }
825
    return false;
826
}
827
828
impHandler(talk)
829
{
830
    const std::string args = event.args;
831
    Being *being = nullptr;
832
833
    if (!args.empty() && args[0] == ':')
834
    {
835
        being = actorManager->findBeing(fromInt(atoi(
836
            args.substr(1).c_str()), BeingId));
837
    }
838
    else
839
    {
840
        being = findBeing(args, true);
841
    }
842
843
    if (being == nullptr)
844
        return false;
845
846
    if (being->canTalk())
847
    {
848
        being->talkTo();
849
    }
850
    else if (being->getType() == ActorType::Player)
851
    {
852
        CREATEWIDGET(BuySellDialog,
853
            being->getName());
854
    }
855
    return true;
856
}
857
858
impHandler0(stopAttack)
859
{
860
    if (localPlayer != nullptr)
861
    {
862
        localPlayer->stopAttack(false);
863
        // not consume if target attack key pressed
864
        if (inputManager.isActionActive(InputAction::TARGET_ATTACK))
865
            return false;
866
        return true;
867
    }
868
    return false;
869
}
870
871
impHandler0(untarget)
872
{
873
    if (localPlayer != nullptr)
874
    {
875
        localPlayer->untarget();
876
        return true;
877
    }
878
    return false;
879
}
880
881
impHandler(attack)
882
{
883
    if ((localPlayer == nullptr) || (actorManager == nullptr))
884
        return false;
885
886
    Being *target = nullptr;
887
    std::string args = event.args;
888
    if (!args.empty())
889
    {
890
        if (args[0] != ':')
891
        {
892
            target = actorManager->findNearestByName(args,
893
                ActorType::Unknown);
894
        }
895
        else
896
        {
897
            target = actorManager->findBeing(fromInt(atoi(
898
                args.substr(1).c_str()), BeingId));
899
            if (target != nullptr &&
900
                target->getType() != ActorType::Monster)
901
            {
902
                target = nullptr;
903
            }
904
        }
905
    }
906
    if (target == nullptr)
907
        target = localPlayer->getTarget();
908
    else
909
        localPlayer->setTarget(target);
910
    if (target != nullptr)
911
        localPlayer->attack(target, true, false);
912
    return true;
913
}
914
915
impHandler(targetAttack)
916
{
917
    if ((localPlayer != nullptr) && (actorManager != nullptr))
918
    {
919
        Being *target = nullptr;
920
        std::string args = event.args;
921
        const bool newTarget = !inputManager.isActionActive(
922
            InputAction::STOP_ATTACK);
923
924
        if (!args.empty())
925
        {
926
            if (args[0] != ':')
927
            {
928
                target = actorManager->findNearestByName(args,
929
                    ActorType::Unknown);
930
            }
931
            else
932
            {
933
                target = actorManager->findBeing(fromInt(atoi(
934
                    args.substr(1).c_str()), BeingId));
935
                if (target != nullptr &&
936
                    target->getType() != ActorType::Monster)
937
                {
938
                    target = nullptr;
939
                }
940
            }
941
        }
942
943
        if ((target == nullptr) && (settings.targetingType == 0U))
944
            target = localPlayer->getTarget();
945
946
        if (target == nullptr)
947
        {
948
            target = actorManager->findNearestLivingBeing(
949
                localPlayer, 90, ActorType::Monster, AllowSort_true);
950
        }
951
952
        localPlayer->attack2(target, newTarget, false);
953
        return true;
954
    }
955
    return false;
956
}
957
958
impHandler0(attackHuman)
959
{
960
    if ((actorManager == nullptr) || (localPlayer == nullptr))
961
        return false;
962
963
    Being *const target = actorManager->findNearestPvpPlayer();
964
    if (target != nullptr)
965
    {
966
        localPlayer->setTarget(target);
967
        localPlayer->attack2(target, true, false);
968
    }
969
    return true;
970
}
971
972
impHandler0(safeVideoMode)
973
{
974
    WindowManager::setFullScreen(false);
975
976
    return true;
977
}
978
979
impHandler0(stopSit)
980
{
981
    if (localPlayer != nullptr)
982
    {
983
        localPlayer->stopAttack(false);
984
        // not consume if target attack key pressed
985
        if (inputManager.isActionActive(InputAction::TARGET_ATTACK))
986
            return false;
987
        if (localPlayer->getTarget() == nullptr)
988
        {
989
            doSit();
990
            return true;
991
        }
992
        return true;
993
    }
994
    return false;
995
}
996
997
impHandler0(showKeyboard)
998
{
999
#if defined(ANDROID) || defined(__SWITCH__)
1000
#ifdef USE_SDL2
1001
    if (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE)
1002
        SDL_StopTextInput();
1003
    else
1004
        SDL_StartTextInput();
1005
#else  // USE_SDL2
1006
1007
    SDL_ANDROID_ToggleScreenKeyboardTextInput(nullptr);
1008
#endif  // USE_SDL2
1009
1010
    return true;
1011
#else  // ANDROID
1012
1013
    return false;
1014
#endif  // ANDROID
1015
}
1016
1017
impHandler0(showWindows)
1018
{
1019
    if (popupMenu != nullptr)
1020
    {
1021
        popupMenu->showWindowsPopup();
1022
        return true;
1023
    }
1024
    return false;
1025
}
1026
1027
impHandler0(openTrade)
1028
{
1029
    const Being *const being = localPlayer->getTarget();
1030
    if ((being != nullptr) && being->getType() == ActorType::Player)
1031
    {
1032
        if (tradeHandler != nullptr)
1033
            tradeHandler->request(being);
1034
        tradePartnerName = being->getName();
1035
        if (tradeWindow != nullptr)
1036
            tradeWindow->clear();
1037
        return true;
1038
    }
1039
    return false;
1040
}
1041
1042
impHandler0(ipcToggle)
1043
{
1044
    if (ipc != nullptr)
1045
    {
1046
        IPC::stop();
1047
        if (ipc == nullptr)
1048
        {
1049
            debugChatTab->chatLog("IPC service stopped.",
1050
                ChatMsgType::BY_SERVER,
1051
                IgnoreRecord_false,
1052
                TryRemoveColors_true);
1053
        }
1054
        else
1055
        {
1056
            debugChatTab->chatLog("Unable to stop IPC service.",
1057
                ChatMsgType::BY_SERVER,
1058
                IgnoreRecord_false,
1059
                TryRemoveColors_true);
1060
        }
1061
    }
1062
    else
1063
    {
1064
        IPC::start();
1065
        if (ipc != nullptr)
1066
        {
1067
            debugChatTab->chatLog(
1068
                strprintf("IPC service available on port %d", ipc->getPort()),
1069
                ChatMsgType::BY_SERVER,
1070
                IgnoreRecord_false,
1071
                TryRemoveColors_true);
1072
        }
1073
        else
1074
        {
1075
            debugChatTab->chatLog("Unable to start IPC service",
1076
                ChatMsgType::BY_SERVER,
1077
                IgnoreRecord_false,
1078
                TryRemoveColors_true);
1079
        }
1080
    }
1081
    return true;
1082
}
1083
1084
impHandler(where)
1085
{
1086
    ChatTab *const tab = event.tab != nullptr ? event.tab : debugChatTab;
1087
    if (tab == nullptr)
1088
        return false;
1089
    std::ostringstream where;
1090
    where << Game::instance()->getCurrentMapName() << ", coordinates: "
1091
        << ((localPlayer->getPixelX() - mapTileSize / 2) / mapTileSize)
1092
        << ", " << ((localPlayer->getPixelY() - mapTileSize) / mapTileSize);
1093
    tab->chatLog(where.str(),
1094
        ChatMsgType::BY_SERVER,
1095
        IgnoreRecord_false,
1096
        TryRemoveColors_true);
1097
    return true;
1098
}
1099
1100
impHandler0(who)
1101
{
1102
    if (chatHandler != nullptr)
1103
        chatHandler->who();
1104
    return true;
1105
}
1106
1107
impHandler0(cleanGraphics)
1108
{
1109
    ResourceManager::clearCache();
1110
1111
    if (debugChatTab != nullptr)
1112
    {
1113
        // TRANSLATORS: clear graphics command message
1114
        debugChatTab->chatLog(_("Cache cleared"),
1115
            ChatMsgType::BY_SERVER,
1116
            IgnoreRecord_false,
1117
            TryRemoveColors_true);
1118
    }
1119
    return true;
1120
}
1121
1122
impHandler0(cleanFonts)
1123
{
1124
    if (gui != nullptr)
1125
        gui->clearFonts();
1126
    if (debugChatTab != nullptr)
1127
    {
1128
        // TRANSLATORS: clear fonts cache message
1129
        debugChatTab->chatLog(_("Cache cleared"),
1130
            ChatMsgType::BY_SERVER,
1131
            IgnoreRecord_false,
1132
            TryRemoveColors_true);
1133
    }
1134
    return true;
1135
}
1136
1137
impHandler(trade)
1138
{
1139
    if (actorManager == nullptr)
1140
        return false;
1141
1142
    const Being *being = actorManager->findBeingByName(
1143
        event.args, ActorType::Player);
1144
    if (being == nullptr)
1145
        being = localPlayer->getTarget();
1146
    if (being != nullptr)
1147
    {
1148
        if (tradeHandler != nullptr)
1149
            tradeHandler->request(being);
1150
        tradePartnerName = being->getName();
1151
        if (tradeWindow != nullptr)
1152
            tradeWindow->clear();
1153
    }
1154
    return true;
1155
}
1156
1157
impHandler0(priceLoad)
1158
{
1159
    if (shopWindow != nullptr)
1160
    {
1161
        shopWindow->loadList();
1162
        return true;
1163
    }
1164
    return false;
1165
}
1166
1167
impHandler0(priceSave)
1168
{
1169
    if (shopWindow != nullptr)
1170
    {
1171
        shopWindow->saveList();
1172
        return true;
1173
    }
1174
    return false;
1175
}
1176
1177
impHandler0(cacheInfo)
1178
{
1179
    if ((chatWindow == nullptr) || (debugChatTab == nullptr))
1180
        return false;
1181
1182
/*
1183
    Font *const font = chatWindow->getFont();
1184
    if (!font)
1185
        return;
1186
1187
    const TextChunkList *const cache = font->getCache();
1188
    if (!cache)
1189
        return;
1190
1191
    unsigned int all = 0;
1192
    // TRANSLATORS: chat fonts message
1193
    debugChatTab->chatLog(_("font cache size"),
1194
        ChatMsgType::BY_SERVER,
1195
        IgnoreRecord_false,
1196
        TryRemoveColors_true);
1197
    std::string str;
1198
    for (int f = 0; f < 256; f ++)
1199
    {
1200
        if (!cache[f].size)
1201
        {
1202
            const unsigned int sz = CAST_S32(cache[f].size);
1203
            all += sz;
1204
            str.append(strprintf("%d: %u, ", f, sz));
1205
        }
1206
    }
1207
    debugChatTab->chatLog(str,
1208
        ChatMsgType::BY_SERVER,
1209
        IgnoreRecord_false,
1210
        TryRemoveColors_true);
1211
    // TRANSLATORS: chat fonts message
1212
    debugChatTab->chatLog(strprintf("%s %d", _("Cache size:"), all),
1213
        ChatMsgType::BY_SERVER,
1214
        IgnoreRecord_false,
1215
        TryRemoveColors_true);
1216
#ifdef DEBUG_FONT_COUNTERS
1217
    debugChatTab->chatLog("",
1218
        ChatMsgType::BY_SERVER,
1219
        IgnoreRecord_false,
1220
        TryRemoveColors_true);
1221
    debugChatTab->chatLog(strprintf("%s %d",
1222
        // TRANSLATORS: chat fonts message
1223
        _("Created:"), font->getCreateCounter()),
1224
        ChatMsgType::BY_SERVER,
1225
        IgnoreRecord_false,
1226
        TryRemoveColors_true);
1227
    debugChatTab->chatLog(strprintf("%s %d",
1228
        // TRANSLATORS: chat fonts message
1229
        _("Deleted:"), font->getDeleteCounter()),
1230
        ChatMsgType::BY_SERVER,
1231
        IgnoreRecord_false,
1232
        TryRemoveColors_true);
1233
#endif
1234
*/
1235
    return true;
1236
}
1237
1238
impHandler0(disconnect)
1239
{
1240
    if (gameHandler != nullptr)
1241
        gameHandler->disconnect2();
1242
    return true;
1243
}
1244
1245
impHandler(undress)
1246
{
1247
    if ((actorManager == nullptr) || (localPlayer == nullptr))
1248
        return false;
1249
1250
    const std::string args = event.args;
1251
    StringVect pars;
1252
    if (!splitParameters(pars, args, " ,", '\"'))
1253
        return false;
1254
    Being *target = nullptr;
1255
    const size_t sz = pars.size();
1256
    if (sz == 0)
1257
    {
1258
        target = localPlayer->getTarget();
1259
    }
1260
    else
1261
    {
1262
        if (pars[0][0] == ':')
1263
        {
1264
            target = actorManager->findBeing(fromInt(atoi(
1265
                pars[0].substr(1).c_str()), BeingId));
1266
            if ((target != nullptr) && target->getType() == ActorType::Monster)
1267
                target = nullptr;
1268
        }
1269
        else
1270
        {
1271
            target = actorManager->findNearestByName(args,
1272
                ActorType::Unknown);
1273
        }
1274
    }
1275
1276
    if (sz == 2)
1277
    {
1278
        if (target != nullptr)
1279
        {
1280
            const int itemId = atoi(pars[1].c_str());
1281
            target->undressItemById(itemId);
1282
        }
1283
    }
1284
    else
1285
    {
1286
        if ((target != nullptr) && (beingHandler != nullptr))
1287
            beingHandler->undress(target);
1288
    }
1289
1290
    return true;
1291
}
1292
1293
impHandler0(dirs)
1294
{
1295
    if (debugChatTab == nullptr)
1296
        return false;
1297
1298
    debugChatTab->chatLog("config directory: "
1299
        + settings.configDir,
1300
        ChatMsgType::BY_SERVER,
1301
        IgnoreRecord_false,
1302
        TryRemoveColors_true);
1303
    debugChatTab->chatLog("logs directory: "
1304
        + settings.localDataDir,
1305
        ChatMsgType::BY_SERVER,
1306
        IgnoreRecord_false,
1307
        TryRemoveColors_true);
1308
    debugChatTab->chatLog("screenshots directory: "
1309
        + settings.screenshotDir,
1310
        ChatMsgType::BY_SERVER,
1311
        IgnoreRecord_false,
1312
        TryRemoveColors_true);
1313
    debugChatTab->chatLog("temp directory: "
1314
        + settings.tempDir,
1315
        ChatMsgType::BY_SERVER,
1316
        IgnoreRecord_false,
1317
        TryRemoveColors_true);
1318
    return true;
1319
}
1320
1321
impHandler0(uptime)
1322
{
1323
    if (debugChatTab == nullptr)
1324
        return false;
1325
1326
    if (cur_time < start_time)
1327
    {
1328
        // TRANSLATORS: uptime command
1329
        debugChatTab->chatLog(strprintf(_("Client uptime: %s"), "unknown"),
1330
            ChatMsgType::BY_SERVER,
1331
            IgnoreRecord_false,
1332
            TryRemoveColors_true);
1333
    }
1334
    else
1335
    {
1336
        // TRANSLATORS: uptime command
1337
        debugChatTab->chatLog(strprintf(_("Client uptime: %s"),
1338
            timeDiffToString(CAST_S32(cur_time - start_time)).c_str()),
1339
            ChatMsgType::BY_SERVER,
1340
            IgnoreRecord_false,
1341
            TryRemoveColors_true);
1342
    }
1343
    return true;
1344
}
1345
1346
#ifdef DEBUG_DUMP_LEAKS1
1347
static void showRes(std::string str, ResourceManager::Resources *res)
1348
{
1349
    if (!res)
1350
        return;
1351
1352
    str.append(toString(res->size()));
1353
    if (debugChatTab)
1354
    {
1355
        debugChatTab->chatLog(str,
1356
            ChatMsgType::BY_SERVER,
1357
            IgnoreRecord_false,
1358
            TryRemoveColors_true);
1359
    }
1360
    logger->log(str);
1361
    ResourceManager::ResourceIterator iter = res->begin();
1362
    const ResourceManager::ResourceIterator iter_end = res->end();
1363
    while (iter != iter_end)
1364
    {
1365
        if (iter->second && iter->second->mRefCount)
1366
        {
1367
            char type = ' ';
1368
            char isNew = 'N';
1369
            if (iter->second->getDumped())
1370
                isNew = 'O';
1371
            else
1372
                iter->second->setDumped(true);
1373
1374
            SubImage *const subImage = dynamic_cast<SubImage *>(
1375
                iter->second);
1376
            Image *const image = dynamic_cast<Image *>(iter->second);
1377
            int id = 0;
1378
            if (subImage)
1379
                type = 'S';
1380
            else if (image)
1381
                type = 'I';
1382
            if (image)
1383
                id = image->getGLImage();
1384
            logger->log("Resource %c%c: %s (%d) id=%d", type,
1385
                isNew, iter->second->getIdPath().c_str(),
1386
                iter->second->mRefCount, id);
1387
        }
1388
        ++ iter;
1389
    }
1390
}
1391
1392
impHandler(dump)
1393
{
1394
    if (!debugChatTab)
1395
        return false;
1396
1397
    if (!event.args.empty())
1398
    {
1399
        ResourceManager::Resources *res = ResourceManager::getResources();
1400
        // TRANSLATORS: dump command
1401
        showRes(_("Resource images:"), res);
1402
        res = ResourceManager::getOrphanedResources();
1403
        // TRANSLATORS: dump command
1404
        showRes(_("Orphaned resource images:"), res);
1405
    }
1406
    else
1407
    {
1408
        ResourceManager::Resources *res = ResourceManager::getResources();
1409
        // TRANSLATORS: dump command
1410
        debugChatTab->chatLog(_("Resource images:") + toString(res->size()),
1411
            ChatMsgType::BY_SERVER,
1412
            IgnoreRecord_false,
1413
            TryRemoveColors_true);
1414
        res = ResourceManager::getOrphanedResources();
1415
        // TRANSLATORS: dump command
1416
        debugChatTab->chatLog(_("Orphaned resource images:")
1417
            + toString(res->size()),
1418
            ChatMsgType::BY_SERVER,
1419
            IgnoreRecord_false,
1420
            TryRemoveColors_true);
1421
    }
1422
    return true;
1423
}
1424
1425
#elif defined ENABLE_MEM_DEBUG
1426
impHandler0(dump)
1427
{
1428
    nvwa::check_leaks();
1429
    return true;
1430
}
1431
#else  // DEBUG_DUMP_LEAKS1
1432
1433
impHandler0(dump)
1434
{
1435
    return true;
1436
}
1437
#endif  // DEBUG_DUMP_LEAKS1
1438
1439
impHandler0(serverIgnoreAll)
1440
{
1441
    if (chatHandler != nullptr)
1442
        chatHandler->ignoreAll();
1443
    return true;
1444
}
1445
1446
impHandler0(serverUnIgnoreAll)
1447
{
1448
    if (chatHandler != nullptr)
1449
        chatHandler->unIgnoreAll();
1450
    return true;
1451
}
1452
1453
PRAGMA6(GCC diagnostic push)
1454
PRAGMA6(GCC diagnostic ignored "-Wnull-dereference")
1455
impHandler0(error)
1456
{
1457
    int *const ptr = nullptr;
1458
    *(ptr + 1) = 20;
1459
//    logger->log("test %d", *ptr);
1460
    exit(1);
1461
}
1462
PRAGMA6(GCC diagnostic pop)
1463
1464
impHandler(dumpGraphics)
1465
{
1466
    std::string str = strprintf("%s,%s,%dX%dX%d,", PACKAGE_OS, SMALL_VERSION,
1467
        mainGraphics->getWidth(), mainGraphics->getHeight(),
1468
        mainGraphics->getBpp());
1469
1470
    if (mainGraphics->getFullScreen())
1471
        str.append("F");
1472
    else
1473
        str.append("W");
1474
    if (mainGraphics->getHWAccel())
1475
        str.append("H");
1476
    else
1477
        str.append("S");
1478
1479
    if (mainGraphics->getDoubleBuffer())
1480
        str.append("D");
1481
    else
1482
        str.append("_");
1483
1484
#if defined USE_OPENGL
1485
    str.append(strprintf(",%d", mainGraphics->getOpenGL()));
1486
#else  // defined USE_OPENGL
1487
1488
    str.append(",0");
1489
#endif  // defined USE_OPENGL
1490
1491
    str.append(strprintf(",%f,", static_cast<double>(settings.guiAlpha)))
1492
        .append(config.getBoolValue("adjustPerfomance") ? "1" : "0")
1493
        .append(config.getBoolValue("alphaCache") ? "1" : "0")
1494
        .append(config.getBoolValue("enableMapReduce") ? "1" : "0")
1495
        .append(config.getBoolValue("beingopacity") ? "1" : "0")
1496
        .append(",")
1497
        .append(config.getBoolValue("enableAlphaFix") ? "1" : "0")
1498
        .append(config.getBoolValue("disableAdvBeingCaching") ? "1" : "0")
1499
        .append(config.getBoolValue("disableBeingCaching") ? "1" : "0")
1500
        .append(config.getBoolValue("particleeffects") ? "1" : "0")
1501
        .append(strprintf(",%d-%d", fps, config.getIntValue("fpslimit")));
1502
    outStringNormal(event.tab, str, str);
1503
    return true;
1504
}
1505
1506
impHandler0(dumpEnvironment)
1507
{
1508
    logger->log1("Start environment variables");
1509
    for (char **env = environ; *env != nullptr; ++ env)
1510
        logger->log1(*env);
1511
    logger->log1("End environment variables");
1512
    if (debugChatTab != nullptr)
1513
    {
1514
        // TRANSLATORS: dump environment command
1515
        debugChatTab->chatLog(_("Environment variables dumped"),
1516
            ChatMsgType::BY_SERVER,
1517
            IgnoreRecord_false,
1518
            TryRemoveColors_true);
1519
    }
1520
    return true;
1521
}
1522
1523
impHandler(dumpTests)
1524
{
1525
    const std::string str = config.getStringValue("testInfo");
1526
    outStringNormal(event.tab, str, str);
1527
    return true;
1528
}
1529
1530
impHandler0(dumpOGL)
1531
{
1532
#ifdef USE_OPENGL
1533
#if !defined(ANDROID) && !defined(__native_client__) && !defined(__SWITCH__)
1534
    NormalOpenGLGraphics::dumpSettings();
1535
#endif  // !defined(ANDROID) && !defined(__native_client__) &&
1536
        // !defined(__SWITCH__)
1537
#endif  // USE_OPENGL
1538
1539
    return true;
1540
}
1541
1542
#ifdef USE_OPENGL
1543
impHandler(dumpGL)
1544
{
1545
    std::string str = graphicsManager.getGLVersion();
1546
    outStringNormal(event.tab, str, str);
1547
    return true;
1548
}
1549
#else  // USE_OPENGL
1550
1551
impHandler0(dumpGL)
1552
{
1553
    return true;
1554
}
1555
#endif  // USE_OPENGL
1556
1557
impHandler(dumpMods)
1558
{
1559
    std::string str = "enabled mods: " + serverConfig.getValue("mods", "");
1560
    outStringNormal(event.tab, str, str);
1561
    return true;
1562
}
1563
1564
#if defined USE_OPENGL && defined DEBUG_SDLFONT
1565
impHandler0(testSdlFont)
1566
{
1567
    Font *font = new Font("fonts/dejavusans.ttf", 18, TTF_STYLE_NORMAL);
1568
    timespec time1;
1569
    timespec time2;
1570
    NullOpenGLGraphics *nullGraphics = new NullOpenGLGraphics;
1571
    STD_VECTOR<std::string> data;
1572
    volatile int width = 0;
1573
1574
    for (int f = 0; f < 300; f ++)
1575
        data.push_back("test " + toString(f) + "string");
1576
    nullGraphics->beginDraw();
1577
1578
    clock_gettime(CLOCK_MONOTONIC, &time1);
1579
    Color color(0, 0, 0, 255);
1580
1581
    for (int f = 0; f < 500; f ++)
1582
    {
1583
        FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, data)
1584
        {
1585
            width += font->getWidth(*it);
1586
            font->drawString(nullGraphics, color, color, *it, 10, 10);
1587
        }
1588
        FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, data)
1589
            font->drawString(nullGraphics, color, color, *it, 10, 10);
1590
1591
        font->doClean();
1592
    }
1593
1594
    clock_gettime(CLOCK_MONOTONIC, &time2);
1595
1596
    delete nullGraphics;
1597
    delete font;
1598
1599
    int64_t diff = (static_cast<long long int>(
1600
        time2.tv_sec) * 1000000000LL + static_cast<long long int>(
1601
        time2.tv_nsec)) / 100000 - (static_cast<long long int>(
1602
        time1.tv_sec) * 1000000000LL + static_cast<long long int>(
1603
        time1.tv_nsec)) / 100000;
1604
    if (debugChatTab)
1605
    {
1606
        debugChatTab->chatLog("sdlfont time: " + toString(diff),
1607
            ChatMsgType::BY_SERVER,
1608
            IgnoreRecord_false,
1609
            TryRemoveColors_true);
1610
    }
1611
    return true;
1612
}
1613
#endif  // defined USE_OPENGL && defined DEBUG_SDLFONT
1614
1615
impHandler0(createItems)
1616
{
1617
    BuyDialog *const dialog = CREATEWIDGETR0(BuyDialog);
1618
    const ItemDB::ItemInfos &items = ItemDB::getItemInfos();
1619
    FOR_EACH (ItemDB::ItemInfos::const_iterator, it, items)
1620
    {
1621
        const ItemInfo *const info = (*it).second;
1622
        if (info == nullptr)
1623
            continue;
1624
        const int id = info->getId();
1625
        if (id <= 500)
1626
            continue;
1627
1628
        dialog->addItem(id,
1629
            ItemType::Unknown,
1630
            ItemColor_one,
1631
            100,
1632
            0);
1633
    }
1634
    dialog->sort();
1635
    return true;
1636
}
1637
1638
impHandler(createItem)
1639
{
1640
    int id = 0;
1641
    int amount = 0;
1642
1643
    if (adminHandler == nullptr)
1644
        return false;
1645
1646
    if (parse2Int(event.args, id, amount))
1647
        adminHandler->createItems(id, ItemColor_one, amount);
1648
    else
1649
        adminHandler->createItems(atoi(event.args.c_str()), ItemColor_one, 1);
1650
    return true;
1651
}
1652
1653
impHandler(uploadConfig)
1654
{
1655
    // TRANSLATORS: upload config chat message
1656
    uploadFile(_("Config uploaded to:"),
1657
        config.getFileName(),
1658
        "?xml",
1659
        event.tab);
1660
    return true;
1661
}
1662
1663
impHandler(uploadServerConfig)
1664
{
1665
    // TRANSLATORS: upload config chat message
1666
    uploadFile(_("Server config Uploaded to:"),
1667
        serverConfig.getFileName(),
1668
        "?xml",
1669
        event.tab);
1670
    return true;
1671
}
1672
1673
impHandler(uploadLog)
1674
{
1675
    // TRANSLATORS: upload log chat message
1676
    uploadFile(_("Log uploaded to:"),
1677
        settings.logFileName,
1678
        "",
1679
        event.tab);
1680
    return true;
1681
}
1682
1683
impHandler0(mercenaryFire)
1684
{
1685
    if (mercenaryHandler != nullptr)
1686
        mercenaryHandler->fire();
1687
    return true;
1688
}
1689
1690
impHandler0(mercenaryToMaster)
1691
{
1692
    if (mercenaryHandler != nullptr)
1693
        mercenaryHandler->moveToMaster();
1694
    return true;
1695
}
1696
1697
impHandler0(homunculusToMaster)
1698
{
1699
    if (homunculusHandler != nullptr)
1700
        homunculusHandler->moveToMaster();
1701
    return true;
1702
}
1703
1704
impHandler0(homunculusFeed)
1705
{
1706
    if (homunculusHandler != nullptr)
1707
        homunculusHandler->feed();
1708
    return true;
1709
}
1710
1711
impHandler(useItem)
1712
{
1713
    StringVect pars;
1714
    if (!splitParameters(pars, event.args, " ,", '\"'))
1715
        return false;
1716
    const int sz = CAST_S32(pars.size());
1717
    if (sz < 1)
1718
        return false;
1719
1720
    const int itemId = atoi(pars[0].c_str());
1721
1722
    if (itemId < SPELL_MIN_ID)
1723
    {
1724
        const Inventory *const inv = PlayerInfo::getInventory();
1725
        if (inv != nullptr)
1726
        {
1727
            ItemColor color = ItemColor_one;
1728
            int16_t useType = 0;
1729
            StringVect pars2;
1730
            if (!splitParameters(pars2, pars[0], " ,", '\"'))
1731
                return false;
1732
            const int sz2 = CAST_S32(pars2.size());
1733
            if (sz2 < 1)
1734
                return false;
1735
            if (sz2 >= 2)
1736
                color = fromInt(atoi(pars2[1].c_str()), ItemColor);
1737
            if (sz >= 2)
1738
                useType = CAST_S16(atoi(pars[1].c_str()));
1739
            const Item *const item = inv->findItem(itemId,
1740
                color);
1741
            PlayerInfo::useEquipItem(item, useType, Sfx_true);
1742
        }
1743
    }
1744
    else if (itemId < SKILL_MIN_ID && (spellManager != nullptr))
1745
    {
1746
        spellManager->useItem(itemId);
1747
    }
1748
    else if (skillDialog != nullptr)
1749
    {
1750
        // +++ probably need get data parameter from args
1751
        skillDialog->useItem(itemId,
1752
            fromBool(config.getBoolValue("skillAutotarget"), AutoTarget),
1753
            0,
1754
            std::string());
1755
    }
1756
    return true;
1757
}
1758
1759
impHandler(useItemInv)
1760
{
1761
    int param1 = 0;
1762
    int param2 = 0;
1763
    const std::string args = event.args;
1764
    if (parse2Int(args, param1, param2))
1765
    {
1766
        Item *const item = getItemByInvIndex(param1,
1767
            InventoryType::Inventory);
1768
        PlayerInfo::useEquipItem(item, CAST_S16(param2), Sfx_true);
1769
    }
1770
    else
1771
    {
1772
        Item *const item = getItemByInvIndex(atoi(event.args.c_str()),
1773
            InventoryType::Inventory);
1774
        PlayerInfo::useEquipItem(item, 0, Sfx_true);
1775
    }
1776
    return true;
1777
}
1778
1779
impHandler(invToStorage)
1780
{
1781
    Item *item = nullptr;
1782
    const int amount = getAmountFromEvent(event, item,
1783
        InventoryType::Inventory);
1784
    if (item == nullptr)
1785
        return true;
1786
    if (amount != 0)
1787
    {
1788
        if (inventoryHandler != nullptr)
1789
        {
1790
            inventoryHandler->moveItem2(InventoryType::Inventory,
1791
                item->getInvIndex(),
1792
                amount,
1793
                InventoryType::Storage);
1794
        }
1795
    }
1796
    else
1797
    {
1798
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreAdd,
1799
            inventoryWindow,
1800
            item,
1801
            0,
1802
            0);
1803
    }
1804
    return true;
1805
}
1806
1807
impHandler(tradeAdd)
1808
{
1809
    Item *item = nullptr;
1810
    const int amount = getAmountFromEvent(event, item,
1811
        InventoryType::Inventory);
1812
    if ((item == nullptr) || PlayerInfo::isItemProtected(item->getId()))
1813
        return true;
1814
1815
    if (amount != 0)
1816
    {
1817
        if (tradeWindow != nullptr)
1818
            tradeWindow->tradeItem(item, amount, true);
1819
    }
1820
    else
1821
    {
1822
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd,
1823
            tradeWindow,
1824
            item,
1825
            0,
1826
            0);
1827
    }
1828
    return true;
1829
}
1830
1831
impHandler(storageToInv)
1832
{
1833
    Item *item = nullptr;
1834
    const int amount = getAmountFromEvent(event, item, InventoryType::Storage);
1835
    if (amount != 0)
1836
    {
1837
        if ((inventoryHandler != nullptr) && (item != nullptr))
1838
        {
1839
            inventoryHandler->moveItem2(InventoryType::Storage,
1840
                item->getInvIndex(),
1841
                amount,
1842
                InventoryType::Inventory);
1843
        }
1844
    }
1845
    else
1846
    {
1847
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreRemove,
1848
            storageWindow,
1849
            item,
1850
            0,
1851
            0);
1852
    }
1853
    return true;
1854
}
1855
1856
impHandler(protectItem)
1857
{
1858
    const int id = atoi(event.args.c_str());
1859
    if (id > 0)
1860
        PlayerInfo::protectItem(id);
1861
    return true;
1862
}
1863
1864
impHandler(unprotectItem)
1865
{
1866
    const int id = atoi(event.args.c_str());
1867
    if (id > 0)
1868
        PlayerInfo::unprotectItem(id);
1869
    return true;
1870
}
1871
1872
impHandler(kick)
1873
{
1874
    if ((localPlayer == nullptr) || (actorManager == nullptr))
1875
        return false;
1876
1877
    Being *target = nullptr;
1878
    std::string args = event.args;
1879
    if (!args.empty())
1880
    {
1881
        if (args[0] != ':')
1882
        {
1883
            target = actorManager->findNearestByName(args,
1884
                ActorType::Unknown);
1885
        }
1886
        else
1887
        {
1888
            target = actorManager->findBeing(fromInt(atoi(
1889
                args.substr(1).c_str()), BeingId));
1890
        }
1891
    }
1892
    if (target == nullptr)
1893
        target = localPlayer->getTarget();
1894
    if ((target != nullptr) && (adminHandler != nullptr))
1895
        adminHandler->kick(target->getId());
1896
    return true;
1897
}
1898
1899
impHandler0(clearDrop)
1900
{
1901
    if (dropShortcut != nullptr)
1902
        dropShortcut->clear(true);
1903
    return true;
1904
}
1905
1906
impHandler0(testInfo)
1907
{
1908
    if (actorManager != nullptr)
1909
    {
1910
        logger->log("actors count: %d", CAST_S32(
1911
            actorManager->size()));
1912
        return true;
1913
    }
1914
    return false;
1915
}
1916
1917
impHandler(craftKey)
1918
{
1919
    const int slot = (event.action - InputAction::CRAFT_1);
1920
    if (slot >= 0 && slot < 9)
1921
    {
1922
        if (inventoryWindow != nullptr)
1923
            inventoryWindow->moveItemToCraft(slot);
1924
        return true;
1925
    }
1926
    return false;
1927
}
1928
1929
impHandler0(resetGameModifiers)
1930
{
1931
    GameModifiers::resetModifiers();
1932
    return true;
1933
}
1934
1935
impHandler(barToChat)
1936
{
1937
    if (chatWindow != nullptr)
1938
    {
1939
        chatWindow->addInputText(event.args,
1940
            true);
1941
        return true;
1942
    }
1943
    return false;
1944
}
1945
1946
impHandler(seen)
1947
{
1948
    if (actorManager == nullptr)
1949
        return false;
1950
1951
    ChatTab *tab = event.tab;
1952
    if (tab == nullptr)
1953
        tab = localChatTab;
1954
    if (tab == nullptr)
1955
        return false;
1956
1957
    if (config.getBoolValue("enableIdCollecting") == false)
1958
    {
1959
        // TRANSLATORS: last seen disabled warning
1960
        tab->chatLog(_("Last seen disabled. "
1961
            "Enable in players / collect players ID and seen log."),
1962
            ChatMsgType::BY_SERVER,
1963
            IgnoreRecord_false,
1964
            TryRemoveColors_true);
1965
        return true;
1966
    }
1967
1968
    const std::string name = event.args;
1969
    if (name.empty())
1970
        return false;
1971
1972
    std::string dir = settings.usersDir;
1973
    dir.append(stringToHexPath(name)).append("/seen.txt");
1974
    if (Files::existsLocal(dir))
1975
    {
1976
        StringVect lines;
1977
        Files::loadTextFileLocal(dir, lines);
1978
        if (lines.size() < 3)
1979
        {
1980
            // TRANSLATORS: last seen error
1981
            tab->chatLog(_("You have never seen this nick."),
1982
                ChatMsgType::BY_SERVER,
1983
                IgnoreRecord_false,
1984
                TryRemoveColors_true);
1985
            return true;
1986
        }
1987
        const std::string message = strprintf(
1988
            // TRANSLATORS: last seen message
1989
            _("Last seen for %s: %s"),
1990
            name.c_str(),
1991
            lines[2].c_str());
1992
        tab->chatLog(message,
1993
            ChatMsgType::BY_SERVER,
1994
            IgnoreRecord_false,
1995
            TryRemoveColors_true);
1996
    }
1997
    else
1998
    {
1999
        // TRANSLATORS: last seen error
2000
        tab->chatLog(_("You have not seen this nick before."),
2001
            ChatMsgType::BY_SERVER,
2002
            IgnoreRecord_false,
2003
            TryRemoveColors_true);
2004
    }
2005
2006
    return true;
2007
}
2008
2009
impHandler(dumpMemoryUsage)
2010
{
2011
    if (event.tab != nullptr)
2012
        MemoryManager::printAllMemory(event.tab);
2013
    else
2014
        MemoryManager::printAllMemory(localChatTab);
2015
    return true;
2016
}
2017
2018
impHandler(setEmoteType)
2019
{
2020
    const std::string &args = event.args;
2021
    if (args == "player" || args.empty())
2022
    {
2023
        settings.emoteType = EmoteType::Player;
2024
    }
2025
    else if (args == "pet")
2026
    {
2027
        settings.emoteType = EmoteType::Pet;
2028
    }
2029
    else if (args == "homun" || args == "homunculus")
2030
    {
2031
        settings.emoteType = EmoteType::Homunculus;
2032
    }
2033
    else if (args == "merc" || args == "mercenary")
2034
    {
2035
        settings.emoteType = EmoteType::Mercenary;
2036
    }
2037
    return true;
2038
}
2039
2040
2
}  // namespace Actions