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 
uploadUpdate(void * ptr,const DownloadStatusT status,size_t total A_UNUSED,const size_t remaining A_UNUSED)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 
uploadFile(const std::string & str,const std::string & fileName,const std::string & addStr,ChatTab * const tab)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 
findBeing(const std::string & name,const bool npc)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 
getItemByInvIndex(const int index,const InventoryTypeT invType)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 
getAmountFromEvent(const InputEvent & event,Item * & item0,const InventoryTypeT invType)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 
impHandler(emote)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 
impHandler(outfit)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 
impHandler0(mouseClick)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 
impHandler0(ok)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 
impHandler(shortcut)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 
impHandler0(quit)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 
impHandler0(dropItem0)447 impHandler0(dropItem0)
448 {
449     if (dropShortcut != nullptr)
450     {
451         dropShortcut->dropFirst();
452         return true;
453     }
454     return false;
455 }
456 
impHandler0(dropItem)457 impHandler0(dropItem)
458 {
459     if (dropShortcut != nullptr)
460     {
461         dropShortcut->dropItems(1);
462         return true;
463     }
464     return false;
465 }
466 
impHandler(dropItemId)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 
impHandler(dropItemInv)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 
impHandler(dropItemIdAll)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 
impHandler(dropItemInvAll)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
impHandler(heal)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 
impHandler0(heal)582 impHandler0(heal)
583 {
584     return false;
585 }
586 #endif  // TMWA_SUPPORT
587 
impHandler0(healmd)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 
impHandler0(itenplz)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 
impHandler0(setHome)642 impHandler0(setHome)
643 {
644     if (localPlayer != nullptr)
645     {
646         localPlayer->setHome();
647         return true;
648     }
649     return false;
650 }
651 
impHandler0(magicAttack)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 
impHandler0(copyEquippedToOutfit)667 impHandler0(copyEquippedToOutfit)
668 {
669     if (outfitWindow != nullptr)
670     {
671         outfitWindow->copyFromEquiped();
672         return true;
673     }
674     return false;
675 }
676 
impHandler(pickup)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 
doSit()697 static void doSit()
698 {
699     if (inputManager.isActionActive(InputAction::EMOTE))
700         localPlayer->updateSit();
701     else
702         localPlayer->toggleSit();
703 }
704 
impHandler0(sit)705 impHandler0(sit)
706 {
707     if (localPlayer != nullptr)
708     {
709         doSit();
710         return true;
711     }
712     return false;
713 }
714 
impHandler(screenshot)715 impHandler(screenshot)
716 {
717     Game::createScreenshot(event.args);
718     return true;
719 }
720 
impHandler0(ignoreInput)721 impHandler0(ignoreInput)
722 {
723     return true;
724 }
725 
impHandler(buy)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 
impHandler(sell)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 
impHandler(talk)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 
impHandler0(stopAttack)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 
impHandler0(untarget)871 impHandler0(untarget)
872 {
873     if (localPlayer != nullptr)
874     {
875         localPlayer->untarget();
876         return true;
877     }
878     return false;
879 }
880 
impHandler(attack)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 
impHandler(targetAttack)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 
impHandler0(attackHuman)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 
impHandler0(safeVideoMode)972 impHandler0(safeVideoMode)
973 {
974     WindowManager::setFullScreen(false);
975 
976     return true;
977 }
978 
impHandler0(stopSit)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 
impHandler0(showKeyboard)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 
impHandler0(showWindows)1017 impHandler0(showWindows)
1018 {
1019     if (popupMenu != nullptr)
1020     {
1021         popupMenu->showWindowsPopup();
1022         return true;
1023     }
1024     return false;
1025 }
1026 
impHandler0(openTrade)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 
impHandler0(ipcToggle)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 
impHandler(where)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 
impHandler0(who)1100 impHandler0(who)
1101 {
1102     if (chatHandler != nullptr)
1103         chatHandler->who();
1104     return true;
1105 }
1106 
impHandler0(cleanGraphics)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 
impHandler0(cleanFonts)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 
impHandler(trade)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 
impHandler0(priceLoad)1157 impHandler0(priceLoad)
1158 {
1159     if (shopWindow != nullptr)
1160     {
1161         shopWindow->loadList();
1162         return true;
1163     }
1164     return false;
1165 }
1166 
impHandler0(priceSave)1167 impHandler0(priceSave)
1168 {
1169     if (shopWindow != nullptr)
1170     {
1171         shopWindow->saveList();
1172         return true;
1173     }
1174     return false;
1175 }
1176 
impHandler0(cacheInfo)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 
impHandler0(disconnect)1238 impHandler0(disconnect)
1239 {
1240     if (gameHandler != nullptr)
1241         gameHandler->disconnect2();
1242     return true;
1243 }
1244 
impHandler(undress)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 
impHandler0(dirs)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 
impHandler0(uptime)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
showRes(std::string str,ResourceManager::Resources * res)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 
impHandler(dump)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
impHandler0(dump)1426 impHandler0(dump)
1427 {
1428     nvwa::check_leaks();
1429     return true;
1430 }
1431 #else  // DEBUG_DUMP_LEAKS1
1432 
impHandler0(dump)1433 impHandler0(dump)
1434 {
1435     return true;
1436 }
1437 #endif  // DEBUG_DUMP_LEAKS1
1438 
impHandler0(serverIgnoreAll)1439 impHandler0(serverIgnoreAll)
1440 {
1441     if (chatHandler != nullptr)
1442         chatHandler->ignoreAll();
1443     return true;
1444 }
1445 
impHandler0(serverUnIgnoreAll)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")
impHandler0(error)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 
impHandler(dumpGraphics)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 
impHandler0(dumpEnvironment)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 
impHandler(dumpTests)1523 impHandler(dumpTests)
1524 {
1525     const std::string str = config.getStringValue("testInfo");
1526     outStringNormal(event.tab, str, str);
1527     return true;
1528 }
1529 
impHandler0(dumpOGL)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
impHandler(dumpGL)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 
impHandler0(dumpGL)1551 impHandler0(dumpGL)
1552 {
1553     return true;
1554 }
1555 #endif  // USE_OPENGL
1556 
impHandler(dumpMods)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
impHandler0(testSdlFont)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 
impHandler0(createItems)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 
impHandler(createItem)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 
impHandler(uploadConfig)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 
impHandler(uploadServerConfig)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 
impHandler(uploadLog)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 
impHandler0(mercenaryFire)1683 impHandler0(mercenaryFire)
1684 {
1685     if (mercenaryHandler != nullptr)
1686         mercenaryHandler->fire();
1687     return true;
1688 }
1689 
impHandler0(mercenaryToMaster)1690 impHandler0(mercenaryToMaster)
1691 {
1692     if (mercenaryHandler != nullptr)
1693         mercenaryHandler->moveToMaster();
1694     return true;
1695 }
1696 
impHandler0(homunculusToMaster)1697 impHandler0(homunculusToMaster)
1698 {
1699     if (homunculusHandler != nullptr)
1700         homunculusHandler->moveToMaster();
1701     return true;
1702 }
1703 
impHandler0(homunculusFeed)1704 impHandler0(homunculusFeed)
1705 {
1706     if (homunculusHandler != nullptr)
1707         homunculusHandler->feed();
1708     return true;
1709 }
1710 
impHandler(useItem)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 
impHandler(useItemInv)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 
impHandler(invToStorage)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 
impHandler(tradeAdd)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 
impHandler(storageToInv)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 
impHandler(protectItem)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 
impHandler(unprotectItem)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 
impHandler(kick)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 
impHandler0(clearDrop)1899 impHandler0(clearDrop)
1900 {
1901     if (dropShortcut != nullptr)
1902         dropShortcut->clear(true);
1903     return true;
1904 }
1905 
impHandler0(testInfo)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 
impHandler(craftKey)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 
impHandler0(resetGameModifiers)1929 impHandler0(resetGameModifiers)
1930 {
1931     GameModifiers::resetModifiers();
1932     return true;
1933 }
1934 
impHandler(barToChat)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 
impHandler(seen)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 
impHandler(dumpMemoryUsage)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 
impHandler(setEmoteType)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 }  // namespace Actions
2041