summaryrefslogtreecommitdiff
path: root/src/net/ea/playerhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/ea/playerhandler.cpp')
-rw-r--r--src/net/ea/playerhandler.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp
new file mode 100644
index 000000000..05b6cf9e3
--- /dev/null
+++ b/src/net/ea/playerhandler.cpp
@@ -0,0 +1,639 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011 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 "net/ea/playerhandler.h"
+
+#include "log.h"
+#include "party.h"
+#include "playerinfo.h"
+#include "units.h"
+
+#include "gui/okdialog.h"
+#include "gui/skilldialog.h"
+#include "gui/statuswindow.h"
+#include "gui/viewport.h"
+
+#include "net/messagein.h"
+#include "net/npchandler.h"
+
+#include "net/ea/eaprotocol.h"
+
+#include "utils/gettext.h"
+
+#include "debug.h"
+
+extern OkDialog *weightNotice;
+extern OkDialog *deathNotice;
+extern int weightNoticeTime;
+
+// Max. distance we are willing to scroll after a teleport;
+// everything beyond will reset the port hard.
+static const int MAP_TELEPORT_SCROLL_DISTANCE = 8;
+
+// TODO Move somewhere else
+namespace
+{
+ /**
+ * Listener used for handling the overweigth message.
+ */
+ struct WeightListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event A_UNUSED)
+ {
+ weightNotice = NULL;
+ }
+ } weightListener;
+
+ /**
+ * Listener used for handling death message.
+ */
+ struct DeathListener : public gcn::ActionListener
+ {
+ void action(const gcn::ActionEvent &event A_UNUSED)
+ {
+ Net::getPlayerHandler()->respawn();
+ deathNotice = NULL;
+
+ Client::closeDialogs();
+
+ if (viewport)
+ viewport->closePopupMenu();
+
+ Net::NpcHandler *handler = Net::getNpcHandler();
+ if (handler)
+ handler->clearDialogs();
+ if (player_node)
+ player_node->respawn();
+ }
+ } deathListener;
+
+} // anonymous namespace
+
+static const char *randomDeathMessage()
+{
+ static char const *const deadMsg[] =
+ {
+ N_("You are dead."),
+ N_("We regret to inform you that your character was killed in "
+ "battle."),
+ N_("You are not that alive anymore."),
+ N_("The cold hands of the grim reaper are grabbing for your soul."),
+ N_("Game Over!"),
+ N_("Insert coin to continue."),
+ N_("No, kids. Your character did not really die. It... "
+ "err... went to a better place."),
+ N_("Your plan of breaking your enemies weapon by "
+ "bashing it with your throat failed."),
+ N_("I guess this did not run too well."),
+ // NetHack reference:
+ N_("Do you want your possessions identified?"),
+ // Secret of Mana reference:
+ N_("Sadly, no trace of you was ever found..."),
+ // Final Fantasy VI reference:
+ N_("Annihilated."),
+ // Earthbound reference:
+ N_("Looks like you got your head handed to you."),
+ // Leisure Suit Larry 1 reference:
+ N_("You screwed up again, dump your body down the tubes "
+ "and get you another one."),
+ // Monty Python references (Dead Parrot sketch mostly):
+ N_("You're not dead yet. You're just resting."),
+ N_("You are no more."),
+ N_("You have ceased to be."),
+ N_("You've expired and gone to meet your maker."),
+ N_("You're a stiff."),
+ N_("Bereft of life, you rest in peace."),
+ N_("If you weren't so animated, you'd be pushing up the daisies."),
+ N_("Your metabolic processes are now history."),
+ N_("You're off the twig."),
+ N_("You've kicked the bucket."),
+ N_("You've shuffled off your mortal coil, run down the "
+ "curtain and joined the bleedin' choir invisibile."),
+ N_("You are an ex-player."),
+ N_("You're pining for the fjords.")
+ };
+
+ const int random = static_cast<int>(rand() % (sizeof(deadMsg)
+ / sizeof(deadMsg[0])));
+ return gettext(deadMsg[random]);
+}
+
+namespace Ea
+{
+
+PlayerHandler::PlayerHandler()
+{
+}
+
+void PlayerHandler::decreaseAttribute(int attr A_UNUSED)
+{
+ // Supported by eA?
+}
+
+void PlayerHandler::ignorePlayer(const std::string &player A_UNUSED,
+ bool ignore A_UNUSED)
+{
+ // TODO
+}
+
+void PlayerHandler::ignoreAll(bool ignore A_UNUSED)
+{
+ // TODO
+}
+
+bool PlayerHandler::canCorrectAttributes() const
+{
+ return false;
+}
+
+Vector PlayerHandler::getDefaultWalkSpeed() const
+{
+ // Return an normalized speed for any side
+ // as the offset is calculated elsewhere.
+ return Vector(150, 150, 0);
+}
+
+void PlayerHandler::processWalkResponse(Net::MessageIn &msg)
+{
+ /*
+ * This client assumes that all walk messages succeed,
+ * and that the server will send a correction notice
+ * otherwise.
+ */
+ Uint16 srcX, srcY, dstX, dstY;
+ msg.readInt32(); //tick
+ msg.readCoordinatePair(srcX, srcY, dstX, dstY);
+ if (player_node)
+ player_node->setRealPos(dstX, dstY);
+}
+
+void PlayerHandler::processPlayerWarp(Net::MessageIn &msg)
+{
+ std::string mapPath = msg.readString(16);
+ int x = msg.readInt16();
+ int y = msg.readInt16();
+
+ logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y);
+
+ if (!player_node)
+ logger->log1("SMSG_PLAYER_WARP player_node null");
+
+ /*
+ * We must clear the local player's target *before* the call
+ * to changeMap, as it deletes all beings.
+ */
+ if (player_node)
+ player_node->stopAttack();
+
+ Game *game = Game::instance();
+
+ const std::string &currentMapName = game->getCurrentMapName();
+ bool sameMap = (currentMapName == mapPath);
+
+ // Switch the actual map, deleting the previous one if necessary
+ mapPath = mapPath.substr(0, mapPath.rfind("."));
+ game->changeMap(mapPath);
+
+ float scrollOffsetX = 0.0f;
+ float scrollOffsetY = 0.0f;
+
+ if (player_node)
+ {
+ Map *map = game->getCurrentMap();
+ if (map)
+ {
+ if (x >= map->getWidth())
+ x = map->getWidth() - 1;
+ if (y >= map->getHeight())
+ y = map->getHeight() - 1;
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+ /* Scroll if neccessary */
+ if (!sameMap
+ || (abs(x - player_node->getTileX())
+ > MAP_TELEPORT_SCROLL_DISTANCE)
+ || (abs(y - player_node->getTileY())
+ > MAP_TELEPORT_SCROLL_DISTANCE))
+ {
+ scrollOffsetX = static_cast<float>((x
+ - player_node->getTileX())
+ * map->getTileWidth());
+ scrollOffsetY = static_cast<float>((y
+ - player_node->getTileY())
+ * map->getTileHeight());
+ }
+ }
+
+ player_node->setAction(Being::STAND);
+ player_node->setTileCoords(x, y);
+ player_node->navigateClean();
+ }
+
+ logger->log("Adjust scrolling by %d:%d",
+ static_cast<int>(scrollOffsetX),
+ static_cast<int>(scrollOffsetY));
+
+ if (viewport)
+ viewport->scrollBy(scrollOffsetX, scrollOffsetY);
+}
+
+void PlayerHandler::processPlayerStatUpdate1(Net::MessageIn &msg)
+{
+ int type = msg.readInt16();
+ int value = msg.readInt32();
+ if (!player_node)
+ return;
+
+ switch (type)
+ {
+ case 0x0000:
+ player_node->setWalkSpeed(Vector(static_cast<float>(
+ value), static_cast<float>(value), 0));
+ PlayerInfo::setStatBase(WALK_SPEED, value);
+ PlayerInfo::setStatMod(WALK_SPEED, 0);
+ break;
+ case 0x0004: break; // manner
+ case 0x0005:
+ PlayerInfo::setAttribute(HP, value);
+ if (player_node->isInParty() && Party::getParty(1))
+ {
+ PartyMember *m = Party::getParty(1)
+ ->getMember(player_node->getId());
+ if (m)
+ {
+ m->setHp(value);
+ m->setMaxHp(PlayerInfo::getAttribute(MAX_HP));
+ }
+ }
+ break;
+ case 0x0006:
+ PlayerInfo::setAttribute(MAX_HP, value);
+
+ if (player_node->isInParty() && Party::getParty(1))
+ {
+ PartyMember *m = Party::getParty(1)->getMember(
+ player_node->getId());
+ if (m)
+ {
+ m->setHp(PlayerInfo::getAttribute(HP));
+ m->setMaxHp(value);
+ }
+ }
+ break;
+ case 0x0007:
+ PlayerInfo::setAttribute(MP, value);
+ break;
+ case 0x0008:
+ PlayerInfo::setAttribute(MAX_MP, value);
+ break;
+ case 0x0009:
+ PlayerInfo::setAttribute(CHAR_POINTS, value);
+ break;
+ case 0x000b:
+ PlayerInfo::setAttribute(LEVEL, value);
+ if (player_node)
+ {
+ player_node->setLevel(value);
+ player_node->updateName();
+ }
+ break;
+ case 0x000c:
+ PlayerInfo::setAttribute(SKILL_POINTS, value);
+ if (skillDialog)
+ skillDialog->update();
+ break;
+ case 0x0018:
+ if (!weightNotice)
+ {
+ const int max
+ = PlayerInfo::getAttribute(MAX_WEIGHT) / 2;
+ const int total
+ = PlayerInfo::getAttribute(TOTAL_WEIGHT);
+ if (value >= max && total < max)
+ {
+ weightNoticeTime = cur_time + 10;
+ weightNotice = new OkDialog(_("Message"),
+ _("You are carrying more than "
+ "half your weight. You are "
+ "unable to regain health."), false);
+ weightNotice->addActionListener(
+ &weightListener);
+ }
+ else if (value < max && total >= max)
+ {
+ weightNoticeTime = cur_time + 10;
+ weightNotice = new OkDialog(_("Message"),
+ _("You are carrying less than "
+ "half your weight. You "
+ "can regain health."), false);
+ weightNotice->addActionListener(
+ &weightListener);
+ }
+ }
+ PlayerInfo::setAttribute(TOTAL_WEIGHT, value);
+ break;
+ case 0x0019:
+ PlayerInfo::setAttribute(MAX_WEIGHT, value);
+ break;
+
+ case 0x0029:
+ PlayerInfo::setStatBase(EA_ATK, value);
+ break;
+ case 0x002a:
+ PlayerInfo::setStatMod(EA_ATK, value);
+ break;
+
+ case 0x002b:
+ PlayerInfo::setStatBase(EA_MATK, value);
+ break;
+ case 0x002c:
+ PlayerInfo::setStatMod(EA_MATK, value);
+ break;
+
+ case 0x002d:
+ PlayerInfo::setStatBase(EA_DEF, value);
+ break;
+ case 0x002e:
+ PlayerInfo::setStatMod(EA_DEF, value);
+ break;
+
+ case 0x002f:
+ PlayerInfo::setStatBase(EA_MDEF, value);
+ break;
+ case 0x0030:
+ PlayerInfo::setStatMod(EA_MDEF, value);
+ break;
+
+ case 0x0031:
+ PlayerInfo::setStatBase(EA_HIT, value);
+ break;
+
+ case 0x0032:
+ PlayerInfo::setStatBase(EA_FLEE, value);
+ break;
+ case 0x0033:
+ PlayerInfo::setStatMod(EA_FLEE, value);
+ break;
+
+ case 0x0034:
+ PlayerInfo::setStatBase(EA_CRIT, value);
+ break;
+
+ case 0x0035:
+ player_node->setAttackSpeed(value);
+ PlayerInfo::setStatBase(ATTACK_SPEED, value);
+ PlayerInfo::setStatMod(ATTACK_SPEED, 0);
+ break;
+
+ case 0x0037:
+ PlayerInfo::setStatBase(EA_JOB, value);
+ break;
+
+ case 500:
+ player_node->setGMLevel(value);
+ break;
+
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_1 "
+ + toString(type) + "," + toString(value));
+ break;
+ }
+
+ if (PlayerInfo::getAttribute(HP) == 0 && !deathNotice)
+ {
+ deathNotice = new OkDialog(_("Message"),
+ randomDeathMessage(),
+ false);
+ deathNotice->addActionListener(&deathListener);
+ player_node->setAction(Being::DEAD);
+ }
+}
+
+void PlayerHandler::processPlayerStatUpdate2(Net::MessageIn &msg)
+{
+ int type = msg.readInt16();
+ switch (type)
+ {
+ case 0x0001:
+ PlayerInfo::setAttribute(EXP, msg.readInt32());
+ break;
+ case 0x0002:
+ PlayerInfo::setStatExperience(EA_JOB, msg.readInt32(),
+ PlayerInfo::getStatExperience(EA_JOB).second);
+ break;
+ case 0x0014:
+ {
+ int oldMoney = PlayerInfo::getAttribute(MONEY);
+ int newMoney = msg.readInt32();
+ if (newMoney > oldMoney)
+ {
+ SERVER_NOTICE(strprintf(_("You picked up %s."),
+ Units::formatCurrency(newMoney -
+ oldMoney).c_str()))
+ }
+ else if (newMoney < oldMoney)
+ {
+ SERVER_NOTICE(strprintf(_("You spent %s."),
+ Units::formatCurrency(oldMoney -
+ newMoney).c_str()))
+ }
+
+ PlayerInfo::setAttribute(MONEY, newMoney);
+ break;
+ }
+ case 0x0016:
+ PlayerInfo::setAttribute(EXP_NEEDED, msg.readInt32());
+ break;
+ case 0x0017:
+ PlayerInfo::setStatExperience(EA_JOB,
+ PlayerInfo::getStatExperience(EA_JOB).first,
+ msg.readInt32());
+ break;
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_2 " + toString(type));
+ break;
+ }
+}
+
+void PlayerHandler::processPlayerStatUpdate3(Net::MessageIn &msg)
+{
+ int type = msg.readInt32();
+ int base = msg.readInt32();
+ int bonus = msg.readInt32();
+
+ PlayerInfo::setStatBase(type, base, false);
+ PlayerInfo::setStatMod(type, bonus);
+}
+
+void PlayerHandler::processPlayerStatUpdate4(Net::MessageIn &msg)
+{
+ int type = msg.readInt16();
+ int ok = msg.readInt8();
+ int value = msg.readInt8();
+
+ if (ok != 1)
+ {
+ int oldValue = PlayerInfo::getStatBase(type);
+ int points = PlayerInfo::getAttribute(CHAR_POINTS);
+ points += oldValue - value;
+ PlayerInfo::setAttribute(CHAR_POINTS, points);
+ SERVER_NOTICE(_("Cannot raise skill!"))
+ }
+
+ PlayerInfo::setStatBase(type, value);
+}
+
+void PlayerHandler::processPlayerStatUpdate5(Net::MessageIn &msg)
+{
+ PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16());
+
+ int val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_STR, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_STR, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_AGI, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_AGI, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_VIT, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_VIT, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_INT, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_INT, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_DEX, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_DEX, msg.readInt8());
+ else
+ msg.readInt8();
+
+ val = msg.readInt8();
+ PlayerInfo::setStatBase(EA_LUK, val);
+ if (statusWindow)
+ statusWindow->setPointsNeeded(EA_LUK, msg.readInt8());
+ else
+ msg.readInt8();
+
+ PlayerInfo::setStatBase(EA_ATK, msg.readInt16(), false);
+ PlayerInfo::setStatMod(EA_ATK, msg.readInt16());
+
+ val = msg.readInt16();
+ PlayerInfo::setStatBase(EA_MATK, val, false);
+
+ val = msg.readInt16();
+ PlayerInfo::setStatMod(EA_MATK, val);
+
+ PlayerInfo::setStatBase(EA_DEF, msg.readInt16(), false);
+ PlayerInfo::setStatMod(EA_DEF, msg.readInt16());
+
+ PlayerInfo::setStatBase(EA_MDEF, msg.readInt16(), false);
+ PlayerInfo::setStatMod(EA_MDEF, msg.readInt16());
+
+ PlayerInfo::setStatBase(EA_HIT, msg.readInt16());
+
+ PlayerInfo::setStatBase(EA_FLEE, msg.readInt16(), false);
+ PlayerInfo::setStatMod(EA_FLEE, msg.readInt16());
+
+ PlayerInfo::setStatBase(EA_CRIT, msg.readInt16());
+
+ msg.readInt16(); // manner
+}
+
+void PlayerHandler::processPlayerStatUpdate6(Net::MessageIn &msg)
+{
+ int type = msg.readInt16();
+ if (statusWindow)
+ {
+ switch (type)
+ {
+ case 0x0020:
+ statusWindow->setPointsNeeded(EA_STR, msg.readInt8());
+ break;
+ case 0x0021:
+ statusWindow->setPointsNeeded(EA_AGI, msg.readInt8());
+ break;
+ case 0x0022:
+ statusWindow->setPointsNeeded(EA_VIT, msg.readInt8());
+ break;
+ case 0x0023:
+ statusWindow->setPointsNeeded(EA_INT, msg.readInt8());
+ break;
+ case 0x0024:
+ statusWindow->setPointsNeeded(EA_DEX, msg.readInt8());
+ break;
+ case 0x0025:
+ statusWindow->setPointsNeeded(EA_LUK, msg.readInt8());
+ break;
+ default:
+ logger->log("QQQQ PLAYER_STAT_UPDATE_6 "
+ + toString(type));
+ break;
+ }
+ }
+}
+
+void PlayerHandler::processPlayerArrowMessage(Net::MessageIn &msg)
+{
+ int type = msg.readInt16();
+
+ switch (type)
+ {
+ case 0:
+ SERVER_NOTICE(_("Equip arrows first."))
+ break;
+ case 3:
+ // arrows equiped
+ break;
+ default:
+ logger->log("QQQQ 0x013b: Unhandled message %i", type);
+ break;
+ }
+}
+
+bool PlayerHandler::canUseMagic() const
+{
+ return PlayerInfo::getStatEffective(EA_MATK) > 0;
+}
+
+int PlayerHandler::getJobLocation() const
+{
+ return EA_JOB;
+}
+
+} // namespace Ea