/*
* The ManaPlus Client
* Copyright (C) 2012-2016 The ManaPlus Developers
*
* This file is part of The ManaPlus Client.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "actions/actions.h"
#include "actormanager.h"
#include "configuration.h"
#include "game.h"
#ifdef USE_OPENGL
#include "graphicsmanager.h"
#endif
#include "main.h"
#include "spellmanager.h"
#include "actions/actiondef.h"
#include "being/localplayer.h"
#include "being/playerinfo.h"
#include "const/spells.h"
#include "const/resources/skill.h"
#include "gui/gui.h"
#include "gui/popupmanager.h"
#include "gui/sdlinput.h"
#include "gui/windowmanager.h"
#include "gui/shortcut/dropshortcut.h"
#include "gui/shortcut/emoteshortcut.h"
#include "gui/shortcut/itemshortcut.h"
#include "gui/popups/popupmenu.h"
#include "gui/windows/buydialog.h"
#include "gui/windows/okdialog.h"
#include "gui/windows/tradewindow.h"
#include "gui/windows/quitdialog.h"
#include "gui/windows/buyselldialog.h"
#include "gui/windows/chatwindow.h"
#include "gui/windows/helpwindow.h"
#include "gui/windows/inventorywindow.h"
#include "gui/windows/itemamountwindow.h"
#include "gui/windows/npcdialog.h"
#include "gui/windows/outfitwindow.h"
#include "gui/windows/setupwindow.h"
#include "gui/windows/shopwindow.h"
#include "gui/windows/shortcutwindow.h"
#include "gui/windows/skilldialog.h"
#include "gui/windows/whoisonline.h"
#include "gui/widgets/createwidget.h"
#include "gui/widgets/tabs/chat/chattab.h"
#include "input/inputactionoperators.h"
#if defined USE_OPENGL
#include "render/normalopenglgraphics.h"
#endif
#include "net/adminhandler.h"
#include "net/beinghandler.h"
#ifdef EATHENA_SUPPORT
#include "net/buyingstorehandler.h"
#endif
#include "net/buysellhandler.h"
#include "net/chathandler.h"
#include "net/download.h"
#include "net/gamehandler.h"
#include "net/inventoryhandler.h"
#include "net/ipc.h"
#ifdef EATHENA_SUPPORT
#include "net/mercenaryhandler.h"
#include "net/net.h"
#include "net/vendinghandler.h"
#endif
#include "net/npchandler.h"
#include "net/playerhandler.h"
#include "net/serverfeatures.h"
#include "net/uploadcharinfo.h"
#include "net/tradehandler.h"
#include "resources/iteminfo.h"
#include "resources/memorymanager.h"
#include "resources/resourcemanager/resourcemanager.h"
#include "utils/chatutils.h"
#include "utils/delete2.h"
#include "utils/gettext.h"
#include "utils/files.h"
#include "utils/timer.h"
#include "utils/mathutils.h"
#ifdef ANDROID
#ifndef USE_SDL2
#include <SDL_screenkeyboard.h>
#endif
#endif
#include <sstream>
#include "debug.h"
extern std::string tradePartnerName;
extern QuitDialog *quitDialog;
extern int start_time;
extern char **environ;
namespace Actions
{
static int uploadUpdate(void *ptr,
const DownloadStatusT status,
size_t total A_UNUSED,
const size_t remaining A_UNUSED) A_NONNULL(1);
static int uploadUpdate(void *ptr,
const DownloadStatusT status,
size_t total A_UNUSED,
const size_t remaining A_UNUSED)
{
if (status == DownloadStatus::Idle || status == DownloadStatus::Starting)
return 0;
UploadChatInfo *const info = reinterpret_cast<UploadChatInfo*>(ptr);
if (!info)
return 0;
if (status == DownloadStatus::Complete)
{
std::string str = Net::Download::getUploadResponse();
const size_t sz = str.size();
if (sz > 0)
{
if (str[sz - 1] == '\n')
str = str.substr(0, sz - 1);
str.append(info->addStr);
ChatTab *const tab = info->tab;
if (chatWindow && (!tab || chatWindow->isTabPresent(tab)))
{
str = strprintf("%s [@@%s |%s@@]",
info->text.c_str(), str.c_str(), str.c_str());
outStringNormal(tab, str, str);
}
else
{
CREATEWIDGET(OkDialog,
// TRANSLATORS: file uploaded message
_("File uploaded"),
str,
// TRANSLATORS: ok dialog button
_("OK"),
DialogType::OK,
Modal_true,
ShowCenter_false,
nullptr,
260);
}
}
}
delete2(info->upload);
delete info;
return 0;
}
static void uploadFile(const std::string &str,
const std::string &fileName,
const std::string &addStr,
ChatTab *const tab)
{
UploadChatInfo *const info = new UploadChatInfo;
Net::Download *const upload = new Net::Download(info,
"http://sprunge.us",
&uploadUpdate,
false, true, false);
info->upload = upload;
info->text = str;
info->addStr = addStr;
info->tab = tab;
upload->setFile(fileName);
upload->start();
}
static Being *findBeing(const std::string &name, const bool npc)
{
if (!localPlayer || !actorManager)
return nullptr;
Being *being = nullptr;
if (name.empty())
{
being = localPlayer->getTarget();
}
else
{
being = actorManager->findBeingByName(
name, ActorType::Unknown);
}
if (!being && npc)
{
being = actorManager->findNearestLivingBeing(
localPlayer, 1, ActorType::Npc, AllowSort_true);
if (being)
{
if (abs(being->getTileX() - localPlayer->getTileX()) > 1
|| abs(being->getTileY() - localPlayer->getTileY()) > 1)
{
being = nullptr;
}
}
}
if (!being && npc)
{
being = actorManager->findNearestLivingBeing(
localPlayer, 1, ActorType::Player, AllowSort_true);
if (being)
{
if (abs(being->getTileX() - localPlayer->getTileX()) > 1
|| abs(being->getTileY() - localPlayer->getTileY()) > 1)
{
being = nullptr;
}
}
}
return being;
}
static Item *getItemByInvIndex(const InputEvent &event,
const InventoryTypeT invType)
{
const int index = atoi(event.args.c_str());
const Inventory *inv = nullptr;
switch (invType)
{
case InventoryType::Storage:
inv = PlayerInfo::getStorageInventory();
break;
case InventoryType::Inventory:
inv = PlayerInfo::getInventory();
break;
case InventoryType::Trade:
case InventoryType::Npc:
#ifdef EATHENA_SUPPORT
case InventoryType::Cart:
case InventoryType::Vending:
case InventoryType::Mail:
case InventoryType::Craft:
#endif
case InventoryType::TypeEnd:
default:
break;
}
if (inv)
return inv->getItem(index);
return nullptr;
}
static int getAmountFromEvent(const InputEvent &event,
Item *&item0,
const InventoryTypeT invType)
{
Item *const item = getItemByInvIndex(event, invType);
item0 = item;
if (!item)
return 0;
std::string str = event.args;
removeToken(str, " ");
if (str.empty())
return 0;
int amount = 0;
if (str[0] == '-')
{
if (str.size() > 1)
{
amount = item->getQuantity() - atoi(str.substr(1).c_str());
if (amount <= 0 || amount > item->getQuantity())
amount = item->getQuantity();
}
}
else if (str == "/")
{
amount = item->getQuantity() / 2;
}
else if (str == "all")
{
amount = item->getQuantity();
}
else
{
amount = atoi(str.c_str());
}
return amount;
}
impHandler(emote)
{
const int emotion = 1 + (event.action - InputAction::EMOTE_1);
if (emotion > 0)
{
if (emoteShortcut)
emoteShortcut->useEmotePlayer(emotion);
if (Game::instance())
Game::instance()->setValidSpeed();
return true;
}
return false;
}
impHandler(outfit)
{
if (inputManager.isActionActive(InputAction::WEAR_OUTFIT))
{
const int num = event.action - InputAction::OUTFIT_1;
if (outfitWindow && num >= 0)
{
outfitWindow->wearOutfit(num);
if (Game::instance())
Game::instance()->setValidSpeed();
return true;
}
}
else if (inputManager.isActionActive(InputAction::COPY_OUTFIT))
{
const int num = event.action - InputAction::OUTFIT_1;
if (outfitWindow && num >= 0)
{
outfitWindow->copyOutfit(num);
if (Game::instance())
Game::instance()->setValidSpeed();
return true;
}
}
return false;
}
impHandler0(mouseClick)
{
if (!guiInput || !gui)
return false;
int mouseX, mouseY;
Gui::getMouseState(mouseX, mouseY);
guiInput->simulateMouseClick(mouseX, mouseY, MouseButton::RIGHT);
return true;
}
impHandler0(ok)
{
// Close the Browser if opened
if (helpWindow && helpWindow->isWindowVisible())
{
helpWindow->setVisible(Visible_false);
return true;
}
// Close the config window, cancelling changes if opened
else if (setupWindow && setupWindow->isWindowVisible())
{
setupWindow->action(ActionEvent(nullptr, "cancel"));
return true;
}
else if (NpcDialog *const dialog = NpcDialog::getActive())
{
dialog->action(ActionEvent(nullptr, "ok"));
return true;
}
else if (popupMenu->isPopupVisible())
{
popupMenu->select();
}
return false;
}
impHandler(shortcut)
{
if (itemShortcutWindow)
{
const int num = itemShortcutWindow->getTabIndex();
if (num >= 0 && num < CAST_S32(SHORTCUT_TABS))
{
if (itemShortcut[num])
{
itemShortcut[num]->useItem(event.action
- InputAction::SHORTCUT_1);
}
}
return true;
}
return false;
}
impHandler0(quit)
{
if (!Game::instance())
return false;
if (popupManager && popupManager->isPopupMenuVisible())
{
popupManager->closePopupMenu();
return true;
}
else if (!quitDialog)
{
CREATEWIDGETV(quitDialog, QuitDialog,
&quitDialog);
quitDialog->requestMoveToTop();
return true;
}
return false;
}
impHandler0(dropItem0)
{
if (dropShortcut)
{
dropShortcut->dropFirst();
return true;
}
return false;
}
impHandler0(dropItem)
{
if (dropShortcut)
{
dropShortcut->dropItems();
return true;
}
return false;
}
impHandler(dropItemId)
{
const Inventory *const inv = PlayerInfo::getInventory();
if (!inv)
return false;
// +++ ignoring item color for now
Item *const item = inv->findItem(atoi(event.args.c_str()),
ItemColor_one);
if (item && !PlayerInfo::isItemProtected(item->getId()))
{
ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop,
inventoryWindow, item);
}
return true;
}
impHandler(dropItemInv)
{
Item *const item = getItemByInvIndex(event, InventoryType::Inventory);
if (item && !PlayerInfo::isItemProtected(item->getId()))
{
ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop,
inventoryWindow, item);
}
return true;
}
impHandler(dropItemIdAll)
{
const Inventory *const inv = PlayerInfo::getInventory();
if (!inv)
return false;
// +++ ignoring item color for now
Item *const item = inv->findItem(atoi(event.args.c_str()),
ItemColor_one);
if (item && !PlayerInfo::isItemProtected(item->getId()))
PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true);
return true;
}
impHandler(dropItemInvAll)
{
Item *const item = getItemByInvIndex(event, InventoryType::Inventory);
if (item && !PlayerInfo::isItemProtected(item->getId()))
PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true);
return true;
}
impHandler(heal)
{
#ifdef EATHENA_SUPPORT
if (Net::getNetworkType() != ServerType::TMWATHENA)
return false;
#endif
if (actorManager && localPlayer)
{
std::string args = event.args;
if (!args.empty())
{
const Being *being = nullptr;
if (args[0] == ':')
{
being = actorManager->findBeing(fromInt(atoi(
args.substr(1).c_str()), BeingId));
if (being && being->getType() == ActorType::Monster)
being = nullptr;
}
else
{
being = actorManager->findBeingByName(args, ActorType::Player);
}
if (being)
actorManager->heal(being);
}
else
{
Being *target = localPlayer->getTarget();
if (inputManager.isActionActive(InputAction::STOP_ATTACK))
{
if (!target || target->getType() != ActorType::Player)
{
target = actorManager->findNearestLivingBeing(
localPlayer, 10, ActorType::Player, AllowSort_true);
}
}
else
{
if (!target)
target = localPlayer;
}
actorManager->heal(target);
}
if (Game::instance())
Game::instance()->setValidSpeed();
return true;
}
return false;
}
impHandler0(healmd)
{
#ifdef EATHENA_SUPPORT
if (Net::getNetworkType() != ServerType::TMWATHENA)
return false;
#endif
if (actorManager)
{
const int matk = PlayerInfo::getStatEffective(Attributes::PLAYER_MATK);
int maxHealingRadius;
// magic levels < 2
if (PlayerInfo::getSkillLevel(340) < 2
|| PlayerInfo::getSkillLevel(341) < 2)
{
maxHealingRadius = matk / 100 + 1;
}
else
{
maxHealingRadius = (12 * fastSqrtInt(matk) + matk) / 100 + 1;
}
Being *target = actorManager->findMostDamagedPlayer(maxHealingRadius);
if (target)
actorManager->heal(target);
if (Game::instance())
Game::instance()->setValidSpeed();
return true;
}
return false;
}
impHandler0(itenplz)
{
#ifdef EATHENA_SUPPORT
if (Net::getNetworkType() != ServerType::TMWATHENA)
return false;
#endif
if (actorManager)
{
if (playerHandler &&
playerHandler->canUseMagic() &&
PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 3)
{
actorManager->itenplz();
}
return true;
}
return false;
}
impHandler0(setHome)
{
if (localPlayer)
{
localPlayer->setHome();
return true;
}
return false;
}
impHandler0(magicAttack)
{
#ifdef EATHENA_SUPPORT
if (Net::getNetworkType() != ServerType::TMWATHENA)
return false;
#endif
if (localPlayer)
{
localPlayer->magicAttack();
return true;
}
return false;
}
impHandler0(copyEquippedToOutfit)
{
if (outfitWindow)
{
outfitWindow->copyFromEquiped();
return true;
}
return false;
}
impHandler(pickup)
{
if (!localPlayer)
return false;
const std::string args = event.args;
if (args.empty())
{
localPlayer->pickUpItems();
}
else
{
FloorItem *const item = actorManager->findItem(fromInt(
atoi(args.c_str()), BeingId));
if (item)
localPlayer->pickUp(item);
}
return true;
}
static void doSit()
{
if (inputManager.isActionActive(InputAction::EMOTE))
localPlayer->updateSit();
else
localPlayer->toggleSit();
}
impHandler0(sit)
{
if (localPlayer)
{
doSit();
return true;
}
return false;
}
impHandler0(screenshot)
{
Game::createScreenshot();
return true;
}
impHandler0(ignoreInput)
{
return true;
}
impHandler(buy)
{
if (!serverFeatures)
return false;
const std::string args = event.args;
Being *being = findBeing(args, false);
if (!being && !serverFeatures->haveVending())
{
if (whoIsOnline)
{
const std::set<std::string> &players =
whoIsOnline->getOnlineNicks();
if (players.find(args) != players.end())
{
if (buySellHandler)
buySellHandler->requestSellList(args);
return true;
}
}
return false;
}
if (!being)
being = findBeing(args, true);
if (!being)
return false;
if (being->getType() == ActorType::Npc)
{
if (npcHandler)
npcHandler->buy(being->getId());
return true;
}
else if (being->getType() == ActorType::Player)
{
#ifdef EATHENA_SUPPORT
if (vendingHandler && serverFeatures->haveVending())
vendingHandler->open(being);
else
#endif
if (buySellHandler)
buySellHandler->requestSellList(being->getName());
return true;
}
return false;
}
impHandler(sell)
{
if (!serverFeatures)
return false;
const std::string args = event.args;
Being *being = findBeing(args, false);
if (!being && !serverFeatures->haveVending())
{
if (whoIsOnline)
{
const std::set<std::string> &players =
whoIsOnline->getOnlineNicks();
if (players.find(args) != players.end())
{
if (buySellHandler)
buySellHandler->requestBuyList(args);
return true;
}
}
return false;
}
if (!being)
being = findBeing(args, true);
if (!being)
return false;
if (being->getType() == ActorType::Npc)
{
if (npcHandler)
npcHandler->sell(being->getId());
return true;
}
else if (being->getType() == ActorType::Player)
{
#ifdef EATHENA_SUPPORT
if (buyingStoreHandler && serverFeatures->haveVending())
buyingStoreHandler->open(being);
else
#endif
if (buySellHandler)
buySellHandler->requestBuyList(being->getName());
return true;
}
return false;
}
impHandler(talk)
{
Being *being = findBeing(event.args, true);
if (!being)
return false;
if (being->canTalk())
{
being->talkTo();
}
else if (being->getType() == ActorType::Player)
{
CREATEWIDGET(BuySellDialog,
being->getName());
}
return true;
}
impHandler0(stopAttack)
{
if (localPlayer)
{
localPlayer->stopAttack();
// not consume if target attack key pressed
if (inputManager.isActionActive(InputAction::TARGET_ATTACK))
return false;
return true;
}
return false;
}
impHandler0(untarget)
{
if (localPlayer)
{
localPlayer->untarget();
return true;
}
return false;
}
impHandler(attack)
{
if (!localPlayer || !actorManager)
return false;
Being *target = nullptr;
std::string args = event.args;
if (!args.empty())
{
if (args[0] != ':')
{
target = actorManager->findNearestByName(args);
}
else
{
target = actorManager->findBeing(fromInt(atoi(
args.substr(1).c_str()), BeingId));
if (target && target->getType() != ActorType::Monster)
target = nullptr;
}
}
if (!target)
target = localPlayer->getTarget();
else
localPlayer->setTarget(target);
if (target)
localPlayer->attack(target, true);
return true;
}
impHandler(targetAttack)
{
if (localPlayer && actorManager)
{
Being *target = nullptr;
std::string args = event.args;
const bool newTarget = !inputManager.isActionActive(
InputAction::STOP_ATTACK);
if (!args.empty())
{
if (args[0] != ':')
{
target = actorManager->findNearestByName(args);
}
else
{
target = actorManager->findBeing(fromInt(atoi(
args.substr(1).c_str()), BeingId));
if (target && target->getType() != ActorType::Monster)
target = nullptr;
}
}
if (!target && !settings.targetingType)
target = localPlayer->getTarget();
if (!target)
{
target = actorManager->findNearestLivingBeing(
localPlayer, 90, ActorType::Monster, AllowSort_true);
}
localPlayer->attack2(target, newTarget);
return true;
}
return false;
}
impHandler0(attackHuman)
{
if (!actorManager || !localPlayer)
return false;
Being *const target = actorManager->findNearestPvpPlayer();
if (target)
{
localPlayer->setTarget(target);
localPlayer->attack2(target, true);
}
return true;
}
impHandler0(safeVideoMode)
{
WindowManager::setFullScreen(false);
return true;
}
impHandler0(stopSit)
{
if (localPlayer)
{
localPlayer->stopAttack();
// not consume if target attack key pressed
if (inputManager.isActionActive(InputAction::TARGET_ATTACK))
return false;
if (!localPlayer->getTarget())
{
doSit();
return true;
}
return true;
}
return false;
}
impHandler0(showKeyboard)
{
#ifdef ANDROID
#ifdef USE_SDL2
if (SDL_IsTextInputActive())
SDL_StopTextInput();
else
SDL_StartTextInput();
#else
SDL_ANDROID_ToggleScreenKeyboardTextInput(nullptr);
#endif
return true;
#else
return false;
#endif
}
impHandler0(showWindows)
{
if (popupMenu)
{
popupMenu->showWindowsPopup();
return true;
}
return false;
}
impHandler0(openTrade)
{
const Being *const being = localPlayer->getTarget();
if (being && being->getType() == ActorType::Player)
{
if (tradeHandler)
tradeHandler->request(being);
tradePartnerName = being->getName();
if (tradeWindow)
tradeWindow->clear();
return true;
}
return false;
}
impHandler0(ipcToggle)
{
if (ipc)
{
IPC::stop();
if (!ipc)
{
debugChatTab->chatLog("IPC service stopped.",
ChatMsgType::BY_SERVER);
}
else
{
debugChatTab->chatLog("Unable to stop IPC service.",
ChatMsgType::BY_SERVER);
}
}
else
{
IPC::start();
if (ipc)
{
debugChatTab->chatLog(
strprintf("IPC service available on port %d", ipc->getPort()),
ChatMsgType::BY_SERVER);
}
else
{
debugChatTab->chatLog("Unable to start IPC service",
ChatMsgType::BY_SERVER);
}
}
return true;
}
impHandler(where)
{
ChatTab *const tab = event.tab != nullptr ? event.tab : debugChatTab;
if (!tab)
return false;
std::ostringstream where;
where << Game::instance()->getCurrentMapName() << ", coordinates: "
<< ((localPlayer->getPixelX() - mapTileSize / 2) / mapTileSize)
<< ", " << ((localPlayer->getPixelY() - mapTileSize) / mapTileSize);
tab->chatLog(where.str(), ChatMsgType::BY_SERVER);
return true;
}
impHandler0(who)
{
if (chatHandler)
chatHandler->who();
return true;
}
impHandler0(cleanGraphics)
{
resourceManager->clearCache();
if (debugChatTab)
{
// TRANSLATORS: clear graphics command message
debugChatTab->chatLog(_("Cache cleaned"),
ChatMsgType::BY_SERVER);
}
return true;
}
impHandler0(cleanFonts)
{
if (gui)
gui->clearFonts();
if (debugChatTab)
{
// TRANSLATORS: clear fonts cache message
debugChatTab->chatLog(_("Cache cleaned"),
ChatMsgType::BY_SERVER);
}
return true;
}
impHandler(trade)
{
if (!actorManager)
return false;
const Being *being = actorManager->findBeingByName(
event.args, ActorType::Player);
if (!being)
being = localPlayer->getTarget();
if (being)
{
if (tradeHandler)
tradeHandler->request(being);
tradePartnerName = being->getName();
if (tradeWindow)
tradeWindow->clear();
}
return true;
}
impHandler0(priceLoad)
{
if (shopWindow)
{
shopWindow->loadList();
return true;
}
return false;
}
impHandler0(priceSave)
{
if (shopWindow)
{
shopWindow->saveList();
return true;
}
return false;
}
impHandler0(cacheInfo)
{
if (!chatWindow || !debugChatTab)
return false;
/*
Font *const font = chatWindow->getFont();
if (!font)
return;
const TextChunkList *const cache = font->getCache();
if (!cache)
return;
unsigned int all = 0;
// TRANSLATORS: chat fonts message
debugChatTab->chatLog(_("font cache size"), ChatMsgType::BY_SERVER);
std::string str;
for (int f = 0; f < 256; f ++)
{
if (!cache[f].size)
{
const unsigned int sz = CAST_S32(cache[f].size);
all += sz;
str.append(strprintf("%d: %u, ", f, sz));
}
}
debugChatTab->chatLog(str, ChatMsgType::BY_SERVER);
// TRANSLATORS: chat fonts message
debugChatTab->chatLog(strprintf("%s %d", _("Cache size:"), all),
ChatMsgType::BY_SERVER);
#ifdef DEBUG_FONT_COUNTERS
debugChatTab->chatLog("", ChatMsgType::BY_SERVER);
debugChatTab->chatLog(strprintf("%s %d",
// TRANSLATORS: chat fonts message
_("Created:"), font->getCreateCounter()),
ChatMsgType::BY_SERVER);
debugChatTab->chatLog(strprintf("%s %d",
// TRANSLATORS: chat fonts message
_("Deleted:"), font->getDeleteCounter()),
ChatMsgType::BY_SERVER);
#endif
*/
return true;
}
impHandler0(disconnect)
{
if (gameHandler)
gameHandler->disconnect2();
return true;
}
impHandler(undress)
{
if (!actorManager || !localPlayer)
return false;
const std::string args = event.args;
Being *target = nullptr;
if (!args.empty())
{
if (args[0] == ':')
{
target = actorManager->findBeing(fromInt(atoi(
args.substr(1).c_str()), BeingId));
if (target && target->getType() == ActorType::Monster)
target = nullptr;
}
else
{
target = actorManager->findNearestByName(event.args);
}
}
if (!target)
target = localPlayer->getTarget();
if (target && beingHandler)
beingHandler->undress(target);
return true;
}
impHandler0(dirs)
{
if (!debugChatTab)
return false;
debugChatTab->chatLog("config directory: "
+ settings.configDir,
ChatMsgType::BY_SERVER);
debugChatTab->chatLog("logs directory: "
+ settings.localDataDir,
ChatMsgType::BY_SERVER);
debugChatTab->chatLog("screenshots directory: "
+ settings.screenshotDir,
ChatMsgType::BY_SERVER);
debugChatTab->chatLog("temp directory: "
+ settings.tempDir,
ChatMsgType::BY_SERVER);
return true;
}
impHandler0(uptime)
{
if (!debugChatTab)
return false;
if (cur_time < start_time)
{
// TRANSLATORS: uptime command
debugChatTab->chatLog(strprintf(_("Client uptime: %s"), "unknown"),
ChatMsgType::BY_SERVER);
}
else
{
// TRANSLATORS: uptime command
debugChatTab->chatLog(strprintf(_("Client uptime: %s"),
timeDiffToString(cur_time - start_time).c_str()),
ChatMsgType::BY_SERVER);
}
return true;
}
#ifdef DEBUG_DUMP_LEAKS1
static void showRes(std::string str, ResourceManager::Resources *res)
{
if (!res)
return;
str.append(toString(res->size()));
if (debugChatTab)
debugChatTab->chatLog(str, ChatMsgType::BY_SERVER);
logger->log(str);
ResourceManager::ResourceIterator iter = res->begin();
const ResourceManager::ResourceIterator iter_end = res->end();
while (iter != iter_end)
{
if (iter->second && iter->second->getRefCount())
{
char type = ' ';
char isNew = 'N';
if (iter->second->getDumped())
isNew = 'O';
else
iter->second->setDumped(true);
SubImage *const subImage = dynamic_cast<SubImage *const>(
iter->second);
Image *const image = dynamic_cast<Image *const>(iter->second);
int id = 0;
if (subImage)
type = 'S';
else if (image)
type = 'I';
if (image)
id = image->getGLImage();
logger->log("Resource %c%c: %s (%d) id=%d", type,
isNew, iter->second->getIdPath().c_str(),
iter->second->getRefCount(), id);
}
++ iter;
}
}
impHandler(dump)
{
if (!debugChatTab)
return false;
if (!event.args.empty())
{
ResourceManager::Resources *res = resourceManager->getResources();
// TRANSLATORS: dump command
showRes(_("Resource images:"), res);
res = resourceManager->getOrphanedResources();
// TRANSLATORS: dump command
showRes(_("Resource orphaned images:"), res);
}
else
{
ResourceManager::Resources *res = resourceManager->getResources();
// TRANSLATORS: dump command
debugChatTab->chatLog(_("Resource images:") + toString(res->size()),
ChatMsgType::BY_SERVER);
res = resourceManager->getOrphanedResources();
// TRANSLATORS: dump command
debugChatTab->chatLog(_("Resource orphaned images:")
+ toString(res->size()),
ChatMsgType::BY_SERVER);
}
return true;
}
#elif defined ENABLE_MEM_DEBUG
impHandler0(dump)
{
check_leaks();
return true;
}
#else
impHandler0(dump)
{
return true;
}
#endif
impHandler0(serverIgnoreAll)
{
if (chatHandler)
chatHandler->ignoreAll();
return true;
}
impHandler0(serverUnIgnoreAll)
{
if (chatHandler)
chatHandler->unIgnoreAll();
return true;
}
PRAGMA6(GCC diagnostic push)
PRAGMA6(GCC diagnostic ignored "-Wnull-dereference")
impHandler0(error)
{
int *const ptr = nullptr;
*(ptr + 1) = 20;
// logger->log("test %d", *ptr);
exit(1);
}
PRAGMA6(GCC diagnostic pop)
impHandler(dumpGraphics)
{
std::string str = strprintf("%s,%s,%dX%dX%d,", PACKAGE_OS, SMALL_VERSION,
mainGraphics->getWidth(), mainGraphics->getHeight(),
mainGraphics->getBpp());
if (mainGraphics->getFullScreen())
str.append("F");
else
str.append("W");
if (mainGraphics->getHWAccel())
str.append("H");
else
str.append("S");
if (mainGraphics->getDoubleBuffer())
str.append("D");
else
str.append("_");
#if defined USE_OPENGL
str.append(strprintf(",%d", mainGraphics->getOpenGL()));
#else
str.append(",0");
#endif
str.append(strprintf(",%f,", static_cast<double>(settings.guiAlpha)))
.append(config.getBoolValue("adjustPerfomance") ? "1" : "0")
.append(config.getBoolValue("alphaCache") ? "1" : "0")
.append(config.getBoolValue("enableMapReduce") ? "1" : "0")
.append(config.getBoolValue("beingopacity") ? "1" : "0")
.append(",")
.append(config.getBoolValue("enableAlphaFix") ? "1" : "0")
.append(config.getBoolValue("disableAdvBeingCaching") ? "1" : "0")
.append(config.getBoolValue("disableBeingCaching") ? "1" : "0")
.append(config.getBoolValue("particleeffects") ? "1" : "0")
.append(strprintf(",%d-%d", fps, config.getIntValue("fpslimit")));
outStringNormal(event.tab, str, str);
return true;
}
impHandler0(dumpEnvironment)
{
logger->log1("Start environment variables");
for (char **env = environ; *env; ++ env)
logger->log1(*env);
logger->log1("End environment variables");
if (debugChatTab)
{
// TRANSLATORS: dump environment command
debugChatTab->chatLog(_("Environment variables dumped"),
ChatMsgType::BY_SERVER);
}
return true;
}
impHandler(dumpTests)
{
const std::string str = config.getStringValue("testInfo");
outStringNormal(event.tab, str, str);
return true;
}
impHandler0(dumpOGL)
{
#if defined(USE_OPENGL) && !defined(ANDROID) && !defined(__native_client__)
NormalOpenGLGraphics::dumpSettings();
#endif
return true;
}
#ifdef USE_OPENGL
impHandler(dumpGL)
{
std::string str = graphicsManager.getGLVersion();
outStringNormal(event.tab, str, str);
return true;
}
#else
impHandler0(dumpGL)
{
return true;
}
#endif
impHandler(dumpMods)
{
std::string str = "enabled mods: " + serverConfig.getValue("mods", "");
outStringNormal(event.tab, str, str);
return true;
}
#if defined USE_OPENGL && defined DEBUG_SDLFONT
impHandler0(testSdlFont)
{
Font *font = new Font("fonts/dejavusans.ttf", 18);
timespec time1;
timespec time2;
NullOpenGLGraphics *nullGraphics = new NullOpenGLGraphics;
std::vector<std::string> data;
volatile int width = 0;
for (int f = 0; f < 300; f ++)
data.push_back("test " + toString(f) + "string");
nullGraphics->beginDraw();
clock_gettime(CLOCK_MONOTONIC, &time1);
Color color(0, 0, 0, 255);
for (int f = 0; f < 500; f ++)
{
FOR_EACH (std::vector<std::string>::const_iterator, it, data)
{
width += font->getWidth(*it);
font->drawString(nullGraphics, color, color, *it, 10, 10);
}
FOR_EACH (std::vector<std::string>::const_iterator, it, data)
font->drawString(nullGraphics, color, color, *it, 10, 10);
font->doClean();
}
clock_gettime(CLOCK_MONOTONIC, &time2);
delete nullGraphics;
delete font;
int64_t diff = (static_cast<long long int>(
time2.tv_sec) * 1000000000LL + static_cast<long long int>(
time2.tv_nsec)) / 100000 - (static_cast<long long int>(
time1.tv_sec) * 1000000000LL + static_cast<long long int>(
time1.tv_nsec)) / 100000;
if (debugChatTab)
{
debugChatTab->chatLog("sdlfont time: " + toString(diff),
ChatMsgType::BY_SERVER);
}
return true;
}
#endif
impHandler0(createItems)
{
BuyDialog *const dialog = CREATEWIDGETR0(BuyDialog);
const ItemDB::ItemInfos &items = ItemDB::getItemInfos();
FOR_EACH (ItemDB::ItemInfos::const_iterator, it, items)
{
const ItemInfo *const info = (*it).second;
if (!info)
continue;
const int id = info->getId();
if (id <= 500)
continue;
dialog->addItem(id,
ItemType::Unknown,
ItemColor_one,
100,
0);
}
dialog->sort();
return true;
}
impHandler(uploadConfig)
{
// TRANSLATORS: upload config chat message
uploadFile(_("Uploaded config into:"),
config.getFileName(),
"?xml",
event.tab);
return true;
}
impHandler(uploadServerConfig)
{
// TRANSLATORS: upload config chat message
uploadFile(_("Uploaded server config into:"),
serverConfig.getFileName(),
"?xml",
event.tab);
return true;
}
impHandler(uploadLog)
{
// TRANSLATORS: upload log chat message
uploadFile(_("Uploaded log into:"),
settings.logFileName,
"?txt",
event.tab);
return true;
}
impHandler0(mercenaryFire)
{
#ifdef EATHENA_SUPPORT
if (mercenaryHandler)
mercenaryHandler->fire();
return true;
#else
return false;
#endif
}
impHandler(useItem)
{
const int itemId = atoi(event.args.c_str());
if (itemId < SPELL_MIN_ID)
{
const Inventory *const inv = PlayerInfo::getInventory();
if (inv)
{
// +++ ignoring item color for now
const Item *const item = inv->findItem(itemId,
ItemColor_one);
PlayerInfo::useEquipItem(item, Sfx_true);
}
}
else if (itemId < SKILL_MIN_ID && spellManager)
{
spellManager->useItem(itemId);
}
else if (skillDialog)
{
skillDialog->useItem(itemId,
fromBool(config.getBoolValue("skillAutotarget"), AutoTarget),
0);
}
return true;
}
impHandler(useItemInv)
{
Item *const item = getItemByInvIndex(event, InventoryType::Inventory);
PlayerInfo::useEquipItem(item, Sfx_true);
return true;
}
impHandler(invToStorage)
{
Item *item = nullptr;
const int amount = getAmountFromEvent(event, item,
InventoryType::Inventory);
if (!item)
return true;
if (amount)
{
if (inventoryHandler)
{
inventoryHandler->moveItem2(InventoryType::Inventory,
item->getInvIndex(),
amount,
InventoryType::Storage);
}
}
else
{
ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreAdd,
inventoryWindow, item);
}
return true;
}
impHandler(tradeAdd)
{
Item *item = nullptr;
const int amount = getAmountFromEvent(event, item,
InventoryType::Inventory);
if (!item || PlayerInfo::isItemProtected(item->getId()))
return true;
if (amount)
{
if (tradeWindow)
tradeWindow->tradeItem(item, amount, true);
}
else
{
ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd,
tradeWindow, item);
}
return true;
}
impHandler(storageToInv)
{
Item *item = nullptr;
const int amount = getAmountFromEvent(event, item, InventoryType::Storage);
if (amount)
{
if (inventoryHandler && item)
{
inventoryHandler->moveItem2(InventoryType::Storage,
item->getInvIndex(),
amount,
InventoryType::Inventory);
}
}
else
{
ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreRemove,
storageWindow, item);
}
return true;
}
impHandler(protectItem)
{
const int id = atoi(event.args.c_str());
if (id > 0)
PlayerInfo::protectItem(id);
return true;
}
impHandler(unprotectItem)
{
const int id = atoi(event.args.c_str());
if (id > 0)
PlayerInfo::unprotectItem(id);
return true;
}
impHandler(kick)
{
if (!localPlayer || !actorManager)
return false;
Being *target = nullptr;
std::string args = event.args;
if (!args.empty())
{
if (args[0] != ':')
{
target = actorManager->findNearestByName(args);
}
else
{
target = actorManager->findBeing(fromInt(atoi(
args.substr(1).c_str()), BeingId));
}
}
if (!target)
target = localPlayer->getTarget();
if (target && adminHandler)
adminHandler->kick(target->getId());
return true;
}
impHandler0(clearDrop)
{
if (dropShortcut)
dropShortcut->clear();
return true;
}
impHandler0(testInfo)
{
if (actorManager)
{
logger->log("actors count: %d", CAST_S32(
actorManager->size()));
return true;
}
return false;
}
impHandlerE(craftKey)
{
#ifdef EATHENA_SUPPORT
const int slot = (event.action - InputAction::CRAFT_1);
if (slot >= 0 && slot < 9)
{
if (inventoryWindow)
inventoryWindow->moveItemToCraft(slot);
return true;
}
#endif
return false;
}
impHandler0(resetGameModifiers)
{
GameModifiers::resetModifiers();
return true;
}
impHandler(barToChat)
{
if (chatWindow)
{
chatWindow->addInputText(event.args);
return true;
}
return false;
}
impHandler(seen)
{
if (!actorManager)
return false;
ChatTab *tab = event.tab;
if (!tab)
tab = localChatTab;
if (!tab)
return false;
if (config.getBoolValue("enableIdCollecting") == false)
{
// TRANSLATORS: last seen disabled warning
tab->chatLog(_("Last seen disabled. "
"Enable in players / collect players id and seen log."),
ChatMsgType::BY_SERVER);
return true;
}
const std::string name = event.args;
if (name.empty())
return false;
std::string dir = settings.usersDir;
dir.append(stringToHexPath(name)).append("/seen.txt");
if (Files::existsLocal(dir))
{
StringVect lines;
Files::loadTextFileLocal(dir, lines);
if (lines.size() < 3)
{
// TRANSLATORS: last seen error
tab->chatLog(_("You not saw this nick."),
ChatMsgType::BY_SERVER);
return true;
}
const std::string message = strprintf(
// TRANSLATORS: last seen message
_("Last seen for %s: %s"),
name.c_str(),
lines[2].c_str());
tab->chatLog(message, ChatMsgType::BY_SERVER);
}
else
{
// TRANSLATORS: last seen error
tab->chatLog(_("You not saw this nick."),
ChatMsgType::BY_SERVER);
}
return true;
}
impHandler(dumpMemoryUsage)
{
if (event.tab)
memoryManager.printAllMemory(event.tab);
else
memoryManager.printAllMemory(localChatTab);
return true;
}
impHandler(setEmoteType)
{
const std::string &args = event.args;
if (args == "player" || args.empty())
{
settings.emoteType = EmoteType::Player;
}
else if (args == "pet")
{
settings.emoteType = EmoteType::Pet;
}
#ifdef EATHENA_SUPPORT
else if (args == "homun" || args == "homunculus")
{
settings.emoteType = EmoteType::Homunculus;
}
else if (args == "merc" || args == "mercenary")
{
settings.emoteType = EmoteType::Mercenary;
}
#endif
return true;
}
} // namespace Actions