diff options
author | Björn Steinbrink <B.Steinbrink@gmx.de> | 2006-01-22 13:31:13 +0000 |
---|---|---|
committer | Björn Steinbrink <B.Steinbrink@gmx.de> | 2006-01-22 13:31:13 +0000 |
commit | bd56bf8afdab16383ed8ad08412a8c807f84af85 (patch) | |
tree | 0e963ada63bcbe3c50dd77986aaa15b9ba49816a /src | |
parent | 5359640b6f271af31f6423df9d661433eff89a3e (diff) | |
download | mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.gz mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.bz2 mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.xz mana-bd56bf8afdab16383ed8ad08412a8c807f84af85.zip |
Merged NETWORK branch (includes BEING_OVERHAUL).
Diffstat (limited to 'src')
112 files changed, 5123 insertions, 3597 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8d446363..131555f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,8 +61,8 @@ tmw_SOURCES = graphic/spriteset.cpp \ gui/ministatus.h \ gui/newskill.cpp \ gui/newskill.h \ - gui/npc.cpp \ - gui/npc.h \ + gui/npclistdialog.cpp \ + gui/npclistdialog.h \ gui/npc_text.cpp \ gui/npc_text.h \ gui/ok_dialog.cpp \ @@ -116,16 +116,44 @@ tmw_SOURCES = graphic/spriteset.cpp \ gui/hbox.cpp \ gui/updatewindow.h \ gui/updatewindow.cpp \ + net/beinghandler.h \ + net/beinghandler.cpp \ + net/buysellhandler.h \ + net/buysellhandler.cpp \ + net/charserverhandler.h \ + net/charserverhandler.cpp \ + net/chathandler.h \ + net/chathandler.cpp \ + net/equipmenthandler.h \ + net/equipmenthandler.cpp \ + net/inventoryhandler.h \ + net/inventoryhandler.cpp \ + net/itemhandler.h \ + net/itemhandler.cpp \ + net/loginhandler.h \ + net/loginhandler.cpp \ + net/maploginhandler.cpp \ + net/maploginhandler.h \ + net/messagehandler.cpp \ + net/messagehandler.h \ net/messagein.cpp \ net/messagein.h \ net/messageout.cpp \ net/messageout.h \ net/network.cpp \ net/network.h \ + net/npchandler.cpp \ + net/npchandler.h \ net/packet.cpp \ net/packet.h \ + net/playerhandler.cpp \ + net/playerhandler.h \ net/protocol.cpp \ net/protocol.h \ + net/skillhandler.cpp \ + net/skillhandler.h \ + net/tradehandler.cpp \ + net/tradehandler.h \ resources/image.cpp \ resources/image.h \ resources/imagewriter.cpp \ @@ -151,6 +179,8 @@ tmw_SOURCES = graphic/spriteset.cpp \ base64.h \ being.cpp \ being.h \ + beingmanager.cpp \ + beingmanager.h \ configlistener.h \ configuration.cpp \ configuration.h \ @@ -169,15 +199,24 @@ tmw_SOURCES = graphic/spriteset.cpp \ inventory.h \ item.cpp \ item.h \ + localplayer.cpp \ + localplayer.h \ + lockedarray.h \ log.cpp \ log.h \ + logindata.h \ main.cpp \ main.h \ map.cpp\ map.h \ + monster.cpp\ + monster.h \ + npc.cpp \ + npc.h \ openglgraphics.cpp\ openglgraphics.h \ - playerinfo.h \ + player.cpp \ + player.h \ properties.h \ serverinfo.h \ sound.cpp \ diff --git a/src/being.cpp b/src/being.cpp index 08341bb9..aeb90860 100644 --- a/src/being.cpp +++ b/src/being.cpp @@ -20,7 +20,6 @@ * * $Id$ */ - #include "being.h" #include <algorithm> @@ -30,153 +29,29 @@ #include "graphics.h" #include "log.h" #include "map.h" +#include "monster.h" +#include "player.h" #include "graphic/spriteset.h" #include "gui/gui.h" -#include "net/messageout.h" -#include "net/protocol.h" - -#include "resources/resourcemanager.h" - -extern Being* autoTarget; -extern std::map<int, Spriteset*> monsterset; - -// From main.cpp -extern Spriteset *hairset; -extern Spriteset *playerset; - -// From engine.cpp extern Spriteset *emotionset; -extern Spriteset *npcset; -extern Spriteset *weaponset; - -Being *player_node = NULL; - -Beings beings; - -signed char hairtable[16][4][2] = { - // S(x,y) W(x,y) N(x,y) E(x,y) - { { 0, 0}, {-1, 2}, {-1, 2}, { 0, 2} }, // STAND - { { 0, 2}, {-2, 3}, {-1, 2}, { 1, 3} }, // WALK 1st frame - { { 0, 3}, {-2, 4}, {-1, 3}, { 1, 4} }, // WALK 2nd frame - { { 0, 1}, {-2, 2}, {-1, 2}, { 1, 2} }, // WALK 3rd frame - { { 0, 2}, {-2, 3}, {-1, 2}, { 1, 3} }, // WALK 4th frame - { { 0, 1}, { 1, 2}, {-1, 3}, {-2, 2} }, // ATTACK 1st frame - { { 0, 1}, {-1, 2}, {-1, 3}, { 0, 2} }, // ATTACK 2nd frame - { { 0, 2}, {-4, 3}, { 0, 4}, { 3, 3} }, // ATTACK 3rd frame - { { 0, 2}, {-4, 3}, { 0, 4}, { 3, 3} }, // ATTACK 4th frame - { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 1st frame - { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 2nd frame - { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 3rd frame - { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 4th frame - { { 0, 4}, {-1, 6}, {-1, 6}, { 0, 6} }, // SIT - { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, // ?? HIT - { { 0, 16}, {-1, 6}, {-1, 6}, { 0, 6} } // DEAD -}; PATH_NODE::PATH_NODE(Uint16 iX, Uint16 iY): x(iX), y(iY) { } -Being* createBeing(Uint32 id, Uint16 job, Map *map) -{ - Being *being = new Being; - - being->setId(id); - being->job = job; - being->setMap(map); - - beings.push_back(being); - - // If the being is a player, request the name - if (being->getType() == Being::PLAYER) - { - MessageOut outMsg; - outMsg.writeInt16(0x0094); - outMsg.writeInt32(being->getId());//readLong(2)); - } - // If the being is a monster then load the monsterset - else if (being->job >= 1002 && - monsterset.find(being->job - 1002) == monsterset.end()) - { - std::stringstream filename; - - filename << "graphics/sprites/monster" << (being->job - 1002) << ".png"; - logger->log("%s",filename.str().c_str()); - - Spriteset *tmp = ResourceManager::getInstance()->createSpriteset( - filename.str(), 60, 60); - if (!tmp) { - logger->error("Unable to load monster spriteset!"); - } else { - monsterset[being->job - 1002] = tmp; - } - } - - return being; -} - -void remove_node(Being *being) -{ - delete being; - beings.remove(being); -} - -Being *findNode(Uint32 id) -{ - for (Beings::iterator i = beings.begin(); i != beings.end(); i++) - { - Being *being = (*i); - if (being->getId() == id) { - return being; - } - } - return NULL; -} - -class FindNodeFunctor -{ - public: - bool operator() (Being *being) - { - Uint16 other_y = y + ((being->getType() == Being::NPC) ? 1 : 0); - return (being->x == x && (being->y == y || being->y == other_y) && - being->action != Being::MONSTER_DEAD && - (type == Being::UNKNOWN || being->getType() == type)); - } - - Uint16 x, y; - Being::Type type; -} nodeFinder; - -Being *findNode(Uint16 x, Uint16 y) -{ - return findNode(x, y, Being::UNKNOWN); -} - -Being* findNode(Uint16 x, Uint16 y, Being::Type type) -{ - nodeFinder.x = x; - nodeFinder.y = y; - nodeFinder.type = type; - - Beings::iterator i = find_if(beings.begin(), beings.end(), nodeFinder); - - return (i == beings.end()) ? NULL : *i; -} - -Being::Being(): - job(0), +Being::Being(Uint32 id, Uint16 job, Map *map): + job(job), x(0), y(0), direction(SOUTH), action(0), mFrame(0), speech_color(0), walk_time(0), emotion(0), emotion_time(0), aspd(350), - mId(0), + mId(id), mWeapon(0), mWalkSpeed(150), mMap(NULL), @@ -185,6 +60,7 @@ Being::Being(): damage_time(0), showSpeech(false), showDamage(false) { + setMap(map); } Being::~Being() @@ -325,48 +201,13 @@ Being::logic() showDamage = false; } - // Execute next walk or attack command for players - if (getType() == PLAYER) - { - switch (action) { - case WALK: - mFrame = (get_elapsed_time(walk_time) * 4) / mWalkSpeed; - if (mFrame >= 4) { - nextStep(); - } - break; - case ATTACK: - mFrame = (get_elapsed_time(walk_time) * 4) / aspd; - if (mFrame >= 4) { - nextStep(); - if (autoTarget && this == player_node) { - attack(autoTarget); - } - } - break; - } - } - - if (getType() == MONSTER && action != STAND) - { - mFrame = (get_elapsed_time(walk_time) * 4) / mWalkSpeed; - - if (mFrame >= 4 && action != MONSTER_DEAD) - { - nextStep(); - } - } - // Update pixel coordinates mPx = x * 32; mPy = y * 32; - if (getType() == PLAYER || getType() == MONSTER) - { - mPy += getYOffset(); - mPx += getXOffset(); - } - + mPy += getYOffset(); + mPx += getXOffset(); + if (emotion != 0) { emotion_time--; @@ -406,7 +247,7 @@ Being::drawSpeech(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) graphics->setFont(hitRedFont); } - int textY = (getType() == PLAYER) ? 70 : 32; + int textY = (getType() == MONSTER) ? 32 : 70; int ft = get_elapsed_time(damage_time) - 1500; float a = (ft > 0) ? 1.0 - ft / 1500.0 : 1.0; @@ -419,29 +260,11 @@ Being::drawSpeech(Graphics *graphics, Sint32 offsetX, Sint32 offsetY) // Reset alpha value graphics->setColor(gcn::Color(255, 255, 255)); } - - // Potentially draw [TARGET] above this being - if (this == autoTarget) - { - graphics->setFont(speechFont); - int dy = (getType() == PLAYER) ? 90 : 52; - - graphics->drawText("[TARGET]", px + 15, py - dy, - gcn::Graphics::CENTER); - } } Being::Type Being::getType() const { - if (job < 10) { - return PLAYER; - } else if (job >= 100 & job < 200) { - return NPC; - } else if (job >= 1000 && job < 1200) { - return MONSTER; - } else { - return UNKNOWN; - } + return UNKNOWN; } void Being::setWeaponById(Uint16 weapon) @@ -524,85 +347,12 @@ Being::getYOffset() const void Being::draw(Graphics *graphics, int offsetX, int offsetY) { - unsigned char dir = direction / 2; int px = mPx + offsetX; int py = mPy + offsetY; - int frame; - switch (getType()) + if (emotion) { - case PLAYER: - frame = action; - - if (action != SIT && action != DEAD) - { - frame += mFrame; - } - - if (action == ATTACK && getWeapon() > 0) - { - frame += 4 * (getWeapon() - 1); - } - - graphics->drawImage(playerset->spriteset[frame + 16 * dir], - px - 16, py - 32); - - if (getWeapon() != 0 && action == ATTACK) - { - Image *image = weaponset->spriteset[ - 16 * (getWeapon() - 1) + 4 * mFrame + dir]; - - graphics->drawImage(image, px - 64, py - 80); - } - - if (getHairColor() <= NR_HAIR_COLORS) - { - int hf = getHairColor() - 1 + 10 * (dir + 4 * - (getHairStyle() - 1)); - - graphics->drawImage(hairset->spriteset[hf], - px - 2 + 2 * hairtable[frame][dir][0], - py - 50 + 2 * hairtable[frame][dir][1]); - } - - if (emotion != 0) - { - graphics->drawImage(emotionset->spriteset[emotion - 1], - px + 3, py - 90); - } - - // Draw player name - if (this != player_node) { - graphics->setFont(speechFont); - graphics->drawText(mName, px + 15, py + 30, gcn::Graphics::CENTER); - } - break; - - case NPC: - graphics->drawImage(npcset->spriteset[job - 100], px - 8, py - 52); - break; - - case MONSTER: - if (mFrame >= 4) - { - mFrame = 3; - } - - frame = action; - if (action != MONSTER_DEAD) { - frame += mFrame; - } - graphics->drawImage( - monsterset[job-1002]->spriteset[dir + 4 * frame], - px - 12, py - 25); - if (emotion != 0) - { - graphics->drawImage(emotionset->spriteset[emotion - 1], - px + 3, py - 60); - } - break; - - default: - break; + graphics->drawImage(emotionset->spriteset[emotion - 1], + px + 3, py - 60); } } diff --git a/src/being.h b/src/being.h index 59ee7ca2..446cb906 100644 --- a/src/being.h +++ b/src/being.h @@ -53,6 +53,7 @@ class Being : public Sprite public: enum Type { UNKNOWN, + LOCALPLAYER, PLAYER, NPC, MONSTER @@ -96,12 +97,12 @@ class Being : public Sprite /** * Constructor. */ - Being(); + Being(Uint32 id, Uint16 job, Map *map); /** * Destructor. */ - ~Being(); + virtual ~Being(); /** * Removes all path nodes from this being. @@ -111,7 +112,7 @@ class Being : public Sprite /** * Sets a new destination for this being to walk to. */ - void setDestination(Uint16 destX, Uint16 destY); + virtual void setDestination(Uint16 destX, Uint16 destY); /** * Puts a "speech balloon" above this being for the specified amount @@ -178,7 +179,7 @@ class Being : public Sprite /** * Performs being logic. */ - void + virtual void logic(); /** @@ -190,7 +191,7 @@ class Being : public Sprite /** * Returns the type of the being. */ - Type getType() const; + virtual Type getType() const; /** * Gets the weapon picture id. @@ -276,7 +277,7 @@ class Being : public Sprite int getYOffset() const; - private: + protected: /** * Sets the new path for this being. */ @@ -300,39 +301,4 @@ class Being : public Sprite Sint32 mPx, mPy; /**< Pixel coordinates */ }; -/** - * Return a specific id Being - */ -Being* -findNode(Uint32 id); - -/** - * Return a being at specific coordinates - */ -Being* -findNode(Uint16 x, Uint16 y); - -/** - * Return a being at specific coordinates with specific type - */ -Being* -findNode(Uint16 x, Uint16 y, Being::Type type); - -/** - * Create a being and add it to the list of beings - */ -Being* -createBeing(Uint32 id, Uint16 job, Map *map); - -/** - * Remove a Being - */ -void -remove_node(Being *being); - -extern Being *player_node; - -typedef std::list<Being*> Beings; -extern Beings beings; - #endif diff --git a/src/beingmanager.cpp b/src/beingmanager.cpp new file mode 100644 index 00000000..e6c88041 --- /dev/null +++ b/src/beingmanager.cpp @@ -0,0 +1,143 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "beingmanager.h" + +#include "localplayer.h" +#include "monster.h" +#include "npc.h" +#include "player.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +class FindBeingFunctor +{ + public: + bool operator() (Being *being) + { + Uint16 other_y = y + ((being->getType() == Being::NPC) ? 1 : 0); + return (being->x == x && (being->y == y || being->y == other_y) && + being->action != Being::MONSTER_DEAD && + (type == Being::UNKNOWN || being->getType() == type)); + } + + Uint16 x, y; + Being::Type type; +} beingFinder; + +BeingManager::BeingManager(Network *network): + mNetwork(network) +{ +} + +void BeingManager::setMap(Map *map) +{ + mMap = map; + if (player_node) + player_node->setMap(map); +} + +void BeingManager::setPlayer(LocalPlayer *player) +{ + player_node = player; + mBeings.push_back(player); +} + +Being* BeingManager::createBeing(Uint32 id, Uint16 job) +{ + Being *being; + + if (job < 10) + { + being = new Player(id, job, mMap); + MessageOut outMsg(mNetwork); + outMsg.writeInt16(0x0094); + outMsg.writeInt32(id);//readLong(2)); + } + else if (job >= 100 & job < 200) + being = new NPC(id, job, mMap, mNetwork); + else if (job >= 1000 && job < 1200) + being = new Monster(id, job, mMap); + else + being = new Being(id, job, mMap); + + mBeings.push_back(being); + + return being; +} + +void BeingManager::destroyBeing(Being *being) +{ + mBeings.remove(being); + delete being; +} + +Being* BeingManager::findBeing(Uint32 id) +{ + for (Beings::iterator i = mBeings.begin(); i != mBeings.end(); i++) + { + Being *being = (*i); + if (being->getId() == id) { + return being; + } + } + return NULL; +} + +Being* BeingManager::findBeing(Uint16 x, Uint16 y, Being::Type type) +{ + beingFinder.x = x; + beingFinder.y = y; + beingFinder.type = type; + + Beings::iterator i = find_if(mBeings.begin(), mBeings.end(), beingFinder); + + return (i == mBeings.end()) ? NULL : *i; +} + +Beings* BeingManager::getAll() +{ + return &mBeings; +} + +void BeingManager::clear() +{ + if (player_node) + { + mBeings.remove(player_node); + } + + Beings::iterator i; + for (i = mBeings.begin(); i != mBeings.end(); i++) + { + delete (*i); + } + + mBeings.clear(); + + if (player_node) + { + mBeings.push_back(player_node); + } +} diff --git a/src/beingmanager.h b/src/beingmanager.h new file mode 100644 index 00000000..e50e804c --- /dev/null +++ b/src/beingmanager.h @@ -0,0 +1,88 @@ +/* + * the mana world + * copyright 2004 the mana world development team + * + * this file is part of the mana world. + * + * the mana world 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. + * + * the mana world 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 the mana world; if not, write to the free software + * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa + * + * $id: being.cpp,v 1.98 2005/12/25 01:28:03 b_lindeijer exp $ + */ + +#ifndef _TMW_BEINGMANAGER_H +#define _TMW_BEINGMANAGER_H + +#include "being.h" + +class LocalPlayer; +class Map; +class Network; + +typedef std::list<Being*> Beings; + +class BeingManager +{ + public: + BeingManager(Network *network); + + /** + * Sets the map on which beings are created + */ + void setMap(Map *map); + + /** + * Sets the current player + */ + void setPlayer(LocalPlayer *player); + + /** + * Create a being and add it to the list of beings + */ + Being* createBeing(Uint32 id, Uint16 job); + + /** + * Remove a Being + */ + void destroyBeing(Being *being); + + /** + * Return a specific id Being + */ + Being* findBeing(Uint32 id); + + /** + * Return a being at specific coordinates + */ + Being* findBeing(Uint16 x, Uint16 y, Being::Type type = Being::UNKNOWN); + + /** + * Returns the whole list of beings + */ + Beings* getAll(); + + /** + * Destroys all beings except the local player + */ + void clear(); + + protected: + Beings mBeings; + Map *mMap; + Network *mNetwork; +}; + +extern BeingManager *beingManager; + +#endif diff --git a/src/engine.cpp b/src/engine.cpp index 2fa3257e..dc767eb6 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -27,9 +27,11 @@ #include <sstream> #include "being.h" +#include "beingmanager.h" #include "floor_item.h" #include "game.h" #include "graphics.h" +#include "localplayer.h" #include "log.h" #include "main.h" #include "map.h" @@ -40,6 +42,9 @@ #include "gui/gui.h" #include "gui/minimap.h" +#include "net/messageout.h" +#include "net/protocol.h" + #include "resources/itemmanager.h" #include "resources/mapreader.h" #include "resources/resourcemanager.h" @@ -59,9 +64,10 @@ Spriteset *npcset; Spriteset *weaponset; -Engine::Engine(): +Engine::Engine(Network *network): mShowDebugPath(false), - mCurrentMap(NULL) + mCurrentMap(NULL), + mNetwork(network) { // Load the sprite sets ResourceManager *resman = ResourceManager::getInstance(); @@ -105,24 +111,19 @@ Map *Engine::getCurrentMap() return mCurrentMap; } -void Engine::changeMap(const std::string &mapPath) +void Engine::changeMap(std::string mapPath) { // Clean up floor items empty_floor_items(); - // Remove the local player, so it is not deleted - if (player_node != NULL) - { - beings.remove(player_node); - } + beingManager->clear(); - // Delete all beings (except the local player) - std::list<Being*>::iterator i; - for (i = beings.begin(); i != beings.end(); i++) - { - delete (*i); - } - beings.clear(); + // Generate full map path + mapPath = "maps/" + mapPath; + mapPath = mapPath.substr(0, mapPath.rfind(".")) + ".tmx.gz"; + + // Store in global var + map_path = mapPath; // Attempt to load the new map Map *newMap = MapReader::readMap(mapPath); @@ -131,16 +132,13 @@ void Engine::changeMap(const std::string &mapPath) logger->error("Could not find map file"); } - // Re-add the local player node and transfer him to the newly loaded map - if (player_node != NULL) - { - beings.push_back(player_node); - player_node->setMap(newMap); - } - // Start playing new music file when necessary std::string oldMusic = ""; + // Notify the minimap and beingManager about the map change + minimap->setMap(newMap); + beingManager->setMap(newMap); + if (mCurrentMap) { oldMusic = mCurrentMap->getProperty("music"); delete mCurrentMap; @@ -155,15 +153,17 @@ void Engine::changeMap(const std::string &mapPath) mCurrentMap = newMap; - // Notify the minimap about the map change - minimap->setMap(mCurrentMap); + // Send "map loaded" + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_MAP_LOADED); } void Engine::logic() { + Beings *beings = beingManager->getAll(); // Update beings - std::list<Being*>::iterator beingIterator = beings.begin(); - while (beingIterator != beings.end()) + Beings::iterator beingIterator = beings->begin(); + while (beingIterator != beings->end()) { Being *being = (*beingIterator); @@ -172,7 +172,7 @@ void Engine::logic() if (being->action == Being::MONSTER_DEAD && being->mFrame >= 20) { delete being; - beingIterator = beings.erase(beingIterator); + beingIterator = beings->erase(beingIterator); } else { beingIterator++; @@ -251,10 +251,22 @@ void Engine::draw(Graphics *graphics) } // Draw player speech - for (std::list<Being*>::iterator i = beings.begin(); i != beings.end(); i++) + Beings *beings = beingManager->getAll(); + for (Beings::iterator i = beings->begin(); i != beings->end(); i++) { (*i)->drawSpeech(graphics, -map_x, -map_y); } + // Draw target marker if needed + Being *target; + if ((target = player_node->getTarget())) + { + graphics->setFont(speechFont); + int dy = (target->getType() == Being::PLAYER) ? 90 : 52; + + graphics->drawText("[TARGET]", target->getPixelX() - map_x + 15, + target->getPixelY() - map_y - dy, gcn::Graphics::CENTER); + } + gui->draw(); } diff --git a/src/engine.h b/src/engine.h index 8ab8fb05..87cf1900 100644 --- a/src/engine.h +++ b/src/engine.h @@ -29,8 +29,8 @@ extern int camera_x, camera_y; class Graphics; -class Image; class Map; +class Network; /** * Game engine that does the main drawing. @@ -41,7 +41,7 @@ class Engine /** * Constructor. */ - Engine(); + Engine(Network *network); /** * Destructor. @@ -56,7 +56,7 @@ class Engine /** * Sets the currently active map. */ - void changeMap(const std::string &mapName); + void changeMap(std::string mapName); /** * Performs engine logic. @@ -77,7 +77,7 @@ class Engine bool mShowDebugPath; Map *mCurrentMap; - Image *attackTarget; + Network *mNetwork; }; extern Engine *engine; diff --git a/src/equipment.cpp b/src/equipment.cpp index 85b5eda3..cdbe387c 100644 --- a/src/equipment.cpp +++ b/src/equipment.cpp @@ -24,8 +24,6 @@ #include "equipment.h" #include "item.h" -Equipment *Equipment::mInstance = NULL; - Equipment::Equipment(): mArrows(NULL) { @@ -39,16 +37,6 @@ Equipment::~Equipment() { } -Equipment* -Equipment::getInstance() -{ - if (!mInstance) { - mInstance = new Equipment(); - } - - return mInstance; -} - void Equipment::removeEquipment(Item *item) { @@ -59,3 +47,9 @@ Equipment::removeEquipment(Item *item) } } } + +void Equipment::setEquipment(int index, Item *item) +{ + mEquipment[index] = item; + item->setEquipped(true); +} diff --git a/src/equipment.h b/src/equipment.h index 2bb53e42..cc805004 100644 --- a/src/equipment.h +++ b/src/equipment.h @@ -24,8 +24,6 @@ #ifndef _TMW_EQUIPMENT_H_ #define _TMW_EQUIPMENT_H_ -#include <stdlib.h> - class Item; #define EQUIPMENT_SIZE 10 @@ -34,9 +32,14 @@ class Equipment { public: /** - * Retrieve an instance of the equipment class. + * Constructor. + */ + Equipment(); + + /** + * Destructor. */ - static Equipment* getInstance(); + ~Equipment(); /** * Get equipment at the given slot. @@ -48,13 +51,13 @@ class Equipment * Set equipment at the given slot. */ void - setEquipment(int index, Item *item) { mEquipment[index] = item; } + setEquipment(int index, Item *item); /** * Remove equipment from the given slot. */ void - removeEquipment(int index) { mEquipment[index] = NULL; } + removeEquipment(int index) { mEquipment[index] = 0; } /** * Remove the given item from equipment. @@ -74,21 +77,8 @@ class Equipment setArrows(Item *arrows) { mArrows = arrows; } protected: - /** - * Constructor. - */ - Equipment(); - - /** - * Destructor. - */ - ~Equipment(); - Item *mEquipment[EQUIPMENT_SIZE]; Item *mArrows; - - private: - static Equipment *mInstance; }; #endif diff --git a/src/floor_item.cpp b/src/floor_item.cpp index 106ad210..4f2b84b3 100755 --- a/src/floor_item.cpp +++ b/src/floor_item.cpp @@ -86,7 +86,7 @@ void remove_floor_item(unsigned int id) } } -unsigned int find_floor_item_by_cor(unsigned short x, unsigned short y) +FloorItem* find_floor_item_by_cor(unsigned short x, unsigned short y) { FloorItems::iterator i; @@ -96,7 +96,7 @@ unsigned int find_floor_item_by_cor(unsigned short x, unsigned short y) if (floorItem->getX() == x && floorItem->getY() == y) { - return floorItem->getId(); + return floorItem; } } diff --git a/src/floor_item.h b/src/floor_item.h index abec9840..2471de2c 100755 --- a/src/floor_item.h +++ b/src/floor_item.h @@ -115,6 +115,6 @@ FloorItem *find_floor_item_by_id(unsigned int int_id); void remove_floor_item(unsigned int int_id); /** Find a Item id based on its coordinates */ -unsigned int find_floor_item_by_cor(unsigned short x, unsigned short y); +FloorItem* find_floor_item_by_cor(unsigned short x, unsigned short y); #endif diff --git a/src/game.cpp b/src/game.cpp index e4064040..dcd7b9d9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -31,19 +31,14 @@ #include <guichan/sdl/sdlinput.hpp> -#include "being.h" +#include "beingmanager.h" #include "configuration.h" #include "engine.h" -#include "equipment.h" #include "floor_item.h" #include "graphics.h" -#include "inventory.h" -#include "item.h" +#include "localplayer.h" #include "log.h" -#include "main.h" -#include "map.h" -#include "playerinfo.h" -#include "sound.h" +#include "npc.h" #include "gui/buy.h" #include "gui/buysell.h" @@ -54,10 +49,8 @@ #include "gui/help.h" #include "gui/inventorywindow.h" #include "gui/minimap.h" -#include "gui/npc.h" +#include "gui/npclistdialog.h" #include "gui/npc_text.h" -#include "gui/ok_dialog.h" -#include "gui/requesttrade.h" #include "gui/sell.h" #include "gui/setup.h" #include "gui/skill.h" @@ -67,32 +60,36 @@ #include "gui/trade.h" #include "gui/debugwindow.h" -#include "net/messagein.h" -#include "net/messageout.h" +#include "net/beinghandler.h" +#include "net/buysellhandler.h" +#include "net/chathandler.h" +#include "net/equipmenthandler.h" +#include "net/inventoryhandler.h" +#include "net/itemhandler.h" #include "net/network.h" -#include "net/protocol.h" +#include "net/npchandler.h" +#include "net/playerhandler.h" +#include "net/skillhandler.h" +#include "net/tradehandler.h" #include "resources/imagewriter.h" extern Graphics *graphics; extern gcn::SDLInput *guiInput; +class Map; + std::string map_path; -std::string tradePartnerName; -bool refresh_beings = false; -unsigned char keyb_state; +bool done = false; volatile int tick_time; volatile bool action_time = false; -int server_tick; -int fps = 0, frame = 0, current_npc = 0; -Uint16 startX = 0, startY = 0; -Being *autoTarget = NULL; +int fps = 0, frame = 0; Engine *engine = NULL; SDL_Joystick *joypad = NULL; /**< Joypad object */ -OkDialog *weightNotice = NULL; -OkDialog *deathNotice = NULL; +extern Window *weightNotice; +extern Window *deathNotice; ConfirmDialog *exitConfirm = NULL; ChatWindow *chatWindow; @@ -116,41 +113,17 @@ TradeWindow *tradeWindow; HelpWindow *helpWindow; DebugWindow *debugWindow; -Inventory *inventory = NULL; +BeingManager *beingManager = NULL; -const int EMOTION_TIME = 150; /**< Duration of emotion icon */ const int MAX_TIME = 10000; -class WeightNoticeListener : public gcn::ActionListener -{ - public: - void action(const std::string &eventId) - { - weightNotice = NULL; - } -} weightNoticeListener; - - -/** - * Listener used for handling death message. - */ -class DeathNoticeListener : public gcn::ActionListener { - public: - void action(const std::string &eventId) { - MessageOut outMsg; - outMsg.writeInt16(0x00b2); - outMsg.writeInt8(0); - deathNotice = NULL; - } -} deathNoticeListener; - /** * Listener used for exitting handling. */ class ExitListener : public gcn::ActionListener { void action(const std::string &eventId) { if (eventId == "yes") { - state = EXIT_STATE; + done = true; } exitConfirm = NULL; } @@ -191,16 +164,16 @@ int get_elapsed_time(int start_time) /** * Create all the various globally accessible gui windows */ -void createGuiWindows() +void createGuiWindows(Network *network) { // Create dialogs chatWindow = new ChatWindow( - config.getValue("homeDir", "") + std::string("/chatlog.txt")); + config.getValue("homeDir", "") + std::string("/chatlog.txt"), network); menuWindow = new MenuWindow(); - statusWindow = new StatusWindow(); + statusWindow = new StatusWindow(player_node); miniStatusWindow = new MiniStatusWindow(); - buyDialog = new BuyDialog(); - sellDialog = new SellDialog(); + buyDialog = new BuyDialog(network); + sellDialog = new SellDialog(network); buySellDialog = new BuySellDialog(); inventoryWindow = new InventoryWindow(); npcTextDialog = new NpcTextDialog(); @@ -209,9 +182,9 @@ void createGuiWindows() //newSkillWindow = new NewSkillDialog(); setupWindow = new Setup(); minimap = new Minimap(); - equipmentWindow = new EquipmentWindow(); + equipmentWindow = new EquipmentWindow(player_node->mEquipment); chargeDialog = new ChargeDialog(); - tradeWindow = new TradeWindow(); + tradeWindow = new TradeWindow(network); //buddyWindow = new BuddyWindow(); helpWindow = new HelpWindow(); debugWindow = new DebugWindow(); @@ -278,9 +251,9 @@ void destroyGuiWindows() delete debugWindow; } -void do_init() +void do_init(Network *network) { - engine->changeMap(map_path); + beingManager = new BeingManager(network); // Initialize timers tick_time = 0; @@ -288,18 +261,9 @@ void do_init() SDL_AddTimer(1000, nextSecond, NULL); // Seconds counter // Initialize beings - player_node = createBeing(account_ID, 0, engine->getCurrentMap()); - player_node->x = startX; - player_node->y = startY; - player_node->setHairColor(player_info->hairColor); - player_node->setHairStyle(player_info->hairStyle); - - if (player_info->weapon == 11) - { - player_info->weapon = 2; - } - - player_node->setWeapon(player_info->weapon); + beingManager->setPlayer(player_node); + player_node->setNetwork(network); + engine->changeMap(map_path); // Initialize joypad SDL_InitSubSystem(SDL_INIT_JOYSTICK); @@ -350,23 +314,42 @@ bool saveScreenshot(SDL_Surface *screenshot) return ImageWriter::writePNG(screenshot, filename.str()); } -void game() +void game(Network *network) { - // Needs to be initialised _before_ the engine is created... - inventory = new Inventory(); - - createGuiWindows(); - engine = new Engine(); - do_init(); + createGuiWindows(network); + engine = new Engine(network); + do_init(network); int gameTime = tick_time; - while (state == GAME_STATE) + BeingHandler beingHandler; + BuySellHandler buySellHandler; + ChatHandler chatHandler; + EquipmentHandler equipmentHandler; + InventoryHandler inventoryHandler; + ItemHandler itemHandler; + NPCHandler npcHandler; + PlayerHandler playerHandler; + SkillHandler skillHandler; + TradeHandler tradeHandler; + + network->registerHandler(&beingHandler); + network->registerHandler(&buySellHandler); + network->registerHandler(&chatHandler); + network->registerHandler(&equipmentHandler); + network->registerHandler(&inventoryHandler); + network->registerHandler(&itemHandler); + network->registerHandler(&npcHandler); + network->registerHandler(&playerHandler); + network->registerHandler(&skillHandler); + network->registerHandler(&tradeHandler); + + while (!done) { // Handle all necessary game logic while (get_elapsed_time(gameTime) > 0) { - do_input(); + do_input(network); engine->logic(); gameTime++; } @@ -385,22 +368,22 @@ void game() SDL_Delay(10); } - // Handle network stuff and flush it - do_parse(); - flush(); + // Handle network stuff + while(network->messageReady()) + network->dispatchMessages(); + + network->flush(); } - do_exit(); + do_exit(network); } -void do_exit() +void do_exit(Network *network) { delete engine; delete player_node; destroyGuiWindows(); - closeConnection(); - - delete inventory; + network->disconnect(); if (joypad != NULL) { @@ -408,7 +391,7 @@ void do_exit() } } -void do_input() +void do_input(Network *network) { // Get the state of the keyboard keys Uint8* keys; @@ -503,7 +486,7 @@ void do_input() // Quit by pressing Enter if the exit confirm is there if (exitConfirm) { - state = EXIT_STATE; + done = true; } // Close the Browser if opened else if (helpWindow->isVisible()) @@ -528,12 +511,12 @@ void do_input() case SDLK_z: if (!chatWindow->isFocused()) { - Uint32 id = find_floor_item_by_cor( + FloorItem *item = find_floor_item_by_cor( player_node->x, player_node->y); // If none below the player, try the tile in front of // the player - if (!id) { + if (!item) { Uint16 x = player_node->x; Uint16 y = player_node->y; @@ -549,11 +532,11 @@ void do_input() case Being::SE: x++; y++; break; default: break; } - id = find_floor_item_by_cor(x, y); + item = find_floor_item_by_cor(x, y); } - if (id) - pickUp(id); + if (item) + player_node->pickUp(item); used = true; } @@ -595,11 +578,7 @@ void do_input() break; } - switch (player_node->action) - { - case Being::STAND: action(2, 0); break; - case Being::SIT: action(3, 0); break; - } + player_node->toggleSit(); used = true; break; @@ -647,9 +626,7 @@ void do_input() if (emotion) { - MessageOut outMsg; - outMsg.writeInt16(0x00bf); - outMsg.writeInt8(emotion); + player_node->emote(emotion); action_time = false; used = true; } @@ -660,7 +637,7 @@ void do_input() // Quit event else if (event.type == SDL_QUIT) { - state = EXIT_STATE; + done = true; } // Push input to GUI when not used @@ -677,1258 +654,111 @@ void do_input() { Uint16 x = player_node->x; Uint16 y = player_node->y; - Sint16 xDirection = 0; - Sint16 yDirection = 0; Being::Direction Direction = Being::DIR_NONE; // Translate pressed keys to movement and direction if (keys[SDLK_UP] || keys[SDLK_KP8] || joy[JOY_UP]) { - yDirection = -1; - if (player_node->action != Being::WALK) - Direction = Being::NORTH; + Direction = Being::NORTH; } if (keys[SDLK_DOWN] || keys[SDLK_KP2] || joy[JOY_DOWN]) { - yDirection = 1; - if (player_node->action != Being::WALK) - Direction = Being::SOUTH; + Direction = Being::SOUTH; } if (keys[SDLK_LEFT] || keys[SDLK_KP4] || joy[JOY_LEFT]) { - xDirection = -1; - if (player_node->action != Being::WALK) + // Allow diagonal walking + // TODO: Make this nicer, once we got a bitfield for directions + if (Direction == Being::NORTH) + Direction = Being::NW; + else if (Direction == Being::SOUTH) + Direction = Being::SW; + else Direction = Being::WEST; } if (keys[SDLK_RIGHT] || keys[SDLK_KP6] || joy[JOY_RIGHT]) { - xDirection = 1; - if (player_node->action != Being::WALK) + // Allow diagonal walking + // TODO: Make this nicer, once we got a bitfield for directions + if (Direction == Being::NORTH) + Direction = Being::NE; + else if (Direction == Being::SOUTH) + Direction = Being::SE; + else Direction = Being::EAST; } if (keys[SDLK_KP1]) // Bottom Left { - xDirection = -1; - yDirection = 1; - if (player_node->action != Being::WALK) - Direction = Being::SW; + Direction = Being::SW; } if (keys[SDLK_KP3]) // Bottom Right { - xDirection = 1; - yDirection = 1; - if (player_node->action != Being::WALK) - Direction = Being::SE; + Direction = Being::SE; } if (keys[SDLK_KP7]) // Top Left { - xDirection = -1; - yDirection = -1; - if (player_node->action != Being::WALK) - Direction = Being::NW; + Direction = Being::NW; } if (keys[SDLK_KP9]) // Top Right { - xDirection = 1; - yDirection = -1; - if (player_node->action != Being::WALK) - Direction = Being::NE; + Direction = Being::NE; } - Map *tiledMap = engine->getCurrentMap(); - - // Allow keyboard control to interrupt an existing path - if ((xDirection != 0 || yDirection != 0) && - player_node->action == Being::WALK) - { - player_node->setDestination(x, y); - } - - if (player_node->action != Being::WALK) - { - // Prevent skipping corners over colliding tiles - if ((xDirection != 0) && tiledMap->tileCollides(x + xDirection, y)) - xDirection = 0; - if ((yDirection != 0) && tiledMap->tileCollides(x, y + yDirection)) - yDirection = 0; - - // Choose a straight direction when diagonal target is blocked - if ((yDirection != 0) && (xDirection != 0) && - !tiledMap->getWalk(x + xDirection, y + yDirection)) - xDirection = 0; - - // Walk to where the player can actually go - if (((xDirection != 0) || (yDirection != 0)) && - tiledMap->getWalk(x + xDirection, y + yDirection)) - { - walk(x + xDirection, y + yDirection, Direction); - player_node->setDestination(x + xDirection, y + yDirection); - } - else if (Direction != Being::DIR_NONE) - { - // Update the player direction to where he wants to walk - // Warning: Not communicated to the server yet - player_node->direction = Direction; - } - } + player_node->walk(Direction); // Attacking monsters - if (player_node->action == Being::STAND) - { - if (keys[SDLK_LCTRL] || keys[SDLK_RCTRL] || joy[JOY_BTN0]) - { - Being *monster = attack(x, y, player_node->direction); - if (monster == NULL && autoTarget != NULL && - monster != player_node) - { - attack(autoTarget); - } - else if ((keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT]) && monster != player_node) - { - autoTarget = monster; - } - } - } - - if (joy[JOY_BTN1]) - { - Uint32 id = find_floor_item_by_cor(player_node->x, player_node->y); - - if (id) - pickUp(id); - } - else if (joy[JOY_BTN2] && action_time) + if (keys[SDLK_LCTRL] || keys[SDLK_RCTRL] || joy[JOY_BTN0]) { - if (player_node->action == Being::STAND) - action(2, 0); - else if (player_node->action == Being::SIT) - action(3, 0); - action_time = false; - } - } -} - -void do_parse() -{ - Map *tiledMap = engine->getCurrentMap(); - Equipment *equipment = Equipment::getInstance(); - - int n_items; - Being *being; - - // We need at least 2 bytes to identify a packet - while (packetReady()) - { - MessageIn msg = get_next_message(); - - // Parse packet based on their id - switch (msg.getId()) - { - case SMSG_LOGIN_SUCCESS: - // Connected to game server succesfully, set spawn point - msg.readInt32(); // server tick - msg.readCoordinates(player_node->x, player_node->y, - player_node->direction); - msg.skip(2); // unknown - break; - - // Received speech from being - case SMSG_BEING_CHAT: - { - Sint16 chatMsgLength = msg.readInt16() - 8; - being = findNode(msg.readInt32()); - - if (!being || chatMsgLength <= 0) - { - break; - } - - std::string chatMsg = msg.readString(chatMsgLength); - - chatWindow->chatLog(chatMsg, BY_OTHER); - - chatMsg.erase(0, chatMsg.find(" : ", 0) + 3); - being->setSpeech(chatMsg, SPEECH_TIME); - } - break; - - case SMSG_PLAYER_CHAT: - case SMSG_GM_CHAT: - { - Sint16 chatMsgLength = msg.readInt16() - 4; - - if (chatMsgLength <= 0) - { - break; - } - - std::string chatMsg = msg.readString(chatMsgLength); - - if (msg.getId() == SMSG_PLAYER_CHAT) - { - chatWindow->chatLog(chatMsg, BY_PLAYER); - - std::string::size_type pos = chatMsg.find(" : ", 0); - if (pos != std::string::npos) - { - chatMsg.erase(0, pos + 3); - } - player_node->setSpeech(chatMsg, SPEECH_TIME); - } - else - { - chatWindow->chatLog(chatMsg, BY_GM); - } - } - break; - - case SMSG_WALK_RESPONSE: - // It is assumed by the client any request to walk actually - // succeeds on the server. The plan is to have a correction - // message when the server senses the client has the wrong - // idea. - break; - - case SMSG_BEING_VISIBLE: - case SMSG_BEING_MOVE: - // Information about a being in range - { - Uint32 id = msg.readInt32(); - Uint16 speed = msg.readInt16(); - msg.readInt16(); // unknown - msg.readInt16(); // unknown - msg.readInt16(); // option - Uint16 job = msg.readInt16(); // class - - being = findNode(id); + Being *target = NULL; + bool newTarget = keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT]; - if (being == NULL) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && id >= 110000000) - { - break; - } - - being = createBeing(id, job, tiledMap); - } - else if (msg.getId() == 0x0078) - { - being->clearPath(); - being->mFrame = 0; - being->walk_time = tick_time; - being->action = Being::STAND; - } - - // Prevent division by 0 when calculating frame - if (speed == 0) { speed = 150; } - - being->setWalkSpeed(speed); - being->job = job; - being->setHairStyle(msg.readInt16()); - being->setWeapon(msg.readInt16()); - msg.readInt16(); // head option bottom - - if (msg.getId() == SMSG_BEING_MOVE) - { - msg.readInt32(); // server tick - } - - msg.readInt16(); // shield - msg.readInt16(); // head option top - msg.readInt16(); // head option mid - being->setHairColor(msg.readInt16()); - msg.readInt16(); // unknown - msg.readInt16(); // head dir - msg.readInt16(); // guild - msg.readInt16(); // unknown - msg.readInt16(); // unknown - msg.readInt16(); // manner - msg.readInt16(); // karma - msg.readInt8(); // unknown - msg.readInt8(); // sex - - if (msg.getId() == SMSG_BEING_MOVE) - { - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); - being->action = Being::STAND; - being->x = srcX; - being->y = srcY; - being->setDestination(dstX, dstY); - } - else - { - msg.readCoordinates(being->x, being->y, - being->direction); - } - - msg.readInt8(); // unknown - msg.readInt8(); // unknown - msg.readInt8(); // unknown / sit - } - break; - - case SMSG_BEING_REMOVE: - // A being should be removed or has died - being = findNode(msg.readInt32()); - - if (being != NULL) - { - if (msg.readInt8() == 1) - { - // Death - switch (being->getType()) - { - case Being::MONSTER: - being->action = Being::MONSTER_DEAD; - being->mFrame = 0; - being->walk_time = tick_time; - break; - - default: - being->action = Being::DEAD; - break; - } - } - else - { - remove_node(being); - } - - if (being == autoTarget) - { - autoTarget = NULL; - } - } - break; - - case SMSG_PLAYER_UPDATE_1: - case SMSG_PLAYER_UPDATE_2: - case SMSG_PLAYER_MOVE: - // An update about a player, potentially including movement. - { - Uint32 id = msg.readInt32(); - Uint16 speed = msg.readInt16(); - msg.readInt16(); // option 1 - msg.readInt16(); // option 2 - msg.readInt16(); // option - Uint16 job = msg.readInt16(); - - being = findNode(id); - - if (being == NULL) - { - being = createBeing(id, job, tiledMap); - } - - being->setWalkSpeed(speed); - being->job = job; - being->setHairStyle(msg.readInt16()); - being->setWeaponById(msg.readInt16()); // item id 1 - msg.readInt16(); // item id 2 - msg.readInt16(); // head option bottom - - if (msg.getId() == SMSG_PLAYER_MOVE) - { - msg.readInt32(); // server tick - } - - msg.readInt16(); // head option top - msg.readInt16(); // head option mid - being->setHairColor(msg.readInt16()); - msg.readInt16(); // unknown - msg.readInt16(); // head dir - msg.readInt32(); // guild - msg.readInt32(); // emblem - msg.readInt16(); // manner - msg.readInt8(); // karma - msg.readInt8(); // sex - - if (msg.getId() == SMSG_PLAYER_MOVE) - { - Uint16 srcX, srcY, dstX, dstY; - msg.readCoordinatePair(srcX, srcY, dstX, dstY); - being->x = srcX; - being->y = srcY; - being->setDestination(dstX, dstY); - } - else - { - msg.readCoordinates(being->x, being->y, - being->direction); - } - - msg.readInt8(); // unknown - msg.readInt8(); // unknown - - if (msg.getId() == SMSG_PLAYER_UPDATE_1) - { - if (msg.readInt8() == 2) - { - being->action = Being::SIT; - } - } - else if (msg.getId() == SMSG_PLAYER_MOVE) - { - msg.readInt8(); // unknown - } - - msg.readInt8(); // Lv - msg.readInt8(); // unknown - - being->walk_time = tick_time; - being->mFrame = 0; - } - break; - - case SMSG_NPC_MESSAGE: - msg.readInt16(); // length - current_npc = msg.readInt32(); - npcTextDialog->addText(msg.readString(msg.getLength() - 8)); - npcListDialog->setVisible(false); - npcTextDialog->setVisible(true); - break; - - case SMSG_NPC_NEXT: - case SMSG_NPC_CLOSE: - // Next/Close button in NPC dialog, currently unused - break; - - case SMSG_TRADE_REQUEST: - // If a trade window or request window is already open, send a - // trade cancel to any other trade request. - // - // Note that it would be nice if the server would prevent this - // situation, and that the requesting player would get a - // special message about the player being occupied. - - if (tradeWindow->isVisible() || requestTradeDialogOpen) - { - MessageOut outMsg; - outMsg.writeInt16(CMSG_TRADE_RESPONSE); - outMsg.writeInt8(4); - break; - } - - requestTradeDialogOpen = true; - tradePartnerName = msg.readString(24); - new RequestTradeDialog(tradePartnerName); - break; - - case SMSG_TRADE_RESPONSE: - switch (msg.readInt8()) - { - case 0: // Too far away - chatWindow->chatLog("Trading isn't possible. " - "Trade partner is too far away.", - BY_SERVER); - break; - case 1: // Character doesn't exist - chatWindow->chatLog("Trading isn't possible. " - "Character doesn't exist.", - BY_SERVER); - break; - case 2: // Invite request check failed... - chatWindow->chatLog("Trade canceled due to an " - "unknown reason.", BY_SERVER); - break; - case 3: // Trade accepted - tradeWindow->reset(); - tradeWindow->setCaption( - "Trade: You and " + tradePartnerName); - tradeWindow->setVisible(true); - requestTradeDialogOpen = false; - break; - case 4: // Trade canceled - chatWindow->chatLog("Trade canceled.", BY_SERVER); - tradeWindow->setVisible(false); - break; - default: // Shouldn't happen as well, but to be sure - chatWindow->chatLog("Unhandled trade cancel packet", - BY_SERVER); - break; - } - break; - - case SMSG_TRADE_ITEM_ADD: - { - Sint32 amount = msg.readInt32(); - Sint16 type = msg.readInt16(); - msg.readInt8(); // identified flag - msg.readInt8(); // attribute - msg.readInt8(); // refine - msg.skip(8); // card (4 shorts) - - // TODO: handle also identified, etc - if (type == 0) { - tradeWindow->addMoney(amount); - } else { - tradeWindow->addItem(type, false, amount, false); - } - } - break; - - case SMSG_TRADE_ITEM_ADD_RESPONSE: - // Trade: New Item add response (was 0x00ea, now 01b1) - { - Item *item = inventory->getItem(msg.readInt16()); - Sint16 quantity = msg.readInt16(); - - switch (msg.readInt8()) - { - case 0: - // Successfully added item - if (item->isEquipment() && item->isEquipped()) - { - inventory->unequipItem(item); - } - tradeWindow->addItem(item->getId(), true, quantity, - item->isEquipment()); - item->increaseQuantity(-quantity); - break; - case 1: - // Add item failed - player overweighted - chatWindow->chatLog("Failed adding item. Trade " - "partner is over weighted.", - BY_SERVER); - break; - default: - chatWindow->chatLog("Failed adding item for " - "unknown reason.", BY_SERVER); - break; - } - } - break; - - case SMSG_TRADE_OK: - // 0 means ok from myself, 1 means ok from other; - tradeWindow->receivedOk(msg.readInt8() == 0); - break; - - case SMSG_TRADE_CANCEL: - chatWindow->chatLog("Trade canceled.", BY_SERVER); - tradeWindow->setVisible(false); - tradeWindow->reset(); - break; - - case SMSG_TRADE_COMPLETE: - chatWindow->chatLog("Trade completed.", BY_SERVER); - tradeWindow->setVisible(false); - tradeWindow->reset(); - break; - - case SMSG_PLAYER_INVENTORY: - { - // Only called on map load / warp. First reset all items - // to not load them twice on map change. - inventory->resetItems(); - msg.readInt16(); // length - Sint32 number = (msg.getLength() - 4) / 18; - - for (int loop = 0; loop < number; loop++) - { - Sint16 index = msg.readInt16(); - Sint16 itemId = msg.readInt16(); - msg.readInt8(); // type - msg.readInt8(); // identify flag - Sint16 amount = msg.readInt16(); - msg.skip(2); // unknown - msg.skip(8); // card (4 shorts) - - inventory->addItem(index, itemId, amount, false); - - // Trick because arrows are not considered equipment - if (itemId == 1199 || itemId == 529) - { - inventory->getItem(index)->setEquipment(true); - } - } - } - break; - - case SMSG_PLAYER_EQUIPMENT: - { - msg.readInt16(); // length - Sint32 number = (msg.getLength() - 4) / 20; - - for (int loop = 0; loop < number; loop++) - { - Sint16 index = msg.readInt16(); - Sint16 itemId = msg.readInt16(); - msg.readInt8(); // type - msg.readInt8(); // identify flag - msg.readInt16(); // equip type - Sint16 equipPoint = msg.readInt16(); - msg.readInt8(); // attribute - msg.readInt8(); // refine - msg.skip(8); // card - - inventory->addItem(index, itemId, 1, true); - - if (equipPoint) - { - int mask = 1; - int position = 0; - while (!(equipPoint & mask)) - { - mask <<= 1; - position++; - } - Item *item = inventory->getItem(index); - item->setEquipped(true); - equipment->setEquipment(position - 1, item); - } - } - } - break; - - case SMSG_ITEM_USE_RESPONSE: - { - Sint16 index = msg.readInt16(); - Sint16 amount = msg.readInt16(); - - if (msg.readInt8() == 0) { - chatWindow->chatLog("Failed to use item", BY_SERVER); - } else { - inventory->getItem(index)->setQuantity(amount); - } - } - break; - - case SMSG_PLAYER_WARP: - { - // Set new map path - map_path = "maps/" + msg.readString(16); - map_path = map_path.substr(0, map_path.rfind(".")) + - ".tmx.gz"; - - Uint16 x = msg.readInt16(); - Uint16 y = msg.readInt16(); - - logger->log("Warping to %s (%d, %d)", - map_path.c_str(), x, y); - - // Switch the actual map, deleting the previous one - engine->changeMap(map_path); - tiledMap = engine->getCurrentMap(); - - autoTarget = NULL; - current_npc = 0; - - player_node->action = Being::STAND; - player_node->mFrame = 0; - player_node->x = x; - player_node->y = y; - - // Send "map loaded" - MessageOut outMsg; - outMsg.writeInt16(CMSG_MAP_LOADED); - } - break; - - case SMSG_SKILL_FAILED: - // Action failed (ex. sit because you have not reached the - // right level) - CHATSKILL action; - action.skill = msg.readInt16(); - action.bskill = msg.readInt16(); - action.unused = msg.readInt16(); // unknown - action.success = msg.readInt8(); - action.reason = msg.readInt8(); - if (action.success != SKILL_FAILED && - action.bskill == BSKILL_EMOTE) - { - printf("Action: %d/%d", action.bskill, action.success); - } - chatWindow->chatLog(action); - break; - - case SMSG_PLAYER_STAT_UPDATE_1: - { - Sint16 type = msg.readInt16(); - Sint32 value = msg.readInt32(); - - switch (type) - { - //case 0x0000: - // player_node->setWalkSpeed(msg.readInt32()); - // break; - case 0x0005: player_info->hp = value; break; - case 0x0006: player_info->maxHp = value; break; - case 0x0007: player_info->mp = value; break; - case 0x0008: player_info->maxMp = value; break; - case 0x000b: player_info->lvl = value; break; - case 0x000c: - player_info->skillPoint = value; - skillDialog->setPoints(player_info->skillPoint); - break; - case 0x0018: - if (value >= player_info->maxWeight / 2 && - player_info->totalWeight < - player_info->maxWeight / 2) - { - weightNotice = new OkDialog("Message", - "You are carrying more then half your " - "weight. You are unable to regain " - "health.", - &weightNoticeListener); - } - player_info->totalWeight = value; - break; - case 0x0019: player_info->maxWeight = value; break; - case 0x0037: player_info->jobLvl = value; break; - case 0x0009: - player_info->statsPointsToAttribute = value; - break; - case 0x0029: player_info->ATK = value; break; - case 0x002b: player_info->MATK = value; break; - case 0x002d: player_info->DEF = value; break; - case 0x002f: player_info->MDEF = value; break; - case 0x0031: player_info->HIT = value; break; - case 0x0032: player_info->FLEE = value; break; - case 0x0035: player_node->aspd = value; break; - } + // A set target has highest priority + if (newTarget || !player_node->getTarget()) + { + Uint16 targetX = x, targetY = y; - if (player_info->hp == 0 && deathNotice == NULL) - { - deathNotice = new OkDialog("Message", - "You're now dead, press ok to restart", - &deathNoticeListener); - player_node->action = Being::DEAD; - } - } - break; - - // Stop walking - // case 0x0088: // Disabled because giving some problems - //if (being = findNode(readInt32(2))) { - // if (being->getId() != player_node->getId()) { - // being->action = STAND; - // being->mFrame = 0; - // set_coordinates(being->coordinates, - // readWord(6), readWord(8), - // get_direction(being->coordinates)); - // } - //} - //break; - - case SMSG_BEING_ACTION: + switch (player_node->direction) { - Being *srcBeing = findNode(msg.readInt32()); - Being *dstBeing = findNode(msg.readInt32()); - msg.readInt32(); // server tick - msg.readInt32(); // src speed - msg.readInt32(); // dst speed - Sint16 param1 = msg.readInt16(); - msg.readInt16(); // param 2 - Sint8 type = msg.readInt8(); - msg.readInt16(); // param 3 - - switch (type) - { - case 0: // Damage - if (dstBeing == NULL) break; - - dstBeing->setDamage(param1, SPEECH_TIME); - - if (srcBeing != NULL && - srcBeing != player_node) - { - // buggy - srcBeing->action = Being::ATTACK; - srcBeing->mFrame = 0; - srcBeing->walk_time = tick_time; - } - break; - - case 2: // Sit - if (srcBeing == NULL) break; - srcBeing->mFrame = 0; - srcBeing->action = Being::SIT; - break; - - case 3: // Stand up - if (srcBeing == NULL) break; - srcBeing->mFrame = 0; - srcBeing->action = Being::STAND; - break; - } - } - break; - - case SMSG_PLAYER_STAT_UPDATE_2: - switch (msg.readInt16()) { - case 0x0001: - player_info->xp = msg.readInt32(); - break; - case 0x0002: - player_info->jobXp = msg.readInt32(); - break; - case 0x0014: - player_info->gp = msg.readInt32(); + case Being::SOUTH: + targetY++; break; - case 0x0016: - player_info->xpForNextLevel = msg.readInt32(); - break; - case 0x0017: - player_info->jobXpForNextLevel = msg.readInt32(); - break; - } - break; - - case SMSG_BEING_LEVELUP: - if ((Uint32)msg.readInt32() == player_node->getId()) { - logger->log("Level up"); - sound.playSfx("sfx/levelup.ogg"); - } else { - logger->log("Someone else went level up"); - } - msg.readInt32(); // type - break; - - case SMSG_BEING_EMOTION: - if (!(being = findNode(msg.readInt32()))) - { - break; - } - - being->emotion = msg.readInt8(); - being->emotion_time = EMOTION_TIME; - break; - - case SMSG_PLAYER_STAT_UPDATE_3: - { - Sint32 type = msg.readInt32(); - Sint32 base = msg.readInt32(); - Sint32 bonus = msg.readInt32(); - Sint32 total = base + bonus; - - switch (type) { - case 0x000d: player_info->STR = total; break; - case 0x000e: player_info->AGI = total; break; - case 0x000f: player_info->VIT = total; break; - case 0x0010: player_info->INT = total; break; - case 0x0011: player_info->DEX = total; break; - case 0x0012: player_info->LUK = total; break; - } - } - break; - - case SMSG_NPC_BUY_SELL_CHOICE: - buyDialog->setVisible(false); - buyDialog->reset(); - sellDialog->setVisible(false); - sellDialog->reset(); - buySellDialog->setVisible(true); - current_npc = msg.readInt32(); - break; - - case SMSG_NPC_BUY: - msg.readInt16(); // length - n_items = (msg.getLength() - 4) / 11; - buyDialog->reset(); - buyDialog->setMoney(player_info->gp); - buyDialog->setVisible(true); - - for (int k = 0; k < n_items; k++) - { - Sint32 value = msg.readInt32(); - msg.readInt32(); // DCvalue - msg.readInt8(); // type - Sint16 itemId = msg.readInt16(); - buyDialog->addItem(itemId, value); - } - break; - - case SMSG_NPC_SELL: - msg.readInt16(); // length - n_items = (msg.getLength() - 4) / 10; - if (n_items > 0) { - sellDialog->reset(); - sellDialog->setVisible(true); - - for (int k = 0; k < n_items; k++) - { - Sint16 index = msg.readInt16(); - Sint32 value = msg.readInt32(); - msg.readInt32(); // OCvalue - - Item *item = inventory->getItem(index); - if (item && !(item->isEquipped())) { - sellDialog->addItem(item, value); - } - } - } - else { - chatWindow->chatLog("Nothing to sell", BY_SERVER); - current_npc = 0; - } - break; - - case SMSG_NPC_BUY_RESPONSE: - if (msg.readInt8() == 0) { - chatWindow->chatLog("Thanks for buying", BY_SERVER); - } else { - chatWindow->chatLog("Unable to buy", BY_SERVER); - } - break; - - case SMSG_NPC_SELL_RESPONSE: - if (msg.readInt8() == 0) { - chatWindow->chatLog("Thanks for selling", BY_SERVER); - } else { - chatWindow->chatLog("Unable to sell", BY_SERVER); - } - break; - - case SMSG_PLAYER_INVENTORY_ADD: - { - Sint16 index = msg.readInt16(); - Sint16 amount = msg.readInt16(); - Sint16 itemId = msg.readInt16(); - msg.readInt8(); // identify flag - msg.readInt8(); // attribute - msg.readInt8(); // refine - msg.skip(8); // card - Sint16 equipType = msg.readInt16(); - msg.readInt8(); // type - Sint8 fail = msg.readInt8(); - - if (fail > 0) { - chatWindow->chatLog("Unable to pick up item", - BY_SERVER); - } else { - inventory->addItem(index, itemId, amount, - equipType != 0); - } - } - break; - - case SMSG_PLAYER_INVENTORY_REMOVE: - { - Sint16 index = msg.readInt16(); - Sint16 amount = msg.readInt16(); - inventory->getItem(index)->increaseQuantity(-amount); - } - break; - - case SMSG_PLAYER_INVENTORY_USE: - { - Sint16 index = msg.readInt16(); - msg.readInt16(); // item id - msg.readInt32(); // id - Sint16 amountLeft = msg.readInt16(); - msg.readInt8(); // type - - inventory->getItem(index)->setQuantity(amountLeft); - } - break; - - case SMSG_PLAYER_SKILLS: - msg.readInt16(); // length - n_items = (msg.getLength() - 4) / 37; - skillDialog->cleanList(); - - for (int k = 0; k < n_items; k++) - { - Sint16 skillId = msg.readInt16(); - msg.readInt16(); // target type - msg.readInt16(); // unknown - Sint16 level = msg.readInt16(); - Sint16 sp = msg.readInt16(); - msg.readInt16(); // range - std::string skillName = msg.readString(24); - Sint8 up = msg.readInt8(); - - if (level != 0 || up != 0) - { - if (skillDialog->hasSkill(skillId)) { - skillDialog->setSkill(skillId, level, sp); - } - else { - skillDialog->addSkill(skillId, level, sp); - } - } - } - break; - - case 0x010c: - // Display MVP player - msg.readInt32(); // id - chatWindow->chatLog("MVP player", BY_SERVER); - break; - - case SMSG_ITEM_VISIBLE: - case SMSG_ITEM_DROPPED: - { - Uint32 id = msg.readInt32(); - Sint16 itemId = msg.readInt16(); - msg.readInt8(); // identify flag - Uint16 x = msg.readInt16(); - Uint16 y = msg.readInt16(); - msg.skip(4); // amount,subX,subY / subX,subY,amount - - add_floor_item(new FloorItem(id, itemId, x, y, tiledMap)); - } - break; - - case SMSG_ITEM_REMOVE: - remove_floor_item(msg.readInt32()); - break; - - case SMSG_NPC_CHOICE: - msg.readInt16(); // length - current_npc = msg.readInt32(); - npcListDialog->parseItems(msg.readString(msg.getLength() - 8)); - npcListDialog->setVisible(true); - break; - - case SMSG_BEING_CHANGE_LOOKS: - if (!(being = findNode(msg.readInt32()))) - { - break; - } - switch (msg.readInt8()) { - case 1: - being->setHairStyle(msg.readInt8()); - break; - case 2: - being->setWeapon(msg.readInt8()); - break; - case 6: - being->setHairColor(msg.readInt8()); - break; - default: - msg.readInt8(); // unsupported + case Being::WEST: + targetX--; break; - } - break; - - case SMSG_PLAYER_EQUIP: - { - Sint16 index = msg.readInt16(); - Sint16 equipPoint = msg.readInt16(); - Sint8 type = msg.readInt8(); - - logger->log("Equipping: %i %i %i", - index, equipPoint, type); - if (type == 0) { - chatWindow->chatLog("Unable to equip.", BY_SERVER); - } - else if (equipPoint) - { - // Unequip any existing equipped item in this position - int mask = 1; - int position = 0; - while (!(equipPoint & mask)) { - mask <<= 1; - position++; - } - logger->log("Position %i", position - 1); - Item *item = equipment->getEquipment(position - 1); - if (item) { - item->setEquipped(false); - } - - item = inventory->getItem(index); - item->setEquipped(true); - equipment->setEquipment(position - 1, item); - player_node->setWeaponById(item->getId()); - } - } - break; - - case 0x01d7: - // Equipment related - { - being = findNode(msg.readInt32()); - msg.readInt8(); // equip point - Sint16 itemId1 = msg.readInt16(); - msg.readInt16(); // item id 2 - - if (being != NULL) - { - being->setWeaponById(itemId1); - } - } - break; - - case SMSG_PLAYER_UNEQUIP: - { - Sint16 index = msg.readInt16(); - Sint16 equipPoint = msg.readInt16(); - Sint8 type = msg.readInt8(); - - if (type == 0) { - chatWindow->chatLog("Unable to unequip.", BY_SERVER); + case Being::NORTH: + targetY--; break; - } - if (equipPoint == 0) { - // No point given, no point in searching + case Being::EAST: + targetX++; break; - } - - int mask = 1; - int position = 0; - while (!(equipPoint & mask)) { - mask <<= 1; - position++; - } - - Item *item = inventory->getItem(index); - - if (item != NULL) - { - item->setEquipped(false); - - switch (item->getId()) { - case 529: - case 1199: - equipment->setArrows(NULL); - break; - case 521: - case 522: - case 530: - case 536: - case 1200: - case 1201: - player_node->setWeapon(0); - // TODO: Why this break? Shouldn't a weapon be - // unequipped in inventory too? - break; - default: - equipment->removeEquipment(position - 1); - break; - } - logger->log("Unequipping: %i %i(%i) %i", - index, equipPoint, type, position - 1); - } - } - break; - - case SMSG_PLAYER_ARROW_EQUIP: - { - Sint16 id = msg.readInt16(); - - if (id > 1) { - Item *item = inventory->getItem(id); - if (item) { - item->setEquipped(true); - equipment->setArrows(item); - logger->log("Arrows equipped: %i", id); - } - } } - break; - case SMSG_PLAYER_ARROW_MESSAGE: - { - Sint16 type = msg.readInt16(); - - switch (type) { - case 0: - chatWindow->chatLog("Equip arrows first", - BY_SERVER); - break; - default: - logger->log("0x013b: Unhandled message %i", type); - break; - } - } - break; - - case SMSG_PLAYER_STAT_UPDATE_4: - { - Sint16 type = msg.readInt16(); - Sint8 fail = msg.readInt8(); - Sint8 value = msg.readInt8(); - - if (fail == 1) - { - switch (type) { - case 0x000d: player_info->STR = value; break; - case 0x000e: player_info->AGI = value; break; - case 0x000f: player_info->VIT = value; break; - case 0x0010: player_info->INT = value; break; - case 0x0011: player_info->DEX = value; break; - case 0x0012: player_info->LUK = value; break; - } - } - } - break; - - // Updates stats and status points - case SMSG_PLAYER_STAT_UPDATE_5: - player_info->statsPointsToAttribute = msg.readInt16(); - player_info->STR = msg.readInt8(); - player_info->STRUp = msg.readInt8(); - player_info->AGI = msg.readInt8(); - player_info->AGIUp = msg.readInt8(); - player_info->VIT = msg.readInt8(); - player_info->VITUp = msg.readInt8(); - player_info->INT = msg.readInt8(); - player_info->INTUp = msg.readInt8(); - player_info->DEX = msg.readInt8(); - player_info->DEXUp = msg.readInt8(); - player_info->LUK = msg.readInt8(); - player_info->LUKUp = msg.readInt8(); - player_info->ATK = msg.readInt16(); // ATK - player_info->ATKBonus = msg.readInt16(); // ATK bonus - player_info->MATK = msg.readInt16(); // MATK max - player_info->MATKBonus = msg.readInt16(); // MATK min - player_info->DEF = msg.readInt16(); // DEF - player_info->DEFBonus = msg.readInt16(); // DEF bonus - player_info->MDEF = msg.readInt16(); // MDEF - player_info->MDEFBonus = msg.readInt16(); // MDEF bonus - player_info->HIT = msg.readInt16(); // HIT - player_info->FLEE = msg.readInt16(); // FLEE - player_info->FLEEBonus = msg.readInt16(); // FLEE bonus - msg.readInt16(); // critical - msg.readInt16(); // unknown - break; - - case SMSG_PLAYER_STAT_UPDATE_6: - switch (msg.readInt16()) { - case 0x0020: player_info->STRUp = msg.readInt8(); break; - case 0x0021: player_info->AGIUp = msg.readInt8(); break; - case 0x0022: player_info->VITUp = msg.readInt8(); break; - case 0x0023: player_info->INTUp = msg.readInt8(); break; - case 0x0024: player_info->DEXUp = msg.readInt8(); break; - case 0x0025: player_info->LUKUp = msg.readInt8(); break; - } - break; + // Attack priorioty is: Monster, Player, auto target + target = beingManager->findBeing( + targetX, targetY, Being::MONSTER); + if (!target) + target = beingManager->findBeing( + targetX, targetY, Being::PLAYER); + } - case SMSG_BEING_NAME_RESPONSE: - if ((being = findNode(msg.readInt32()))) - { - being->setName(msg.readString(24)); - } - break; - - case SMSG_WHO_ANSWER: - { - std::stringstream userMsg; - userMsg << "Online users: "; - userMsg << msg.readInt32(); - chatWindow->chatLog(userMsg.str(), BY_SERVER); - } - break; + player_node->attack(target, newTarget); + } - case 0x0119: - // Change in players look - break; + if (joy[JOY_BTN1]) + { + FloorItem *item = find_floor_item_by_cor( + player_node->x, player_node->y); - default: - // Manage non implemented packets - logger->log("Unhandled packet: %x", msg.getId()); - break; + if (item) + player_node->pickUp(item); + } + else if (joy[JOY_BTN2] && action_time) + { + player_node->toggleSit(); + action_time = false; } - - skip(msg.getLength()); } } @@ -29,17 +29,13 @@ #define SPEECH_TIME 80 #define SPEECH_MAX_TIME 100 -#define LOCK 254 -#define IDLE 255 - -class Being; +class Network; +class NPC; extern std::string map_path; -extern std::string tradePartnerName; -extern int fps, current_npc; +extern int fps; extern volatile int tick_time; extern int server_tick; -extern unsigned short startX, startY; enum { JOY_UP, @@ -63,22 +59,22 @@ enum { /** * Main game loop */ -void game(); +void game(Network*); /** * Check user input */ -void do_input(); +void do_input(Network*); /** * Parse data received from map server into input buffer */ -void do_parse(); +void do_parse(Network*); /** * Clean the engine */ -void do_exit(); +void do_exit(Network*); /** * Returns elapsed time. (Warning: very unsafe function, it supposes the delay diff --git a/src/gui/buddywindow.cpp b/src/gui/buddywindow.cpp index c37f2a6d..cfa21b63 100644 --- a/src/gui/buddywindow.cpp +++ b/src/gui/buddywindow.cpp @@ -29,6 +29,8 @@ #include "chat.h" #include "scrollarea.h" +#include "../resources/buddylist.h" + extern ChatWindow *chatWindow; BuddyWindow::BuddyWindow(): @@ -36,8 +38,10 @@ BuddyWindow::BuddyWindow(): { setContentSize(124, 202); + mBuddyList = new BuddyList(); + mListbox = new gcn::ListBox(); - mListbox->setListModel(this); + mListbox->setListModel(mBuddyList); ScrollArea *scrollArea = new ScrollArea(mListbox); scrollArea->setDimension(gcn::Rectangle( @@ -68,7 +72,7 @@ void BuddyWindow::action(const std::string& eventId) int selected = mListbox->getSelected(); if ( selected > -1 ) { - std::string who = getElementAt(selected); + std::string who = mBuddyList->getElementAt(selected); chatWindow->setInputText(who +": "); } } @@ -76,8 +80,8 @@ void BuddyWindow::action(const std::string& eventId) int selected = mListbox->getSelected(); if ( selected > -1 ) { - std::string who = getElementAt(selected); - removeBuddy(who); + std::string who = mBuddyList->getElementAt(selected); + mBuddyList->removeBuddy(who); } } else if (eventId == "Cancel") { diff --git a/src/gui/buddywindow.h b/src/gui/buddywindow.h index 82ef9935..6eeb7999 100644 --- a/src/gui/buddywindow.h +++ b/src/gui/buddywindow.h @@ -30,15 +30,14 @@ #include "../guichanfwd.h" -#include "../resources/buddylist.h" +class BuddyList; /** * Window showing buddy list. * * \ingroup Interface */ -class BuddyWindow : public Window, public BuddyList, - public gcn::ActionListener +class BuddyWindow : public Window, public gcn::ActionListener { public: /** @@ -52,6 +51,7 @@ class BuddyWindow : public Window, public BuddyList, void action(const std::string &actionId); private: + BuddyList *mBuddyList; gcn::ListBox *mListbox; }; diff --git a/src/gui/buy.cpp b/src/gui/buy.cpp index 1c0b2ea7..65f2e525 100644 --- a/src/gui/buy.cpp +++ b/src/gui/buy.cpp @@ -33,7 +33,7 @@ #include "shop.h" #include "slider.h" -#include "../game.h" +#include "../npc.h" #include "../resources/iteminfo.h" #include "../resources/itemmanager.h" @@ -42,8 +42,8 @@ #include "../net/protocol.h" -BuyDialog::BuyDialog(): - Window("Buy"), +BuyDialog::BuyDialog(Network *network): + Window("Buy"), mNetwork(network), m_money(0), m_amountItems(0), m_maxItems(0) { itemList = new ListBox(this); @@ -221,7 +221,7 @@ void BuyDialog::action(const std::string& eventId) // there a better way to ensure this fails in an _obivous_ way in C++? else if (eventId == "buy" && (m_amountItems > 0 && m_amountItems <= m_maxItems)) { - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_BUY_REQUEST); outMsg.writeInt16(8); outMsg.writeInt16(m_amountItems); diff --git a/src/gui/buy.h b/src/gui/buy.h index 7e9ef069..6a1c9829 100644 --- a/src/gui/buy.h +++ b/src/gui/buy.h @@ -34,6 +34,8 @@ #include "../guichanfwd.h" +class Network; + /** * The buy dialog. * @@ -48,7 +50,7 @@ class BuyDialog : public Window, public gcn::ActionListener, * * @see Window::Window */ - BuyDialog(); + BuyDialog(Network *network); /** * Resets the dialog, clearing shop inventory. @@ -86,6 +88,7 @@ class BuyDialog : public Window, public gcn::ActionListener, std::string getElementAt(int i); private: + Network *mNetwork; gcn::Button *buyButton; gcn::Button *quitButton; gcn::Button *increaseButton; diff --git a/src/gui/buysell.cpp b/src/gui/buysell.cpp index 178c2719..6547a849 100644 --- a/src/gui/buysell.cpp +++ b/src/gui/buysell.cpp @@ -25,17 +25,14 @@ #include "button.h" -#include "../game.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../npc.h" BuySellDialog::BuySellDialog(): Window("Shop") { - buyButton = new Button("Buy"); - sellButton = new Button("Sell"); - cancelButton = new Button("Cancel"); + gcn::Button *buyButton = new Button("Buy"); + gcn::Button *sellButton = new Button("Sell"); + gcn::Button *cancelButton = new Button("Cancel"); buyButton->setPosition(10, 10); sellButton->setPosition( @@ -63,22 +60,13 @@ BuySellDialog::BuySellDialog(): void BuySellDialog::action(const std::string& eventId) { - int actionId = -1; - if (eventId == "buy") { - actionId = 0; + current_npc->buy(); } else if (eventId == "sell") { - actionId = 1; + current_npc->sell(); } else if (eventId == "cancel") { current_npc = 0; } - if (actionId > -1) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); - outMsg.writeInt32(current_npc); - outMsg.writeInt8(actionId); - } - setVisible(false); } diff --git a/src/gui/buysell.h b/src/gui/buysell.h index a3e54df0..2d3c7bd3 100644 --- a/src/gui/buysell.h +++ b/src/gui/buysell.h @@ -28,8 +28,6 @@ #include "window.h" -#include "../guichanfwd.h" - /** * A dialog to choose between buying or selling at a shop. * @@ -50,11 +48,6 @@ class BuySellDialog : public Window, public gcn::ActionListener * Called when receiving actions from the widgets. */ void action(const std::string& eventId); - - private: - gcn::Button *buyButton; - gcn::Button *sellButton; - gcn::Button *cancelButton; }; #endif diff --git a/src/gui/char_select.cpp b/src/gui/char_select.cpp index 209284f7..b86a01e3 100644 --- a/src/gui/char_select.cpp +++ b/src/gui/char_select.cpp @@ -25,66 +25,52 @@ #include <sstream> #include <string> -#include <SDL.h> #include <guichan/widgets/label.hpp> #include "button.h" +#include "confirm_dialog.h" #include "ok_dialog.h" #include "playerbox.h" #include "textfield.h" #include "windowcontainer.h" -#include "../being.h" + #include "../game.h" -#include "../log.h" +#include "../localplayer.h" #include "../main.h" -#include "../playerinfo.h" -#include "../net/messagein.h" #include "../net/messageout.h" -#include "../net/network.h" -#include "../net/protocol.h" -CharSelectDialog::CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m): +/** + * Listener for confirming character deletion. + */ +class CharDeleteConfirm : public ConfirmDialog +{ + public: + CharDeleteConfirm(CharSelectDialog *master); + void action(const std::string &eventId); + private: + CharSelectDialog *master; +}; + +CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m): ConfirmDialog(m, "Confirm", "Are you sure you want to delete this character?"), - master(m), mStatus(0) + master(m) { } -void CharSelectDialog::CharDeleteConfirm::action(const std::string &eventId) +void CharDeleteConfirm::action(const std::string &eventId) { //ConfirmDialog::action(eventId); if (eventId == "yes") { master->attemptCharDelete(); - ConfirmDialog::yesButton->setEnabled(false); - ConfirmDialog::noButton->setEnabled(false); - mStatus = 1; - } - else - { - ConfirmDialog::action(eventId); } + ConfirmDialog::action(eventId); } -void CharSelectDialog::CharDeleteConfirm::logic() -{ - if (mStatus == 1) - { - if (packetReady()) - { - master->checkCharDelete(); - ConfirmDialog::action("yes"); - } - else - { - flush(); - } - } -} - -CharSelectDialog::CharSelectDialog(): - Window("Select Character"), mStatus(0), mCurrentSlot(0) +CharSelectDialog::CharSelectDialog(Network *network, LockedArray<LocalPlayer*> *charInfo): + Window("Select Character"), mNetwork(network), mCharInfo(charInfo) { selectButton = new Button("Ok"); cancelButton = new Button("Cancel"); @@ -148,25 +134,7 @@ CharSelectDialog::CharSelectDialog(): selectButton->requestFocus(); setLocationRelativeTo(getParent()); - setPlayerInfo(NULL); -} - -void CharSelectDialog::changeSlot(int slot) -{ - mCurrentSlot = slot; - if (mCurrentSlot < 0) - { - mCurrentSlot = MAX_SLOT; - } - else if (mCurrentSlot > MAX_SLOT) - { - mCurrentSlot = 0; - } - - if (char_info[mCurrentSlot] == NULL) - { - newCharButton->setEnabled(true); - } + updatePlayerInfo(); } void CharSelectDialog::action(const std::string& eventId) @@ -180,7 +148,6 @@ void CharSelectDialog::action(const std::string& eventId) previousButton->setEnabled(false); nextButton->setEnabled(false); attemptCharSelect(); - mStatus = 1; } else if (eventId == "cancel") { @@ -191,33 +158,36 @@ void CharSelectDialog::action(const std::string& eventId) if (n_character < MAX_SLOT + 1) { // Start new character dialog - new CharCreateDialog(this, mCurrentSlot); + new CharCreateDialog(this, mCharInfo->getPos(), mNetwork); + mCharInfo->lock(); } } else if (eventId == "delete") { // Delete character - if (n_character > 0) + if (mCharInfo->getEntry()) { new CharDeleteConfirm(this); } } else if (eventId == "previous") { - changeSlot(mCurrentSlot - 1); + mCharInfo->prev(); } else if (eventId == "next") { - changeSlot(mCurrentSlot + 1); + mCharInfo->next(); } } -void CharSelectDialog::setPlayerInfo(PLAYER_INFO *pi) +void CharSelectDialog::updatePlayerInfo() { + LocalPlayer *pi = mCharInfo->getEntry(); + if (pi) { std::stringstream nameCaption, levelCaption, jobCaption, moneyCaption; - nameCaption << pi->name; + nameCaption << pi->getName(); levelCaption << "Lvl: " << pi->lvl; jobCaption << "Job Lvl: " << pi->jobLvl; moneyCaption << "Gold: " << pi->gp; @@ -226,14 +196,11 @@ void CharSelectDialog::setPlayerInfo(PLAYER_INFO *pi) levelLabel->setCaption(levelCaption.str()); jobLevelLabel->setCaption(jobCaption.str()); moneyLabel->setCaption(moneyCaption.str()); - if (mStatus != 1) - { - newCharButton->setEnabled(false); - delCharButton->setEnabled(true); - selectButton->setEnabled(true); - } - playerBox->hairStyle = pi->hairStyle - 1; - playerBox->hairColor = pi->hairColor - 1; + newCharButton->setEnabled(false); + delCharButton->setEnabled(true); + selectButton->setEnabled(true); + playerBox->hairStyle = pi->getHairStyle() - 1; + playerBox->hairColor = pi->getHairColor() - 1; playerBox->showPlayer = true; } else { @@ -254,141 +221,29 @@ void CharSelectDialog::setPlayerInfo(PLAYER_INFO *pi) void CharSelectDialog::attemptCharDelete() { // Request character deletion - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0068); - outMsg.writeInt32(char_info[mCurrentSlot]->id); + outMsg.writeInt32(mCharInfo->getEntry()->mLoginId); outMsg.writeString("a@a.com", 40); -} - -void CharSelectDialog::checkCharDelete() -{ - MessageIn msg = get_next_message(); - - if (msg.getId() == 0x006f) - { - skip(msg.getLength()); - delete char_info[mCurrentSlot]; - n_character--; - char_info[mCurrentSlot] = NULL; - setPlayerInfo(NULL); - new OkDialog(this, "Info", "Player deleted"); - } - else if (msg.getId() == 0x0070) - { - new OkDialog(this, "Error", "Failed to delete character."); - skip(msg.getLength()); - } - else { - new OkDialog(this, "Error", "Unknown"); - skip(msg.getLength()); - } + mCharInfo->lock(); } void CharSelectDialog::attemptCharSelect() { // Request character selection - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0066); - outMsg.writeInt8(mCurrentSlot); -} - -void -CharSelectDialog::checkCharSelect() -{ - // Receive reply - MessageIn msg = get_next_message(); - if (state == ERROR_STATE) - { - return; - } - - logger->log("CharSelect: Packet ID: %x, Length: %d, in_size: %d", - msg.getId(), msg.getLength(), in_size); - - if (msg.getId() == 0x0071) - { - char_ID = msg.readInt32(); - map_path = "maps/" + msg.readString(16); - map_path = map_path.substr(0, map_path.rfind(".")) + ".tmx.gz"; - map_address = msg.readInt32(); - map_port = msg.readInt16(); - player_info = char_info[mCurrentSlot]; - // Clear unselected players infos - for (int i = 0; i < MAX_SLOT + 1; i++) - { - if (i != mCurrentSlot) - { - delete char_info[i]; - } - } - free(char_info); - state = CONNECTING_STATE; - - logger->log("CharSelect: Map: %s", map_path.c_str()); - logger->log("CharSelect: Server: %s:%i", iptostring(map_address), - map_port); - closeConnection(); - } - else if (msg.getId() == 0x006c) - { - switch (msg.readInt8()) { - case 0: - errorMessage = "Access denied"; - break; - case 1: - errorMessage = "Cannot use this ID"; - break; - default: - errorMessage = "Unknown failure to select character"; - break; - } - skip(msg.getLength()); - } - else if (msg.getId() == 0x0081) - { - switch (msg.readInt8()) { - case 1: - errorMessage = "Map server offline"; - break; - case 3: - errorMessage = "Speed hack detected"; - break; - case 8: - errorMessage = "Duplicated login"; - break; - default: - errorMessage = "Unkown error with 0x0081"; - break; - } - closeConnection(); - state = ERROR_STATE; - } - - // Todo: add other packets + outMsg.writeInt8(mCharInfo->getPos()); + mCharInfo->lock(); } void CharSelectDialog::logic() { - if (n_character > 0) - { - setPlayerInfo(char_info[mCurrentSlot]); - } - - if (mStatus == 1) - { - if (packetReady()) - { - checkCharSelect(); - } - else - { - flush(); - } - } + updatePlayerInfo(); } -CharCreateDialog::CharCreateDialog(Window *parent, int slot): - Window("Create Character", true, parent), mStatus(0), mSlot(slot) +CharCreateDialog::CharCreateDialog(Window *parent, int slot, Network *network): + Window("Create Character", true, parent), mNetwork(network), mSlot(slot) { nameField = new TextField(""); nameLabel = new gcn::Label("Name:"); @@ -454,21 +309,6 @@ CharCreateDialog::CharCreateDialog(Window *parent, int slot): setLocationRelativeTo(getParent()); } -void CharCreateDialog::logic() -{ - if (mStatus == 1) - { - if (packetReady()) - { - checkCharCreate(); - } - else - { - flush(); - } - } -} - void CharCreateDialog::action(const std::string& eventId) { if (eventId == "create") { @@ -476,7 +316,7 @@ void CharCreateDialog::action(const std::string& eventId) // Attempt to create the character createButton->setEnabled(false); attemptCharCreate(); - mStatus = 1; + windowContainer->scheduleDelete(this); } else { new OkDialog(this, "Error", @@ -511,7 +351,7 @@ std::string CharCreateDialog::getName() void CharCreateDialog::attemptCharCreate() { // Send character infos - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0067); outMsg.writeString(getName(), 24); outMsg.writeInt8(5); @@ -524,73 +364,3 @@ void CharCreateDialog::attemptCharCreate() outMsg.writeInt16(playerBox->hairColor + 1); outMsg.writeInt16(playerBox->hairStyle + 1); } - -void CharCreateDialog::checkCharCreate() -{ - MessageIn msg = get_next_message(); - - if (msg.getId() == 0x006d) - { - PLAYER_INFO *tempPlayer = new PLAYER_INFO; - - tempPlayer->id = msg.readInt32(); - tempPlayer->xp = msg.readInt32(); - tempPlayer->gp = msg.readInt32(); - tempPlayer->jobXp = msg.readInt32(); - tempPlayer->jobLvl = msg.readInt32(); - msg.skip(8); // unknown - msg.readInt32(); // option - msg.readInt32(); // karma - msg.readInt32(); // manner - msg.skip(2); // unknown - tempPlayer->hp = msg.readInt16(); - tempPlayer->maxHp = msg.readInt16(); - tempPlayer->mp = msg.readInt16(); - tempPlayer->maxMp = msg.readInt16(); - msg.readInt16(); // speed - msg.readInt16(); // class - tempPlayer->hairStyle = msg.readInt16(); - tempPlayer->weapon = msg.readInt16(); - tempPlayer->lvl = msg.readInt16(); - msg.readInt16(); // skill point - msg.readInt16(); // head bottom - msg.readInt16(); // shield - msg.readInt16(); // head option top - msg.readInt16(); // head option mid - tempPlayer->hairColor = msg.readInt16(); - msg.readInt16(); // unknown - tempPlayer->name = msg.readString(24); - tempPlayer->STR = msg.readInt8(); - tempPlayer->AGI = msg.readInt8(); - tempPlayer->VIT = msg.readInt8(); - tempPlayer->INT = msg.readInt8(); - tempPlayer->DEX = msg.readInt8(); - tempPlayer->LUK = msg.readInt8(); - int slot = msg.readInt8(); // character slot - msg.readInt8(); // unknown - - n_character++; - char_info[slot] = tempPlayer; - windowContainer->scheduleDelete(this); - } - else if (msg.getId() == 0x006e) - { - new OkDialog(this, "Error", "Failed to create character"); - createButton->setEnabled(true); - } - else - { - new OkDialog(this, "Error", "Unknown error"); - createButton->setEnabled(true); - } - - skip(msg.getLength()); -} - -void charSelectInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; - } -} diff --git a/src/gui/char_select.h b/src/gui/char_select.h index 7db67699..94d69e06 100644 --- a/src/gui/char_select.h +++ b/src/gui/char_select.h @@ -24,15 +24,14 @@ #ifndef _CHAR_SELECT_H #define _CHAR_SELECT_H -#include "confirm_dialog.h" #include "window.h" #include "../guichanfwd.h" +#include "../lockedarray.h" -#include <SDL_events.h> - +class LocalPlayer; +class Network; class PlayerBox; -struct PLAYER_INFO; /** * Character selection dialog. @@ -42,18 +41,22 @@ struct PLAYER_INFO; class CharSelectDialog : public Window, public gcn::ActionListener { public: + friend class CharDeleteConfirm; /** * Constructor. */ - CharSelectDialog(); + CharSelectDialog(Network *network, LockedArray<LocalPlayer*> *charInfo); void action(const std::string& eventId); - void setPlayerInfo(PLAYER_INFO* pi); + void updatePlayerInfo(); void logic(); private: + Network *mNetwork; + LockedArray<LocalPlayer*> *mCharInfo; + gcn::Button *selectButton; gcn::Button *cancelButton; gcn::Button *newCharButton; @@ -65,47 +68,18 @@ class CharSelectDialog : public Window, public gcn::ActionListener gcn::Label *levelLabel; gcn::Label *jobLevelLabel; gcn::Label *moneyLabel; - - int mStatus; - int mCurrentSlot; PlayerBox *playerBox; - - void changeSlot(int slot); /** * Communicate character deletion to the server. */ void attemptCharDelete(); - - /** - * Check server answer. - */ - void checkCharDelete(); /** * Communicate character selection to the server. */ void attemptCharSelect(); - - /** - * Check server answer. - */ - void checkCharSelect(); - - /** - * Listener for confirming character deletion. - */ - class CharDeleteConfirm : public ConfirmDialog - { - public: - CharDeleteConfirm(CharSelectDialog *master); - void action(const std::string &eventId); - void logic(); - private: - CharSelectDialog *master; - int mStatus; - }; }; /** @@ -119,15 +93,14 @@ class CharCreateDialog : public Window, public gcn::ActionListener /** * Constructor. */ - CharCreateDialog(Window *parent = NULL, int slot = 0); - - void logic(); + CharCreateDialog(Window *parent, int slot, Network *network); void action(const std::string& eventId); std::string getName(); private: + Network *mNetwork; gcn::TextField *nameField; gcn::Label *nameLabel; gcn::Button *nextHairColorButton; @@ -140,21 +113,13 @@ class CharCreateDialog : public Window, public gcn::ActionListener gcn::Button *cancelButton; PlayerBox *playerBox; - - int mStatus; + int mSlot; /** * Communicate character creation to the server. */ void attemptCharCreate(); - - /** - * Receive new char info. - */ - void checkCharCreate(); }; -void charSelectInputHandler(SDL_KeyboardEvent *keyEvent); - #endif diff --git a/src/gui/char_server.cpp b/src/gui/char_server.cpp index ed0818c8..c66d4436 100644 --- a/src/gui/char_server.cpp +++ b/src/gui/char_server.cpp @@ -24,26 +24,18 @@ #include "char_server.h" #include <sstream> -#include <SDL.h> #include "button.h" #include "listbox.h" -#include "ok_dialog.h" #include "scrollarea.h" -#include "../log.h" #include "../main.h" -#include "../playerinfo.h" #include "../serverinfo.h" -#include "../net/messagein.h" -#include "../net/messageout.h" -#include "../net/network.h" - extern SERVER_INFO **server_info; ServerSelectDialog::ServerSelectDialog(): - Window("Select Server"), mStatus(NET_IDLE) + Window("Select Server") { serverListModel = new ServerListModel(); serverList = new ListBox(serverListModel); @@ -98,51 +90,17 @@ void ServerSelectDialog::action(const std::string& eventId) { if (eventId == "ok") { - int index = serverList->getSelected(); - const char *host = iptostring(server_info[index]->address); - short port = server_info[index]->port; - openConnection(host, port); okButton->setEnabled(false); - //cancelButton->setEnabled(false); - mStatus = NET_CONNECTING; + state = CHAR_CONNECT_STATE; } else if (eventId == "cancel") { state = LOGIN_STATE; } } -void -ServerSelectDialog::logic() +SERVER_INFO* ServerSelectDialog::getServerInfo() { - switch (mStatus) - { - case NET_CONNECTING: - mStatus = pollConnection(); - break; - case NET_ERROR: - logger->log("ServerSelect::Unable to connect"); - errorMessage = "Unable to connect to char server"; - state = ERROR_STATE; - closeConnection(); - break; - case NET_CONNECTED: - attemptServerSelect(serverList->getSelected()); - mStatus = NET_DATA; - break; - case NET_DATA: - // TODO: cannot substitute with packetReady() because of eAthena - // sending 4 unknown bytes. - if (in_size > 6) - { - skip(4); - checkServerSelect(); - } - else - { - flush(); - } - break; - } + return server_info[serverList->getSelected()]; } int @@ -158,113 +116,3 @@ ServerListModel::getElementAt(int i) s << server_info[i]->name << " (" << server_info[i]->online_users << ")"; return s.str(); } - -void -charServerInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = LOGIN_STATE; - } -} - -void -ServerSelectDialog::attemptServerSelect(int index) -{ - // Send login infos - MessageOut outMsg; - outMsg.writeInt16(0x0065); - outMsg.writeInt32(account_ID); - outMsg.writeInt32(session_ID1); - outMsg.writeInt32(session_ID2); - outMsg.writeInt16(0); // unknown - outMsg.writeInt8(sex); -} - -void -ServerSelectDialog::checkServerSelect() -{ - MessageIn msg = get_next_message(); - - if (msg.getId() == 0x006b) - { - // Skip length word and an additional mysterious 20 bytes - msg.skip(2 + 20); - - // Derive number of characters from message length - n_character = (msg.getLength() - 24) / 106; - char_info = (PLAYER_INFO**)malloc(sizeof(PLAYER_INFO*) * (MAX_SLOT+1)); - for (int i = 0; i < MAX_SLOT + 1; i++) - char_info[i] = NULL; - - for (int i = 0; i < n_character; i++) - { - PLAYER_INFO *tempPlayer = new PLAYER_INFO; - - tempPlayer->totalWeight = 0; - tempPlayer->maxWeight = 0; - tempPlayer->lastAttackTime = 0; - tempPlayer->id = msg.readInt32(); - tempPlayer->xp = msg.readInt32(); - tempPlayer->gp = msg.readInt32(); - tempPlayer->jobXp = msg.readInt32(); - tempPlayer->jobLvl = msg.readInt32(); - msg.skip(8); // unknown - msg.readInt32(); // option - msg.readInt32(); // karma - msg.readInt32(); // manner - msg.skip(2); // unknown - tempPlayer->hp = msg.readInt16(); - tempPlayer->maxHp = msg.readInt16(); - tempPlayer->mp = msg.readInt16(); - tempPlayer->maxMp = msg.readInt16(); - msg.readInt16(); // speed - msg.readInt16(); // class - tempPlayer->hairStyle = msg.readInt16(); - tempPlayer->weapon = msg.readInt16(); - tempPlayer->lvl = msg.readInt16(); - msg.readInt16(); // skill point - msg.readInt16(); // head bottom - msg.readInt16(); // shield - msg.readInt16(); // head option top - msg.readInt16(); // head option mid - tempPlayer->hairColor = msg.readInt16(); - msg.readInt16(); // unknown - tempPlayer->name = msg.readString(24); - tempPlayer->STR = msg.readInt8(); - tempPlayer->AGI = msg.readInt8(); - tempPlayer->VIT = msg.readInt8(); - tempPlayer->INT = msg.readInt8(); - tempPlayer->DEX = msg.readInt8(); - tempPlayer->LUK = msg.readInt8(); - int slot = msg.readInt8(); // character slot - msg.readInt8(); // unknown - - char_info[slot] = tempPlayer; - - logger->log("CharServer: Player: %s (%d)", - char_info[slot]->name.c_str(), slot); - } - - state = CHAR_SELECT_STATE; - skip(msg.getLength()); - } - else if (msg.getId() == 0x006c) - { - std::string errorStr; - switch (msg.readInt8()) { - case 0: errorStr = "Access denied"; break; - case 1: errorStr = "Cannot use this ID"; break; - default: errorStr = "Rejected from server"; break; - } - new OkDialog("Error", errorStr); - skip(msg.getLength()); - closeConnection(); - } - else - { - new OkDialog("Error", "Unknown error"); - skip(msg.getLength()); - } - // Todo: add other packets -} diff --git a/src/gui/char_server.h b/src/gui/char_server.h index f36ee76e..ed6e4c14 100644 --- a/src/gui/char_server.h +++ b/src/gui/char_server.h @@ -26,12 +26,13 @@ #include <guichan/actionlistener.hpp> #include <guichan/listmodel.hpp> -#include <SDL_events.h> #include "window.h" #include "../guichanfwd.h" +class SERVER_INFO; + /** * The list model for the server list. */ @@ -68,9 +69,9 @@ class ServerSelectDialog : public Window, public gcn::ActionListener { void action(const std::string& eventId); /** - * Updates dialog logic + * Returns the index of the selected server */ - void logic(); + SERVER_INFO* getServerInfo(); private: ServerListModel *serverListModel; @@ -78,12 +79,6 @@ class ServerSelectDialog : public Window, public gcn::ActionListener { gcn::Button *okButton; gcn::Button *cancelButton; gcn::ScrollArea *scrollArea; - int mStatus; - - void attemptServerSelect(int index); - void checkServerSelect(); }; -void charServerInputHandler(SDL_KeyboardEvent *keyEvent); - #endif diff --git a/src/gui/chargedialog.cpp b/src/gui/chargedialog.cpp index 4ef28e16..da02cada 100644 --- a/src/gui/chargedialog.cpp +++ b/src/gui/chargedialog.cpp @@ -28,7 +28,7 @@ #include "progressbar.h" -#include "../playerinfo.h" +#include "../localplayer.h" ChargeDialog::ChargeDialog(): Window("") @@ -42,15 +42,15 @@ ChargeDialog::ChargeDialog(): void ChargeDialog::logic() { // calculate time since the last attack was made - player_info->lastAttackTime += .01; // this a hack until someone explains + player_node->lastAttackTime += .01; // this a hack until someone explains // to me how to work the timer - if (player_info->lastAttackTime > 1) + if (player_node->lastAttackTime > 1) { - player_info->lastAttackTime = 1; + player_node->lastAttackTime = 1; } // reset the progress bar to display accurate time since attack - progBar->setProgress(player_info->lastAttackTime); + progBar->setProgress(player_node->lastAttackTime); Window::logic(); } diff --git a/src/gui/chargedialog.h b/src/gui/chargedialog.h index d17bdeb2..22dc5d23 100644 --- a/src/gui/chargedialog.h +++ b/src/gui/chargedialog.h @@ -23,8 +23,6 @@ #ifndef _TMW_CHARGE_H #define _TMW_CHARGE_H -#include <guichan/actionlistener.hpp> - #include "window.h" class ProgressBar; diff --git a/src/gui/chat.cpp b/src/gui/chat.cpp index 748f4848..29fbebfe 100644 --- a/src/gui/chat.cpp +++ b/src/gui/chat.cpp @@ -34,16 +34,17 @@ #include "../game.h" #include "../graphics.h" +#include "../localplayer.h" #include "../log.h" -#include "../playerinfo.h" #include "../net/messageout.h" #include "../net/protocol.h" extern Graphics *graphics; -ChatWindow::ChatWindow(const std::string &logfile): +ChatWindow::ChatWindow(const std::string &logfile, Network *network): Window(""), + mNetwork(network), mTmpVisible(false) { setWindowName("Chat"); @@ -206,7 +207,7 @@ ChatWindow::action(const std::string& eventId) curHist = history.end(); // Send the message to the server - chatSend(player_info->name.c_str(), message.c_str()); + chatSend(player_node->getName().c_str(), message.c_str()); // Clear the text from the chat input chatInput->setText(""); @@ -262,7 +263,7 @@ ChatWindow::chatSend(std::string nick, std::string msg) if (msg.substr(0, IS_ANNOUNCE_LENGTH) == IS_ANNOUNCE) { msg.erase(0, IS_ANNOUNCE_LENGTH); - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(0x0099); outMsg.writeInt16(msg.length() + 4); outMsg.writeString(msg, msg.length()); @@ -281,7 +282,7 @@ ChatWindow::chatSend(std::string nick, std::string msg) } else if (msg.substr(0, IS_WHO_LENGTH) == IS_WHO) { - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(0x00c1); } else @@ -295,7 +296,7 @@ ChatWindow::chatSend(std::string nick, std::string msg) nick += msg; msg = nick; - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_CHAT_MESSAGE); outMsg.writeInt16(msg.length() + 4); outMsg.writeString(msg, msg.length()); diff --git a/src/gui/chat.h b/src/gui/chat.h index 5b470e27..63b30db3 100644 --- a/src/gui/chat.h +++ b/src/gui/chat.h @@ -36,6 +36,7 @@ #include "../guichanfwd.h" class BrowserBox; +class Network; class ScrollArea; #define BY_GM 0 // those should be self-explanatory =) @@ -116,7 +117,7 @@ class ChatWindow : public Window, public gcn::ActionListener, /** * Constructor. */ - ChatWindow(const std::string &logfile); + ChatWindow(const std::string &logfile, Network *network); /** * Destructor. @@ -193,6 +194,7 @@ class ChatWindow : public Window, public gcn::ActionListener, void setVisible(bool visible); private: + Network *mNetwork; std::ofstream chatlog_file; bool mTmpVisible; diff --git a/src/gui/connection.cpp b/src/gui/connection.cpp index c7eb3667..372537a8 100644 --- a/src/gui/connection.cpp +++ b/src/gui/connection.cpp @@ -23,22 +23,29 @@ #include "connection.h" +#include <guichan/actionlistener.hpp> + #include <guichan/widgets/label.hpp> #include "button.h" #include "progressbar.h" -#include "../game.h" -#include "../log.h" #include "../main.h" -#include "../net/messagein.h" -#include "../net/messageout.h" -#include "../net/network.h" -#include "../net/protocol.h" +class ConnectionActionListener : public gcn::ActionListener +{ + public: + void action(const std::string& eventId) + { + if (eventId == "cancel") + { + state = EXIT_STATE; + } + } +} connectionActionListener; ConnectionDialog::ConnectionDialog(): - Window("Info"), mProgress(0), mStatus(NET_CONNECTING) + Window("Info"), mProgress(0) { setContentSize(200, 100); @@ -46,7 +53,7 @@ ConnectionDialog::ConnectionDialog(): cancelButton = new Button("Cancel"); cancelButton->setPosition(5, 100 - 5 - cancelButton->getHeight()); cancelButton->setEventId("cancel"); - cancelButton->addActionListener(this); + cancelButton->addActionListener(&connectionActionListener); mProgressBar = new ProgressBar(0.0, 5, cancelButton->getY() - 25, 200 - 10, 20, 128, 128, 128); @@ -59,9 +66,6 @@ ConnectionDialog::ConnectionDialog(): add(mProgressBar); setLocationRelativeTo(getParent()); - - const char *host = iptostring(map_address); - openConnection(host, map_port); } void ConnectionDialog::logic() @@ -73,91 +77,4 @@ void ConnectionDialog::logic() } mProgressBar->setProgress(mProgress); Window::logic(); - - switch (mStatus) - { - case NET_CONNECTING: - mStatus = pollConnection(); - break; - case NET_ERROR: - logger->log("Connection::Unable to connect"); - errorMessage = "Unable to connect to map server"; - state = ERROR_STATE; - closeConnection(); - break; - case NET_CONNECTED: - attemptMapLogin(); - mStatus = NET_DATA; - break; - case NET_DATA: - if (in_size > 6) - { - skip(4); - checkMapLogin(); - state = GAME_STATE; - } - else - { - flush(); - } - break; - } -} - -void ConnectionDialog::action(const std::string& eventId) -{ - if (eventId == "cancel") - { - state = EXIT_STATE; - } -} - -void ConnectionDialog::attemptMapLogin() -{ - // Send login infos - MessageOut outMsg; - outMsg.writeInt16(0x0072); - outMsg.writeInt32(account_ID); - outMsg.writeInt32(char_ID); - outMsg.writeInt32(session_ID1); - outMsg.writeInt32(session_ID2); - outMsg.writeInt8(sex); -} - -void ConnectionDialog::checkMapLogin() -{ - MessageIn msg = get_next_message(); - - if (msg.getId() == SMSG_LOGIN_SUCCESS) - { - unsigned char direction; - msg.readInt32(); // server tick - msg.readCoordinates(startX, startY, direction); - msg.skip(2); // unknown - logger->log("Protocol: Player start position: (%d, %d), Direction: %d", - startX, startY, direction); - } - else if (msg.getId() == 0x0081) - { - logger->log("Warning: Map server D/C"); - } - else - { - logger->error("Unknown packet: map_start"); - } - - skip(msg.getLength()); - - // Send "map loaded" - // TODO: be able to reuse the same msg - MessageOut newMsg; - newMsg.writeInt16(0x007d); -} - -void connectionInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; - } } diff --git a/src/gui/connection.h b/src/gui/connection.h index 342b9f8d..7a072d2e 100644 --- a/src/gui/connection.h +++ b/src/gui/connection.h @@ -24,14 +24,8 @@ #ifndef _TMW_CONNECTION_H #define _TMW_CONNECTION_H -#include <iosfwd> -#include <guichan/actionlistener.hpp> -#include <SDL_events.h> - #include "window.h" -#include "../guichanfwd.h" - class ProgressBar; /** @@ -39,7 +33,7 @@ class ProgressBar; * * \ingroup Interface */ -class ConnectionDialog : public Window, public gcn::ActionListener +class ConnectionDialog : public Window { public: /** @@ -49,25 +43,11 @@ class ConnectionDialog : public Window, public gcn::ActionListener */ ConnectionDialog(); - /** - * Called when receiving actions from the widgets. - */ - void action(const std::string& eventId); - void logic(); private: ProgressBar *mProgressBar; float mProgress; - int mStatus; - - void attemptMapLogin(); - void checkMapLogin(); }; -/** - * Handle input - */ -void connectionInputHandler(SDL_KeyboardEvent *keyEvent); - #endif diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index d3b5fc6b..f1b9b4ba 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -35,8 +35,8 @@ #include <sstream> -EquipmentWindow::EquipmentWindow(): - Window("Equipment") +EquipmentWindow::EquipmentWindow(Equipment *equipment): + Window("Equipment"), mEquipment(equipment) { setWindowName("Equipment"); setDefaultSize(5, 230, 200, 90); @@ -57,7 +57,6 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) // Draw window graphics Window::draw(graphics); - Equipment *equipment = Equipment::getInstance(); Item *item; Image *image; @@ -66,7 +65,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) graphics->drawRectangle(gcn::Rectangle(10 + 36 * (i % 4), 36 * (i / 4) + 25, 32, 32)); - if (!(item = equipment->getEquipment(i))) { + if (!(item = mEquipment->getEquipment(i))) { continue; } @@ -78,7 +77,7 @@ void EquipmentWindow::draw(gcn::Graphics *graphics) graphics->setColor(gcn::Color(0, 0, 0)); graphics->drawRectangle(gcn::Rectangle(160, 25, 32, 32)); - if (!(item = equipment->getArrows())) { + if (!(item = mEquipment->getArrows())) { return; } diff --git a/src/gui/equipmentwindow.h b/src/gui/equipmentwindow.h index a3241057..651c593a 100644 --- a/src/gui/equipmentwindow.h +++ b/src/gui/equipmentwindow.h @@ -24,10 +24,9 @@ #ifndef _TMW_EQUIPMENT_H #define _TMW_EQUIPMENT_H -#include <guichan/actionlistener.hpp> - #include "window.h" +class Equipment; class Spriteset; /** @@ -41,7 +40,7 @@ class EquipmentWindow : public Window /** * Constructor. */ - EquipmentWindow(); + EquipmentWindow(Equipment *equipment); /** * Destructor. @@ -56,6 +55,8 @@ class EquipmentWindow : public Window private: Spriteset *itemset; + Equipment *mEquipment; + }; extern EquipmentWindow *equipmentWindow; diff --git a/src/gui/error.cpp b/src/gui/error.cpp index 42f4b217..4a980caa 100644 --- a/src/gui/error.cpp +++ b/src/gui/error.cpp @@ -41,11 +41,3 @@ void ErrorDialog::action(const std::string& eventId) state = LOGIN_STATE; } } - -void errorInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; - } -} diff --git a/src/gui/error.h b/src/gui/error.h index 9eb0cbde..feb864f2 100644 --- a/src/gui/error.h +++ b/src/gui/error.h @@ -25,7 +25,6 @@ #define _TMW_ERROR_H #include <iosfwd> -#include <SDL_events.h> #include "ok_dialog.h" @@ -54,9 +53,4 @@ class ErrorDialog : public OkDialog { void action(const std::string& eventId); }; -/** - * Handle input - */ -void errorInputHandler(SDL_KeyboardEvent *keyEvent); - #endif diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 7149e5e8..491a4009 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -39,24 +39,22 @@ #include "windowcontainer.h" #include "../being.h" +#include "../beingmanager.h" #include "../configlistener.h" #include "../configuration.h" #include "../engine.h" #include "../floor_item.h" -#include "../game.h" #include "../graphics.h" +#include "../localplayer.h" #include "../log.h" #include "../main.h" #include "../map.h" - -#include "../net/protocol.h" +#include "../npc.h" #include "../resources/image.h" #include "../resources/resourcemanager.h" #include "../resources/sdlimageloader.h" -extern Being* autoTarget; - // Guichan stuff Gui *gui; gcn::SDLInput *guiInput; // GUI input @@ -262,13 +260,12 @@ Gui::mousePress(int mx, int my, int button) Being *being; FloorItem *floorItem; - if ((being = findNode(tilex, tiley)) && being != player_node) + if ((being = beingManager->findBeing(tilex, tiley)) && being->getType() != Being::LOCALPLAYER) { showPopup(mx, my, being); return; } - else if((floorItem = find_floor_item_by_id( - find_floor_item_by_cor(tilex, tiley)))) + else if((floorItem = find_floor_item_by_cor(tilex, tiley))) { showPopup(mx, my, floorItem); return; @@ -287,27 +284,23 @@ Gui::mousePress(int mx, int my, int button) if (button == gcn::MouseInput::LEFT) { Being *being; - Uint32 floorItemId; + FloorItem *item; // Interact with some being - if ((being = findNode(tilex, tiley))) + if ((being = beingManager->findBeing(tilex, tiley))) { switch (being->getType()) { case Being::NPC: - talk(being); - current_npc = being->getId(); + dynamic_cast<NPC*>(being)->talk(); break; case Being::MONSTER: case Being::PLAYER: - if (being->action == Being::MONSTER_DEAD || - player_node->action != Being::STAND || - being == player_node) + if (being->action == Being::MONSTER_DEAD) break; - autoTarget = being; - attack(being); + player_node->attack(being, true); break; default: @@ -315,13 +308,13 @@ Gui::mousePress(int mx, int my, int button) } } // Pick up some item - else if ((floorItemId = find_floor_item_by_cor(tilex, tiley))) + else if ((item = find_floor_item_by_cor(tilex, tiley))) { int dx = tilex - player_node->x; int dy = tiley - player_node->y; if ((dx * dx + dy * dy) < 4) - pickUp(floorItemId); + player_node->pickUp(item); } // Just walk around else if (engine->getCurrentMap()->getWalk(tilex, tiley)) @@ -330,10 +323,8 @@ Gui::mousePress(int mx, int my, int button) Uint8 *keys = SDL_GetKeyState(NULL); if (!(keys[SDLK_LSHIFT] || keys[SDLK_RSHIFT])) { - walk(tilex, tiley, 0); player_node->setDestination(tilex, tiley); - - autoTarget = NULL; + player_node->stopAttack(); } } } diff --git a/src/gui/inventorywindow.cpp b/src/gui/inventorywindow.cpp index 0b8a37a7..3b786f62 100644 --- a/src/gui/inventorywindow.cpp +++ b/src/gui/inventorywindow.cpp @@ -36,14 +36,11 @@ #include "itemcontainer.h" #include "scrollarea.h" -#include "../inventory.h" #include "../item.h" -#include "../playerinfo.h" +#include "../localplayer.h" #include "../resources/iteminfo.h" -extern Inventory *inventory; - InventoryWindow::InventoryWindow(): Window("Inventory") { @@ -56,7 +53,7 @@ InventoryWindow::InventoryWindow(): useButton = new Button("Use"); dropButton = new Button("Drop"); - items = new ItemContainer(inventory); + items = new ItemContainer(player_node->mInventory); invenScroll = new ScrollArea(items); invenScroll->setPosition(8, 8); invenScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); @@ -98,8 +95,8 @@ void InventoryWindow::logic() // Update weight information std::stringstream tempstr; - tempstr << "Total Weight: " << player_info->totalWeight - << " - Maximum Weight: " << player_info->maxWeight; + tempstr << "Total Weight: " << player_node->totalWeight + << " - Maximum Weight: " << player_node->maxWeight; weightLabel->setCaption(tempstr.str()); weightLabel->adjustSize(); } @@ -115,14 +112,14 @@ void InventoryWindow::action(const std::string &eventId) if (eventId == "use") { if (item->isEquipment()) { if (item->isEquipped()) { - inventory->unequipItem(item); + player_node->unequipItem(item); } else { - inventory->equipItem(item); + player_node->equipItem(item); } } else { - inventory->useItem(item); + player_node->useItem(item); } } else if (eventId == "drop") diff --git a/src/gui/item_amount.cpp b/src/gui/item_amount.cpp index d92f53e1..2ac46283 100644 --- a/src/gui/item_amount.cpp +++ b/src/gui/item_amount.cpp @@ -28,10 +28,8 @@ #include "slider.h" #include "trade.h" -#include "../inventory.h" #include "../item.h" - -extern Inventory *inventory; +#include "../localplayer.h" ItemAmountWindow::ItemAmountWindow(int usage, Window *parent, Item *item): Window("Select amount of items to drop.", true, parent), @@ -114,7 +112,7 @@ void ItemAmountWindow::action(const std::string& eventId) } else if (eventId == "Drop") { - inventory->dropItem(mItem, mItemAmountTextBox->getInt()); + player_node->dropItem(mItem, mItemAmountTextBox->getInt()); scheduleDelete(); } else if (eventId == "AddTrade") diff --git a/src/gui/linkhandler.h b/src/gui/linkhandler.h index 65c0d433..33416ec7 100644 --- a/src/gui/linkhandler.h +++ b/src/gui/linkhandler.h @@ -34,9 +34,6 @@ class LinkHandler virtual ~LinkHandler() { } virtual void handleLink(const std::string& link) = 0; - - protected: - LinkHandler() { } }; #endif diff --git a/src/gui/login.cpp b/src/gui/login.cpp index 9c198dbb..696e4c6f 100644 --- a/src/gui/login.cpp +++ b/src/gui/login.cpp @@ -25,27 +25,20 @@ #include <string> #include <sstream> -#include <SDL.h> #include <guichan/widgets/label.hpp> #include "../main.h" #include "../configuration.h" -#include "../graphics.h" #include "../log.h" -#include "../serverinfo.h" +#include "../logindata.h" #include "button.h" #include "checkbox.h" +#include "ok_dialog.h" #include "passwordfield.h" #include "textfield.h" -#include "../net/messagein.h" -#include "../net/messageout.h" -#include "../net/network.h" - -SERVER_INFO **server_info; - void WrongDataNoticeListener::setTarget(gcn::TextField *textField) { @@ -64,8 +57,8 @@ WrongDataNoticeListener::action(const std::string &eventId) } } -LoginDialog::LoginDialog(): - Window("Login"), mStatus(NET_IDLE) +LoginDialog::LoginDialog(LoginData *loginData): + Window("Login"), mLoginData(loginData) { userLabel = new gcn::Label("Name:"); passLabel = new gcn::Label("Password:"); @@ -144,10 +137,6 @@ LoginDialog::LoginDialog(): wrongDataNoticeListener = NULL; } -LoginDialog::~LoginDialog() -{ -} - void LoginDialog::action(const std::string& eventId) { @@ -179,14 +168,16 @@ LoginDialog::action(const std::string& eventId) } else { - const std::string host(config.getValue("host", "animesites.de")); - short port = (short)config.getValue("port", 0); - // Attempt to connect to login server - openConnection(host.c_str(), port); + mLoginData->hostname = config.getValue("host", "animesites.de"); + mLoginData->port = (short)config.getValue("port", 0); + mLoginData->username = userField->getText(); + mLoginData->password = passField->getText(); + okButton->setEnabled(false); //cancelButton->setEnabled(false); registerButton->setEnabled(false); - mStatus = NET_CONNECTING; + + state = ACCOUNT_STATE; } } else if (eventId == "cancel") @@ -198,147 +189,3 @@ LoginDialog::action(const std::string& eventId) state = REGISTER_STATE; } } - -void -LoginDialog::logic() -{ - switch (mStatus) - { - case NET_CONNECTING: - mStatus = pollConnection(); - break; - case NET_ERROR: - logger->log("Login::Unable to connect"); - errorMessage = "Unable to connect to login server"; - state = ERROR_STATE; - closeConnection(); - logger->log("Connection closed"); - break; - case NET_DATA: - if (packetReady()) - { - checkLogin(); - closeConnection(); - } - else - { - flush(); - } - break; - case NET_CONNECTED: - logger->log("Connected..."); - std::string user = userField->getText(); - const std::string password = passField->getText(); - attemptLogin(user, password); - mStatus = NET_DATA; - break; - } -} - -void -loginInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; - } -} - -void -LoginDialog::attemptLogin(const std::string& user, const std::string& pass) -{ - // Send login infos - MessageOut outMsg; - outMsg.writeInt16(0x0064); - outMsg.writeInt32(0); // client version - outMsg.writeString(user, 24); - outMsg.writeString(pass, 24); - outMsg.writeInt8(0); // unknown -} - -void -LoginDialog::checkLogin() -{ - // Receive reply - MessageIn msg = get_next_message(); - if (state == ERROR_STATE) - { - return; - } - - // Login ok - if (msg.getId() == 0x0069) - { - // Skip the length word - msg.skip(2); - - n_server = (msg.getLength() - 47) / 32; - server_info = (SERVER_INFO**)malloc(sizeof(SERVER_INFO*) * n_server); - - session_ID1 = msg.readInt32(); - account_ID = msg.readInt32(); - session_ID2 = msg.readInt32(); - msg.skip(30); // unknown - sex = msg.readInt8(); - - for (int i = 0; i < n_server; i++) - { - server_info[i] = new SERVER_INFO; - - server_info[i]->address = msg.readInt32(); - server_info[i]->port = msg.readInt16(); - server_info[i]->name = msg.readString(20); - server_info[i]->online_users = msg.readInt32(); - msg.skip(2); // unknown - - logger->log("Network: Server: %s (%s:%d)", - server_info[i]->name.c_str(), - iptostring(server_info[i]->address), - server_info[i]->port); - } - skip(msg.getLength()); - - state = CHAR_SERVER_STATE; - } - else if (msg.getId() == 0x006a) - { - int loginError = msg.readInt8(); - logger->log("Login::error code: %i", loginError); - - switch (loginError) { - case 0: - errorMessage = "Unregistered ID"; - break; - case 1: - errorMessage = "Wrong password"; - break; - case 2: - errorMessage = "Account expired"; - break; - case 3: - errorMessage = "Rejected from server"; - break; - case 4: - errorMessage = "You have been blocked by the GM Team"; - break; - case 6: - errorMessage = "You have been banned for 5 minutes"; - break; - case 9: - errorMessage = "This account is already logged in"; - break; - default: - errorMessage = "Unknown error"; - break; - } - skip(msg.getLength()); - state = ERROR_STATE; - } - else { - skip(msg.getLength()); - logger->log("Login::Unknown error"); - errorMessage = "Unknown error"; - state = ERROR_STATE; - } - // Todo: add other packets, also encrypted -} diff --git a/src/gui/login.h b/src/gui/login.h index cc6ace3d..669752c5 100644 --- a/src/gui/login.h +++ b/src/gui/login.h @@ -26,13 +26,11 @@ #include <iosfwd> #include <guichan/actionlistener.hpp> -#include <SDL_events.h> -#include "ok_dialog.h" #include "window.h" #include "../guichanfwd.h" -class LoginDialog; +class LoginData; /** * Listener used for handling wrong data. @@ -57,23 +55,13 @@ class LoginDialog : public Window, public gcn::ActionListener { * * @see Window::Window */ - LoginDialog(); - - /** - * Destructor - */ - ~LoginDialog(); + LoginDialog(LoginData *loginData); /** * Called when receiving actions from the widgets. */ void action(const std::string& eventId); - /** - * Updates dialog logic. - */ - void logic(); - // Made them public to have the possibility to request focus // from external functions. gcn::TextField *userField; @@ -88,17 +76,10 @@ class LoginDialog : public Window, public gcn::ActionListener { gcn::Button *okButton; gcn::Button *cancelButton; gcn::Button *registerButton; - int mStatus; - - void attemptLogin(const std::string& user, const std::string& pass); - void checkLogin(); WrongDataNoticeListener *wrongDataNoticeListener; -}; -/** - * Handle input - */ -void loginInputHandler(SDL_KeyboardEvent *keyEvent); + LoginData *mLoginData; +}; #endif diff --git a/src/gui/minimap.cpp b/src/gui/minimap.cpp index b23d7b1c..1165d7bb 100644 --- a/src/gui/minimap.cpp +++ b/src/gui/minimap.cpp @@ -24,6 +24,7 @@ #include "minimap.h" #include "../being.h" +#include "../beingmanager.h" #include "../graphics.h" #include "../map.h" @@ -86,38 +87,36 @@ void Minimap::draw(gcn::Graphics *graphics) mMapImage, getPadding(), getTitleBarHeight()); } - std::list<Being*>::iterator bi; + Beings *beings = beingManager->getAll(); + Beings::iterator bi; - for (bi = beings.begin(); bi != beings.end(); bi++) + for (bi = beings->begin(); bi != beings->end(); bi++) { Being *being = (*bi); + int dotSize = 1; - if (being == player_node) - { - // Player dot - graphics->setColor(gcn::Color(209, 52, 61)); - graphics->fillRectangle(gcn::Rectangle( - being->x / 2 + getPadding() - 1, - being->y / 2 + getTitleBarHeight() - 1, 3, 3)); - } - else - { - switch (being->getType()) { - case Being::PLAYER: - graphics->setColor(gcn::Color(61, 52, 209)); - break; - - case Being::MONSTER: - graphics->setColor(gcn::Color(209, 52, 61)); - break; - - default: - break; - } - - graphics->fillRectangle(gcn::Rectangle( - being->x / 2 + getPadding(), - being->y / 2 + getTitleBarHeight(), 1, 1)); + switch (being->getType()) { + case Being::LOCALPLAYER: + dotSize = 3; + graphics->setColor(gcn::Color(209, 52, 61)); + break; + + case Being::PLAYER: + graphics->setColor(gcn::Color(61, 52, 209)); + break; + + case Being::MONSTER: + graphics->setColor(gcn::Color(209, 52, 61)); + break; + + default: + break; } + + int offset = (dotSize - 1) / 2; + + graphics->fillRectangle(gcn::Rectangle( + being->x / 2 + getPadding() - offset, + being->y / 2 + getTitleBarHeight() - offset, 1, 1)); } } diff --git a/src/gui/ministatus.cpp b/src/gui/ministatus.cpp index 6820d561..515a4014 100644 --- a/src/gui/ministatus.cpp +++ b/src/gui/ministatus.cpp @@ -30,7 +30,7 @@ #include <guichan/imagefont.hpp> #include "progressbar.h" -#include "../playerinfo.h" +#include "../localplayer.h" MiniStatusWindow::MiniStatusWindow(): Window("") @@ -67,13 +67,13 @@ MiniStatusWindow::MiniStatusWindow(): void MiniStatusWindow::update() { // HP Bar coloration - if (player_info->hp < int(player_info->maxHp / 3)) + if (player_node->hp < int(player_node->maxHp / 3)) { hpBar->setColor(223, 32, 32); // Red } else { - if (player_info->hp < int((player_info->maxHp / 3) * 2)) + if (player_node->hp < int((player_node->maxHp / 3) * 2)) { hpBar->setColor(230, 171, 34); // Orange } @@ -83,16 +83,16 @@ void MiniStatusWindow::update() } } - hpBar->setProgress((float)player_info->hp / (float)player_info->maxHp); - // mpBar->setProgress((float)player_info->mp / (float)player_info->maxMp); + hpBar->setProgress((float)player_node->hp / (float)player_node->maxHp); + // mpBar->setProgress((float)player_node->mp / (float)player_node->maxMp); // Update and center labels std::stringstream updatedText; - updatedText << player_info->hp; + updatedText << player_node->hp; hpLabel->setCaption(updatedText.str()); hpLabel->adjustSize(); updatedText.str(""); - updatedText << player_info->mp; + updatedText << player_node->mp; mpLabel->setCaption(updatedText.str()); mpLabel->adjustSize(); hpLabel->setPosition(hpBar->getX() + int((hpBar->getWidth() / 2) - (hpLabel->getWidth() / 2)), diff --git a/src/gui/ministatus.h b/src/gui/ministatus.h index f10717bc..0b081f47 100644 --- a/src/gui/ministatus.h +++ b/src/gui/ministatus.h @@ -26,21 +26,19 @@ #include <iosfwd> -#include <guichan/actionlistener.hpp> - #include "window.h" #include "../guichanfwd.h" class ProgressBar; - /** * The player mini-status dialog. * * \ingroup Interface */ -class MiniStatusWindow : public Window, public gcn::ActionListener { +class MiniStatusWindow : public Window +{ public: /** * Constructor. @@ -52,14 +50,9 @@ class MiniStatusWindow : public Window, public gcn::ActionListener { */ void draw(gcn::Graphics *graphics); - /** - * Does Nothing - **/ - void action(const std::string&) {}; - private: /** - * Updates this dialog with values from PLAYER_INFO *char_info + * Updates this dialog with values from player_node */ void update(); diff --git a/src/gui/npc_text.cpp b/src/gui/npc_text.cpp index f4bdfb0f..0769021e 100644 --- a/src/gui/npc_text.cpp +++ b/src/gui/npc_text.cpp @@ -23,22 +23,21 @@ #include "npc_text.h" +#include <string> + #include "scrollarea.h" #include "button.h" #include "textbox.h" -#include "../game.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../npc.h" NpcTextDialog::NpcTextDialog(): Window("NPC") { - textBox = new TextBox(); - textBox->setEditable(false); - scrollArea = new ScrollArea(textBox); - okButton = new Button("OK"); + mTextBox = new TextBox(); + mTextBox->setEditable(false); + gcn::ScrollArea *scrollArea = new ScrollArea(mTextBox); + Button *okButton = new Button("OK"); setContentSize(260, 175); scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); @@ -61,13 +60,13 @@ NpcTextDialog::NpcTextDialog(): void NpcTextDialog::setText(const char *text) { - textBox->setText(text); + mTextBox->setText(text); } void NpcTextDialog::addText(const std::string &text) { - textBox->setText(textBox->getText() + text + "\n"); + mTextBox->setText(mTextBox->getText() + text + "\n"); } void @@ -75,11 +74,9 @@ NpcTextDialog::action(const std::string& eventId) { if (eventId == "ok") { - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_NEXT_REQUEST); - outMsg.writeInt32(current_npc); setText(""); setVisible(false); + current_npc->nextDialog(); current_npc = 0; } } diff --git a/src/gui/npc_text.h b/src/gui/npc_text.h index 4a0e8b49..a1d75aab 100644 --- a/src/gui/npc_text.h +++ b/src/gui/npc_text.h @@ -25,7 +25,6 @@ #define _TMW_NPC_TEXT_H #include <iosfwd> -#include <string> #include <guichan/actionlistener.hpp> #include "window.h" @@ -70,9 +69,7 @@ class NpcTextDialog : public Window, public gcn::ActionListener addText(const std::string &string); private: - gcn::Button *okButton; - gcn::TextBox *textBox; - gcn::ScrollArea *scrollArea; + gcn::TextBox *mTextBox; }; #endif diff --git a/src/gui/npc.cpp b/src/gui/npclistdialog.cpp index de491b73..9b5239f5 100644 --- a/src/gui/npc.cpp +++ b/src/gui/npclistdialog.cpp @@ -21,7 +21,7 @@ * $Id$ */ -#include "npc.h" +#include "npclistdialog.h" #include <sstream> @@ -29,10 +29,7 @@ #include "scrollarea.h" #include "listbox.h" -#include "../game.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../npc.h" NpcListDialog::NpcListDialog(): Window("NPC") @@ -118,12 +115,9 @@ NpcListDialog::action(const std::string& eventId) if (choice) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_LIST_CHOICE); - outMsg.writeInt32(current_npc); - outMsg.writeInt8(choice); setVisible(false); reset(); + current_npc->dialogChoice(choice); current_npc = 0; } } diff --git a/src/gui/npc.h b/src/gui/npclistdialog.h index 6ab49138..8b98076e 100644 --- a/src/gui/npc.h +++ b/src/gui/npclistdialog.h @@ -21,8 +21,8 @@ * $Id$ */ -#ifndef _TMW_NPC_H -#define _TMW_NPC_H +#ifndef _TMW_GUI_NPCLISTDIALOG_H +#define _TMW_GUI_NPCLISTDIALOG_H #include <iosfwd> #include <vector> diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp index 8d0ace57..464242f1 100644 --- a/src/gui/popupmenu.cpp +++ b/src/gui/popupmenu.cpp @@ -35,19 +35,14 @@ #include "../being.h" #include "../floor_item.h" -#include "../game.h" -#include "../graphics.h" -#include "../inventory.h" #include "../item.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../localplayer.h" +#include "../npc.h" #include "../resources/iteminfo.h" #include "../resources/itemmanager.h" -extern Being* autoTarget; -extern Inventory* inventory; +extern std::string tradePartnerName; PopupMenu::PopupMenu(): Window(), @@ -129,11 +124,7 @@ void PopupMenu::handleLink(const std::string& link) mBeing->getType() == Being::NPC && current_npc == 0) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_TALK); - outMsg.writeInt32(mBeing->getId()); - outMsg.writeInt8(0); - current_npc = mBeing->getId(); + dynamic_cast<NPC*>(mBeing)->talk(); } // Trade action @@ -141,11 +132,7 @@ void PopupMenu::handleLink(const std::string& link) mBeing != NULL && mBeing->getType() == Being::PLAYER) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_TRADE_REQUEST); - outMsg.writeInt32(mBeing->getId()); - //tradePartner.flush(); - //tradePartner << "Trade: You and " << being->name<< ""; + player_node->trade(mBeing); tradePartnerName = mBeing->getName(); } @@ -154,8 +141,7 @@ void PopupMenu::handleLink(const std::string& link) mBeing != NULL && mBeing->getType() == Being::PLAYER) { - autoTarget = mBeing; - attack(mBeing); + player_node->attack(mBeing, true); } /* @@ -177,7 +163,7 @@ void PopupMenu::handleLink(const std::string& link) // Pick Up Floor Item action else if ((link == "pickup") && mFloorItem != NULL) { - pickUp(mFloorItem->getId()); + player_node->pickUp(mFloorItem); } // Look To action @@ -192,16 +178,16 @@ void PopupMenu::handleLink(const std::string& link) { if (mItem->isEquipped()) { - inventory->unequipItem(mItem); + player_node->unequipItem(mItem); } else { - inventory->equipItem(mItem); + player_node->equipItem(mItem); } } else { - inventory->useItem(mItem); + player_node->useItem(mItem); } } diff --git a/src/gui/register.cpp b/src/gui/register.cpp index 71d1a740..9b4cf79d 100644 --- a/src/gui/register.cpp +++ b/src/gui/register.cpp @@ -25,32 +25,24 @@ #include <string> #include <sstream> -#include <SDL.h> #include <guichan/widgets/label.hpp> #include "../main.h" #include "../configuration.h" -#include "../graphics.h" #include "../log.h" -#include "../serverinfo.h" +#include "../logindata.h" #include "button.h" #include "checkbox.h" +#include "login.h" #include "passwordfield.h" #include "radiobutton.h" #include "textfield.h" #include "ok_dialog.h" -#include "../net/messagein.h" -#include "../net/messageout.h" -#include "../net/network.h" - -//OkDialog *wrongLoginNotice = NULL; -extern SERVER_INFO **server_info; - -RegisterDialog::RegisterDialog(): - Window("Register"), mStatus(NET_IDLE) +RegisterDialog::RegisterDialog(LoginData *loginData): + Window("Register"), mLoginData(loginData) { userLabel = new gcn::Label("Name:"); passwordLabel = new gcn::Label("Password:"); @@ -214,6 +206,7 @@ RegisterDialog::action(const std::string& eventId) if (wrongRegisterNotice) { delete wrongRegisterNotice; + wrongRegisterNotice = NULL; } wrongRegisterNotice = new OkDialog("Error", errorMsg.str(), wrongDataNoticeListener); @@ -221,159 +214,15 @@ RegisterDialog::action(const std::string& eventId) else { // No errors detected, register the new user. - const std::string host(config.getValue("host", "animesites.de")); - short port = (short)config.getValue("port", 0); - // Attempt to connect to login server - openConnection(host.c_str(), port); registerButton->setEnabled(false); - mStatus = NET_CONNECTING; - } - } -} - -void -RegisterDialog::logic() -{ - switch (mStatus) - { - case NET_CONNECTING: - mStatus = pollConnection(); - break; - case NET_ERROR: - logger->log("Register::Unable to connect"); - errorMessage = "Unable to connect to login server"; - state = ERROR_STATE; - closeConnection(); - logger->log("Connection closed"); - break; - case NET_DATA: - if (packetReady()) - { - checkRegistration(); - closeConnection(); - } - else - { - flush(); - } - break; - case NET_CONNECTED: - logger->log("Connected..."); - std::string user = userField->getText(); - const std::string password = passwordField->getText(); - if (femaleButton->isMarked()) - { - user += "_F"; - } - else - { - user += "_M"; - } - attemptRegistration(user, password); - mStatus = NET_DATA; - break; - } -} -void -RegisterDialog::attemptRegistration(const std::string& user, - const std::string& pass) -{ - // Send login infos - MessageOut outMsg; - outMsg.writeInt16(0x0064); - outMsg.writeInt32(0); // client version - outMsg.writeString(user, 24); - outMsg.writeString(pass, 24); - outMsg.writeInt8(0); // unknown -} + mLoginData->hostname = config.getValue("host", "animesites.de"); + mLoginData->port = (short)config.getValue("port", 0); + mLoginData->username = userField->getText(); + mLoginData->password = passwordField->getText(); + mLoginData->username += femaleButton->isMarked() ? "_F" : "_M"; -void -RegisterDialog::checkRegistration() -{ - // Receive reply - MessageIn msg = get_next_message(); - if (state == ERROR_STATE) - { - return; - } - - // Login ok - if (msg.getId() == 0x0069) - { - // Skip the length word - msg.skip(2); - - n_server = (msg.getLength() - 47) / 32; - server_info = (SERVER_INFO**)malloc(sizeof(SERVER_INFO*) * n_server); - - session_ID1 = msg.readInt32(); - account_ID = msg.readInt32(); - session_ID2 = msg.readInt32(); - msg.skip(30); // unknown - sex = msg.readInt8(); - - for (int i = 0; i < n_server; i++) - { - server_info[i] = new SERVER_INFO; - - server_info[i]->address = msg.readInt32(); - server_info[i]->port = msg.readInt16(); - server_info[i]->name = msg.readString(20); - server_info[i]->online_users = msg.readInt32(); - msg.skip(2); // unknown - - logger->log("Network: Server: %s (%s:%d)", - server_info[i]->name.c_str(), - iptostring(server_info[i]->address), - server_info[i]->port); + state = ACCOUNT_STATE; } - skip(msg.getLength()); - - state = CHAR_SERVER_STATE; - } - else if (msg.getId() == 0x006a) - { - int loginError = msg.readInt8(); - logger->log("Login::error code: %i", loginError); - - switch (loginError) { - case 0: - errorMessage = "Unregistered ID"; - break; - case 1: - errorMessage = "Wrong password"; - break; - case 2: - errorMessage = "Account expired"; - break; - case 3: - errorMessage = "Rejected from server"; - break; - case 4: - errorMessage = "You have been blocked by the GM Team"; - break; - case 9: - errorMessage = "This account is already logged in"; - break; - } - skip(msg.getLength()); - state = ERROR_STATE; - } - else { - skip(msg.getLength()); - logger->log("Login::Unknown error"); - errorMessage = "Unknown error"; - state = ERROR_STATE; - } - // Todo: add other packets, also encrypted -} - -void -registerInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; } } diff --git a/src/gui/register.h b/src/gui/register.h index 25a867bd..34892ec0 100644 --- a/src/gui/register.h +++ b/src/gui/register.h @@ -26,12 +26,14 @@ #include <iosfwd> #include <guichan/actionlistener.hpp> -#include <SDL_events.h> #include "window.h" -#include "login.h" #include "../guichanfwd.h" +class LoginData; +class OkDialog; +class WrongDataNoticeListener; + /** * The login dialog. * @@ -44,18 +46,13 @@ class RegisterDialog : public Window, public gcn::ActionListener { * * @see Window::Window */ - RegisterDialog(); + RegisterDialog(LoginData *loginData); /** * Called when receiving actions from the widgets. */ void action(const std::string& eventId); - /** - * Updates dialog logic. - */ - void logic(); - // Made them public to have the possibility to request focus // from external functions. gcn::TextField *userField; @@ -72,19 +69,11 @@ class RegisterDialog : public Window, public gcn::ActionListener { gcn::Button *cancelButton; gcn::RadioButton *maleButton; gcn::RadioButton *femaleButton; - int mStatus; - void attemptRegistration(const std::string& user, - const std::string& pass); - void checkRegistration(); - WrongDataNoticeListener *wrongDataNoticeListener; OkDialog *wrongRegisterNotice; -}; -/** - * Handle input - */ -void registerInputHandler(SDL_KeyboardEvent *keyEvent); + LoginData *mLoginData; +}; #endif diff --git a/src/gui/requesttrade.cpp b/src/gui/requesttrade.cpp index 075567af..54f7c208 100644 --- a/src/gui/requesttrade.cpp +++ b/src/gui/requesttrade.cpp @@ -23,53 +23,46 @@ #include "requesttrade.h" -#include <sstream> #include <guichan/widgets/label.hpp> #include "button.h" -#include "../net/messageout.h" -#include "../net/protocol.h" - -bool requestTradeDialogOpen = false; +#include "../localplayer.h" RequestTradeDialog::RequestTradeDialog(const std::string &name): Window("Request for Trade", true) { + gcn::Label *nameLabel[2]; nameLabel[0] = new gcn::Label(""); nameLabel[1] = new gcn::Label(""); - acceptButton = new Button("Accept"); - cancelButton = new Button("Cancel"); + mAcceptButton = new Button("Accept"); + mCancelButton = new Button("Cancel"); setContentSize(260, 75); nameLabel[0]->setPosition(5, 30); nameLabel[1]->setPosition(5, 40); - cancelButton->setPosition( - 260 - 5 - cancelButton->getWidth(), - 75 - 5 - cancelButton->getHeight()); - acceptButton->setPosition( - cancelButton->getX() - 5 - acceptButton->getWidth(), - cancelButton->getY()); + mCancelButton->setPosition( + 260 - 5 - mCancelButton->getWidth(), + 75 - 5 - mCancelButton->getHeight()); + mAcceptButton->setPosition( + mCancelButton->getX() - 5 - mAcceptButton->getWidth(), + mCancelButton->getY()); - acceptButton->setEventId("accept"); - cancelButton->setEventId("cancel"); + mAcceptButton->setEventId("accept"); + mCancelButton->setEventId("cancel"); - acceptButton->addActionListener(this); - cancelButton->addActionListener(this); + mAcceptButton->addActionListener(this); + mCancelButton->addActionListener(this); add(nameLabel[0]); add(nameLabel[1]); - add(acceptButton); - add(cancelButton); - - std::stringstream cap[2]; - cap[0] << name << " wants to trade with you."; - cap[1] << "Do you want to accept?"; + add(mAcceptButton); + add(mCancelButton); - nameLabel[0]->setCaption(cap[0].str()); + nameLabel[0]->setCaption(name + " wants to trade with you."); nameLabel[0]->adjustSize(); - nameLabel[1]->setCaption(cap[1].str()); + nameLabel[1]->setCaption("Do you want to accept?"); nameLabel[1]->adjustSize(); setLocationRelativeTo(getParent()); @@ -77,17 +70,12 @@ RequestTradeDialog::RequestTradeDialog(const std::string &name): void RequestTradeDialog::action(const std::string& eventId) { - int choice = 4; // 4 means trade canceled + bool accept = false; if (eventId == "accept") { - choice = 3; // ok to trade - } - else if (eventId == "cancel") { - requestTradeDialogOpen = false; + accept = true; } - MessageOut outMsg; - outMsg.writeInt16(CMSG_TRADE_RESPONSE); - outMsg.writeInt8(choice); + player_node->tradeReply(accept); scheduleDelete(); } diff --git a/src/gui/requesttrade.h b/src/gui/requesttrade.h index 706b0b0e..d9ea67ed 100644 --- a/src/gui/requesttrade.h +++ b/src/gui/requesttrade.h @@ -30,8 +30,6 @@ #include "window.h" #include "../guichanfwd.h" -extern bool requestTradeDialogOpen; - /** * The request trade dialog. * @@ -53,9 +51,8 @@ class RequestTradeDialog : public Window, public gcn::ActionListener void action(const std::string& eventId); private: - gcn::Button *acceptButton; - gcn::Button *cancelButton; - gcn::Label *nameLabel[2]; + gcn::Button *mAcceptButton; + gcn::Button *mCancelButton; }; #endif diff --git a/src/gui/sell.cpp b/src/gui/sell.cpp index ea5b948f..f89055b4 100644 --- a/src/gui/sell.cpp +++ b/src/gui/sell.cpp @@ -34,8 +34,8 @@ #include "shop.h" #include "slider.h" -#include "../game.h" #include "../item.h" +#include "../npc.h" #include "../resources/iteminfo.h" #include "../resources/itemmanager.h" @@ -44,8 +44,9 @@ #include "../net/protocol.h" -SellDialog::SellDialog(): +SellDialog::SellDialog(Network *network): Window("Sell"), + mNetwork(network), m_maxItems(0), m_amountItems(0) { itemList = new ListBox(this); @@ -216,7 +217,7 @@ void SellDialog::action(const std::string& eventId) // Attempt sell assert(m_amountItems > 0 && m_amountItems <= m_maxItems); - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_NPC_SELL_REQUEST); outMsg.writeInt16(8); outMsg.writeInt16(shopInventory[selectedItem].index); diff --git a/src/gui/sell.h b/src/gui/sell.h index 142819b5..1a5fabf5 100644 --- a/src/gui/sell.h +++ b/src/gui/sell.h @@ -34,6 +34,7 @@ #include "../guichanfwd.h" class Item; +class Network; struct ITEM_SHOP; @@ -51,7 +52,7 @@ class SellDialog : public Window, public gcn::ActionListener, * * @see Window::Window */ - SellDialog(); + SellDialog(Network *network); /** * Resets the dialog, clearing inventory. @@ -84,6 +85,7 @@ class SellDialog : public Window, public gcn::ActionListener, std::string getElementAt(int i); private: + Network *mNetwork; gcn::Button *sellButton; gcn::Button *quitButton; gcn::Button *increaseButton; diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp index 2114e635..34f5b360 100644 --- a/src/gui/setup.cpp +++ b/src/gui/setup.cpp @@ -23,18 +23,24 @@ #include "setup.h" -#include <iostream> #include <sstream> #include <guichan/widgets/container.hpp> #include <guichan/widgets/label.hpp> #include "button.h" +#include "chat.h" #include "checkbox.h" +#include "equipmentwindow.h" +#include "help.h" +#include "inventorywindow.h" #include "listbox.h" #include "ok_dialog.h" +#include "minimap.h" #include "scrollarea.h" +#include "skill.h" #include "slider.h" +#include "status.h" #include "tabbedcontainer.h" #include "../configuration.h" @@ -44,13 +50,8 @@ #include "../sound.h" extern Graphics *graphics; -#include "chat.h" -#include "equipmentwindow.h" -#include "help.h" -#include "inventorywindow.h" -#include "minimap.h" -#include "skill.h" -#include "status.h" + +extern SDL_Joystick *joypad; extern SDL_Joystick *joypad; diff --git a/src/gui/skill.cpp b/src/gui/skill.cpp index 8a073bdd..ba117b67 100644 --- a/src/gui/skill.cpp +++ b/src/gui/skill.cpp @@ -29,10 +29,7 @@ #include "listbox.h" #include "scrollarea.h" -#include "../playerinfo.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../localplayer.h" #include "../graphics.h" extern Graphics *graphics; @@ -64,8 +61,7 @@ const char *skill_db[] = { SkillDialog::SkillDialog(): - Window("Skills"), - skillPoints(0) + Window("Skills") { setWindowName("Skills"); setDefaultSize(graphics->getWidth() - 255, 25, 240, 240); @@ -119,18 +115,16 @@ void SkillDialog::action(const std::string& eventId) { // Increment skill int selectedSkill = skillListBox->getSelected(); - if (player_info->skillPoint > 0 && selectedSkill >= 0) + if (selectedSkill >= 0) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_SKILL_LEVELUP_REQUEST); - outMsg.writeInt16(skillList[selectedSkill]->id); + player_node->raiseSkill(skillList[selectedSkill]->id); } } else if (eventId == "skill") { incButton->setEnabled( skillListBox->getSelected() > -1 && - skillPoints > 0); + player_node->skillPoint > 0); } else if (eventId == "close") { @@ -138,17 +132,15 @@ void SkillDialog::action(const std::string& eventId) } } -void SkillDialog::setPoints(int i) +void SkillDialog::update() { - skillPoints = i; - if (pointsLabel != NULL) { char tmp[128]; - sprintf(tmp, "Skill points: %i", skillPoints); + sprintf(tmp, "Skill points: %i", player_node->skillPoint); pointsLabel->setCaption(tmp); } - incButton->setEnabled(skillListBox->getSelected() > -1 && skillPoints > 0); + incButton->setEnabled(skillListBox->getSelected() > -1 && player_node->skillPoint > 0); } int SkillDialog::getNumberOfElements() diff --git a/src/gui/skill.h b/src/gui/skill.h index b4c2522f..98a21438 100644 --- a/src/gui/skill.h +++ b/src/gui/skill.h @@ -46,17 +46,6 @@ struct SKILL { class SkillDialog : public Window, public gcn::ActionListener, public gcn::ListModel { - private: - gcn::ListBox *skillListBox; - gcn::ScrollArea *skillScrollArea; - gcn::Label *pointsLabel; - gcn::Button *incButton; - gcn::Button *useButton; - gcn::Button *closeButton; - - std::vector<SKILL*> skillList; - int skillPoints; - public: /** * Constructor. @@ -70,7 +59,7 @@ class SkillDialog : public Window, public gcn::ActionListener, void action(const std::string&); - void setPoints(int i); + void update(); int getNumberOfElements(); std::string getElementAt(int); @@ -79,6 +68,16 @@ class SkillDialog : public Window, public gcn::ActionListener, void addSkill(int id, int lv, int sp); void setSkill(int id, int lv, int sp); void cleanList(); + + private: + gcn::ListBox *skillListBox; + gcn::ScrollArea *skillScrollArea; + gcn::Label *pointsLabel; + gcn::Button *incButton; + gcn::Button *useButton; + gcn::Button *closeButton; + + std::vector<SKILL*> skillList; }; extern SkillDialog *skillDialog; diff --git a/src/gui/status.cpp b/src/gui/status.cpp index 43456a29..c6416409 100644 --- a/src/gui/status.cpp +++ b/src/gui/status.cpp @@ -29,17 +29,15 @@ #include "button.h" #include "progressbar.h" -#include "../playerinfo.h" - -#include "../net/messageout.h" -#include "../net/protocol.h" +#include "../localplayer.h" #include "../graphics.h" extern Graphics *graphics; -StatusWindow::StatusWindow(): - Window(player_info->name) +StatusWindow::StatusWindow(LocalPlayer *player): + Window(player->getName()), + mPlayer(player) { setWindowName("Status"); setResizable(true); @@ -222,46 +220,46 @@ void StatusWindow::update() // Status Part // ----------- updateText.str(""); - updateText << "Level: " << player_info->lvl; + updateText << "Level: " << mPlayer->lvl; lvlLabel->setCaption(updateText.str()); lvlLabel->adjustSize(); updateText.str(""); - updateText << "Money: " << player_info->gp << " GP"; + updateText << "Money: " << mPlayer->gp << " GP"; gpLabel->setCaption(updateText.str()); gpLabel->adjustSize(); updateText.str(""); - updateText << "Job: " << player_info->jobLvl; + updateText << "Job: " << mPlayer->jobLvl; jobXpLabel->setCaption(updateText.str()); jobXpLabel->adjustSize(); updateText.str(""); - updateText << player_info->hp << "/" << player_info->maxHp; + updateText << mPlayer->hp << "/" << mPlayer->maxHp; hpValueLabel->setCaption(updateText.str()); hpValueLabel->adjustSize(); updateText.str(""); - updateText << player_info->mp << "/" << player_info->maxMp; + updateText << mPlayer->mp << "/" << mPlayer->maxMp; mpValueLabel->setCaption(updateText.str()); mpValueLabel->adjustSize(); updateText.str(""); - updateText << (int)player_info->xp << "/" << (int)player_info->xpForNextLevel; + updateText << (int)mPlayer->xp << "/" << (int)mPlayer->xpForNextLevel; xpValueLabel->setCaption(updateText.str()); xpValueLabel->adjustSize(); updateText.str(""); - updateText << (int)player_info->jobXp << "/" << (int)player_info->jobXpForNextLevel; + updateText << (int)mPlayer->jobXp << "/" << (int)mPlayer->jobXpForNextLevel; jobValueLabel->setCaption(updateText.str()); jobValueLabel->adjustSize(); // HP Bar coloration - if (player_info->hp < int(player_info->maxHp / 3)) + if (mPlayer->hp < int(mPlayer->maxHp / 3)) { hpBar->setColor(223, 32, 32); // Red } - else if (player_info->hp < int((player_info->maxHp / 3) * 2)) + else if (mPlayer->hp < int((mPlayer->maxHp / 3) * 2)) { hpBar->setColor(230, 171, 34); // Orange } @@ -270,61 +268,48 @@ void StatusWindow::update() hpBar->setColor(0, 171, 34); // Green } - hpBar->setProgress((float)player_info->hp / (float)player_info->maxHp); - // mpBar->setProgress((float)player_info->mp / (float)player_info->maxMp); + hpBar->setProgress((float)mPlayer->hp / (float)mPlayer->maxHp); + // mpBar->setProgress((float)mPlayer->mp / (float)mPlayer->maxMp); xpBar->setProgress( - (float)player_info->xp / (float)player_info->xpForNextLevel); + (float)mPlayer->xp / (float)mPlayer->xpForNextLevel); jobXpBar->setProgress( - (float)player_info->jobXp / (float)player_info->jobXpForNextLevel); + (float)mPlayer->jobXp / (float)mPlayer->jobXpForNextLevel); // Stats Part // ---------- - std::stringstream statsStr[6]; - std::stringstream figureStr[6]; - std::stringstream pointsStr[6]; - - statsStr[0] << "Strength:"; - figureStr[0] << (int)player_info->STR; - statsStr[1] << "Agility:"; - figureStr[1] << (int)player_info->AGI; - statsStr[2] << "Vitality:"; - figureStr[2] << (int)player_info->VIT; - statsStr[3] << "Intelligence:"; - figureStr[3] << (int)player_info->INT; - statsStr[4] << "Dexterity:"; - figureStr[4] << (int)player_info->DEX; - statsStr[5] << "Luck:"; - figureStr[5] << (int)player_info->LUK; - - int statusPoints = player_info->statsPointsToAttribute; + static const std::string attrNames[6] = { + "Strength", + "Agility", + "Vitality", + "Intelligence", + "Dexterity", + "Luck" + }; + + int statusPoints = mPlayer->statsPointsToAttribute; updateText.str(""); updateText << "Remaining Status Points: " << statusPoints; - pointsStr[0] << (int)player_info->STRUp; - pointsStr[1] << (int)player_info->AGIUp; - pointsStr[2] << (int)player_info->VITUp; - pointsStr[3] << (int)player_info->INTUp; - pointsStr[4] << (int)player_info->DEXUp; - pointsStr[5] << (int)player_info->LUKUp; - - // Enable buttons for which there are enough status points - statsButton[0]->setEnabled(player_info->STRUp <= statusPoints); - statsButton[1]->setEnabled(player_info->AGIUp <= statusPoints); - statsButton[2]->setEnabled(player_info->VITUp <= statusPoints); - statsButton[3]->setEnabled(player_info->INTUp <= statusPoints); - statsButton[4]->setEnabled(player_info->DEXUp <= statusPoints); - statsButton[5]->setEnabled(player_info->LUKUp <= statusPoints); - // Update labels for (int i = 0; i < 6; i++) { - statsLabel[i]->setCaption(statsStr[i].str()); + std::stringstream sstr; + + statsLabel[i]->setCaption(attrNames[i]); statsLabel[i]->adjustSize(); - statsDisplayLabel[i]->setCaption(figureStr[i].str()); + + sstr.str(""); + sstr << (int)mPlayer->ATTR[i]; + statsDisplayLabel[i]->setCaption(sstr.str()); statsDisplayLabel[i]->adjustSize(); - pointsLabel[i]->setCaption(pointsStr[i].str()); + + sstr.str(""); + sstr << (int)mPlayer->ATTR_UP[i]; + pointsLabel[i]->setCaption(sstr.str()); pointsLabel[i]->adjustSize(); + + statsButton[i]->setEnabled(mPlayer->ATTR_UP[i] <= statusPoints); } remainingStatsPointsLabel->setCaption(updateText.str()); remainingStatsPointsLabel->adjustSize(); @@ -333,43 +318,43 @@ void StatusWindow::update() // Attack TODO: Count equipped Weapons and items attack bonuses updateText.str(""); - updateText << int(player_info->ATK + player_info->ATKBonus); + updateText << int(mPlayer->ATK + mPlayer->ATK_BONUS); statsAttackPoints->setCaption(updateText.str()); statsAttackPoints->adjustSize(); // Defense TODO: Count equipped Armors and items defense bonuses updateText.str(""); - updateText << int(player_info->DEF + player_info->DEFBonus); + updateText << int(mPlayer->DEF + mPlayer->DEF_BONUS); statsDefensePoints->setCaption(updateText.str()); statsDefensePoints->adjustSize(); // Magic Attack TODO: Count equipped items M.Attack bonuses updateText.str(""); - updateText << int(player_info->MATK + player_info->MATKBonus); + updateText << int(mPlayer->MATK + mPlayer->MATK_BONUS); statsMagicAttackPoints->setCaption(updateText.str()); statsMagicAttackPoints->adjustSize(); // Magic Defense TODO: Count equipped items M.Defense bonuses updateText.str(""); - updateText << int(player_info->MDEF + player_info->MDEFBonus); + updateText << int(mPlayer->MDEF + mPlayer->MDEF_BONUS); statsMagicDefensePoints->setCaption(updateText.str()); statsMagicDefensePoints->adjustSize(); // Accuracy % updateText.str(""); - updateText << (int)player_info->HIT; + updateText << (int)mPlayer->HIT; statsAccuracyPoints->setCaption(updateText.str()); statsAccuracyPoints->adjustSize(); // Evasion % updateText.str(""); - updateText << (int)player_info->FLEE; + updateText << (int)mPlayer->FLEE; statsEvadePoints->setCaption(updateText.str()); statsEvadePoints->adjustSize(); // Reflex % updateText.str(""); - updateText << ((int)player_info->DEX / 4); // + counter + updateText << ((int)mPlayer->DEX / 4); // + counter statsReflexPoints->setCaption(updateText.str()); statsReflexPoints->adjustSize(); @@ -402,33 +387,29 @@ void StatusWindow::action(const std::string& eventId) // Stats Part if (eventId.length() == 3) { - MessageOut outMsg; - outMsg.writeInt16(CMSG_STAT_UPDATE_REQUEST); - if (eventId == "STR") { - outMsg.writeInt16(0x000d); + player_node->raiseAttribute(LocalPlayer::STR); } if (eventId == "AGI") { - outMsg.writeInt16(0x000e); + player_node->raiseAttribute(LocalPlayer::AGI); } if (eventId == "VIT") { - outMsg.writeInt16(0x000f); + player_node->raiseAttribute(LocalPlayer::VIT); } if (eventId == "INT") { - outMsg.writeInt16(0x0010); + player_node->raiseAttribute(LocalPlayer::INT); } if (eventId == "DEX") { - outMsg.writeInt16(0x0011); + player_node->raiseAttribute(LocalPlayer::DEX); } if (eventId == "LUK") { - outMsg.writeInt16(0x0012); + player_node->raiseAttribute(LocalPlayer::LUK); } - outMsg.writeInt8(1); } } diff --git a/src/gui/status.h b/src/gui/status.h index 1df4e346..c01b5676 100644 --- a/src/gui/status.h +++ b/src/gui/status.h @@ -32,6 +32,7 @@ #include "../guichanfwd.h" +class LocalPlayer; class ProgressBar; @@ -45,7 +46,7 @@ class StatusWindow : public Window, public gcn::ActionListener { /** * Constructor. */ - StatusWindow(); + StatusWindow(LocalPlayer *player); /** * Called when receiving actions from widget. @@ -63,6 +64,7 @@ class StatusWindow : public Window, public gcn::ActionListener { void update(); private: + LocalPlayer *mPlayer; /** * Status Part diff --git a/src/gui/trade.cpp b/src/gui/trade.cpp index 767888f4..f7286fd2 100644 --- a/src/gui/trade.cpp +++ b/src/gui/trade.cpp @@ -43,8 +43,9 @@ #include "../resources/iteminfo.h" -TradeWindow::TradeWindow(): - Window("Trade: You") +TradeWindow::TradeWindow(Network *network): + Window("Trade: You"), + mNetwork(network) { setContentSize(322, 150); @@ -186,8 +187,8 @@ void TradeWindow::increaseQuantity(int index, bool own, int quantity) void TradeWindow::reset() { - myInventory->resetItems(); - partnerInventory->resetItems(); + myInventory->clear(); + partnerInventory->clear(); tradeButton->setEnabled(false); okButton->setEnabled(true); ok_other = false; @@ -227,7 +228,7 @@ void TradeWindow::receivedOk(bool own) void TradeWindow::tradeItem(Item *item, int quantity) { - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(item->getInvIndex()); outMsg.writeInt32(quantity); @@ -299,7 +300,7 @@ void TradeWindow::action(const std::string &eventId) } else if (eventId == "cancel") { - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_CANCEL_REQUEST); } else if (eventId == "ok") @@ -312,7 +313,7 @@ void TradeWindow::action(const std::string &eventId) tempMoney[1] << tempInt; moneyField->setText(tempMoney[1].str()); - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ITEM_ADD_REQUEST); outMsg.writeInt16(0); outMsg.writeInt32(tempInt); @@ -320,12 +321,12 @@ void TradeWindow::action(const std::string &eventId) moneyField->setText(""); } moneyField->setEnabled(false); - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_ADD_COMPLETE); } else if (eventId == "trade") { - MessageOut outMsg; + MessageOut outMsg(mNetwork); outMsg.writeInt16(CMSG_TRADE_OK); } } diff --git a/src/gui/trade.h b/src/gui/trade.h index 1a2d0d99..d95cd9ce 100644 --- a/src/gui/trade.h +++ b/src/gui/trade.h @@ -33,6 +33,7 @@ class Inventory; class Item; class ItemContainer; +class Network; class ScrollArea; /** @@ -46,7 +47,7 @@ class TradeWindow : public Window, gcn::ActionListener /** * Constructor. */ - TradeWindow(); + TradeWindow(Network *network); /** * Destructor. @@ -115,6 +116,7 @@ class TradeWindow : public Window, gcn::ActionListener ItemContainer *partnerItemContainer; private: + Network *mNetwork; gcn::Label *itemNameLabel; gcn::Label *itemDescriptionLabel; gcn::Label *moneyLabel; diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index 6d4f93df..59382a4e 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -315,14 +315,6 @@ void UpdaterWindow::download() } } -void updateInputHandler(SDL_KeyboardEvent *keyEvent) -{ - if (keyEvent->keysym.sym == SDLK_ESCAPE) - { - state = EXIT_STATE; - } -} - void UpdaterWindow::logic() { // Update Scroll logic diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index 4fd29ff2..ad554375 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -27,7 +27,6 @@ #include <guichan/actionlistener.hpp> #include <string> #include <vector> -#include <SDL_events.h> #include "window.h" @@ -194,6 +193,4 @@ class UpdaterWindow : public Window, public gcn::ActionListener ScrollArea *mScrollArea; /**< Used to scroll news box. */ }; -void updateInputHandler(SDL_KeyboardEvent *keyEvent); - #endif diff --git a/src/inventory.cpp b/src/inventory.cpp index 3a2d3742..6cfcb921 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -23,12 +23,8 @@ #include "inventory.h" -#include "equipment.h" #include "item.h" -#include "net/messageout.h" -#include "net/protocol.h" - Inventory::Inventory() { items = new Item[INVENTORY_SIZE]; @@ -65,7 +61,7 @@ void Inventory::addItem(int index, int id, int quantity, bool equipment) } -void Inventory::resetItems() +void Inventory::clear() { for (int i = 0; i < INVENTORY_SIZE; i++) { items[i].setId(-1); @@ -95,44 +91,6 @@ bool Inventory::contains(Item *item) return false; } -int Inventory::useItem(Item *item) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_PLAYER_INVENTORY_USE); - outMsg.writeInt16(item->getInvIndex()); - outMsg.writeInt32(item->getId()); - // Note: id is dest of item, usually player_node->account_ID ?? - return 0; -} - -int Inventory::dropItem(Item *item, int quantity) -{ - // TODO: Fix wrong coordinates of drops, serverside? - MessageOut outMsg; - outMsg.writeInt16(CMSG_PLAYER_INVENTORY_DROP); - outMsg.writeInt16(item->getInvIndex()); - outMsg.writeInt16(quantity); - return 0; -} - -void Inventory::equipItem(Item *item) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_PLAYER_EQUIP); - outMsg.writeInt16(item->getInvIndex()); - outMsg.writeInt16(0); -} - -void Inventory::unequipItem(Item *item) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_PLAYER_UNEQUIP); - outMsg.writeInt16(item->getInvIndex()); - - // Tidy equipment directly to avoid weapon still shown bug, by instance - Equipment::getInstance()->removeEquipment(item); -} - int Inventory::getFreeSlot() { for (int i = 2; i < INVENTORY_SIZE; i++) { diff --git a/src/inventory.h b/src/inventory.h index bd6da744..b1f4fb01 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -60,20 +60,6 @@ class Inventory bool contains(Item *item); /** - * Equips an item. - */ - void equipItem(Item *item); - - /** - * Unequips an item. - */ - void unequipItem(Item *item); - - int useItem(Item *item); - - int dropItem(Item *item, int quantity); - - /** * Returns id of next free slot or -1 if all occupied. */ int getFreeSlot(); @@ -81,7 +67,7 @@ class Inventory /** * Reset all item slots. */ - void resetItems(); + void clear(); /** * Get the number of slots filled with an item diff --git a/src/localplayer.cpp b/src/localplayer.cpp new file mode 100644 index 00000000..d76ebc30 --- /dev/null +++ b/src/localplayer.cpp @@ -0,0 +1,386 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "localplayer.h" + +#include "game.h" +#include "equipment.h" +#include "floor_item.h" +#include "inventory.h" +#include "item.h" +#include "main.h" +#include "sound.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +LocalPlayer *player_node = NULL; + +LocalPlayer::LocalPlayer(Uint32 id, Uint16 job, Map *map): + Player(id, job, map), + mInventory(new Inventory()), + mEquipment(new Equipment()), + mTarget(NULL), + mTrading(false) +{ +} + +LocalPlayer::~LocalPlayer() +{ + delete mInventory; +} + +void LocalPlayer::logic() +{ + switch (action) { + case WALK: + mFrame = (get_elapsed_time(walk_time) * 4) / mWalkSpeed; + if (mFrame >= 4) { + nextStep(); + } + break; + + case ATTACK: + mFrame = (get_elapsed_time(walk_time) * 4) / aspd; + if (mFrame >= 4) { + nextStep(); + attack(); + } + break; + } + + Being::logic(); +} + +Being::Type LocalPlayer::getType() const +{ + return LOCALPLAYER; +} + +void LocalPlayer::clearInventory() +{ + mInventory->clear(); +} + +void LocalPlayer::addInvItem(int id, int quantity, bool equipment) +{ + mInventory->addItem(id, quantity, equipment); +} + +void LocalPlayer::addInvItem(int index, int id, int quantity, bool equipment) +{ + mInventory->addItem(index, id, quantity, equipment); +} + +Item* LocalPlayer::getInvItem(int index) +{ + return mInventory->getItem(index); +} + +void LocalPlayer::equipItem(Item *item) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PLAYER_EQUIP); + outMsg.writeInt16(item->getInvIndex()); + outMsg.writeInt16(0); +} + +void LocalPlayer::unequipItem(Item *item) +{ + if (!item) + return; + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PLAYER_UNEQUIP); + outMsg.writeInt16(item->getInvIndex()); + + // Tidy equipment directly to avoid weapon still shown bug, by instance + mEquipment->removeEquipment(item); +} + +void LocalPlayer::useItem(Item *item) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PLAYER_INVENTORY_USE); + outMsg.writeInt16(item->getInvIndex()); + outMsg.writeInt32(item->getId()); + // Note: id is dest of item, usually player_node->account_ID ?? +} + +void LocalPlayer::dropItem(Item *item, int quantity) +{ + // TODO: Fix wrong coordinates of drops, serverside? + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_PLAYER_INVENTORY_DROP); + outMsg.writeInt16(item->getInvIndex()); + outMsg.writeInt16(quantity); +} + +void LocalPlayer::pickUp(FloorItem *item) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_ITEM_PICKUP); + outMsg.writeInt32(item->getId()); +} + +void LocalPlayer::walk(Being::Direction dir) +{ + if (!mMap || dir == DIR_NONE) + return; + + if (action == WALK) + { + // Avoid sending the coordinates to the server + Being::setDestination(x, y); + return; + } + + Sint16 dx = 0, dy = 0; + switch (dir) + { + case SOUTH: + dy = 1; + break; + + case WEST: + dx = -1; + break; + + case NORTH: + dy = -1; + break; + + case EAST: + dx = 1; + break; + + case SW: + dx = -1; + dy = 1; + break; + + case NW: + dx = -1; + dy = -1; + break; + + case NE: + dx = 1; + dy = -1; + break; + + case SE: + dx = 1; + dy = 1; + break; + + default: + break; + } + + // Prevent skipping corners over colliding tiles + if (dx && mMap->tileCollides(x + dx, y)) + dx = 0; + if (dy && mMap->tileCollides(x, y + dy)) + dy = 0; + + // Choose a straight direction when diagonal target is blocked + if (dx && dy && !mMap->getWalk(x + dx, y + dy)) + dx = 0; + + // Walk to where the player can actually go + if ((dx || dy) && mMap->getWalk(x + dx, y + dy)) + { + setDestination(x + dx, y + dy); + } + else if (dir != DIR_NONE) + { + // Update the player direction to where he wants to walk + // Warning: Not communicated to the server yet + direction = dir; + } +} + +void LocalPlayer::setDestination(Uint16 x, Uint16 y) +{ + char temp[3]; + MessageOut outMsg(mNetwork); + set_coordinates(temp, x, y, direction); + outMsg.writeInt16(0x0085); + outMsg.writeString(temp, 3); + Being::setDestination(x, y); +} + +void LocalPlayer::raiseAttribute(Attribute attr) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_STAT_UPDATE_REQUEST); + + switch (attr) + { + case STR: + outMsg.writeInt16(0x000d); + break; + + case AGI: + outMsg.writeInt16(0x000e); + break; + + case VIT: + outMsg.writeInt16(0x000f); + break; + + case INT: + outMsg.writeInt16(0x0010); + break; + + case DEX: + outMsg.writeInt16(0x0011); + break; + + case LUK: + outMsg.writeInt16(0x0012); + break; + } + outMsg.writeInt8(1); +} + +void LocalPlayer::raiseSkill(Uint16 skillId) +{ + if (skillPoint <= 0) + return; + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_SKILL_LEVELUP_REQUEST); + outMsg.writeInt16(skillId); +} + +void LocalPlayer::toggleSit() +{ + char type; + switch (action) + { + case STAND: type = 2; break; + case SIT: type = 3; break; + default: return; + } + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(0x0089); + outMsg.writeInt32(0); + outMsg.writeInt8(type); +} + +void LocalPlayer::emote(Uint8 emotion) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(0x00bf); + outMsg.writeInt8(emotion); +} + +void LocalPlayer::tradeReply(bool accept) +{ + if (!accept) + mTrading = false; + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_TRADE_RESPONSE); + outMsg.writeInt8(accept ? 3 : 4); +} + +void LocalPlayer::trade(Being *being) const +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_TRADE_REQUEST); + outMsg.writeInt32(being->getId()); +} + +bool LocalPlayer::tradeRequestOk() const +{ + return !mTrading; +} + +void LocalPlayer::attack(Being *target, bool keep) +{ + // Can only attack when standing still + if (action != STAND) + return; + + if (keep && target) + mTarget = target; + else if (mTarget) + target = mTarget; + + if (!target) + return; + + int dist_x = target->x - x; + int dist_y = target->y - y; + + if (abs(dist_y) >= abs(dist_x)) + { + if (dist_y > 0) + direction = SOUTH; + else + direction = NORTH; + } + else + { + if (dist_x > 0) + direction = EAST; + else + direction = WEST; + } + + // Implement charging attacks here + lastAttackTime = 0; + + action = ATTACK; + walk_time = tick_time; + if (getWeapon() == 2) + sound.playSfx("sfx/bow_shoot_1.ogg"); + else + sound.playSfx("sfx/fist-swish.ogg"); + + MessageOut outMsg(mNetwork); + outMsg.writeInt16(0x0089); + outMsg.writeInt32(target->getId()); + outMsg.writeInt8(0); +} + +void LocalPlayer::stopAttack() +{ + mTarget = NULL; +} + +Being* LocalPlayer::getTarget() const +{ + return mTarget; +} + +void LocalPlayer::revive() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(0x00b2); + outMsg.writeInt8(0); +} diff --git a/src/localplayer.h b/src/localplayer.h new file mode 100644 index 00000000..9bd0bf37 --- /dev/null +++ b/src/localplayer.h @@ -0,0 +1,149 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_LOCALPLAYER_H +#define _TMW_LOCALPLAYER_H + +#include "player.h" + +// TODO move into some sane place... +#define MAX_SLOT 2 + +class Equipment; +class FloorItem; +class Inventory; +class Item; +class Network; + +class LocalPlayer : public Player +{ + public: + enum Attribute { + STR = 0, AGI, VIT, INT, DEX, LUK + }; + + LocalPlayer(Uint32 id, Uint16 job, Map *map); + + virtual ~LocalPlayer(); + + void setNetwork(Network *network) { mNetwork = network; } + + virtual void logic(); + + virtual Type getType() const; + + void clearInventory(); + void addInvItem(int id, int quantity, bool equipment); + void addInvItem(int index, int id, int quantity, bool equipment); + Item* getInvItem(int index); + + /** + * Equips an item. + */ + void equipItem(Item *item); + + /** + * Unequips an item. + */ + void unequipItem(Item *item); + + void useItem(Item *item); + void dropItem(Item *item, int quantity); + void pickUp(FloorItem *item); + + /** + * Sents a trade request to the given being. + */ + void trade(Being *being) const; + + /** + * Accept or decline a trade offer + */ + void tradeReply(bool accept); + + /** + * Returns true when the player is ready to accept a trade offer. + * Returns false otherwise. + */ + bool tradeRequestOk() const; + + /** + * Sets the trading state of the player, i.e. whether or not he is + * currently involved into some trade. + */ + void setTrading(bool trading) { mTrading = trading; }; + + void attack(Being *target=NULL, bool keep=false); + void stopAttack(); + Being* getTarget() const; + + void walk(Being::Direction dir); + + /** + * Sets a new destination for this being to walk to. + */ + virtual void setDestination(Uint16 x, Uint16 y); + + void raiseAttribute(Attribute attr); + void raiseSkill(Uint16 skillId); + + void toggleSit(); + void emote(Uint8 emotion); + + void revive(); + + Uint32 mLoginId; + + Uint32 xp, jobXp; + Uint16 lvl; + Uint32 jobLvl; + Uint32 xpForNextLevel, jobXpForNextLevel; + Uint16 hp, maxHp, mp, maxMp; + Uint32 gp; + + Uint32 totalWeight, maxWeight; + + Uint8 ATTR[6]; + Uint8 ATTR_UP[6]; + + Sint16 ATK, MATK, DEF, MDEF, HIT, FLEE; + Sint16 ATK_BONUS, MATK_BONUS, DEF_BONUS, MDEF_BONUS, FLEE_BONUS; + + Uint16 statPoint, skillPoint; + Uint16 statsPointsToAttribute; + + float lastAttackTime; /**< Used to synchronize the charge dialog */ + + Inventory *mInventory; + Equipment *mEquipment; + + protected: + Network *mNetwork; + Being *mTarget; + + bool mTrading; +}; + +extern LocalPlayer *player_node; + +#endif diff --git a/src/lockedarray.h b/src/lockedarray.h new file mode 100644 index 00000000..53b111bd --- /dev/null +++ b/src/lockedarray.h @@ -0,0 +1,110 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_LOCKEDARRAY_H +#define _TMW_LOCKEDARRAY_H + +/** + * A _very_ basic array class that allows simple iteration and jumps, keeping + * its currently selected entry and providing a mechanism to lock this + * position. Anyone can unlock it though, so its your job to use it the right + * way ;) + */ + +template<class T> +class LockedArray +{ + public: + LockedArray(unsigned int size); + ~LockedArray(); + + void lock() { mLocked = true; }; + void unlock() { mLocked = false; }; + + bool isLocked() const { return mLocked; }; + + T getEntry() const { return mData[mCurEntry]; }; + void setEntry(T entry) { mData[mCurEntry] = entry; }; + + void next(); + void prev(); + void select(unsigned int pos); + unsigned int getPos() const { return mCurEntry; } + + unsigned int getSize() const { return mSize; }; + + protected: + unsigned int mSize; + + T* mData; + + unsigned int mCurEntry; + bool mLocked; +}; + +template<class T> +LockedArray<T>::LockedArray(unsigned int size): + mSize(size), mData(new T[size]), mCurEntry(0), mLocked(false) +{ + for (unsigned int i = 0; i < mSize; i++) + mData[i] = 0; +} + +template<class T> +LockedArray<T>::~LockedArray() +{ + if (mData) + delete [] mData; +} + +template<class T> +void LockedArray<T>::next() +{ + if (mLocked) + return; + + if (++mCurEntry == mSize) + mCurEntry = 0; +} + +template<class T> +void LockedArray<T>::prev() +{ + if (mLocked) + return; + + mCurEntry = mCurEntry ? (--mCurEntry) : (mSize - 1); +} + +template<class T> +void LockedArray<T>::select(unsigned int pos) +{ + if (mLocked) + return; + + mCurEntry = pos; + if (mCurEntry >= mSize) + mCurEntry = 0; +} + +#endif diff --git a/src/logindata.h b/src/logindata.h new file mode 100644 index 00000000..fa4628a1 --- /dev/null +++ b/src/logindata.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_LOGINDATA_H +#define _TMW_LOGINDATA_H + +struct LoginData +{ + std::string username; + std::string password; + std::string hostname; + short port; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index f1f7cd4f..c9320713 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,10 +41,14 @@ #include "configuration.h" #include "game.h" #include "graphics.h" +#include "lockedarray.h" +#include "localplayer.h" #include "log.h" +#include "logindata.h" #ifdef USE_OPENGL #include "openglgraphics.h" #endif +#include "serverinfo.h" #include "sound.h" #include "graphic/spriteset.h" @@ -60,18 +64,24 @@ #include "gui/updatewindow.h" #include "gui/textfield.h" +#include "net/charserverhandler.h" +#include "net/loginhandler.h" +#include "net/maploginhandler.h" +#include "net/messageout.h" +#include "net/network.h" + #include "resources/image.h" #include "resources/resourcemanager.h" // Account infos int account_ID, session_ID1, session_ID2; char sex, n_server, n_character; -PLAYER_INFO **char_info; -PLAYER_INFO *player_info; Spriteset *hairset = NULL, *playerset = NULL; Graphics *graphics; +SERVER_INFO **server_info; + int map_address, char_ID; short map_port; unsigned char state; @@ -393,6 +403,69 @@ void parseOptions(int argc, char *argv[], Options &options) } } +CharServerHandler charServerHandler; +LoginData accountLoginData; +LoginHandler loginHandler; +LockedArray<LocalPlayer*> charInfo(MAX_SLOT + 1); +MapLoginHandler mapLoginHandler; + +// TODO Find some nice place for these functions +void accountLogin(Network *network, LoginData *loginData) +{ + logger->log("Trying to connect to account server..."); + network->connect(loginData->hostname.c_str(), loginData->port); + network->registerHandler(&loginHandler); + + // Send login infos + MessageOut outMsg(network); + outMsg.writeInt16(0x0064); + outMsg.writeInt32(0); // client version + outMsg.writeString(loginData->username, 24); + outMsg.writeString(loginData->password, 24); + outMsg.writeInt8(0); // unknown +} + +void charLogin(Network *network, const SERVER_INFO *si) +{ + logger->log("Trying to connect to char server..."); + network->connect(iptostring(si->address), si->port); + network->registerHandler(&charServerHandler); + charServerHandler.setCharInfo(&charInfo); + + // Send login infos + MessageOut outMsg(network); + outMsg.writeInt16(0x0065); + outMsg.writeInt32(account_ID); + outMsg.writeInt32(session_ID1); + outMsg.writeInt32(session_ID2); + outMsg.writeInt16(0); // unknown + outMsg.writeInt8(sex); + + // We get 4 useless bytes before the real answer comes in + network->skip(4); +} + +void mapLogin(Network *network) +{ + const char *host = iptostring(map_address); + MessageOut outMsg(network); + + logger->log("Trying to connect to map server..."); + network->connect(host, map_port); + network->registerHandler(&mapLoginHandler); + + // Send login infos + outMsg.writeInt16(0x0072); + outMsg.writeInt32(account_ID); + outMsg.writeInt32(char_ID); + outMsg.writeInt32(session_ID1); + outMsg.writeInt32(session_ID2); + outMsg.writeInt8(sex); + + // We get 4 useless bytes before the real answer comes in + network->skip(4); +} + /** Main */ int main(int argc, char *argv[]) { @@ -434,12 +507,13 @@ int main(int argc, char *argv[]) unsigned int oldstate = !state; // We start with a status change. Window *currentDialog = NULL; - void (*inputHandler)(SDL_KeyboardEvent*) = NULL; Image *login_wallpaper = NULL; sound.playMusic(TMW_DATADIR "data/music/Magick - Real.ogg"); + SDLNet_Init(); + Network *network = new Network(); while (state != EXIT_STATE) { // Handle SDL events @@ -450,9 +524,8 @@ int main(int argc, char *argv[]) break; case SDL_KEYDOWN: - if (inputHandler) { - inputHandler(&event.key); - } + if (event.key.keysym.sym == SDLK_ESCAPE) + state = EXIT_STATE; break; } @@ -460,6 +533,14 @@ int main(int argc, char *argv[]) } gui->logic(); + network->flush(); + network->dispatchMessages(); + + if (network->getState() == Network::ERROR) + { + state = ERROR_STATE; + errorMessage = "Got disconnected from server!"; + } if (!login_wallpaper) { @@ -480,22 +561,35 @@ int main(int argc, char *argv[]) graphics->updateScreen(); if (state != oldstate) { - if (oldstate == UPDATE_STATE) { - ResourceManager::getInstance()-> - searchAndAddArchives("/updates", ".zip", 0); + switch (oldstate) + { + case UPDATE_STATE: + ResourceManager::getInstance()-> + searchAndAddArchives("/updates", ".zip", 0); + break; + + // Those states don't cause a network disconnect + case ACCOUNT_STATE: + case CHAR_CONNECT_STATE: + case CONNECTING_STATE: + break; + + default: + network->disconnect(); + network->clearHandlers(); + break; } oldstate = state; - if (currentDialog) { + if (currentDialog && state != ACCOUNT_STATE && state != CHAR_CONNECT_STATE) { delete currentDialog; } switch (state) { case LOGIN_STATE: logger->log("State: LOGIN"); - currentDialog = new LoginDialog(); - inputHandler = loginInputHandler; + currentDialog = new LoginDialog(&accountLoginData); if (!options.username.empty()) { LoginDialog *loginDialog = (LoginDialog*)currentDialog; @@ -507,14 +601,12 @@ int main(int argc, char *argv[]) case REGISTER_STATE: logger->log("State: REGISTER"); - currentDialog = new RegisterDialog(); - inputHandler = loginInputHandler; + currentDialog = new RegisterDialog(&accountLoginData); break; case CHAR_SERVER_STATE: logger->log("State: CHAR_SERVER"); currentDialog = new ServerSelectDialog(); - inputHandler = charServerInputHandler; if (options.chooseDefault) { ((ServerSelectDialog*)currentDialog)->action("ok"); } @@ -522,8 +614,7 @@ int main(int argc, char *argv[]) case CHAR_SELECT_STATE: logger->log("State: CHAR_SELECT"); - currentDialog = new CharSelectDialog(); - inputHandler = charSelectInputHandler; + currentDialog = new CharSelectDialog(network, &charInfo); if (options.chooseDefault) { ((CharSelectDialog*)currentDialog)->action("ok"); } @@ -533,29 +624,40 @@ int main(int argc, char *argv[]) sound.fadeOutMusic(1000); currentDialog = NULL; - inputHandler = NULL; login_wallpaper->decRef(); login_wallpaper = NULL; logger->log("State: GAME"); - game(); + game(network); + state = EXIT_STATE; break; case UPDATE_STATE: logger->log("State: UPDATE"); currentDialog = new UpdaterWindow(); - inputHandler = updateInputHandler; break; + case ERROR_STATE: logger->log("State: ERROR"); currentDialog = new ErrorDialog(errorMessage); - inputHandler = errorInputHandler; + network->disconnect(); + network->clearHandlers(); break; + case CONNECTING_STATE: logger->log("State: CONNECTING"); + mapLogin(network); currentDialog = new ConnectionDialog(); - inputHandler = connectionInputHandler; break; + + case CHAR_CONNECT_STATE: + charLogin(network, ((ServerSelectDialog*)currentDialog)->getServerInfo()); + break; + + case ACCOUNT_STATE: + accountLogin(network, &accountLoginData); + break; + default: state = EXIT_STATE; break; @@ -563,6 +665,9 @@ int main(int argc, char *argv[]) } } + delete network; + SDLNet_Quit(); + if (nullFile) { fclose(nullFile); @@ -26,7 +26,6 @@ #include <string> -class Image; class Sound; #if (defined __USE_UNIX98 || defined __FreeBSD__) @@ -44,7 +43,9 @@ class Sound; enum { EXIT_STATE, LOGIN_STATE, + ACCOUNT_STATE, REGISTER_STATE, + CHAR_CONNECT_STATE, CHAR_SERVER_STATE, CHAR_SELECT_STATE, CHAR_NEW_STATE, diff --git a/src/map.cpp b/src/map.cpp index 5150bcff..248c07d0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -26,11 +26,12 @@ #include <algorithm> #include <queue> -#include "tileset.h" -#include "being.h" +#include "beingmanager.h" #include "graphics.h" -#include "resources/image.h" #include "sprite.h" +#include "tileset.h" + +#include "resources/image.h" MetaTile::MetaTile(): whichList(0) @@ -217,8 +218,9 @@ Map::getWalk(int x, int y) } // Check for collision with a being - std::list<Being*>::iterator i = beings.begin(); - for (i = beings.begin(); i != beings.end(); i++) { + Beings *beings = beingManager->getAll(); + Beings::iterator i; + for (i = beings->begin(); i != beings->end(); i++) { // job 45 is a portal, they don't collide if ((*i)->x == x && (*i)->y == y && (*i)->job != 45) { return false; diff --git a/src/monster.cpp b/src/monster.cpp new file mode 100644 index 00000000..f3b58d46 --- /dev/null +++ b/src/monster.cpp @@ -0,0 +1,101 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "monster.h" + +#include <sstream> + +#include "game.h" +#include "graphics.h" +#include "log.h" + +#include "graphic/spriteset.h" + +#include "resources/resourcemanager.h" + +extern std::map<int, Spriteset*> monsterset; + +Monster::Monster(Uint32 id, Uint16 job, Map *map): + Being(id, job, map) +{ + // Load monster spriteset, if necessary + if (monsterset.find(job - 1002) == monsterset.end()) + { + std::stringstream filename; + + filename << "graphics/sprites/monster" << (job - 1002) << ".png"; + logger->log("%s",filename.str().c_str()); + + Spriteset *tmp = ResourceManager::getInstance()->createSpriteset( + filename.str(), 60, 60); + if (!tmp) { + logger->error("Unable to load monster spriteset!"); + } else { + monsterset[job - 1002] = tmp; + } + } +} + +void Monster::logic() +{ + if (action != STAND) + { + mFrame = (get_elapsed_time(walk_time) * 4) / mWalkSpeed; + + if (mFrame >= 4 && action != MONSTER_DEAD) + { + nextStep(); + } + } + + Being::logic(); +} + +Being::Type Monster::getType() const +{ + return MONSTER; +} + +void Monster::draw(Graphics *graphics, int offsetX, int offsetY) +{ + unsigned char dir = direction / 2; + int px = mPx + offsetX; + int py = mPy + offsetY; + int frame; + + if (mFrame >= 4) + { + mFrame = 3; + } + + frame = action; + if (action != MONSTER_DEAD) { + frame += mFrame; + } + + graphics->drawImage( + monsterset[job-1002]->spriteset[dir + 4 * frame], + px - 12, py - 25); + + Being::draw(graphics, offsetX, offsetY); +} diff --git a/src/playerinfo.h b/src/monster.h index 0a5b1de8..6f3d2a89 100644 --- a/src/playerinfo.h +++ b/src/monster.h @@ -21,31 +21,21 @@ * $Id$ */ -#ifndef _TMW_PLAYERINFO_ -#define _TMW_PLAYERINFO_ +#ifndef _TMW_MONSTER_H +#define _TMW_MONSTER_H -#define MAX_SLOT 2 +#include "being.h" -#include <string> - -struct PLAYER_INFO +class Monster : public Being { - int id; - float lastAttackTime; /**< Used to synchronize the charge dialog */ - std::string name; /**< Player name */ - short hp, maxHp, mp, maxMp, lvl; - short statsPointsToAttribute; - int xp, xpForNextLevel, gp, jobXp, jobXpForNextLevel, jobLvl; - short statPoint, skillPoint, hairColor, hairStyle; - char STR, AGI, VIT, INT, DEX, LUK; - char STRUp, AGIUp, VITUp, INTUp, DEXUp, LUKUp; - int ATK, ATKBonus, MATK, MATKBonus, DEF, DEFBonus, MDEF; - int MDEFBonus, HIT, FLEE, FLEEBonus; - int totalWeight, maxWeight; - short weapon; -}; + public: + Monster(Uint32 id, Uint16 job, Map *map); + + virtual void logic(); -extern PLAYER_INFO **char_info; -extern PLAYER_INFO *player_info; + virtual Type getType() const; + + virtual void draw(Graphics *graphics, int offsetX, int offsetY); +}; #endif diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp new file mode 100644 index 00000000..9578f5c1 --- /dev/null +++ b/src/net/beinghandler.cpp @@ -0,0 +1,356 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "beinghandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../game.h" +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" +#include "../sound.h" + +const int EMOTION_TIME = 150; /**< Duration of emotion icon */ + +BeingHandler::BeingHandler() +{ + static const Uint16 _messages[] = { + SMSG_BEING_VISIBLE, + SMSG_BEING_MOVE, + SMSG_BEING_REMOVE, + SMSG_BEING_ACTION, + SMSG_BEING_LEVELUP, + SMSG_BEING_EMOTION, + SMSG_BEING_CHANGE_LOOKS, + SMSG_BEING_NAME_RESPONSE, + SMSG_PLAYER_UPDATE_1, + SMSG_PLAYER_UPDATE_2, + SMSG_PLAYER_MOVE, + 0x0119, + 0 + }; + handledMessages = _messages; +} + +void BeingHandler::handleMessage(MessageIn *msg) +{ + Uint32 id; + Uint16 job, speed; + Sint16 param1; + Sint8 type; + Being *srcBeing, *dstBeing; + + switch (msg->getId()) + { + case SMSG_BEING_VISIBLE: + case SMSG_BEING_MOVE: + // Information about a being in range + id = msg->readInt32(); + speed = msg->readInt16(); + msg->readInt16(); // unknown + msg->readInt16(); // unknown + msg->readInt16(); // option + job = msg->readInt16(); // class + + dstBeing = beingManager->findBeing(id); + + if (dstBeing == NULL) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && id >= 110000000) + { + break; + } + + dstBeing = beingManager->createBeing(id, job); + } + else if (msg->getId() == 0x0078) + { + dstBeing->clearPath(); + dstBeing->mFrame = 0; + dstBeing->walk_time = tick_time; + dstBeing->action = Being::STAND; + } + + // Prevent division by 0 when calculating frame + if (speed == 0) { speed = 150; } + + dstBeing->setWalkSpeed(speed); + dstBeing->job = job; + dstBeing->setHairStyle(msg->readInt16()); + dstBeing->setWeapon(msg->readInt16()); + msg->readInt16(); // head option bottom + + if (msg->getId() == SMSG_BEING_MOVE) + { + msg->readInt32(); // server tick + } + + msg->readInt16(); // shield + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + dstBeing->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + msg->readInt16(); // head dir + msg->readInt16(); // guild + msg->readInt16(); // unknown + msg->readInt16(); // unknown + msg->readInt16(); // manner + msg->readInt16(); // karma + msg->readInt8(); // unknown + msg->readInt8(); // sex + + if (msg->getId() == SMSG_BEING_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg->readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->action = Being::STAND; + dstBeing->x = srcX; + dstBeing->y = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + msg->readCoordinates(dstBeing->x, dstBeing->y, + dstBeing->direction); + } + + msg->readInt8(); // unknown + msg->readInt8(); // unknown + msg->readInt8(); // unknown / sit + break; + + case SMSG_BEING_REMOVE: + // A being should be removed or has died + dstBeing = beingManager->findBeing(msg->readInt32()); + + if (!dstBeing) + break; + + if (msg->readInt8() == 1) + { + // Death + switch (dstBeing->getType()) + { + case Being::MONSTER: + dstBeing->action = Being::MONSTER_DEAD; + dstBeing->mFrame = 0; + dstBeing->walk_time = tick_time; + break; + + default: + dstBeing->action = Being::DEAD; + break; + } + } + else + { + beingManager->destroyBeing(dstBeing); + } + + if (dstBeing == player_node->getTarget()) + { + player_node->stopAttack(); + } + break; + + case SMSG_BEING_ACTION: + srcBeing = beingManager->findBeing(msg->readInt32()); + dstBeing = beingManager->findBeing(msg->readInt32()); + msg->readInt32(); // server tick + msg->readInt32(); // src speed + msg->readInt32(); // dst speed + param1 = msg->readInt16(); + msg->readInt16(); // param 2 + type = msg->readInt8(); + msg->readInt16(); // param 3 + + switch (type) + { + case 0: // Damage + if (dstBeing == NULL) break; + + dstBeing->setDamage(param1, SPEECH_TIME); + + if (srcBeing != NULL && + srcBeing != player_node) + { + // buggy + srcBeing->action = Being::ATTACK; + srcBeing->mFrame = 0; + srcBeing->walk_time = tick_time; + } + break; + + case 2: // Sit + if (srcBeing == NULL) break; + srcBeing->mFrame = 0; + srcBeing->action = Being::SIT; + break; + + case 3: // Stand up + if (srcBeing == NULL) break; + srcBeing->mFrame = 0; + srcBeing->action = Being::STAND; + break; + } + break; + + case SMSG_BEING_LEVELUP: + if ((Uint32)msg->readInt32() == player_node->getId()) { + logger->log("Level up"); + sound.playSfx("sfx/levelup.ogg"); + } else { + logger->log("Someone else went level up"); + } + msg->readInt32(); // type + break; + + case SMSG_BEING_EMOTION: + if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) + { + break; + } + + dstBeing->emotion = msg->readInt8(); + dstBeing->emotion_time = EMOTION_TIME; + break; + + case SMSG_BEING_CHANGE_LOOKS: + if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) + { + break; + } + + switch (msg->readInt8()) { + case 1: + dstBeing->setHairStyle(msg->readInt8()); + break; + case 2: + dstBeing->setWeapon(msg->readInt8()); + break; + case 6: + dstBeing->setHairColor(msg->readInt8()); + break; + default: + msg->readInt8(); // unsupported + break; + } + break; + + case SMSG_BEING_NAME_RESPONSE: + if ((dstBeing = beingManager->findBeing(msg->readInt32()))) + { + dstBeing->setName(msg->readString(24)); + } + break; + + case SMSG_PLAYER_UPDATE_1: + case SMSG_PLAYER_UPDATE_2: + case SMSG_PLAYER_MOVE: + // An update about a player, potentially including movement. + id = msg->readInt32(); + speed = msg->readInt16(); + msg->readInt16(); // option 1 + msg->readInt16(); // option 2 + msg->readInt16(); // option + job = msg->readInt16(); + + dstBeing = beingManager->findBeing(id); + + if (dstBeing == NULL) + { + dstBeing = beingManager->createBeing(id, job); + } + + dstBeing->setWalkSpeed(speed); + dstBeing->job = job; + dstBeing->setHairStyle(msg->readInt16()); + dstBeing->setWeaponById(msg->readInt16()); // item id 1 + msg->readInt16(); // item id 2 + msg->readInt16(); // head option bottom + + if (msg->getId() == SMSG_PLAYER_MOVE) + { + msg->readInt32(); // server tick + } + + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + dstBeing->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + msg->readInt16(); // head dir + msg->readInt32(); // guild + msg->readInt32(); // emblem + msg->readInt16(); // manner + msg->readInt8(); // karma + msg->readInt8(); // sex + + if (msg->getId() == SMSG_PLAYER_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg->readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->x = srcX; + dstBeing->y = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + msg->readCoordinates(dstBeing->x, dstBeing->y, + dstBeing->direction); + } + + msg->readInt8(); // unknown + msg->readInt8(); // unknown + + if (msg->getId() == SMSG_PLAYER_UPDATE_1) + { + if (msg->readInt8() == 2) + { + dstBeing->action = Being::SIT; + } + } + else if (msg->getId() == SMSG_PLAYER_MOVE) + { + msg->readInt8(); // unknown + } + + msg->readInt8(); // Lv + msg->readInt8(); // unknown + + dstBeing->walk_time = tick_time; + dstBeing->mFrame = 0; + break; + + case 0x0119: + // Change in players look + break; + } +} diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h new file mode 100644 index 00000000..03012f39 --- /dev/null +++ b/src/net/beinghandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_BEINGHANDLER_H +#define _TMW_NET_BEINGHANDLER_H + +#include "messagehandler.h" + +class BeingHandler : public MessageHandler +{ + public: + BeingHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/buysellhandler.cpp b/src/net/buysellhandler.cpp new file mode 100644 index 00000000..501762ad --- /dev/null +++ b/src/net/buysellhandler.cpp @@ -0,0 +1,129 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "buysellhandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../beingmanager.h" +#include "../item.h" +#include "../localplayer.h" +#include "../npc.h" + +#include "../gui/buy.h" +#include "../gui/chat.h" +#include "../gui/sell.h" + +extern BuyDialog *buyDialog; +extern SellDialog *sellDialog; +extern Window *buySellDialog; + +BuySellHandler::BuySellHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_BUY_SELL_CHOICE, + SMSG_NPC_BUY, + SMSG_NPC_SELL, + SMSG_NPC_BUY_RESPONSE, + SMSG_NPC_SELL_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void BuySellHandler::handleMessage(MessageIn *msg) +{ + int n_items; + switch (msg->getId()) + { + case SMSG_NPC_BUY_SELL_CHOICE: + buyDialog->setVisible(false); + buyDialog->reset(); + sellDialog->setVisible(false); + sellDialog->reset(); + buySellDialog->setVisible(true); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + break; + + case SMSG_NPC_BUY: + msg->readInt16(); // length + n_items = (msg->getLength() - 4) / 11; + buyDialog->reset(); + buyDialog->setMoney(player_node->gp); + buyDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint32 value = msg->readInt32(); + msg->readInt32(); // DCvalue + msg->readInt8(); // type + Sint16 itemId = msg->readInt16(); + buyDialog->addItem(itemId, value); + } + break; + + case SMSG_NPC_SELL: + msg->readInt16(); // length + n_items = (msg->getLength() - 4) / 10; + if (n_items > 0) { + sellDialog->reset(); + sellDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint16 index = msg->readInt16(); + Sint32 value = msg->readInt32(); + msg->readInt32(); // OCvalue + + Item *item = player_node->getInvItem(index); + if (item && !(item->isEquipped())) { + sellDialog->addItem(item, value); + } + } + } + else { + chatWindow->chatLog("Nothing to sell", BY_SERVER); + current_npc = 0; + } + break; + + case SMSG_NPC_BUY_RESPONSE: + if (msg->readInt8() == 0) { + chatWindow->chatLog("Thanks for buying", BY_SERVER); + } else { + chatWindow->chatLog("Unable to buy", BY_SERVER); + } + break; + + case SMSG_NPC_SELL_RESPONSE: + if (msg->readInt8() == 0) { + chatWindow->chatLog("Thanks for selling", BY_SERVER); + } else { + chatWindow->chatLog("Unable to sell", BY_SERVER); + } + break; + } +} diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h new file mode 100644 index 00000000..673aaac1 --- /dev/null +++ b/src/net/buysellhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_BUYSELLHANDLER_H +#define _TMW_NET_BUYSELLHANDLER_H + +#include "messagehandler.h" + +class BuySellHandler : public MessageHandler +{ + public: + BuySellHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp new file mode 100644 index 00000000..a60e39b0 --- /dev/null +++ b/src/net/charserverhandler.cpp @@ -0,0 +1,210 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "charserverhandler.h" + +#include "messagein.h" +#include "network.h" +#include "protocol.h" + +#include "../game.h" +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" + +#include "../gui/ok_dialog.h" + +CharServerHandler::CharServerHandler() +{ + static const Uint16 _messages[] = { + 0x006b, + 0x006c, + 0x006d, + 0x006e, + 0x006f, + 0x0070, + 0x0071, + 0x0081, + 0 + }; + handledMessages = _messages; +} + +void CharServerHandler::handleMessage(MessageIn *msg) +{ + int slot; + LocalPlayer *tempPlayer; + + logger->log("CharServerHandler: Packet ID: %x, Length: %d", + msg->getId(), msg->getLength()); + switch (msg->getId()) + { + case 0x006b: + // Skip length word and an additional mysterious 20 bytes + msg->skip(2 + 20); + + // Derive number of characters from message length + n_character = (msg->getLength() - 24) / 106; + + for (int i = 0; i < n_character; i++) + { + tempPlayer = readPlayerData(msg, slot); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + logger->log("CharServer: Player: %s (%d)", + tempPlayer->getName().c_str(), slot); + } + + state = CHAR_SELECT_STATE; + break; + + case 0x006c: + switch (msg->readInt8()) { + case 0: + errorMessage = "Access denied"; + break; + case 1: + errorMessage = "Cannot use this ID"; + break; + default: + errorMessage = "Unknown failure to select character"; + break; + } + mCharInfo->unlock(); + break; + + case 0x006d: + tempPlayer = readPlayerData(msg, slot); + mCharInfo->unlock(); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + n_character++; + break; + + case 0x006e: + new OkDialog(NULL, "Error", "Failed to create character"); + break; + + case 0x006f: + delete mCharInfo->getEntry(); + mCharInfo->setEntry(0); + mCharInfo->unlock(); + n_character--; + new OkDialog(NULL, "Info", "Player deleted"); + break; + + case 0x0070: + mCharInfo->unlock(); + new OkDialog(NULL, "Error", "Failed to delete character."); + break; + + case 0x0071: + char_ID = msg->readInt32(); + map_path = msg->readString(16); + map_address = msg->readInt32(); + map_port = msg->readInt16(); + player_node = mCharInfo->getEntry(); + mCharInfo->unlock(); + mCharInfo->select(0); + // Clear unselected players infos + do + { + LocalPlayer *tmp = mCharInfo->getEntry(); + if (tmp != player_node) + delete tmp; + mCharInfo->next(); + } while (mCharInfo->getPos()); + + state = CONNECTING_STATE; + + logger->log("CharSelect: Map: %s", map_path.c_str()); + logger->log("CharSelect: Server: %s:%i", iptostring(map_address), + map_port); + break; + + case 0x0081: + switch (msg->readInt8()) { + case 1: + errorMessage = "Map server offline"; + break; + case 3: + errorMessage = "Speed hack detected"; + break; + case 8: + errorMessage = "Duplicated login"; + break; + default: + errorMessage = "Unkown error with 0x0081"; + break; + } + mCharInfo->unlock(); + state = ERROR_STATE; + break; + } +} + +LocalPlayer* CharServerHandler::readPlayerData(MessageIn *msg, int &slot) +{ + LocalPlayer *tempPlayer = new LocalPlayer(account_ID, 0, NULL); + + tempPlayer->mLoginId = msg->readInt32(); + tempPlayer->totalWeight = 0; + tempPlayer->maxWeight = 0; + tempPlayer->lastAttackTime = 0; + tempPlayer->xp = msg->readInt32(); + tempPlayer->gp = msg->readInt32(); + tempPlayer->jobXp = msg->readInt32(); + tempPlayer->jobLvl = msg->readInt32(); + msg->skip(8); // unknown + msg->readInt32(); // option + msg->readInt32(); // karma + msg->readInt32(); // manner + msg->skip(2); // unknown + tempPlayer->hp = msg->readInt16(); + tempPlayer->maxHp = msg->readInt16(); + tempPlayer->mp = msg->readInt16(); + tempPlayer->maxMp = msg->readInt16(); + msg->readInt16(); // speed + msg->readInt16(); // class + tempPlayer->setHairStyle(msg->readInt16()); + Uint16 weapon = msg->readInt16(); + if (weapon == 11) + weapon = 2; + tempPlayer->setWeapon(weapon); + tempPlayer->lvl = msg->readInt16(); + msg->readInt16(); // skill point + msg->readInt16(); // head bottom + msg->readInt16(); // shield + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + tempPlayer->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + tempPlayer->setName(msg->readString(24)); + for (int i = 0; i < 6; i++) { + tempPlayer->ATTR[i] = msg->readInt8(); + } + slot = msg->readInt8(); // character slot + msg->readInt8(); // unknown + + return tempPlayer; +} diff --git a/src/net/charserverhandler.h b/src/net/charserverhandler.h new file mode 100644 index 00000000..21178377 --- /dev/null +++ b/src/net/charserverhandler.h @@ -0,0 +1,48 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_CHARSERVERHANDLER_H +#define _TMW_NET_CHARSERVERHANDLER_H + +#include "messagehandler.h" + +#include "../lockedarray.h" + +class LocalPlayer; + +class CharServerHandler : public MessageHandler +{ + public: + CharServerHandler(); + + void handleMessage(MessageIn *msg); + + void setCharInfo(LockedArray<LocalPlayer*> *charInfo) { mCharInfo = charInfo; }; + + protected: + LockedArray<LocalPlayer*> *mCharInfo; + + LocalPlayer* readPlayerData(MessageIn *msg, int &slot); +}; + +#endif diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp new file mode 100644 index 00000000..97e8186d --- /dev/null +++ b/src/net/chathandler.cpp @@ -0,0 +1,118 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "chathandler.h" + +#include <SDL_types.h> +#include <string> +#include <sstream> + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../game.h" + +#include "../gui/chat.h" + +extern Being *player_node; + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = { + SMSG_BEING_CHAT, + SMSG_PLAYER_CHAT, + SMSG_GM_CHAT, + SMSG_WHO_ANSWER, + 0x10c, // MVP + 0 + }; + handledMessages = _messages; +} + +void ChatHandler::handleMessage(MessageIn *msg) +{ + Being *being; + std::string chatMsg; + std::stringstream ss; + Sint16 chatMsgLength; + + switch (msg->getId()) + { + // Received speech from being + case SMSG_BEING_CHAT: + chatMsgLength = msg->readInt16() - 8; + being = beingManager->findBeing(msg->readInt32()); + + if (!being || chatMsgLength <= 0) + { + break; + } + + chatMsg = msg->readString(chatMsgLength); + chatWindow->chatLog(chatMsg, BY_OTHER); + chatMsg.erase(0, chatMsg.find(" : ", 0) + 3); + being->setSpeech(chatMsg, SPEECH_TIME); + break; + + case SMSG_PLAYER_CHAT: + case SMSG_GM_CHAT: + chatMsgLength = msg->readInt16() - 4; + + if (chatMsgLength <= 0) + { + break; + } + + chatMsg = msg->readString(chatMsgLength); + + if (msg->getId() == SMSG_PLAYER_CHAT) + { + chatWindow->chatLog(chatMsg, BY_PLAYER); + + std::string::size_type pos = chatMsg.find(" : ", 0); + if (pos != std::string::npos) + { + chatMsg.erase(0, pos + 3); + } + player_node->setSpeech(chatMsg, SPEECH_TIME); + } + else + { + chatWindow->chatLog(chatMsg, BY_GM); + } + break; + + case SMSG_WHO_ANSWER: + ss << "Online users: " << msg->readInt32(); + chatWindow->chatLog(ss.str(), BY_SERVER); + break; + + case 0x010c: + // Display MVP player + msg->readInt32(); // id + chatWindow->chatLog("MVP player", BY_SERVER); + break; + } +} diff --git a/src/net/chathandler.h b/src/net/chathandler.h new file mode 100644 index 00000000..eed19206 --- /dev/null +++ b/src/net/chathandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_CHATHANDLER_H +#define _TMW_NET_CHATHANDLER_H + +#include "messagehandler.h" + +class ChatHandler : public MessageHandler +{ + public: + ChatHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp new file mode 100644 index 00000000..437b5f3e --- /dev/null +++ b/src/net/equipmenthandler.cpp @@ -0,0 +1,210 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "equipmenthandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../equipment.h" +#include "../item.h" +#include "../localplayer.h" +#include "../log.h" + +#include "../gui/chat.h" + +EquipmentHandler::EquipmentHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_EQUIPMENT, + SMSG_PLAYER_EQUIP, + 0x01d7, + SMSG_PLAYER_UNEQUIP, + SMSG_PLAYER_ARROW_EQUIP, + 0 + }; + handledMessages = _messages; +} + +void EquipmentHandler::handleMessage(MessageIn *msg) +{ + Sint32 itemCount; + Sint16 index, equipPoint, itemId; + Sint8 type; + int mask, position; + Being *being; + Item *item; + + switch (msg->getId()) + { + case SMSG_PLAYER_EQUIPMENT: + msg->readInt16(); // length + itemCount = (msg->getLength() - 4) / 20; + + for (int loop = 0; loop < itemCount; loop++) + { + index = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // type + msg->readInt8(); // identify flag + msg->readInt16(); // equip type + equipPoint = msg->readInt16(); + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card + + player_node->addInvItem(index, itemId, 1, true); + + if (equipPoint) + { + mask = 1; + position = 0; + while (!(equipPoint & mask)) + { + mask <<= 1; + position++; + } + item = player_node->getInvItem(index); + player_node->mEquipment->setEquipment(position - 1, item); + } + } + break; + + case SMSG_PLAYER_EQUIP: + index = msg->readInt16(); + equipPoint = msg->readInt16(); + type = msg->readInt8(); + + logger->log("Equipping: %i %i %i", index, equipPoint, type); + + if (!type) { + chatWindow->chatLog("Unable to equip.", BY_SERVER); + break; + } + + if (!equipPoint) { + // No point given, no point in searching + break; + } + + // Unequip any existing equipped item in this position + mask = 1; + position = 0; + while (!(equipPoint & mask)) { + mask <<= 1; + position++; + } + logger->log("Position %i", position - 1); + item = player_node->mEquipment->getEquipment(position - 1); + if (item) { + item->setEquipped(false); + } + + item = player_node->getInvItem(index); + player_node->mEquipment->setEquipment(position - 1, item); + player_node->setWeaponById(item->getId()); + break; + + case 0x01d7: + // Equipment related + being = beingManager->findBeing(msg->readInt32()); + msg->readInt8(); // equip point + itemId = msg->readInt16(); + msg->readInt16(); // item id 2 + + if (!being) + break; + + being->setWeaponById(itemId); + break; + + case SMSG_PLAYER_UNEQUIP: + index = msg->readInt16(); + equipPoint = msg->readInt16(); + type = msg->readInt8(); + + if (!type) { + chatWindow->chatLog("Unable to unequip.", BY_SERVER); + break; + } + + if (!equipPoint) { + // No point given, no point in searching + break; + } + + mask = 1; + position = 0; + while (!(equipPoint & mask)) { + mask <<= 1; + position++; + } + + item = player_node->getInvItem(index); + + if (!item) + break; + + item->setEquipped(false); + + switch (item->getId()) { + case 529: + case 1199: + player_node->mEquipment->setArrows(NULL); + break; + case 521: + case 522: + case 530: + case 536: + case 1200: + case 1201: + player_node->setWeapon(0); + // TODO: Why this break? Shouldn't a weapon be + // unequipped in inventory too? + break; + default: + player_node->mEquipment->removeEquipment(position - 1); + break; + } + logger->log("Unequipping: %i %i(%i) %i", + index, equipPoint, type, position - 1); + break; + + case SMSG_PLAYER_ARROW_EQUIP: + itemId = msg->readInt16(); + + if (itemId <= 1) + break; + + item = player_node->getInvItem(itemId); + if (!item) + break; + + item->setEquipped(true); + player_node->mEquipment->setArrows(item); + logger->log("Arrows equipped: %i", itemId); + break; + } +} diff --git a/src/net/equipmenthandler.h b/src/net/equipmenthandler.h new file mode 100644 index 00000000..656f7a73 --- /dev/null +++ b/src/net/equipmenthandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_EQUIPMENTHANDLER_H +#define _TMW_NET_EQUIPMENTHANDLER_H + +#include "messagehandler.h" + +class EquipmentHandler : public MessageHandler +{ + public: + EquipmentHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp new file mode 100644 index 00000000..3742d327 --- /dev/null +++ b/src/net/inventoryhandler.cpp @@ -0,0 +1,128 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "inventoryhandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../item.h" +#include "../localplayer.h" + +#include "../gui/chat.h" + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_INVENTORY, + SMSG_PLAYER_INVENTORY_ADD, + SMSG_PLAYER_INVENTORY_REMOVE, + SMSG_PLAYER_INVENTORY_USE, + SMSG_ITEM_USE_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void InventoryHandler::handleMessage(MessageIn *msg) +{ + Sint32 number; + Sint16 index, amount, itemId, equipType; + + switch (msg->getId()) + { + case SMSG_PLAYER_INVENTORY: + // Only called on map load / warp. First reset all items + // to not load them twice on map change. + player_node->clearInventory(); + msg->readInt16(); // length + number = (msg->getLength() - 4) / 18; + + for (int loop = 0; loop < number; loop++) + { + index = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // type + msg->readInt8(); // identify flag + amount = msg->readInt16(); + msg->skip(2); // unknown + msg->skip(8); // card (4 shorts) + + player_node->addInvItem(index, itemId, amount, false); + + // Trick because arrows are not considered equipment + if (itemId == 1199 || itemId == 529) + { + player_node->getInvItem(index)->setEquipment(true); + } + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + index = msg->readInt16(); + amount = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // identify flag + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card + equipType = msg->readInt16(); + msg->readInt8(); // type + + if (msg->readInt8()> 0) { + chatWindow->chatLog("Unable to pick up item", BY_SERVER); + } else { + player_node->addInvItem(index, itemId, amount, equipType != 0); + } + break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + index = msg->readInt16(); + amount = msg->readInt16(); + player_node->getInvItem(index)->increaseQuantity(-amount); + break; + + case SMSG_PLAYER_INVENTORY_USE: + index = msg->readInt16(); + msg->readInt16(); // item id + msg->readInt32(); // id + amount = msg->readInt16(); + msg->readInt8(); // type + + player_node->getInvItem(index)->setQuantity(amount); + break; + + case SMSG_ITEM_USE_RESPONSE: + index = msg->readInt16(); + amount = msg->readInt16(); + + if (msg->readInt8() == 0) { + chatWindow->chatLog("Failed to use item", BY_SERVER); + } else { + player_node->getInvItem(index)->setQuantity(amount); + } + break; + } +} diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h new file mode 100644 index 00000000..aedbc3a1 --- /dev/null +++ b/src/net/inventoryhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_INVENTORYHANDLER_H +#define _TMW_NET_INVENTORYHANDLER_H + +#include "messagehandler.h" + +class InventoryHandler : public MessageHandler +{ + public: + InventoryHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp new file mode 100644 index 00000000..d2a938fd --- /dev/null +++ b/src/net/itemhandler.cpp @@ -0,0 +1,67 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "itemhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../engine.h" +#include "../floor_item.h" + +ItemHandler::ItemHandler() +{ + static const Uint16 _messages[] = { + SMSG_ITEM_VISIBLE, + SMSG_ITEM_DROPPED, + SMSG_ITEM_REMOVE, + 0 + }; + handledMessages = _messages; +} + +void ItemHandler::handleMessage(MessageIn *msg) +{ + Uint32 id; + Uint16 x, y; + Sint16 itemId; + + switch (msg->getId()) + { + case SMSG_ITEM_VISIBLE: + case SMSG_ITEM_DROPPED: + id = msg->readInt32(); + itemId = msg->readInt16(); + msg->readInt8(); // identify flag + x = msg->readInt16(); + y = msg->readInt16(); + msg->skip(4); // amount,subX,subY / subX,subY,amount + + add_floor_item(new FloorItem(id, itemId, x, y, engine->getCurrentMap())); + break; + + case SMSG_ITEM_REMOVE: + remove_floor_item(msg->readInt32()); + break; + } +} diff --git a/src/net/itemhandler.h b/src/net/itemhandler.h new file mode 100644 index 00000000..b2104722 --- /dev/null +++ b/src/net/itemhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_ITEMHANDLER_H +#define _TMW_NET_ITEMHANDLER_H + +#include "messagehandler.h" + +class ItemHandler : public MessageHandler +{ + public: + ItemHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp new file mode 100644 index 00000000..fd525d99 --- /dev/null +++ b/src/net/loginhandler.cpp @@ -0,0 +1,114 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "loginhandler.h" + +#include "messagein.h" +#include "network.h" +#include "protocol.h" + +#include "../log.h" +#include "../main.h" +#include "../serverinfo.h" + +extern SERVER_INFO **server_info; + +LoginHandler::LoginHandler() +{ + static const Uint16 _messages[] = { + 0x0069, + 0x006a, + 0 + }; + handledMessages = _messages; +} + +void LoginHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case 0x0069: + // Skip the length word + msg->skip(2); + + n_server = (msg->getLength() - 47) / 32; + server_info = (SERVER_INFO**)malloc(sizeof(SERVER_INFO*) * n_server); + + session_ID1 = msg->readInt32(); + account_ID = msg->readInt32(); + session_ID2 = msg->readInt32(); + msg->skip(30); // unknown + sex = msg->readInt8(); + + for (int i = 0; i < n_server; i++) + { + server_info[i] = new SERVER_INFO; + + server_info[i]->address = msg->readInt32(); + server_info[i]->port = msg->readInt16(); + server_info[i]->name = msg->readString(20); + server_info[i]->online_users = msg->readInt32(); + msg->skip(2); // unknown + + logger->log("Network: Server: %s (%s:%d)", + server_info[i]->name.c_str(), + iptostring(server_info[i]->address), + server_info[i]->port); + } + state = CHAR_SERVER_STATE; + break; + + case 0x006a: + int loginError = msg->readInt8(); + logger->log("Login::error code: %i", loginError); + + switch (loginError) { + case 0: + errorMessage = "Unregistered ID"; + break; + case 1: + errorMessage = "Wrong password"; + break; + case 2: + errorMessage = "Account expired"; + break; + case 3: + errorMessage = "Rejected from server"; + break; + case 4: + errorMessage = "You have been blocked by the GM Team"; + break; + case 6: + errorMessage = "You have been banned for 5 minutes"; + break; + case 9: + errorMessage = "This account is already logged in"; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = ERROR_STATE; + break; + } +} diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h new file mode 100644 index 00000000..99ade7f1 --- /dev/null +++ b/src/net/loginhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_LOGINHANDLER_H +#define _TMW_NET_LOGINHANDLER_H + +#include "messagehandler.h" + +class LoginHandler : public MessageHandler +{ + public: + LoginHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/maploginhandler.cpp b/src/net/maploginhandler.cpp new file mode 100644 index 00000000..27a7b4c6 --- /dev/null +++ b/src/net/maploginhandler.cpp @@ -0,0 +1,63 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "maploginhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" + +MapLoginHandler::MapLoginHandler() +{ + static const Uint16 _messages[] = { + SMSG_LOGIN_SUCCESS, + 0x0081, + 0 + }; + handledMessages = _messages; +} + +void MapLoginHandler::handleMessage(MessageIn *msg) +{ + unsigned char direction; + + switch (msg->getId()) + { + case SMSG_LOGIN_SUCCESS: + msg->readInt32(); // server tick + msg->readCoordinates(player_node->x, player_node->y, direction); + msg->skip(2); // unknown + logger->log("Protocol: Player start position: (%d, %d), Direction: %d", + player_node->x, player_node->y, direction); + state = GAME_STATE; + break; + + case 0x0081: + logger->log("Warning: Map server D/C"); + state = ERROR_STATE; + break; + } +} diff --git a/src/net/maploginhandler.h b/src/net/maploginhandler.h new file mode 100644 index 00000000..fe597549 --- /dev/null +++ b/src/net/maploginhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_MAPLOGINHANDLER_H +#define _TMW_NET_MAPLOGINHANDLER_H + +#include "messagehandler.h" + +class MapLoginHandler : public MessageHandler +{ + public: + MapLoginHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp new file mode 100644 index 00000000..849b6716 --- /dev/null +++ b/src/net/messagehandler.cpp @@ -0,0 +1,45 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "messagehandler.h" + +#include <cassert> + +#include "network.h" + +MessageHandler::MessageHandler(): + mNetwork(0) +{ +} + +MessageHandler::~MessageHandler() +{ + if (mNetwork) + mNetwork->unregisterHandler(this); +} + +void MessageHandler::setNetwork(Network *network) +{ + assert(!(network && mNetwork)); + mNetwork = network; +} diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h new file mode 100644 index 00000000..c09037f6 --- /dev/null +++ b/src/net/messagehandler.h @@ -0,0 +1,48 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_MESSAGEHANDLER_H +#define _TMW_NET_MESSAGEHANDLER_H + +#include <SDL_types.h> + +class MessageIn; +class Network; + +class MessageHandler +{ + public: + const Uint16 *handledMessages; + + MessageHandler(); + virtual ~MessageHandler(); + + virtual void handleMessage(MessageIn *msg) =0; + + void setNetwork(Network *network); + + protected: + Network *mNetwork; +}; + +#endif diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp index 9b27a6a5..f6ed5de6 100644 --- a/src/net/messageout.cpp +++ b/src/net/messageout.cpp @@ -30,63 +30,42 @@ #include "network.h" #include "packet.h" -MessageOut::MessageOut(): - mPacket(0), +MessageOut::MessageOut(Network *network): + mNetwork(network), mData(0), mDataSize(0), mPos(0) { - mData = out + out_size; -} - -MessageOut::~MessageOut() -{ - if (mPacket) { - delete mPacket; - } - - // Don't free this data for now, see above. - //if (mData) { - // free(mData); - //} -} - -void MessageOut::expand(size_t bytes) -{ - /*mData = (char*)realloc(mData, bytes); - mDataSize = bytes;*/ + mData = mNetwork->mOutBuffer + mNetwork->mOutSize; } void MessageOut::writeInt8(Sint8 value) { - expand(mPos + sizeof(Sint8)); mData[mPos] = value; mPos += sizeof(Sint8); - out_size += sizeof(Sint8); + mNetwork->mOutSize+= sizeof(Sint8); } void MessageOut::writeInt16(Sint16 value) { - expand(mPos + sizeof(Sint16)); #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint16 *)(mData + mPos)) = SDL_Swap16(value); #else (*(Sint16 *)(mData + mPos)) = value; #endif mPos += sizeof(Sint16); - out_size += sizeof(Sint16); + mNetwork->mOutSize += sizeof(Sint16); } void MessageOut::writeInt32(Sint32 value) { - expand(mPos + sizeof(Sint32)); #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint32 *)(mData + mPos)) = SDL_Swap32(value); #else (*(Sint32 *)(mData + mPos)) = value; #endif mPos += sizeof(Sint32); - out_size += sizeof(Sint32); + mNetwork->mOutSize += sizeof(Sint32); } void MessageOut::writeString(const std::string &string, int length) @@ -97,39 +76,27 @@ void MessageOut::writeString(const std::string &string, int length) { // Write the length at the start if not fixed writeInt16(string.length()); - expand(mPos + string.length()); } else { // Make sure the length of the string is no longer than specified toWrite = string.substr(0, length); - expand(mPos + length); } // Write the actual string memcpy(&mData[mPos], (void*)toWrite.c_str(), toWrite.length()); mPos += toWrite.length(); - out_size += toWrite.length(); + mNetwork->mOutSize += toWrite.length(); // Pad remaining space with zeros if (length > (int)toWrite.length()) { memset(&mData[mPos], '\0', length - toWrite.length()); mPos += length - toWrite.length(); - out_size += length - toWrite.length(); + mNetwork->mOutSize += length - toWrite.length(); } } -const Packet *MessageOut::getPacket() -{ - if (!mPacket) - { - mPacket = new Packet(mData, mDataSize); - } - - return mPacket; -} - MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs) { msg.writeInt8(rhs); diff --git a/src/net/messageout.h b/src/net/messageout.h index b2ee506e..f6468adb 100644 --- a/src/net/messageout.h +++ b/src/net/messageout.h @@ -27,7 +27,7 @@ #include <iosfwd> #include <SDL_types.h> -class Packet; +class Network; /** * Used for building an outgoing message. @@ -42,12 +42,7 @@ class MessageOut /** * Constructor. */ - MessageOut(); - - /** - * Destructor. - */ - ~MessageOut(); + MessageOut(Network *network); void writeInt8(Sint8 value); /**< Writes a byte. */ void writeInt16(Sint16 value); /**< Writes a short. */ @@ -59,24 +54,9 @@ class MessageOut */ void writeString(const std::string &string, int length = -1); - /** - * Returns an instance of Packet derived from the written data. Use for - * sending the packet. No more writing to the packet may be done after - * a call to this method. - */ - const Packet *getPacket(); - private: - /** - * Expand the packet data to be able to hold more data. - * - * NOTE: For performance enhancements this method could allocate extra - * memory in advance instead of expanding size every time more data is - * added. - */ - void expand(size_t size); + Network *mNetwork; - Packet *mPacket; /**< Created packet. */ char *mData; /**< Data building up. */ unsigned int mDataSize; /**< Size of data. */ unsigned int mPos; /**< Position in the data. */ diff --git a/src/net/network.cpp b/src/net/network.cpp index fbcf199b..3b0652e2 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -23,15 +23,10 @@ #include "network.h" -#include <cassert> -#include <sstream> -#include <SDL_net.h> -#include <SDL_thread.h> - +#include "messagehandler.h" #include "messagein.h" #include "../log.h" -#include "../main.h" /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -81,309 +76,379 @@ short packet_lengths[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -unsigned int buffer_size = 65536; -char *in = NULL; -char *out = NULL; -unsigned int in_size = 0; -unsigned int out_size = 0; -int connectionOpen = NET_IDLE; +const unsigned int BUFFER_SIZE = 65536; -TCPsocket sock; -SDLNet_SocketSet set; -SDL_Thread *mThread = NULL; -SDL_mutex *mMutex = NULL; -IPaddress *ip = NULL; +int networkThread(void *data) +{ + Network *network = static_cast<Network*>(data); -char *iptostring(int address) + if (!network->realConnect()) + return -1; + + network->receive(); + + return 0; +} + +Network::Network(): + mAddress(0), mPort(0), + mInBuffer(new char[BUFFER_SIZE]), + mOutBuffer(new char[BUFFER_SIZE]), + mInSize(0), mOutSize(0), + mToSkip(0), + mState(IDLE), + mWorkerThread(0) { - static char asciiIP[16]; + mMutex = SDL_CreateMutex(); +} - sprintf(asciiIP, "%i.%i.%i.%i", - (unsigned char)(address), - (unsigned char)(address >> 8), - (unsigned char)(address >> 16), - (unsigned char)(address >> 24)); +Network::~Network() +{ + clearHandlers(); - return asciiIP; + if (mAddress) + free(mAddress); + + if (mState != IDLE && mState != ERROR) + disconnect(); + + SDL_DestroyMutex(mMutex); + + delete mInBuffer; + delete mOutBuffer; } -int connectionThread(void *ptr) +bool Network::connect(const char *address, short port) { - // Create the socket for the current session - sock = SDLNet_TCP_Open((IPaddress *)ptr); - if (!sock) + if (mState != IDLE && mState != ERROR) { - logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Tried to connect an already connected socket!"); + return false; } - // Create a socket set to listen to socket - set = SDLNet_AllocSocketSet(1); - if (!set) + if (!address) { - logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Empty address given to Network::connect()!"); + mState = ERROR; + return false; } - // Add the socket to the set - int ret = SDLNet_TCP_AddSocket(set, sock); - if (ret == -1) + if (mAddress) + free(mAddress); + + mAddress = strdup(address); + mPort = port; + + // Reset to sane values + mOutSize = 0; + mInSize = 0; + mToSkip = 0; + + mState = CONNECTING; + mWorkerThread = SDL_CreateThread(networkThread, this); + if (!mWorkerThread) { - logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Unable to create network worker thread"); + mState = ERROR; + return false; } - // Init buffers - in = (char*)malloc(buffer_size); - out = (char*)malloc(buffer_size); - memset(in, '\0', buffer_size); - memset(out, '\0', buffer_size); - in_size = 0; - out_size = 0; - - SDL_mutexP(mMutex); - logger->log("Network::Started session with %s:%i", - iptostring(((IPaddress *)ptr)->host), - ((IPaddress *)ptr)->port); - connectionOpen = NET_CONNECTED; - SDL_mutexV(mMutex); - return NET_CONNECTED; + return true; } -void openConnection(const char* address, short port) +void Network::disconnect() { - //assert(connectionOpen <= NET_IDLE); + if (mState != CONNECTED && mState != CONNECTING) + return; - // Initialize SDL_net - if (SDLNet_Init() == -1) + mState = IDLE; + + if (mWorkerThread) { - logger->log("Error in SDLNet_Init(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + SDL_WaitThread(mWorkerThread, NULL); + mWorkerThread = NULL; } + SDLNet_TCP_Close(mSocket); +} - ip = new IPaddress(); +void Network::registerHandler(MessageHandler *handler) +{ + const Uint16 *i = handler->handledMessages; - // Resolve host name - if (SDLNet_ResolveHost(ip, address, port) == -1) + while(*i) { - logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + mMessageHandlers[*i] = handler; + i++; } - connectionOpen = NET_CONNECTING; - // Create the synchronization lock - mMutex = SDL_CreateMutex(); - // Create the connection thread - mThread = SDL_CreateThread(connectionThread, ip); - if (mThread == NULL) { - logger->log("Unable to create connection thread"); - connectionOpen = NET_ERROR; - } + handler->setNetwork(this); } -int pollConnection() +void Network::unregisterHandler(MessageHandler *handler) { - if (mMutex) - { - SDL_mutexP(mMutex); - } + const Uint16 *i = handler->handledMessages; - switch (connectionOpen) + while(*i) { - case NET_IDLE: - case NET_CONNECTING: - break; - case NET_CONNECTED: - case NET_ERROR: - SDL_WaitThread(mThread, NULL); - mThread = NULL; - SDL_DestroyMutex(mMutex); - mMutex = NULL; - break; + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(*i); + if (iter != mMessageHandlers.end()) + { + mMessageHandlers.erase(iter); + } + i++; } - if (mMutex) + handler->setNetwork(0); +} + +void Network::clearHandlers() +{ + std::map<Uint16, MessageHandler*>::iterator i; + for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++) { - SDL_mutexV(mMutex); + i->second->setNetwork(0); } - return connectionOpen; + mMessageHandlers.clear(); +} + +void Network::dispatchMessages() +{ + if (!messageReady()) + return; + + MessageIn msg = getNextMessage(); + + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(msg.getId()); + + if (iter != mMessageHandlers.end()) + iter->second->handleMessage(&msg); + else + logger->log("Unhandled packet: %x", msg.getId()); + + skip(msg.getLength()); } -void closeConnection() +void Network::flush() { - //assert(connectionOpen > ); + if (!mOutSize || mState != CONNECTED) + return; + + int ret; - if (connectionOpen == NET_ERROR)return; - if (mThread) + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize); + if (ret < (int)mOutSize) { - SDL_WaitThread(mThread, NULL); - mThread = NULL; + logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); + mState = ERROR; } + mOutSize = 0; + SDL_mutexV(mMutex); +} - if (mMutex) +void Network::skip(int len) +{ + SDL_mutexP(mMutex); + mToSkip += len; + if (!mInSize) { - SDL_DestroyMutex(mMutex); - mMutex = NULL; + SDL_mutexV(mMutex); + return; } - if (ip) + if (mInSize >= mToSkip) { - delete ip; - ip = NULL; + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; } - - // Remove the socket from the socket set - int ret = SDLNet_TCP_DelSocket(set, sock); - if (ret == -1) + else { - logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); + mToSkip -= mInSize; + mInSize = 0; } + SDL_mutexV(mMutex); +} - // Close the TCP connection - SDLNet_TCP_Close(sock); - - // Free the socket set - SDLNet_FreeSocketSet(set); - set = NULL; +bool Network::messageReady() +{ + int len = -1; - // Clear buffers - if (in != NULL) + SDL_mutexP(mMutex); + if (mInSize >= 2) { - free(in); - in = NULL; + len = packet_lengths[readWord(0)]; + + if (len == -1 && mInSize > 4) + len = readWord(2); + } - if (out != NULL) + bool ret = (mInSize >= static_cast<unsigned int>(len)); + SDL_mutexV(mMutex); + + return ret; +} + +MessageIn Network::getNextMessage() +{ + while (!messageReady()) { - free(out); - out = NULL; + if (mState == ERROR) + break; } - in_size = 0; - out_size = 0; + SDL_mutexP(mMutex); + int msgId = readWord(0); + int len = packet_lengths[msgId]; - // Shutdown the network API - SDLNet_Quit(); + if (len == -1) + len = readWord(2); - logger->log("Network::Closed session"); - connectionOpen = NET_IDLE; +#ifdef DEBUG + printf("Received packet 0x%x of length %d\n", msgId, length); +#endif + + MessageIn msg(mInBuffer, len); + SDL_mutexV(mMutex); + + return msg; } -void flush() +bool Network::realConnect() { - // Send all available data, waits if not all data can be sent immediately - if (out_size > 0) + IPaddress ipAddress; + + if (SDLNet_ResolveHost(&ipAddress, mAddress, mPort) == -1) { - int ret = SDLNet_TCP_Send(sock, (char*)out, out_size); - if (ret < (int)out_size) - { - logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - out_size -= ret; + logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - int numReady = SDLNet_CheckSockets(set, 0); - if (numReady == -1) + mState = CONNECTING; + + mSocket = SDLNet_TCP_Open(&ipAddress); + if (!mSocket) { - logger->log("Error: SDLNet_CheckSockets"); - return; + logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - else if (numReady == 0) // any socket ready + + logger->log("Network::Started session with %s:%i", + iptostring(ipAddress.host), ipAddress.port); + + mState = CONNECTED; + + return true; +} + +void Network::receive() +{ + SDLNet_SocketSet set; + + if (!(set = SDLNet_AllocSocketSet(1))) { + logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); + mState = ERROR; return; } - else if (numReady == 1) // one socket is ready - { - // Receive data from the socket - int ret = SDLNet_TCP_Recv(sock, in + in_size, buffer_size - in_size); - if (ret <= 0) - { - logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - else { - in_size += ret; - } - } - else // more than one socket is ready.. this should not happen since we only listen once socket. + + if (SDLNet_TCP_AddSocket(set, mSocket) == -1) { - logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; + logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); + mState = ERROR; } -} -unsigned short readWord(int pos) -{ -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap16((*(unsigned short*)(in+(pos)))); -#else - return (*(unsigned short *)(in+(pos))); -#endif -} - -bool packetReady() -{ - bool ret = false; - if (in_size >= 2) + while (mState == CONNECTED) { - int length = packet_lengths[readWord(0)]; - if (length == -1) + // TODO Try to get this to block all the time while still being able + // to escape the loop + int numReady = SDLNet_CheckSockets(set, ((Uint32)500)); + int ret; + switch (numReady) { - if (in_size >= 4) - { - length = readWord(2); - if (in_size >= (unsigned int)length) + case -1: + logger->log("Error: SDLNet_CheckSockets"); + // FALLTHROUGH + case 0: + break; + + case 1: + // Receive data from the socket + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize); + + if (!ret) { - ret = true; + // We got disconnected + mState = IDLE; + logger->log("Disconnected."); } - } - } - else if (in_size >= (unsigned int)length) - { - ret = true; + else if (ret < 0) + { + logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); + mState = ERROR; + } + else { + mInSize += ret; + if (mToSkip) + { + if (mInSize >= mToSkip) + { + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; + } + else + { + mToSkip -= mInSize; + mInSize = 0; + } + } + } + SDL_mutexV(mMutex); + break; + + default: + // more than one socket is ready.. + // this should not happen since we only listen once socket. + logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); + mState = ERROR; + break; } } - return ret; -} - -MessageIn -get_next_message() -{ - // At least 2 bytes should be received for the message ID - while (in_size < 2 && state != ERROR_STATE) flush(); - - int length = packet_lengths[readWord(0)]; - if (length == -1) + if (SDLNet_TCP_DelSocket(set, mSocket) == -1) { - // Another 2 bytes should be received for the length - while (in_size < 4) flush(); - length = readWord(2); + logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); } -#ifdef DEBUG - printf("Received packet 0x%x of length %d\n", readWord(0), length); -#endif + SDLNet_FreeSocketSet(set); +} + +char *iptostring(int address) +{ + static char asciiIP[16]; - // Make sure the whole packet is received - while (in_size < static_cast<unsigned int>(length) && state != ERROR_STATE) flush(); + sprintf(asciiIP, "%i.%i.%i.%i", + (unsigned char)(address), + (unsigned char)(address >> 8), + (unsigned char)(address >> 16), + (unsigned char)(address >> 24)); - return MessageIn(in, length); + return asciiIP; } -void skip(int len) +Uint16 Network::readWord(int pos) { - memcpy(in, in + len, in_size - len); - in_size -= len; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_Swap16((*(Uint16*)(mInBuffer+(pos)))); +#else + return (*(Uint16*)(mInBuffer+(pos))); +#endif } diff --git a/src/net/network.h b/src/net/network.h index db95d7c3..75bde584 100644 --- a/src/net/network.h +++ b/src/net/network.h @@ -24,41 +24,77 @@ #ifndef _TMW_NETWORK_ #define _TMW_NETWORK_ -#define NET_ERROR -1 -#define NET_CONNECTED 0 -#define NET_IDLE 1 -#define NET_CONNECTING 2 -#define NET_DATA 3 +#include <map> +#include <SDL_net.h> +#include <SDL_thread.h> +class MessageHandler; class MessageIn; -/** Convert an address from int format to string */ -char *iptostring(int address); +class Network; -/** Open a session with a server */ -void openConnection(const char* address, short port); +class Network +{ + public: + friend int networkThread(void *data); + friend class MessageOut; -/** Returns the status of the current connection attempt. */ -int pollConnection(); + Network(); + ~Network(); -/** Close a session */ -void closeConnection(); + bool connect(const char *address, short port); + void disconnect(); -/** Send and receive data waiting in the buffers */ -void flush(); + void registerHandler(MessageHandler *handler); + void unregisterHandler(MessageHandler *handler); + void clearHandlers(); -/** Check if a packet is complete */ -bool packetReady(); + int getState() const { return mState; } + bool isConnected() const { return mState == CONNECTED; } -/** - * Returns the next arriving message, waiting for it if necessary. - */ -MessageIn get_next_message(); -extern char *out; + int getInSize() const { return mInSize; } + + void skip(int len); + + bool messageReady(); + MessageIn getNextMessage(); + + void dispatchMessages(); + void flush(); + + enum { + IDLE, + CONNECTED, + CONNECTING, + DATA, + ERROR + }; + + protected: + Uint16 readWord(int pos); + + TCPsocket mSocket; -void skip(int len); + char *mAddress; + short mPort; -extern unsigned int in_size; /**< Amount of data in input buffer. */ -extern unsigned int out_size; /**< Amount of data in output buffer. */ + char *mInBuffer, *mOutBuffer; + unsigned int mInSize, mOutSize; + + unsigned int mToSkip; + + int mState; + + SDL_Thread *mWorkerThread; + SDL_mutex *mMutex; + + std::map<Uint16, MessageHandler*> mMessageHandlers; + + bool realConnect(); + void receive(); +}; + +/** Convert an address from int format to string */ +char *iptostring(int address); #endif diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp new file mode 100644 index 00000000..a803710e --- /dev/null +++ b/src/net/npchandler.cpp @@ -0,0 +1,74 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "npchandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../beingmanager.h" +#include "../npc.h" + +#include "../gui/npclistdialog.h" +#include "../gui/npc_text.h" + +extern NpcListDialog *npcListDialog; +extern NpcTextDialog *npcTextDialog; + +NPCHandler::NPCHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_CHOICE, + SMSG_NPC_MESSAGE, + SMSG_NPC_NEXT, + SMSG_NPC_CLOSE, + 0 + }; + handledMessages = _messages; +} + +void NPCHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_NPC_CHOICE: + msg->readInt16(); // length + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + npcListDialog->parseItems(msg->readString(msg->getLength() - 8)); + npcListDialog->setVisible(true); + break; + + case SMSG_NPC_MESSAGE: + msg->readInt16(); // length + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + npcTextDialog->addText(msg->readString(msg->getLength() - 8)); + npcListDialog->setVisible(false); + npcTextDialog->setVisible(true); + break; + + case SMSG_NPC_NEXT: + case SMSG_NPC_CLOSE: + // Next/Close button in NPC dialog, currently unused + break; + } +} diff --git a/src/net/npchandler.h b/src/net/npchandler.h new file mode 100644 index 00000000..903ecd10 --- /dev/null +++ b/src/net/npchandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_NPCHANDLER_H +#define _TMW_NET_NPCHANDLER_H + +#include "messagehandler.h" + +class NPCHandler : public MessageHandler +{ + public: + NPCHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp new file mode 100644 index 00000000..1a255e0b --- /dev/null +++ b/src/net/playerhandler.cpp @@ -0,0 +1,301 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "playerhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../engine.h" +#include "../localplayer.h" +#include "../log.h" +#include "../npc.h" + +#include "../gui/chat.h" +#include "../gui/ok_dialog.h" +#include "../gui/skill.h" + +// TODO Move somewhere else +OkDialog *weightNotice = NULL; +OkDialog *deathNotice = NULL; + +/** + * Listener used for handling the overweigth message. + */ +// TODO Move somewhere else +class WeightNoticeListener : public gcn::ActionListener +{ + public: + void action(const std::string &eventId) + { + weightNotice = NULL; + } +} weightNoticeListener; + + +/** + * Listener used for handling death message. + */ +// TODO Move somewhere else +class DeathNoticeListener : public gcn::ActionListener { + public: + void action(const std::string &eventId) { + player_node->revive(); + deathNotice = NULL; + } +} deathNoticeListener; + +PlayerHandler::PlayerHandler() +{ + static const Uint16 _messages[] = { + SMSG_WALK_RESPONSE, + SMSG_PLAYER_WARP, + SMSG_PLAYER_STAT_UPDATE_1, + SMSG_PLAYER_STAT_UPDATE_2, + SMSG_PLAYER_STAT_UPDATE_3, + SMSG_PLAYER_STAT_UPDATE_4, + SMSG_PLAYER_STAT_UPDATE_5, + SMSG_PLAYER_STAT_UPDATE_6, + SMSG_PLAYER_ARROW_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void PlayerHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_WALK_RESPONSE: + // It is assumed by the client any request to walk actually + // succeeds on the server. The plan is to have a correction + // message when the server senses the client has the wrong + // idea. + break; + + case SMSG_PLAYER_WARP: + { + std::string mapPath = msg->readString(16); + Uint16 x = msg->readInt16(); + Uint16 y = msg->readInt16(); + + logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y); + + // Switch the actual map, deleting the previous one + engine->changeMap(mapPath); + + current_npc = 0; + + player_node->action = Being::STAND; + player_node->stopAttack(); + player_node->mFrame = 0; + player_node->x = x; + player_node->y = y; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_1: + { + Sint16 type = msg->readInt16(); + Uint32 value = msg->readInt32(); + + switch (type) + { + //case 0x0000: + // player_node->setWalkSpeed(msg->readInt32()); + // break; + case 0x0005: player_node->hp = value; break; + case 0x0006: player_node->maxHp = value; break; + case 0x0007: player_node->mp = value; break; + case 0x0008: player_node->maxMp = value; break; + case 0x000b: player_node->lvl = value; break; + case 0x000c: + player_node->skillPoint = value; + skillDialog->update(); + break; + case 0x0018: + if (value >= player_node->maxWeight / 2 && + player_node->totalWeight < + player_node->maxWeight / 2) + { + weightNotice = new OkDialog("Message", + "You are carrying more then half your " + "weight. You are unable to regain " + "health.", + &weightNoticeListener); + } + player_node->totalWeight = value; + break; + case 0x0019: player_node->maxWeight = value; break; + case 0x0037: player_node->jobLvl = value; break; + case 0x0009: + player_node->statsPointsToAttribute = value; + break; + case 0x0029: player_node->ATK = value; break; + case 0x002b: player_node->MATK = value; break; + case 0x002d: player_node->DEF = value; break; + case 0x002f: player_node->MDEF = value; break; + case 0x0031: player_node->HIT = value; break; + case 0x0032: player_node->FLEE = value; break; + case 0x0035: player_node->aspd = value; break; + } + + if (player_node->hp == 0 && deathNotice == NULL) + { + deathNotice = new OkDialog("Message", + "You're now dead, press ok to restart", + &deathNoticeListener); + player_node->action = Being::DEAD; + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_2: + switch (msg->readInt16()) { + case 0x0001: + player_node->xp = msg->readInt32(); + break; + case 0x0002: + player_node->jobXp = msg->readInt32(); + break; + case 0x0014: + player_node->gp = msg->readInt32(); + break; + case 0x0016: + player_node->xpForNextLevel = msg->readInt32(); + break; + case 0x0017: + player_node->jobXpForNextLevel = msg->readInt32(); + break; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_3: + { + Sint32 type = msg->readInt32(); + Sint32 base = msg->readInt32(); + Sint32 bonus = msg->readInt32(); + Sint32 total = base + bonus; + + switch (type) { + case 0x000d: player_node->ATTR[LocalPlayer::STR] = total; break; + case 0x000e: player_node->ATTR[LocalPlayer::AGI] = total; break; + case 0x000f: player_node->ATTR[LocalPlayer::VIT] = total; break; + case 0x0010: player_node->ATTR[LocalPlayer::INT] = total; break; + case 0x0011: player_node->ATTR[LocalPlayer::DEX] = total; break; + case 0x0012: player_node->ATTR[LocalPlayer::LUK] = total; break; + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_4: + { + Sint16 type = msg->readInt16(); + Sint8 fail = msg->readInt8(); + Sint8 value = msg->readInt8(); + + if (fail == 1) + { + switch (type) { + case 0x000d: player_node->ATTR[LocalPlayer::STR] = value; break; + case 0x000e: player_node->ATTR[LocalPlayer::AGI] = value; break; + case 0x000f: player_node->ATTR[LocalPlayer::VIT] = value; break; + case 0x0010: player_node->ATTR[LocalPlayer::INT] = value; break; + case 0x0011: player_node->ATTR[LocalPlayer::DEX] = value; break; + case 0x0012: player_node->ATTR[LocalPlayer::LUK] = value; break; + } + } + } + break; + + // Updates stats and status points + case SMSG_PLAYER_STAT_UPDATE_5: + player_node->statsPointsToAttribute = msg->readInt16(); + player_node->ATTR[LocalPlayer::STR] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::STR] = msg->readInt8(); + player_node->ATTR[LocalPlayer::AGI] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::AGI] = msg->readInt8(); + player_node->ATTR[LocalPlayer::VIT] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::VIT] = msg->readInt8(); + player_node->ATTR[LocalPlayer::INT] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::INT] = msg->readInt8(); + player_node->ATTR[LocalPlayer::DEX] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::DEX] = msg->readInt8(); + player_node->ATTR[LocalPlayer::LUK] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::LUK] = msg->readInt8(); + player_node->ATK = msg->readInt16(); // ATK + player_node->ATK_BONUS = msg->readInt16(); // ATK bonus + player_node->MATK = msg->readInt16(); // MATK max + player_node->MATK_BONUS = msg->readInt16(); // MATK min + player_node->DEF = msg->readInt16(); // DEF + player_node->DEF_BONUS = msg->readInt16(); // DEF bonus + player_node->MDEF = msg->readInt16(); // MDEF + player_node->MDEF_BONUS = msg->readInt16(); // MDEF bonus + player_node->HIT = msg->readInt16(); // HIT + player_node->FLEE = msg->readInt16(); // FLEE + player_node->FLEE_BONUS = msg->readInt16(); // FLEE bonus + msg->readInt16(); // critical + msg->readInt16(); // unknown + break; + + case SMSG_PLAYER_STAT_UPDATE_6: + switch (msg->readInt16()) { + case 0x0020: player_node->ATTR_UP[LocalPlayer::STR] = msg->readInt8(); break; + case 0x0021: player_node->ATTR_UP[LocalPlayer::AGI] = msg->readInt8(); break; + case 0x0022: player_node->ATTR_UP[LocalPlayer::VIT] = msg->readInt8(); break; + case 0x0023: player_node->ATTR_UP[LocalPlayer::INT] = msg->readInt8(); break; + case 0x0024: player_node->ATTR_UP[LocalPlayer::DEX] = msg->readInt8(); break; + case 0x0025: player_node->ATTR_UP[LocalPlayer::LUK] = msg->readInt8(); break; + } + break; + + case SMSG_PLAYER_ARROW_MESSAGE: + { + Sint16 type = msg->readInt16(); + + switch (type) { + case 0: + chatWindow->chatLog("Equip arrows first", + BY_SERVER); + break; + default: + logger->log("0x013b: Unhandled message %i", type); + break; + } + } + break; + + //Stop walking + //case 0x0088: // Disabled because giving some problems + //if (being = beingManager->findBeing(readInt32(2))) { + // if (being->getId() != player_node->getId()) { + // being->action = STAND; + // being->mFrame = 0; + // set_coordinates(being->coordinates, + // readWord(6), readWord(8), + // get_direction(being->coordinates)); + // } + //} + //break; + } +} diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h new file mode 100644 index 00000000..b28a23f5 --- /dev/null +++ b/src/net/playerhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_PLAYERHANDLER_H +#define _TMW_NET_PLAYERHANDLER_H + +#include "messagehandler.h" + +class PlayerHandler : public MessageHandler +{ + public: + PlayerHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/protocol.cpp b/src/net/protocol.cpp index 28663827..5de1b0cf 100644 --- a/src/net/protocol.cpp +++ b/src/net/protocol.cpp @@ -23,14 +23,6 @@ #include "protocol.h" -#include "messageout.h" - -#include "../being.h" -#include "../game.h" -#include "../main.h" -#include "../playerinfo.h" -#include "../sound.h" - #define LOBYTE(w) ((unsigned char)(w)) #define HIBYTE(w) ((unsigned char)(((unsigned short)(w)) >> 8)) @@ -64,101 +56,3 @@ void set_coordinates(char *data, data[2] = LOBYTE(temp); data[2] |= direction; } - -void walk(unsigned short x, unsigned short y, unsigned char direction) -{ - char temp[3]; - MessageOut outMsg; - set_coordinates(temp, x, y, direction); - outMsg.writeInt16(0x0085); - outMsg.writeString(temp, 3); -} - -void action(char type, int id) -{ - MessageOut outMsg; - outMsg.writeInt16(0x0089); - outMsg.writeInt32(id); - outMsg.writeInt8(type); -} - -void talk(Being *being) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_TALK); - outMsg.writeInt32(being->getId()); - outMsg.writeInt8(0); -} - -void pickUp(Uint32 floorItemId) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_ITEM_PICKUP); - outMsg.writeInt32(floorItemId); -} - -Being* attack(unsigned short x, unsigned short y, unsigned char direction) -{ - Being *target = NULL; - - switch (direction) - { - case Being::SOUTH: - target = findNode(x, y + 1, Being::MONSTER); - if (!target) target = findNode(x, y + 1, Being::PLAYER); - break; - - case Being::WEST: - target = findNode(x - 1, y, Being::MONSTER); - if (!target) target = findNode(x - 1, y, Being::PLAYER); - break; - - case Being::NORTH: - target = findNode(x, y - 1, Being::MONSTER); - if (!target) target = findNode(x, y - 1, Being::PLAYER); - break; - - case Being::EAST: - target = findNode(x + 1, y, Being::MONSTER); - if (!target) target = findNode(x + 1, y, Being::PLAYER); - break; - } - - if (target) { - attack(target); - } - - return target; -} - -void attack(Being *target) -{ - int dist_x = target->x - player_node->x; - int dist_y = target->y - player_node->y; - - if (abs(dist_y) >= abs(dist_x)) - { - if (dist_y > 0) - player_node->direction = Being::SOUTH; - else - player_node->direction = Being::NORTH; - } - else - { - if (dist_x > 0) - player_node->direction = Being::EAST; - else - player_node->direction = Being::WEST; - } - - // Implement charging attacks here - player_info->lastAttackTime = 0; - - player_node->action = Being::ATTACK; - action(0, target->getId()); - player_node->walk_time = tick_time; - if (player_node->getWeapon() == 2) - sound.playSfx("sfx/bow_shoot_1.ogg"); - else - sound.playSfx("sfx/fist-swish.ogg"); -} diff --git a/src/net/protocol.h b/src/net/protocol.h index e7d6f286..716aa0f9 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -24,10 +24,6 @@ #ifndef _TMW_PROTOCOL_ #define _TMW_PROTOCOL_ -#include <SDL_types.h> - -class Being; - // Packets from server to client #define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ #define SMSG_PLAYER_UPDATE_1 0x01d8 @@ -108,7 +104,6 @@ class Being; #define CMSG_PLAYER_EQUIP 0x00a9 #define CMSG_PLAYER_UNEQUIP 0x00ab - /** Decodes src direction */ unsigned char get_src_direction(char data); @@ -118,22 +113,4 @@ unsigned char get_dest_direction(char data); /** Encodes coords and direction in 3 bytes data */ void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); -/** Requests to walk */ -void walk(unsigned short x, unsigned short y, unsigned char direction); - -/** Request to attack */ -Being* attack(unsigned short x, unsigned short y, unsigned char direction); - -/** Request to attack */ -void attack(Being *target); - -/** Request action */ -void action(char type, int id); - -/** Talk to a being */ -void talk(Being *being); - -/** Pick up an item */ -void pickUp(Uint32 floorItemId); - #endif diff --git a/src/net/skillhandler.cpp b/src/net/skillhandler.cpp new file mode 100644 index 00000000..e9dc9c19 --- /dev/null +++ b/src/net/skillhandler.cpp @@ -0,0 +1,93 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "skillhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../gui/chat.h" +#include "../gui/skill.h" + +SkillHandler::SkillHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_SKILLS, + SMSG_SKILL_FAILED, + 0 + }; + handledMessages = _messages; +} + +void SkillHandler::handleMessage(MessageIn *msg) +{ + int skillCount; + + switch (msg->getId()) + { + case SMSG_PLAYER_SKILLS: + msg->readInt16(); // length + skillCount = (msg->getLength() - 4) / 37; + skillDialog->cleanList(); + + for (int k = 0; k < skillCount; k++) + { + Sint16 skillId = msg->readInt16(); + msg->readInt16(); // target type + msg->readInt16(); // unknown + Sint16 level = msg->readInt16(); + Sint16 sp = msg->readInt16(); + msg->readInt16(); // range + std::string skillName = msg->readString(24); + Sint8 up = msg->readInt8(); + + if (level != 0 || up != 0) + { + if (skillDialog->hasSkill(skillId)) { + skillDialog->setSkill(skillId, level, sp); + } + else { + skillDialog->addSkill(skillId, level, sp); + } + } + } + break; + + case SMSG_SKILL_FAILED: + // Action failed (ex. sit because you have not reached the + // right level) + CHATSKILL action; + action.skill = msg->readInt16(); + action.bskill = msg->readInt16(); + action.unused = msg->readInt16(); // unknown + action.success = msg->readInt8(); + action.reason = msg->readInt8(); + if (action.success != SKILL_FAILED && + action.bskill == BSKILL_EMOTE) + { + printf("Action: %d/%d", action.bskill, action.success); + } + chatWindow->chatLog(action); + break; + } +} diff --git a/src/net/skillhandler.h b/src/net/skillhandler.h new file mode 100644 index 00000000..820a7b6a --- /dev/null +++ b/src/net/skillhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_SKILLHANDLER_H +#define _TMW_NET_SKILLHANDLER_H + +#include "messagehandler.h" + +class SkillHandler : public MessageHandler +{ + public: + SkillHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/tradehandler.cpp b/src/net/tradehandler.cpp new file mode 100644 index 00000000..6ed3bab2 --- /dev/null +++ b/src/net/tradehandler.cpp @@ -0,0 +1,179 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "tradehandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../item.h" +#include "../localplayer.h" + +#include "../gui/chat.h" +#include "../gui/requesttrade.h" +#include "../gui/trade.h" + +std::string tradePartnerName; + +TradeHandler::TradeHandler() +{ + static const Uint16 _messages[] = { + SMSG_TRADE_REQUEST, + SMSG_TRADE_RESPONSE, + SMSG_TRADE_ITEM_ADD, + SMSG_TRADE_ITEM_ADD_RESPONSE, + SMSG_TRADE_OK, + SMSG_TRADE_CANCEL, + SMSG_TRADE_COMPLETE, + 0 + }; + handledMessages = _messages; +} + +void TradeHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_TRADE_REQUEST: + // If a trade window or request window is already open, send a + // trade cancel to any other trade request. + // + // Note that it would be nice if the server would prevent this + // situation, and that the requesting player would get a + // special message about the player being occupied. + if (!player_node->tradeRequestOk()) + { + player_node->tradeReply(false); + break; + } + + player_node->setTrading(true); + tradePartnerName = msg->readString(24); + new RequestTradeDialog(tradePartnerName); + break; + + case SMSG_TRADE_RESPONSE: + switch (msg->readInt8()) + { + case 0: // Too far away + chatWindow->chatLog("Trading isn't possible. " + "Trade partner is too far away.", + BY_SERVER); + break; + case 1: // Character doesn't exist + chatWindow->chatLog("Trading isn't possible. " + "Character doesn't exist.", + BY_SERVER); + break; + case 2: // Invite request check failed... + chatWindow->chatLog("Trade cancelled due to an " + "unknown reason.", BY_SERVER); + break; + case 3: // Trade accepted + tradeWindow->reset(); + tradeWindow->setCaption( + "Trade: You and " + tradePartnerName); + tradeWindow->setVisible(true); + break; + case 4: // Trade cancelled + chatWindow->chatLog("Trade cancelled.", BY_SERVER); + tradeWindow->setVisible(false); + player_node->setTrading(false); + break; + default: // Shouldn't happen as well, but to be sure + chatWindow->chatLog("Unhandled trade cancel packet", + BY_SERVER); + break; + } + break; + + case SMSG_TRADE_ITEM_ADD: + { + Sint32 amount = msg->readInt32(); + Sint16 type = msg->readInt16(); + msg->readInt8(); // identified flag + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card (4 shorts) + + // TODO: handle also identified, etc + if (type == 0) { + tradeWindow->addMoney(amount); + } else { + tradeWindow->addItem(type, false, amount, false); + } + } + break; + + case SMSG_TRADE_ITEM_ADD_RESPONSE: + // Trade: New Item add response (was 0x00ea, now 01b1) + { + Item *item = player_node->getInvItem(msg->readInt16()); + Sint16 quantity = msg->readInt16(); + + switch (msg->readInt8()) + { + case 0: + // Successfully added item + if (item->isEquipment() && item->isEquipped()) + { + player_node->unequipItem(item); + } + tradeWindow->addItem(item->getId(), true, quantity, + item->isEquipment()); + item->increaseQuantity(-quantity); + break; + case 1: + // Add item failed - player overweighted + chatWindow->chatLog("Failed adding item. Trade " + "partner is over weighted.", + BY_SERVER); + break; + default: + chatWindow->chatLog("Failed adding item for " + "unknown reason.", BY_SERVER); + break; + } + } + break; + + case SMSG_TRADE_OK: + // 0 means ok from myself, 1 means ok from other; + tradeWindow->receivedOk(msg->readInt8() == 0); + break; + + case SMSG_TRADE_CANCEL: + chatWindow->chatLog("Trade canceled.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + + case SMSG_TRADE_COMPLETE: + chatWindow->chatLog("Trade completed.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + } +} diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h new file mode 100644 index 00000000..a1971004 --- /dev/null +++ b/src/net/tradehandler.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_TRADEHANDLER_H +#define _TMW_NET_TRADEHANDLER_H + +#include "messagehandler.h" + +class Network; + +class TradeHandler : public MessageHandler +{ + public: + TradeHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/npc.cpp b/src/npc.cpp new file mode 100644 index 00000000..fd894969 --- /dev/null +++ b/src/npc.cpp @@ -0,0 +1,99 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "npc.h" + +#include "graphics.h" + +#include "graphic/spriteset.h" + +#include "net/messageout.h" +#include "net/protocol.h" + +extern Spriteset *npcset; + +NPC *current_npc = 0; + +NPC::NPC(Uint32 id, Uint16 job, Map *map, Network *network): + Being(id, job, map), mNetwork(network) +{ +} + +Being::Type NPC::getType() const +{ + return Being::NPC; +} + +void NPC::draw(Graphics *graphics, int offsetX, int offsetY) +{ + int px = mPx + offsetX; + int py = mPy + offsetY; + + graphics->drawImage(npcset->spriteset[job - 100], px - 8, py - 52); + + Being::draw(graphics, offsetX, offsetY); +} + +void NPC::talk() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_TALK); + outMsg.writeInt32(mId); + outMsg.writeInt8(0); + current_npc = this; +} + +void NPC::nextDialog() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_NEXT_REQUEST); + outMsg.writeInt32(mId); +} + +void NPC::dialogChoice(char choice) +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_LIST_CHOICE); + outMsg.writeInt32(mId); + outMsg.writeInt8(choice); +} + +/* + * TODO Unify the buy() and sell() methods, without sacrificing readability of + * the code calling the method. buy(bool buySell) would be bad... + */ +void NPC::buy() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(mId); + outMsg.writeInt8(0); +} + +void NPC::sell() +{ + MessageOut outMsg(mNetwork); + outMsg.writeInt16(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(mId); + outMsg.writeInt8(1); +} diff --git a/src/npc.h b/src/npc.h new file mode 100644 index 00000000..45b3d0c3 --- /dev/null +++ b/src/npc.h @@ -0,0 +1,53 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NPC_H +#define _TMW_NPC_H + +#include "being.h" + +class Network; + +class NPC : public Being +{ + public: + NPC(Uint32 id, Uint16 job, Map *map, Network *network); + + virtual Type getType() const; + + virtual void draw(Graphics *graphics, int offsetX, int offsetY); + + void talk(); + void nextDialog(); + void dialogChoice(char choice); + + void buy(); + void sell(); + + protected: + Network *mNetwork; +}; + +extern NPC *current_npc; + +#endif diff --git a/src/player.cpp b/src/player.cpp new file mode 100644 index 00000000..162f2e79 --- /dev/null +++ b/src/player.cpp @@ -0,0 +1,133 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "player.h" + +#include "game.h" +#include "graphics.h" + +#include "graphic/spriteset.h" + +#include "gui/gui.h" + +extern Spriteset *hairset; +extern Spriteset *playerset; +extern Spriteset *weaponset; + +signed char hairtable[16][4][2] = { + // S(x,y) W(x,y) N(x,y) E(x,y) + { { 0, 0}, {-1, 2}, {-1, 2}, { 0, 2} }, // STAND + { { 0, 2}, {-2, 3}, {-1, 2}, { 1, 3} }, // WALK 1st frame + { { 0, 3}, {-2, 4}, {-1, 3}, { 1, 4} }, // WALK 2nd frame + { { 0, 1}, {-2, 2}, {-1, 2}, { 1, 2} }, // WALK 3rd frame + { { 0, 2}, {-2, 3}, {-1, 2}, { 1, 3} }, // WALK 4th frame + { { 0, 1}, { 1, 2}, {-1, 3}, {-2, 2} }, // ATTACK 1st frame + { { 0, 1}, {-1, 2}, {-1, 3}, { 0, 2} }, // ATTACK 2nd frame + { { 0, 2}, {-4, 3}, { 0, 4}, { 3, 3} }, // ATTACK 3rd frame + { { 0, 2}, {-4, 3}, { 0, 4}, { 3, 3} }, // ATTACK 4th frame + { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 1st frame + { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 2nd frame + { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 3rd frame + { { 0, 0}, {-1, 2}, {-1, 2}, {-1, 2} }, // BOW_ATTACK 4th frame + { { 0, 4}, {-1, 6}, {-1, 6}, { 0, 6} }, // SIT + { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, // ?? HIT + { { 0, 16}, {-1, 6}, {-1, 6}, { 0, 6} } // DEAD +}; + +Player::Player(Uint32 id, Uint16 job, Map *map): + Being(id, job, map) +{ +} + +void Player::logic() +{ + switch (action) { + case WALK: + mFrame = (get_elapsed_time(walk_time) * 4) / mWalkSpeed; + break; + + case ATTACK: + mFrame = (get_elapsed_time(walk_time) * 4) / aspd; + break; + } + + if (mFrame >= 4) { + nextStep(); + } + + Being::logic(); +} + +Being::Type Player::getType() const +{ + return PLAYER; +} + +void Player::draw(Graphics *graphics, int offsetX, int offsetY) +{ + unsigned char dir = direction / 2; + int px = mPx + offsetX; + int py = mPy + offsetY; + int frame = action; + + frame = action; + + if (action != SIT && action != DEAD) + { + frame += mFrame; + } + + if (action == ATTACK && getWeapon() > 0) + { + frame += 4 * (getWeapon() - 1); + } + + graphics->drawImage(playerset->spriteset[frame + 16 * dir], + px - 16, py - 32); + + if (getWeapon() != 0 && action == ATTACK) + { + Image *image = weaponset->spriteset[ + 16 * (getWeapon() - 1) + 4 * mFrame + dir]; + + graphics->drawImage(image, px - 64, py - 80); + } + + if (getHairColor() <= NR_HAIR_COLORS) + { + int hf = getHairColor() - 1 + 10 * (dir + 4 * + (getHairStyle() - 1)); + + graphics->drawImage(hairset->spriteset[hf], + px - 2 + 2 * hairtable[frame][dir][0], + py - 50 + 2 * hairtable[frame][dir][1]); + } + + // Draw player name + if (getType() != LOCALPLAYER) { + graphics->setFont(speechFont); + graphics->drawText(mName, px + 15, py + 30, gcn::Graphics::CENTER); + } + + Being::draw(graphics, offsetX, offsetY); +} diff --git a/src/player.h b/src/player.h new file mode 100644 index 00000000..5d75a1db --- /dev/null +++ b/src/player.h @@ -0,0 +1,44 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_PLAYER_H +#define _TMW_PLAYER_H + +#include "being.h" + +class Graphics; +class Map; + +class Player : public Being +{ + public: + Player(Uint32 id, Uint16 job, Map *map); + + virtual void logic(); + + virtual Type getType() const; + + virtual void draw(Graphics *graphics, int offsetX, int offsetY); +}; + +#endif diff --git a/src/resources/mapreader.h b/src/resources/mapreader.h index 1e4ada64..eb5a8577 100644 --- a/src/resources/mapreader.h +++ b/src/resources/mapreader.h @@ -24,12 +24,10 @@ #ifndef _TMW_MAPREADER_H_ #define _TMW_MAPREADER_H_ -#include <vector> +#include <iosfwd> #include <libxml/tree.h> -#include "../graphic/spriteset.h" - class Map; class Tileset; |