/* * The ManaPlus Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-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 . */ #include "gui/popups/popupmenu.h" #include "actormanager.h" #include "configuration.h" #include "gamemodifiers.h" #include "party.h" #include "being/flooritem.h" #include "being/localplayer.h" #include "being/playerinfo.h" #include "being/playerrelation.h" #include "being/playerrelations.h" #include "const/spells.h" #include "enums/resources/map/mapitemtype.h" #include "input/inputmanager.h" #include "gui/buttontext.h" #include "gui/gui.h" #include "gui/viewport.h" #include "gui/windowmenu.h" #include "gui/windows/chatwindow.h" #include "gui/windows/equipmentwindow.h" #include "gui/windows/inventorywindow.h" #include "gui/windows/minimap.h" #include "gui/windows/ministatuswindow.h" #include "gui/windows/npcdialog.h" #include "gui/windows/outfitwindow.h" #include "gui/windows/socialwindow.h" #include "gui/windows/textcommandeditor.h" #include "gui/windows/textdialog.h" #include "gui/windows/tradewindow.h" #include "gui/widgets/button.h" #include "gui/widgets/createwidget.h" #include "gui/widgets/progressbar.h" #include "gui/widgets/scrollarea.h" #include "gui/widgets/skillinfo.h" #include "gui/widgets/textfield.h" #include "gui/widgets/tabs/chat/whispertab.h" #include "net/adminhandler.h" #include "net/beinghandler.h" #include "net/chathandler.h" #include "net/guildhandler.h" #ifdef EATHENA_SUPPORT #include "net/homunculushandler.h" #include "net/mercenaryhandler.h" #include "net/npchandler.h" #endif #include "net/pethandler.h" #include "net/serverfeatures.h" #ifdef TMWA_SUPPORT #include "net/tmwa/guildmanager.h" #endif #include "resources/chatobject.h" #include "resources/iteminfo.h" #include "resources/db/npcdb.h" #include "resources/item/item.h" #include "resources/map/map.h" #include "resources/map/mapitem.h" #include "resources/map/speciallayer.h" #include "utils/copynpaste.h" #include "utils/gettext.h" #include "utils/process.h" #include "debug.h" std::string tradePartnerName; PopupMenu *popupMenu = nullptr; PopupMenu::PopupMenu() : Popup("PopupMenu", "popupmenu.xml"), mBrowserBox(new BrowserBox(this, BrowserBox::AUTO_SIZE, true, "popupbrowserbox.xml")), mScrollArea(nullptr), mBeingId(BeingId_zero), mFloorItemId(BeingId_zero), mItemId(0), mItemIndex(-1), mItemColor(ItemColor_one), mMapItem(nullptr), mTab(nullptr), mSpell(nullptr), mWindow(nullptr), mRenameListener(), mPlayerListener(), mDialog(nullptr), mButton(nullptr), mNick(), mTextField(nullptr), mType(ActorType::Unknown), mX(0), mY(0) { mBrowserBox->setOpaque(false); mBrowserBox->setLinkHandler(this); mRenameListener.setMapItem(nullptr); mRenameListener.setDialog(nullptr); mPlayerListener.setNick(""); mPlayerListener.setDialog(nullptr); mPlayerListener.setType(ActorType::Unknown); mScrollArea = new ScrollArea(this, mBrowserBox, false); mScrollArea->setVerticalScrollPolicy(ScrollArea::SHOW_AUTO); } void PopupMenu::postInit() { Popup::postInit(); add(mScrollArea); } void PopupMenu::showPopup(const int x, const int y, const Being *const being) { if (!being || !localPlayer || !actorManager) return; mBeingId = being->getId(); mNick = being->getName(); mType = being->getType(); mBrowserBox->clearRows(); mX = x; mY = y; const std::string &name = mNick; mBrowserBox->addRow(name + being->getGenderSignWithSpace()); switch (being->getType()) { case ActorType::Player: { // TRANSLATORS: popup menu item // TRANSLATORS: trade with player mBrowserBox->addRow("/trade 'NAME'", _("Trade")); // TRANSLATORS: popup menu item // TRANSLATORS: trade attack player mBrowserBox->addRow("/attack 'NAME'", _("Attack")); // TRANSLATORS: popup menu item // TRANSLATORS: send whisper to player mBrowserBox->addRow("/whispertext 'NAME'", _("Whisper")); addGmCommands(); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: heal player mBrowserBox->addRow("/heal :'BEINGID'", _("Heal")); mBrowserBox->addRow("##3---"); addPlayerRelation(name); mBrowserBox->addRow("##3---"); addFollow(); addPartyName(being->getPartyName()); const Guild *const guild1 = being->getGuild(); const Guild *const guild2 = localPlayer->getGuild(); if (guild2) { if (guild1) { if (guild1->getId() == guild2->getId()) { mBrowserBox->addRow("/kickguild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: kick player from guild _("Kick from guild")); if (guild2->getServerGuild()) { mBrowserBox->addRow(strprintf( "@@guild-pos|%s >@@", // TRANSLATORS: popup menu item // TRANSLATORS: change player position in guild _("Change pos in guild"))); } } } else if (guild2->getMember(mNick)) { mBrowserBox->addRow("/kickguild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: kick player from guild _("Kick from guild")); if (guild2->getServerGuild()) { mBrowserBox->addRow(strprintf( "@@guild-pos|%s >@@", // TRANSLATORS: popup menu item // TRANSLATORS: change player position in guild _("Change pos in guild"))); } } else { #ifdef TMWA_SUPPORT if (guild2->getServerGuild() || (guildManager && guildManager->havePower())) #endif { mBrowserBox->addRow("/guild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: invite player to guild _("Invite to guild")); } } } // TRANSLATORS: popup menu item // TRANSLATORS: set player invisible for self by id mBrowserBox->addRow("/nuke 'NAME'", _("Nuke")); // TRANSLATORS: popup menu item // TRANSLATORS: move to player location mBrowserBox->addRow("/navigateto 'NAME'", _("Move")); addPlayerMisc(); addBuySell(being); #ifdef EATHENA_SUPPORT addChat(being); #endif break; } case ActorType::Npc: if (!addBeingMenu()) { // TRANSLATORS: popup menu item // TRANSLATORS: talk with npc mBrowserBox->addRow("/talk 'NAME'", _("Talk")); if (serverFeatures->haveNpcWhispers()) { mBrowserBox->addRow("/whispertext NPC:'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: whisper to npc _("Whisper")); } // TRANSLATORS: popup menu item // TRANSLATORS: buy from npc mBrowserBox->addRow("/buy 'NAME'", _("Buy")); // TRANSLATORS: popup menu item // TRANSLATORS: sell to npc mBrowserBox->addRow("/sell 'NAME'", _("Sell")); } mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: move to npc location mBrowserBox->addRow("/navigateto 'NAME'", _("Move")); // TRANSLATORS: popup menu item // TRANSLATORS: add comment to npc mBrowserBox->addRow("addcomment", _("Add comment")); #ifdef EATHENA_SUPPORT addChat(being); #endif break; case ActorType::Monster: { // Monsters can be attacked // TRANSLATORS: popup menu item // TRANSLATORS: attack monster mBrowserBox->addRow("/attack :'BEINGID'", _("Attack")); if (config.getBoolValue("enableAttackFilter")) { mBrowserBox->addRow("##3---"); if (actorManager->isInAttackList(name) || actorManager->isInIgnoreAttackList(name) || actorManager->isInPriorityAttackList(name)) { mBrowserBox->addRow("/removeattack 'NAME'", // TRANSLATORS: remove monster from attack list // TRANSLATORS: popup menu item _("Remove from attack list")); } else { mBrowserBox->addRow("/addpriorityattack 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: add monster to priotiry attack list _("Add to priority attack list")); mBrowserBox->addRow("/addattack 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: add monster to attack list _("Add to attack list")); mBrowserBox->addRow("/addignoreattack 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: add monster to ignore list _("Add to ignore list")); } } break; } #ifdef EATHENA_SUPPORT case ActorType::Mercenary: // TRANSLATORS: popup menu item // TRANSLATORS: Mercenary move to master mBrowserBox->addRow("mercenary to master", _("Move to master")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: fire mercenary mBrowserBox->addRow("fire mercenary", _("Fire")); mBrowserBox->addRow("##3---"); break; case ActorType::Homunculus: // TRANSLATORS: popup menu item // TRANSLATORS: Mercenary move to master mBrowserBox->addRow("homunculus to master", _("Move to master")); // TRANSLATORS: popup menu item // TRANSLATORS: feed homunculus mBrowserBox->addRow("homunculus feed", _("Feed")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: delete homunculus mBrowserBox->addRow("homunculus delete", _("Kill")); mBrowserBox->addRow("##3---"); break; case ActorType::Pet: if (being->getOwner() == localPlayer) { // TRANSLATORS: popup menu item // TRANSLATORS: feed pet mBrowserBox->addRow("pet feed", _("Feed")); // TRANSLATORS: popup menu item // TRANSLATORS: pet drop loot mBrowserBox->addRow("pet drop loot", _("Drop loot")); // TRANSLATORS: popup menu item // TRANSLATORS: pet unequip item mBrowserBox->addRow("pet unequip", _("Unequip")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: pet rename item mBrowserBox->addRow("/setpetname", _("Rename")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: pet return to egg mBrowserBox->addRow("pet to egg", _("Return to egg")); mBrowserBox->addRow("##3---"); } break; case ActorType::SkillUnit: // +++ need impliment menu break; #endif case ActorType::Avatar: case ActorType::Unknown: case ActorType::FloorItem: case ActorType::Portal: case ActorType::LocalPet: default: break; } // TRANSLATORS: popup menu item // TRANSLATORS: add being name to chat mBrowserBox->addRow("/addtext 'NAME'", _("Add name to chat")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } bool PopupMenu::addBeingMenu() { Being *being = actorManager->findBeing(mBeingId); if (!being) return false; BeingInfo *const info = NPCDB::get(fromInt( being->getSubType(), BeingTypeId)); if (!info) return false; const std::vector &menu = info->getMenu(); FOR_EACH (std::vector::const_iterator, it, menu) { const BeingMenuItem &item = *it; mBrowserBox->addRow("/" + item.command, item.name.c_str()); } return true; } void PopupMenu::setMousePos() { if (viewport) { mX = viewport->mMouseX; mY = viewport->mMouseY; } else { Gui::getMouseState(mX, mY); } } void PopupMenu::showPopup(const int x, const int y, const std::vector &beings) { mX = x; mY = y; mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Players")); FOR_EACH (std::vector::const_iterator, it, beings) { const Being *const being = dynamic_cast(*it); const ActorSprite *const actor = *it; if (being && !being->getName().empty()) { mBrowserBox->addRow(strprintf("@@player_%u|%s >@@", static_cast(being->getId()), (being->getName() + being->getGenderSignWithSpace()).c_str())); } else if (actor->getType() == ActorType::FloorItem) { const FloorItem *const floorItem = static_cast(actor); mBrowserBox->addRow(strprintf("@@flooritem_%u|%s >@@", static_cast(actor->getId()), floorItem->getName().c_str())); } } mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showPlayerPopup(const std::string &nick) { if (nick.empty() || !localPlayer) return; setMousePos(); mNick = nick; mBeingId = BeingId_zero; mType = ActorType::Player; mBrowserBox->clearRows(); const std::string &name = mNick; mBrowserBox->addRow(name); // TRANSLATORS: popup menu item // TRANSLATORS: send whisper to player mBrowserBox->addRow("/whispertext 'NAME'", _("Whisper")); addGmCommands(); mBrowserBox->addRow("##3---"); addPlayerRelation(name); mBrowserBox->addRow("##3---"); addFollow(); // TRANSLATORS: popup menu item // TRANSLATORS: add comment to player mBrowserBox->addRow("addcomment", _("Add comment")); if (localPlayer->isInParty()) { const Party *const party = localPlayer->getParty(); if (party) { const PartyMember *const member = party->getMember(mNick); if (member) { // TRANSLATORS: popup menu item // TRANSLATORS: kick player from party mBrowserBox->addRow("/kickparty 'NAME'", _("Kick from party")); mBrowserBox->addRow("##3---"); const PartyMember *const o = party->getMember( localPlayer->getName()); if (o && member->getMap() == o->getMap()) { // TRANSLATORS: popup menu item // TRANSLATORS: move to player position mBrowserBox->addRow("/navigate 'X' 'Y'", _("Move")); } } } } const Guild *const guild2 = localPlayer->getGuild(); if (guild2) { if (guild2->getMember(mNick)) { #ifdef TMWA_SUPPORT if (guild2->getServerGuild() || (guildManager && guildManager->havePower())) #endif { mBrowserBox->addRow("/kickguild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: kick player from guild _("Kick from guild")); } if (guild2->getServerGuild()) { mBrowserBox->addRow(strprintf( // TRANSLATORS: popup menu item // TRANSLATORS: change player position in guild "@@guild-pos|%s >@@", _("Change pos in guild"))); } } else { #ifdef TMWA_SUPPORT if (guild2->getServerGuild() || (guildManager && guildManager->havePower())) #endif { // TRANSLATORS: popup menu item // TRANSLATORS: invite player to guild mBrowserBox->addRow("/guild 'NAME'", _("Invite to guild")); } } } addBuySellDefault(); // TRANSLATORS: popup menu item // TRANSLATORS: add player name to chat mBrowserBox->addRow("/addtext 'NAME'", _("Add name to chat")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(mX, mY); } void PopupMenu::showPopup(const int x, const int y, const FloorItem *const floorItem) { if (!floorItem) return; mX = x; mY = y; mFloorItemId = floorItem->getId(); mType = ActorType::FloorItem; for (int f = 0; f < maxCards; f ++) mItemCards[f] = floorItem->getCard(f); mBrowserBox->clearRows(); const std::string name = floorItem->getName(); mNick = name; mBrowserBox->addRow(name); if (config.getBoolValue("enablePickupFilter")) { if (actorManager->isInPickupList(name) || (actorManager->isInPickupList("") && !actorManager->isInIgnorePickupList(name))) { // TRANSLATORS: popup menu item // TRANSLATORS: pickup item from ground mBrowserBox->addRow("/pickup 'FLOORID'", _("Pick up")); mBrowserBox->addRow("##3---"); } addPickupFilter(name); } else { // TRANSLATORS: popup menu item // TRANSLATORS: pickup item from ground mBrowserBox->addRow("/pickup 'FLOORID'", _("Pick up")); } addProtection(); // TRANSLATORS: popup menu item // TRANSLATORS: add item name to chat mBrowserBox->addRow("/addchat 'FLOORID''CARDS'", _("Add to chat")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(mX, mY); } void PopupMenu::showPopup(const int x, const int y, MapItem *const mapItem) { if (!mapItem) return; mMapItem = mapItem; mX = x; mY = y; mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Map Item")); // TRANSLATORS: popup menu item // TRANSLATORS: rename map item mBrowserBox->addRow("rename map", _("Rename")); // TRANSLATORS: popup menu item // TRANSLATORS: remove map item mBrowserBox->addRow("remove map", _("Remove")); if (localPlayer && localPlayer->isGM()) { mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: warp to map item mBrowserBox->addRow("/slide 'MAPX' 'MAPY'", _("Warp")); } mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showMapPopup(const int x, const int y, const int x2, const int y2, const bool isMinimap) { mX = x2; mY = y2; if (isMinimap) mWindow = minimap; mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Map Item")); if (localPlayer && localPlayer->isGM()) { // TRANSLATORS: popup menu item // TRANSLATORS: warp to map item mBrowserBox->addRow("/slide 'MAPX' 'MAPY'", _("Warp")); } // TRANSLATORS: popup menu item // TRANSLATORS: move to map item mBrowserBox->addRow("/navigate 'X' 'Y'", _("Move")); // TRANSLATORS: popup menu item // TRANSLATORS: move camera to map item mBrowserBox->addRow("/movecamera 'X' 'Y'", _("Move camera")); mBrowserBox->addRow("##3---"); if (isMinimap) { // TRANSLATORS: popup menu item mBrowserBox->addRow("window close", _("Close")); } mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showOutfitsWindowPopup(const int x, const int y) { mX = x; mY = y; mWindow = outfitWindow; mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Outfits")); // TRANSLATORS: popup menu item // TRANSLATORS: clear selected outfit mBrowserBox->addRow("clear outfit", _("Clear outfit")); mBrowserBox->addRow("##3---"); addWindowMenu(outfitWindow); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showSpellPopup(const int x, const int y, TextCommand *const cmd) { if (!cmd) return; mBrowserBox->clearRows(); mSpell = cmd; mX = x; mY = y; // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Spells")); // TRANSLATORS: popup menu item // TRANSLATORS: edit selected spell mBrowserBox->addRow("edit spell", _("Edit spell")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showChatPopup(const int x, const int y, ChatTab *const tab) { if (!tab || !actorManager || !localPlayer) return; mTab = tab; mX = x; mY = y; mWindow = chatWindow; mBrowserBox->clearRows(); const ChatTabType::Type &type = tab->getType(); if (type == ChatTabType::WHISPER || type == ChatTabType::CHANNEL) { // TRANSLATORS: popup menu item // TRANSLATORS: close chat tab mBrowserBox->addRow("chat close", _("Close")); } // TRANSLATORS: popup menu item // TRANSLATORS: remove all text from chat tab mBrowserBox->addRow("/chatclear", _("Clear")); mBrowserBox->addRow("##3---"); if (tab->getAllowHighlight()) { // TRANSLATORS: popup menu item // TRANSLATORS: disable chat tab highlight mBrowserBox->addRow("disable highlight", _("Disable highlight")); } else { // TRANSLATORS: popup menu item // TRANSLATORS: enable chat tab highlight mBrowserBox->addRow("enable highlight", _("Enable highlight")); } if (tab->getRemoveNames()) { // TRANSLATORS: popup menu item // TRANSLATORS: do not remove player names from chat tab mBrowserBox->addRow("dont remove name", _("Don't remove name")); } else { // TRANSLATORS: popup menu item // TRANSLATORS: remove player names from chat tab mBrowserBox->addRow("remove name", _("Remove name")); } if (tab->getNoAway()) { // TRANSLATORS: popup menu item // TRANSLATORS: enable away messages in chat tab mBrowserBox->addRow("enable away", _("Enable away")); } else { // TRANSLATORS: popup menu item // TRANSLATORS: disable away messages in chat tab mBrowserBox->addRow("disable away", _("Disable away")); } mBrowserBox->addRow("##3---"); if (tab->getType() == static_cast(ChatTabType::PARTY)) { // TRANSLATORS: popup menu item // TRANSLATORS: enable away messages in chat tab mBrowserBox->addRow("/leaveparty", _("Leave")); mBrowserBox->addRow("##3---"); } // TRANSLATORS: popup menu item // TRANSLATORS: copy selected text to clipboard mBrowserBox->addRow("/chatclipboard 'X' 'Y'", _("Copy to clipboard")); mBrowserBox->addRow("##3---"); if (type == ChatTabType::WHISPER) { const WhisperTab *const wTab = static_cast(tab); std::string name = wTab->getNick(); const Being* const being = actorManager->findBeingByName( name, ActorType::Player); if (being) { mBeingId = being->getId(); mNick = being->getName(); mType = being->getType(); // TRANSLATORS: popup menu item // TRANSLATORS: trade with player mBrowserBox->addRow("/trade 'NAME'", _("Trade")); // TRANSLATORS: popup menu item // TRANSLATORS: attack player mBrowserBox->addRow("/attack 'NAME'", _("Attack")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: heal player mBrowserBox->addRow("/heal :'BEINGID'", _("Heal")); mBrowserBox->addRow("##3---"); addPlayerRelation(name); mBrowserBox->addRow("##3---"); addFollow(); // TRANSLATORS: popup menu item // TRANSLATORS: move to player position mBrowserBox->addRow("/navigateto 'NAME'", _("Move")); addPlayerMisc(); addBuySell(being); mBrowserBox->addRow("##3---"); addParty(wTab->getNick()); const Guild *const guild1 = being->getGuild(); const Guild *const guild2 = localPlayer->getGuild(); if (guild2) { if (guild1) { if (guild1->getId() == guild2->getId()) { #ifdef TMWA_SUPPORT if (guild2->getServerGuild() || (guildManager && guildManager->havePower())) #endif { mBrowserBox->addRow("/kickguild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: kick player from guild _("Kick from guild")); } if (guild2->getServerGuild()) { mBrowserBox->addRow(strprintf("@@guild-pos|%s >@@", // TRANSLATORS: popup menu item // TRANSLATORS: change player position in guild _("Change pos in guild"))); } } } else { #ifdef TMWA_SUPPORT if (guild2->getServerGuild() || (guildManager && guildManager->havePower())) #endif { mBrowserBox->addRow("/guild 'NAME'", // TRANSLATORS: popup menu item // TRANSLATORS: invite player to guild _("Invite to guild")); } } } } else { mNick = name; mType = ActorType::Player; addPlayerRelation(name); mBrowserBox->addRow("##3---"); addFollow(); if (localPlayer->isInParty()) { const Party *const party = localPlayer->getParty(); if (party) { const PartyMember *const m = party->getMember(mNick); if (m) { // TRANSLATORS: popup menu item // TRANSLATORS: move to player location mBrowserBox->addRow("/navigateto 'NAME'", _("Move")); } } } addPlayerMisc(); addBuySellDefault(); if (serverFeatures->havePartyNickInvite()) addParty(wTab->getNick()); mBrowserBox->addRow("##3---"); } } addWindowMenu(chatWindow); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showChangePos(const int x, const int y) { mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("Change guild position")); if (!localPlayer) return; mX = x; mY = y; const Guild *const guild = localPlayer->getGuild(); if (guild) { const PositionsMap &map = guild->getPositions(); FOR_EACH (PositionsMap::const_iterator, itr, map) { mBrowserBox->addRow(strprintf("@@guild-pos-%u|%s@@", itr->first, itr->second.c_str())); } // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } else { mBeingId = BeingId_zero; mFloorItemId = BeingId_zero; mItemIndex = -1; mItemId = 0; for (int f = 0; f < maxCards; f ++) mItemCards[f] = 0; mMapItem = nullptr; mNick.clear(); mType = ActorType::Unknown; mX = 0; mY = 0; setVisible(Visible_false); } } void PopupMenu::showWindowPopup(Window *const window) { if (!window) return; setMousePos(); mWindow = window; mBrowserBox->clearRows(); // TRANSLATORS: popup menu header mBrowserBox->addRow(_("window")); addWindowMenu(window); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(mX, mY); } void PopupMenu::addWindowMenu(Window *const window) { if (!window) return; if (window->getAlowClose()) { // TRANSLATORS: popup menu item // TRANSLATORS: close window mBrowserBox->addRow("window close", _("Close")); } if (window->isStickyButtonLock()) { if (window->isSticky()) { // TRANSLATORS: popup menu item // TRANSLATORS: unlock window mBrowserBox->addRow("window unlock", _("Unlock")); } else { // TRANSLATORS: popup menu item // TRANSLATORS: lock window mBrowserBox->addRow("window lock", _("Lock")); } } } void PopupMenu::handleLink(const std::string &link, MouseEvent *event A_UNUSED) { Being *being = nullptr; if (actorManager) being = actorManager->findBeing(mBeingId); if (link == "chat close" && mTab) { inputManager.executeChatCommand(InputAction::CLOSE_CHAT_TAB, std::string(), mTab); } else if (link == "remove map" && mMapItem) { if (viewport) { const Map *const map = viewport->getMap(); if (map) { SpecialLayer *const specialLayer = map->getSpecialLayer(); if (specialLayer) { const bool isHome = (mMapItem->getType() == static_cast(MapItemType::HOME)); const int x = static_cast(mMapItem->getX()); const int y = static_cast(mMapItem->getY()); specialLayer->setTile(x, y, static_cast(MapItemType::EMPTY)); if (socialWindow) socialWindow->removePortal(x, y); if (isHome && localPlayer) { localPlayer->removeHome(); localPlayer->saveHomes(); } } } } } else if (link == "rename map" && mMapItem) { mRenameListener.setMapItem(mMapItem); CREATEWIDGETV(mDialog, TextDialog, // TRANSLATORS: dialog caption // TRANSLATORS: number of chars in string should be near original _("Rename map sign "), // TRANSLATORS: label // TRANSLATORS: number of chars in string should be near original _("Name: ")); mRenameListener.setDialog(mDialog); mDialog->setText(mMapItem->getComment()); mDialog->setActionEventId("ok"); mDialog->addActionListener(&mRenameListener); } else if (link == "edit spell" && mSpell) { CREATEWIDGET(TextCommandEditor, mSpell); } else if (link == "addcomment" && !mNick.empty()) { TextDialog *const dialog = CREATEWIDGETR(TextDialog, // TRANSLATORS: dialog caption // TRANSLATORS: number of chars in string should be near original _("Player comment "), // TRANSLATORS: label // TRANSLATORS: number of chars in string should be near original _("Comment: ")); mPlayerListener.setDialog(dialog); mPlayerListener.setNick(mNick); mPlayerListener.setType(mType); if (being) { being->updateComment(); dialog->setText(being->getComment()); } else { dialog->setText(Being::loadComment(mNick, static_cast(mType))); } dialog->setActionEventId("ok"); dialog->addActionListener(&mPlayerListener); } else if (link == "enable highlight" && mTab) { inputManager.executeChatCommand(InputAction::ENABLE_HIGHLIGHT, std::string(), mTab); } else if (link == "disable highlight" && mTab) { inputManager.executeChatCommand(InputAction::DISABLE_HIGHLIGHT, std::string(), mTab); } else if (link == "dont remove name" && mTab) { inputManager.executeChatCommand(InputAction::DONT_REMOVE_NAME, std::string(), mTab); } else if (link == "remove name" && mTab) { inputManager.executeChatCommand(InputAction::REMOVE_NAME, std::string(), mTab); } else if (link == "disable away" && mTab) { inputManager.executeChatCommand(InputAction::DISABLE_AWAY, std::string(), mTab); } else if (link == "enable away" && mTab) { inputManager.executeChatCommand(InputAction::ENABLE_AWAY, std::string(), mTab); } else if (link == "attack moveup") { if (actorManager) { const int idx = actorManager->getAttackMobIndex(mNick); if (idx > 0) { std::list mobs = actorManager->getAttackMobs(); std::list::iterator it = mobs.begin(); std::list::iterator it2 = it; while (it != mobs.end()) { if (*it == mNick) { -- it2; mobs.splice(it2, mobs, it); actorManager->setAttackMobs(mobs); actorManager->rebuildAttackMobs(); break; } ++ it; ++ it2; } if (socialWindow) socialWindow->updateAttackFilter(); } } } else if (link == "priority moveup") { if (actorManager) { const int idx = actorManager-> getPriorityAttackMobIndex(mNick); if (idx > 0) { std::list mobs = actorManager->getPriorityAttackMobs(); std::list::iterator it = mobs.begin(); std::list::iterator it2 = it; while (it != mobs.end()) { if (*it == mNick) { -- it2; mobs.splice(it2, mobs, it); actorManager->setPriorityAttackMobs(mobs); actorManager->rebuildPriorityAttackMobs(); break; } ++ it; ++ it2; } if (socialWindow) socialWindow->updateAttackFilter(); } } } else if (link == "attack movedown") { if (actorManager) { const int idx = actorManager->getAttackMobIndex(mNick); const int size = actorManager->getAttackMobsSize(); if (idx + 1 < size) { std::list mobs = actorManager->getAttackMobs(); std::list::iterator it = mobs.begin(); std::list::iterator it2 = it; while (it != mobs.end()) { if (*it == mNick) { ++ it2; if (it2 == mobs.end()) break; mobs.splice(it, mobs, it2); actorManager->setAttackMobs(mobs); actorManager->rebuildAttackMobs(); break; } ++ it; ++ it2; } if (socialWindow) socialWindow->updateAttackFilter(); } } } else if (link == "priority movedown") { if (localPlayer && actorManager) { const int idx = actorManager ->getPriorityAttackMobIndex(mNick); const int size = actorManager->getPriorityAttackMobsSize(); if (idx + 1 < size) { std::list mobs = actorManager->getPriorityAttackMobs(); std::list::iterator it = mobs.begin(); std::list::iterator it2 = it; while (it != mobs.end()) { if (*it == mNick) { ++ it2; if (it2 == mobs.end()) break; mobs.splice(it, mobs, it2); actorManager->setPriorityAttackMobs(mobs); actorManager->rebuildPriorityAttackMobs(); break; } ++ it; ++ it2; } if (socialWindow) socialWindow->updateAttackFilter(); } } } else if (link == "reset yellow") { GameModifiers::resetModifiers(); } else if (link == "bar to chat" && !mNick.empty()) { if (chatWindow) chatWindow->addInputText(mNick); } else if (link == "items" && being) { if (being == localPlayer) { if (equipmentWindow && !equipmentWindow->isWindowVisible()) equipmentWindow->setVisible(Visible_true); } else { if (beingEquipmentWindow) { beingEquipmentWindow->setBeing(being); beingEquipmentWindow->setVisible(Visible_true); } } } else if (link == "undress item" && being && mItemId) { being->undressItemById(mItemId); } else if (link == "guild-pos" && !mNick.empty()) { showChangePos(getX(), getY()); return; } else if (link == "clear outfit") { if (outfitWindow) outfitWindow->clearCurrentOutfit(); } else if (link == "clipboard copy") { if (mTextField) mTextField->handleCopy(); } else if (link == "clipboard paste") { if (mTextField) mTextField->handlePaste(); } else if (link == "open link" && !mNick.empty()) { openBrowser(mNick); } else if (link == "clipboard link" && !mNick.empty()) { sendBuffer(mNick); } else if (link == "goto" && !mNick.empty()) { adminHandler->gotoName(mNick); } else if (link == "recall" && !mNick.empty()) { adminHandler->recallName(mNick); } else if (link == "revive" && !mNick.empty()) { adminHandler->reviveName(mNick); } else if (link == "ipcheck" && !mNick.empty()) { adminHandler->ipcheckName(mNick); } else if (link == "gm" && !mNick.empty()) { showGMPopup(); return; } else if (link == "window close" && mWindow) { if (Widget::widgetExists(mWindow)) mWindow->close(); } else if (link == "window unlock" && mWindow) { if (Widget::widgetExists(mWindow)) mWindow->setSticky(false); } else if (link == "window lock" && mWindow) { if (Widget::widgetExists(mWindow)) mWindow->setSticky(true); } #ifdef EATHENA_SUPPORT else if (link == "join chat" && being) { const ChatObject *const chat = being->getChat(); if (chat) chatHandler->joinChat(chat, ""); } else if (link == "fire mercenary") { mercenaryHandler->fire(); } else if (link == "mercenary to master") { mercenaryHandler->moveToMaster(); } else if (link == "homunculus to master") { homunculusHandler->moveToMaster(); } else if (link == "homunculus feed") { homunculusHandler->feed(); } else if (link == "homunculus delete") { homunculusHandler->fire(); } else if (link == "craftmenu") { showCraftPopup(); return; } #endif else if (link == "pet feed") { petHandler->feed(); } else if (link == "pet drop loot") { petHandler->dropLoot(); } else if (link == "pet to egg") { petHandler->returnToEgg(); } else if (link == "pet unequip") { petHandler->unequip(); } else if (!link.compare(0, 10, "guild-pos-")) { if (localPlayer) { const int num = atoi(link.substr(10).c_str()); const Guild *const guild = localPlayer->getGuild(); if (guild) { guildHandler->changeMemberPostion( guild->getMember(mNick), num); } } } else if (!link.compare(0, 7, "player_")) { if (actorManager) { mBeingId = fromInt(atoi(link.substr(7).c_str()), BeingId); being = actorManager->findBeing(mBeingId); if (being) { showPopup(getX(), getY(), being); return; } } } else if (!link.compare(0, 10, "flooritem_")) { if (actorManager) { const BeingId id = fromInt(atoi( link.substr(10).c_str()), BeingId); if (id != BeingId_zero) { const FloorItem *const item = actorManager->findItem(id); if (item) { mFloorItemId = item->getId(); for (int f = 0; f < maxCards; f ++) mItemCards[f] = item->getCard(f); showPopup(getX(), getY(), item); return; } } } } else if (!link.compare(0, 12, "hide button_")) { if (windowMenu) windowMenu->showButton(link.substr(12), Visible_false); } else if (!link.compare(0, 12, "show button_")) { if (windowMenu) windowMenu->showButton(link.substr(12), Visible_true); } else if (!link.compare(0, 9, "hide bar_")) { if (miniStatusWindow) miniStatusWindow->showBar(link.substr(9), Visible_false); } else if (!link.compare(0, 9, "show bar_")) { if (miniStatusWindow) miniStatusWindow->showBar(link.substr(9), Visible_true); } else if (!link.compare(0, 12, "show window_")) { const int id = atoi(link.substr(12).c_str()); if (id >= 0) inputManager.executeAction(static_cast(id)); } else if (!link.compare(0, 6, "mute_+")) { if (being) { const int time = atoi(link.substr(6).c_str()); adminHandler->mute(being, 1, time); } } else if (!link.compare(0, 6, "mute_-")) { if (being) { const int time = atoi(link.substr(6).c_str()); adminHandler->mute(being, 0, time); } } else if (!link.empty() && link[0] == '/') { std::string cmd = link.substr(1); replaceAll(cmd, "'NAME'", mNick); replaceAll(cmd, "'X'", toString(mX)); replaceAll(cmd, "'Y'", toString(mY)); replaceAll(cmd, "'BEINGID'", toString(toInt(mBeingId, int))); replaceAll(cmd, "'FLOORID'", toString(toInt(mFloorItemId, int))); replaceAll(cmd, "'ITEMID'", toString(mItemId)); replaceAll(cmd, "'ITEMCOLOR'", toString(toInt(mItemColor, int))); replaceAll(cmd, "'BEINGTYPEID'", toString(static_cast(mType))); replaceAll(cmd, "'PLAYER'", localPlayer->getName()); if (mItemIndex >= 0) replaceAll(cmd, "'INVINDEX'", toString(mItemIndex)); else replaceAll(cmd, "'INVINDEX'", "0"); if (mMapItem) { replaceAll(cmd, "'MAPX'", toString(mMapItem->getX())); replaceAll(cmd, "'MAPY'", toString(mMapItem->getY())); } else { replaceAll(cmd, "'MAPX'", toString(mX)); replaceAll(cmd, "'MAPY'", toString(mY)); } std::string cards; for (int f = 0; f < maxCards; f ++) { const int id = mItemCards[f]; if (id) { cards.append(","); cards.append(toString(id)); } } replaceAll(cmd, "'CARDS'", cards); const size_t pos = cmd.find(' '); const std::string type(cmd, 0, pos); std::string args(cmd, pos == std::string::npos ? cmd.size() : pos + 1); args = trim(args); inputManager.executeChatCommand(type, args, mTab); } // Unknown actions else if (link != "cancel") { logger->log("PopupMenu: Warning, unknown action '%s'", link.c_str()); } setVisible(Visible_false); mBeingId = BeingId_zero; mFloorItemId = BeingId_zero; mItemId = 0; mItemIndex = -1; for (int f = 0; f < maxCards; f ++) mItemCards[f] = 0; mItemColor = ItemColor_one; mMapItem = nullptr; mTab = nullptr; mSpell = nullptr; mWindow = nullptr; mDialog = nullptr; mButton = nullptr; mNick.clear(); mTextField = nullptr; mType = ActorType::Unknown; mX = 0; mY = 0; } void PopupMenu::showPopup(Window *const parent, const int x, const int y, const Item *const item, const InventoryTypeT type) { if (!item) return; mItemId = item->getId(); mItemIndex = item->getInvIndex(); for (int f = 0; f < maxCards; f ++) mItemCards[f] = item->getCard(f); mItemColor = item->getColor(); mWindow = parent; mX = x; mY = y; mNick.clear(); mBrowserBox->clearRows(); const int cnt = item->getQuantity(); const bool isProtected = PlayerInfo::isItemProtected(mItemId); switch (type) { case InventoryType::Inventory: if (tradeWindow && tradeWindow->isWindowVisible() && !isProtected) { // TRANSLATORS: popup menu item // TRANSLATORS: add item to trade mBrowserBox->addRow("/addtrade 'INVINDEX'", _("Add to trade")); if (cnt > 1) { if (cnt > 10) { mBrowserBox->addRow("/addtrade 'INVINDEX' 10", // TRANSLATORS: popup menu item // TRANSLATORS: add 10 item amount to trade _("Add to trade 10")); } mBrowserBox->addRow("/addtrade 'INVINDEX' /", // TRANSLATORS: popup menu item // TRANSLATORS: add half item amount to trade _("Add to trade half")); mBrowserBox->addRow("/addtrade 'INVINDEX' -1", // TRANSLATORS: popup menu item // TRANSLATORS: add all amount except one item to trade _("Add to trade all-1")); mBrowserBox->addRow("/addtrade 'INVINDEX' all", // TRANSLATORS: popup menu item // TRANSLATORS: add all amount item to trade _("Add to trade all")); } mBrowserBox->addRow("##3---"); } if (InventoryWindow::isStorageActive()) { // TRANSLATORS: popup menu item // TRANSLATORS: add item to storage mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store")); if (cnt > 1) { if (cnt > 10) { mBrowserBox->addRow("/invtostorage 'INVINDEX' 10", // TRANSLATORS: popup menu item // TRANSLATORS: add 10 item amount to storage _("Store 10")); } mBrowserBox->addRow("/invtostorage 'INVINDEX' /", // TRANSLATORS: popup menu item // TRANSLATORS: add half item amount to storage _("Store half")); mBrowserBox->addRow("/invtostorage 'INVINDEX' -1", // TRANSLATORS: popup menu item // TRANSLATORS: add all except one item to storage _("Store all-1")); mBrowserBox->addRow("/invtostorage 'INVINDEX' all", // TRANSLATORS: popup menu item // TRANSLATORS: add all item amount to storage _("Store all")); } mBrowserBox->addRow("##3---"); } #ifdef EATHENA_SUPPORT if (npcHandler) { NpcDialog *const dialog = npcHandler->getCurrentNpcDialog(); if (dialog && dialog->getInputState() == NpcDialog::NPC_INPUT_ITEM_CRAFT) { mBrowserBox->addRow("craftmenu", // TRANSLATORS: popup menu item // TRANSLATORS: sub menu for craft _("Move to craft...")); } } #endif addUseDrop(item, isProtected); break; case InventoryType::Storage: // TRANSLATORS: popup menu item // TRANSLATORS: get item from storage mBrowserBox->addRow("/storagetoinv 'INVINDEX'", _("Retrieve")); if (cnt > 1) { if (cnt > 10) { mBrowserBox->addRow("/storagetoinv 'INVINDEX' 10", // TRANSLATORS: popup menu item // TRANSLATORS: get 10 item amount from storage _("Retrieve 10")); } mBrowserBox->addRow("/storagetoinv 'INVINDEX' /", // TRANSLATORS: popup menu item // TRANSLATORS: get half item amount from storage _("Retrieve half")); mBrowserBox->addRow("/storagetoinv 'INVINDEX' -1", // TRANSLATORS: popup menu item // TRANSLATORS: get all except one item amount from storage _("Retrieve all-1")); mBrowserBox->addRow("/storagetoinv 'INVINDEX' all", // TRANSLATORS: popup menu item // TRANSLATORS: get all item amount from storage _("Retrieve all")); } 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; } addProtection(); if (config.getBoolValue("enablePickupFilter")) { mNick = item->getName(); mBrowserBox->addRow("##3---"); addPickupFilter(mNick); } // TRANSLATORS: popup menu item // TRANSLATORS: add item name to chat mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showItemPopup(const int x, const int y, const int itemId, const ItemColor color) { const Inventory *const inv = PlayerInfo::getInventory(); if (!inv) return; Item *const item = inv->findItem(itemId, color); if (item) { showItemPopup(x, y, item); } else { mItemId = itemId; mItemIndex = -1; mItemColor = color; for (int f = 0; f < maxCards; f ++) mItemCards[f] = 0; mX = x; mY = y; mBrowserBox->clearRows(); if (!PlayerInfo::isItemProtected(mItemId)) { // TRANSLATORS: popup menu item // TRANSLATORS: use item mBrowserBox->addRow("/use 'ITEMID'", _("Use")); } addProtection(); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } } void PopupMenu::showItemPopup(const int x, const int y, Item *const item) { mX = x; mY = y; if (item) { mItemId = item->getId(); mItemColor = item->getColor(); mItemIndex = item->getInvIndex(); for (int f = 0; f < maxCards; f ++) mItemCards[f] = item->getCard(f); } else { mItemId = 0; mItemColor = ItemColor_one; mItemIndex = -1; for (int f = 0; f < maxCards; f ++) mItemCards[f] = 0; } mNick.clear(); mBrowserBox->clearRows(); if (item) { const bool isProtected = PlayerInfo::isItemProtected(mItemId); addUseDrop(item, isProtected); if (InventoryWindow::isStorageActive()) { // TRANSLATORS: popup menu item // TRANSLATORS: add item to storage mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store")); } // TRANSLATORS: popup menu item // TRANSLATORS: add item name to chat mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat")); if (config.getBoolValue("enablePickupFilter")) { mNick = item->getName(); mBrowserBox->addRow("##3---"); addPickupFilter(mNick); } } addProtection(); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showDropPopup(const int x, const int y, Item *const item) { mX = x; mY = y; mNick.clear(); mBrowserBox->clearRows(); if (item) { mItemId = item->getId(); mItemColor = item->getColor(); mItemIndex = item->getInvIndex(); for (int f = 0; f < maxCards; f ++) mItemCards[f] = item->getCard(f); const bool isProtected = PlayerInfo::isItemProtected(mItemId); addUseDrop(item, isProtected); if (InventoryWindow::isStorageActive()) { // TRANSLATORS: popup menu item // TRANSLATORS: add item to storage mBrowserBox->addRow("/invtostorage 'INVINDEX'", _("Store")); } addProtection(); // TRANSLATORS: popup menu item // TRANSLATORS: add item name to chat mBrowserBox->addRow("/addchat 'ITEMID''CARDS'", _("Add to chat")); if (config.getBoolValue("enablePickupFilter")) { mNick = item->getName(); mBrowserBox->addRow("##3---"); addPickupFilter(mNick); } } mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item mBrowserBox->addRow("/cleardrops", _("Clear drop window")); mBrowserBox->addRow("##3---"); // TRANSLATORS: popup menu item // TRANSLATORS: close menu mBrowserBox->addRow("cancel", _("Cancel")); showPopup(x, y); } void PopupMenu::showPopup(const int x, const int y, Button *const button) { if (!button || !windowMenu) return; mButton = button; mX = x; mY = y; mBrowserBox->clearRows(); std::vector