00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "net/ea/playerhandler.h"
00023
00024 #include "net/ea/protocol.h"
00025
00026 #include "net/messagein.h"
00027 #include "net/messageout.h"
00028
00029 #include "engine.h"
00030 #include "localplayer.h"
00031 #include "log.h"
00032 #include "npc.h"
00033 #include "units.h"
00034
00035 #include "gui/buy.h"
00036 #include "gui/buysell.h"
00037 #include "gui/gui.h"
00038 #include "gui/npc_text.h"
00039 #include "gui/npcintegerdialog.h"
00040 #include "gui/npclistdialog.h"
00041 #include "gui/npcstringdialog.h"
00042 #include "gui/ok_dialog.h"
00043 #include "gui/sell.h"
00044 #include "gui/skill.h"
00045 #include "gui/storagewindow.h"
00046 #include "gui/viewport.h"
00047
00048 #include "gui/widgets/chattab.h"
00049
00050 #include "utils/stringutils.h"
00051 #include "utils/gettext.h"
00052
00053
00054 OkDialog *weightNotice = NULL;
00055 OkDialog *deathNotice = NULL;
00056
00057
00058
00059 static const int MAP_TELEPORT_SCROLL_DISTANCE = 8;
00060
00061
00062 namespace {
00063
00067 struct WeightListener : public gcn::ActionListener
00068 {
00069 void action(const gcn::ActionEvent &event)
00070 {
00071 weightNotice = NULL;
00072 }
00073 } weightListener;
00074
00078 struct DeathListener : public gcn::ActionListener
00079 {
00080 void action(const gcn::ActionEvent &event)
00081 {
00082 player_node->revive();
00083 deathNotice = NULL;
00084 npcIntegerDialog->reset();
00085 npcIntegerDialog->setVisible(false);
00086 npcListDialog->reset();
00087 npcListDialog->setVisible(false);
00088 npcStringDialog->setValue("");
00089 npcStringDialog->setVisible(false);
00090 npcTextDialog->clearText();
00091 npcTextDialog->setVisible(false);
00092 buyDialog->setVisible(false);
00093 sellDialog->setVisible(false);
00094 buySellDialog->setVisible(false);
00095
00096 if (storageWindow->isVisible())
00097 storageWindow->close();
00098 viewport->closePopupMenu();
00099 }
00100 } deathListener;
00101
00102 }
00103
00104 static const char *randomDeathMessage()
00105 {
00106 static char const *const deadMsg[] =
00107 {
00108 N_("You are dead."),
00109 N_("We regret to inform you that your character was killed in "
00110 "battle."),
00111 N_("You are not that alive anymore."),
00112 N_("The cold hands of the grim reaper are grabbing for your soul."),
00113 N_("Game Over!"),
00114 N_("Insert coin to continue."),
00115 N_("No, kids. Your character did not really die. It... "
00116 "err... went to a better place."),
00117 N_("Your plan of breaking your enemies weapon by "
00118 "bashing it with your throat failed."),
00119 N_("I guess this did not run too well."),
00120
00121 N_("Do you want your possessions identified?"),
00122
00123 N_("Sadly, no trace of you was ever found..."),
00124
00125 N_("Annihilated."),
00126
00127 N_("Looks like you got your head handed to you."),
00128
00129 N_("You screwed up again, dump your body down the tubes "
00130 "and get you another one."),
00131
00132 N_("You're not dead yet. You're just resting."),
00133 N_("You are no more."),
00134 N_("You have ceased to be."),
00135 N_("You've expired and gone to meet your maker."),
00136 N_("You're a stiff."),
00137 N_("Bereft of life, you rest in peace."),
00138 N_("If you weren't so animated, you'd be pushing up the daisies."),
00139 N_("Your metabolic processes are now history."),
00140 N_("You're off the twig."),
00141 N_("You've kicked the bucket."),
00142 N_("You've shuffled off your mortal coil, run down the "
00143 "curtain and joined the bleedin' choir invisibile."),
00144 N_("You are an ex-player."),
00145 N_("You're pining for the fjords.")
00146 };
00147
00148 const int random = rand() % (sizeof(deadMsg) / sizeof(deadMsg[0]));
00149 return gettext(deadMsg[random]);
00150 }
00151
00152 Net::PlayerHandler *playerHandler;
00153
00154 namespace EAthena {
00155
00156 PlayerHandler::PlayerHandler()
00157 {
00158 static const Uint16 _messages[] = {
00159 SMSG_WALK_RESPONSE,
00160 SMSG_PLAYER_WARP,
00161 SMSG_PLAYER_STAT_UPDATE_1,
00162 SMSG_PLAYER_STAT_UPDATE_2,
00163 SMSG_PLAYER_STAT_UPDATE_3,
00164 SMSG_PLAYER_STAT_UPDATE_4,
00165 SMSG_PLAYER_STAT_UPDATE_5,
00166 SMSG_PLAYER_STAT_UPDATE_6,
00167 SMSG_PLAYER_ARROW_MESSAGE,
00168 0
00169 };
00170 handledMessages = _messages;
00171 playerHandler = this;
00172 }
00173
00174 void PlayerHandler::handleMessage(MessageIn &msg)
00175 {
00176 switch (msg.getId())
00177 {
00178 case SMSG_WALK_RESPONSE:
00179
00180
00181
00182
00183
00184 break;
00185
00186 case SMSG_PLAYER_WARP:
00187 {
00188 std::string mapPath = msg.readString(16);
00189 bool nearby;
00190 Uint16 x = msg.readInt16();
00191 Uint16 y = msg.readInt16();
00192
00193 logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y);
00194
00195
00196
00197
00198
00199 player_node->stopAttack();
00200
00201 nearby = (engine->getCurrentMapName() == mapPath);
00202
00203
00204 mapPath = mapPath.substr(0, mapPath.rfind("."));
00205 engine->changeMap(mapPath);
00206
00207 float scrollOffsetX = 0.0f;
00208 float scrollOffsetY = 0.0f;
00209
00210
00211 if (!nearby
00212 || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE)
00213 || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE))
00214 {
00215 scrollOffsetX = (x - player_node->mX) * 32;
00216 scrollOffsetY = (y - player_node->mY) * 32;
00217 }
00218
00219 player_node->setAction(Being::STAND);
00220 player_node->mFrame = 0;
00221 player_node->mX = x;
00222 player_node->mY = y;
00223
00224 logger->log("Adjust scrolling by %d:%d", (int) scrollOffsetX,
00225 (int) scrollOffsetY);
00226
00227 viewport->scrollBy(scrollOffsetX, scrollOffsetY);
00228 }
00229 break;
00230
00231 case SMSG_PLAYER_STAT_UPDATE_1:
00232 {
00233 int type = msg.readInt16();
00234 int value = msg.readInt32();
00235
00236 switch (type)
00237 {
00238
00239
00240
00241 case 0x0005: player_node->setHp(value); break;
00242 case 0x0006: player_node->setMaxHp(value); break;
00243 case 0x0007: player_node->mMp = value; break;
00244 case 0x0008: player_node->mMaxMp = value; break;
00245 case 0x0009:
00246 player_node->mStatsPointsToAttribute = value;
00247 break;
00248 case 0x000b: player_node->setLevel(value); break;
00249 case 0x000c:
00250 player_node->mSkillPoint = value;
00251 skillDialog->update();
00252 break;
00253 case 0x0018:
00254 if (value >= player_node->getMaxWeight() / 2 &&
00255 player_node->getTotalWeight() <
00256 player_node->getMaxWeight() / 2)
00257 {
00258 weightNotice = new OkDialog(_("Message"),
00259 _("You are carrying more than "
00260 "half your weight. You are "
00261 "unable to regain health."));
00262 weightNotice->addActionListener(
00263 &weightListener);
00264 }
00265 player_node->setTotalWeight(value);
00266 break;
00267 case 0x0019: player_node->setMaxWeight(value); break;
00268 case 0x0029: player_node->ATK = value; break;
00269 case 0x002b: player_node->MATK = value; break;
00270 case 0x002d: player_node->DEF = value; break;
00271 case 0x002e: player_node->DEF_BONUS = value; break;
00272 case 0x002f: player_node->MDEF = value; break;
00273 case 0x0031: player_node->HIT = value; break;
00274 case 0x0032: player_node->FLEE = value; break;
00275 case 0x0035: player_node->mAttackSpeed = value; break;
00276 case 0x0037: player_node->mJobLevel = value; break;
00277 }
00278
00279 if (player_node->getHp() == 0 && !deathNotice)
00280 {
00281 deathNotice = new OkDialog(_("Message"),
00282 randomDeathMessage());
00283 deathNotice->addActionListener(&deathListener);
00284 player_node->setAction(Being::DEAD);
00285 }
00286 }
00287 break;
00288
00289 case SMSG_PLAYER_STAT_UPDATE_2:
00290 switch (msg.readInt16()) {
00291 case 0x0001:
00292 player_node->setXp(msg.readInt32());
00293 break;
00294 case 0x0002:
00295 player_node->mJobXp = msg.readInt32();
00296 break;
00297 case 0x0014: {
00298 int curGp = player_node->getMoney();
00299 player_node->setMoney(msg.readInt32());
00300 if (player_node->getMoney() > curGp)
00301 localChatTab->chatLog(_("You picked up ") +
00302 Units::formatCurrency(player_node->getMoney()
00303 - curGp), BY_SERVER);
00304 }
00305 break;
00306 case 0x0016:
00307 player_node->mXpForNextLevel = msg.readInt32();
00308 break;
00309 case 0x0017:
00310 player_node->mJobXpForNextLevel = msg.readInt32();
00311 break;
00312 }
00313 break;
00314
00315 case SMSG_PLAYER_STAT_UPDATE_3:
00316 {
00317 int type = msg.readInt32();
00318 int base = msg.readInt32();
00319 int bonus = msg.readInt32();
00320 int total = base + bonus;
00321
00322 switch (type) {
00323 case 0x000d: player_node->mAttr[LocalPlayer::STR] = total;
00324 break;
00325 case 0x000e: player_node->mAttr[LocalPlayer::AGI] = total;
00326 break;
00327 case 0x000f: player_node->mAttr[LocalPlayer::VIT] = total;
00328 break;
00329 case 0x0010: player_node->mAttr[LocalPlayer::INT] = total;
00330 break;
00331 case 0x0011: player_node->mAttr[LocalPlayer::DEX] = total;
00332 break;
00333 case 0x0012: player_node->mAttr[LocalPlayer::LUK] = total;
00334 break;
00335 }
00336 }
00337 break;
00338
00339 case SMSG_PLAYER_STAT_UPDATE_4:
00340 {
00341 int type = msg.readInt16();
00342 int fail = msg.readInt8();
00343 int value = msg.readInt8();
00344
00345 if (fail != 1)
00346 break;
00347
00348 switch (type) {
00349 case 0x000d: player_node->mAttr[LocalPlayer::STR] = value;
00350 break;
00351 case 0x000e: player_node->mAttr[LocalPlayer::AGI] = value;
00352 break;
00353 case 0x000f: player_node->mAttr[LocalPlayer::VIT] = value;
00354 break;
00355 case 0x0010: player_node->mAttr[LocalPlayer::INT] = value;
00356 break;
00357 case 0x0011: player_node->mAttr[LocalPlayer::DEX] = value;
00358 break;
00359 case 0x0012: player_node->mAttr[LocalPlayer::LUK] = value;
00360 break;
00361 }
00362 }
00363 break;
00364
00365
00366 case SMSG_PLAYER_STAT_UPDATE_5:
00367 player_node->mStatsPointsToAttribute = msg.readInt16();
00368 player_node->mAttr[LocalPlayer::STR] = msg.readInt8();
00369 player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8();
00370 player_node->mAttr[LocalPlayer::AGI] = msg.readInt8();
00371 player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8();
00372 player_node->mAttr[LocalPlayer::VIT] = msg.readInt8();
00373 player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8();
00374 player_node->mAttr[LocalPlayer::INT] = msg.readInt8();
00375 player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8();
00376 player_node->mAttr[LocalPlayer::DEX] = msg.readInt8();
00377 player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8();
00378 player_node->mAttr[LocalPlayer::LUK] = msg.readInt8();
00379 player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8();
00380 player_node->ATK = msg.readInt16();
00381 player_node->ATK_BONUS = msg.readInt16();
00382 player_node->MATK = msg.readInt16();
00383 player_node->MATK_BONUS = msg.readInt16();
00384 player_node->DEF = msg.readInt16();
00385 player_node->DEF_BONUS = msg.readInt16();
00386 player_node->MDEF = msg.readInt16();
00387 player_node->MDEF_BONUS = msg.readInt16();
00388 player_node->HIT = msg.readInt16();
00389 player_node->FLEE = msg.readInt16();
00390 player_node->FLEE_BONUS = msg.readInt16();
00391 msg.readInt16();
00392 msg.readInt16();
00393 break;
00394
00395 case SMSG_PLAYER_STAT_UPDATE_6:
00396 switch (msg.readInt16()) {
00397 case 0x0020:
00398 player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8();
00399 break;
00400 case 0x0021:
00401 player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8();
00402 break;
00403 case 0x0022:
00404 player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8();
00405 break;
00406 case 0x0023:
00407 player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8();
00408 break;
00409 case 0x0024:
00410 player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8();
00411 break;
00412 case 0x0025:
00413 player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8();
00414 break;
00415 }
00416 break;
00417
00418 case SMSG_PLAYER_ARROW_MESSAGE:
00419 {
00420 int type = msg.readInt16();
00421
00422 switch (type) {
00423 case 0:
00424 localChatTab->chatLog(_("Equip arrows first"),
00425 BY_SERVER);
00426 break;
00427 default:
00428 logger->log("0x013b: Unhandled message %i", type);
00429 break;
00430 }
00431 }
00432 break;
00433 }
00434 }
00435
00436 void PlayerHandler::attack(Being *being)
00437 {
00438 MessageOut outMsg(CMSG_PLAYER_ATTACK);
00439 outMsg.writeInt32(being->getId());
00440 outMsg.writeInt8(0);
00441 }
00442
00443 void PlayerHandler::emote(int emoteId)
00444 {
00445 MessageOut outMsg(CMSG_PLAYER_EMOTE);
00446 outMsg.writeInt8(emoteId);
00447 }
00448
00449 void PlayerHandler::increaseStat(LocalPlayer::Attribute attr)
00450 {
00451 MessageOut outMsg(CMSG_STAT_UPDATE_REQUEST);
00452
00453 switch (attr)
00454 {
00455 case LocalPlayer::STR:
00456 outMsg.writeInt16(0x000d);
00457 break;
00458
00459 case LocalPlayer::AGI:
00460 outMsg.writeInt16(0x000e);
00461 break;
00462
00463 case LocalPlayer::VIT:
00464 outMsg.writeInt16(0x000f);
00465 break;
00466
00467 case LocalPlayer::INT:
00468 outMsg.writeInt16(0x0010);
00469 break;
00470
00471 case LocalPlayer::DEX:
00472 outMsg.writeInt16(0x0011);
00473 break;
00474
00475 case LocalPlayer::LUK:
00476 outMsg.writeInt16(0x0012);
00477 break;
00478 }
00479 outMsg.writeInt8(1);
00480 }
00481
00482 void PlayerHandler::decreaseStat(LocalPlayer::Attribute attr)
00483 {
00484
00485 }
00486
00487 void PlayerHandler::pickUp(FloorItem *floorItem)
00488 {
00489 MessageOut outMsg(CMSG_ITEM_PICKUP);
00490 outMsg.writeInt32(floorItem->getId());
00491 }
00492
00493 void PlayerHandler::setDirection(char direction)
00494 {
00495 MessageOut outMsg(CMSG_PLAYER_CHANGE_DIR);
00496 outMsg.writeInt16(0);
00497 outMsg.writeInt8(direction);
00498 }
00499
00500 void PlayerHandler::setDestination(int x, int y, int direction)
00501 {
00502 char temp[4] = "";
00503 set_coordinates(temp, x, y, direction);
00504 MessageOut outMsg(CMSG_PLAYER_CHANGE_DEST);
00505 outMsg.writeString(temp, 3);
00506 }
00507
00508 void PlayerHandler::changeAction(Being::Action action)
00509 {
00510 char type;
00511 switch (action)
00512 {
00513 case Being::SIT: type = 2; break;
00514 case Being::STAND: type = 3; break;
00515 default: return;
00516 }
00517
00518 MessageOut outMsg(CMSG_PLAYER_CHANGE_ACT);
00519 outMsg.writeInt32(0);
00520 outMsg.writeInt8(type);
00521 }
00522
00523 void PlayerHandler::respawn()
00524 {
00525 MessageOut outMsg(CMSG_PLAYER_RESPAWN);
00526 outMsg.writeInt8(0);
00527 }
00528
00529 void PlayerHandler::ingorePlayer(const std::string &player, bool ignore)
00530 {
00531
00532 }
00533
00534 void PlayerHandler::ingoreAll(bool ignore)
00535 {
00536
00537 }
00538
00539 }