summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjörn Steinbrink <B.Steinbrink@gmx.de>2006-01-22 13:31:13 +0000
committerBjörn Steinbrink <B.Steinbrink@gmx.de>2006-01-22 13:31:13 +0000
commitbd56bf8afdab16383ed8ad08412a8c807f84af85 (patch)
tree0e963ada63bcbe3c50dd77986aaa15b9ba49816a /src
parent5359640b6f271af31f6423df9d661433eff89a3e (diff)
downloadmana-client-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.gz
mana-client-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.bz2
mana-client-bd56bf8afdab16383ed8ad08412a8c807f84af85.tar.xz
mana-client-bd56bf8afdab16383ed8ad08412a8c807f84af85.zip
Merged NETWORK branch (includes BEING_OVERHAUL).
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am45
-rw-r--r--src/being.cpp278
-rw-r--r--src/being.h48
-rw-r--r--src/beingmanager.cpp143
-rw-r--r--src/beingmanager.h88
-rw-r--r--src/engine.cpp68
-rw-r--r--src/engine.h8
-rw-r--r--src/equipment.cpp18
-rw-r--r--src/equipment.h28
-rwxr-xr-xsrc/floor_item.cpp4
-rwxr-xr-xsrc/floor_item.h2
-rw-r--r--src/game.cpp1444
-rw-r--r--src/game.h18
-rw-r--r--src/gui/buddywindow.cpp12
-rw-r--r--src/gui/buddywindow.h6
-rw-r--r--src/gui/buy.cpp8
-rw-r--r--src/gui/buy.h5
-rw-r--r--src/gui/buysell.cpp24
-rw-r--r--src/gui/buysell.h7
-rw-r--r--src/gui/char_select.cpp324
-rw-r--r--src/gui/char_select.h59
-rw-r--r--src/gui/char_server.cpp160
-rw-r--r--src/gui/char_server.h13
-rw-r--r--src/gui/chargedialog.cpp10
-rw-r--r--src/gui/chargedialog.h2
-rw-r--r--src/gui/chat.cpp13
-rw-r--r--src/gui/chat.h4
-rw-r--r--src/gui/connection.cpp113
-rw-r--r--src/gui/connection.h22
-rw-r--r--src/gui/equipmentwindow.cpp9
-rw-r--r--src/gui/equipmentwindow.h7
-rw-r--r--src/gui/error.cpp8
-rw-r--r--src/gui/error.h6
-rw-r--r--src/gui/gui.cpp35
-rw-r--r--src/gui/inventorywindow.cpp17
-rw-r--r--src/gui/item_amount.cpp6
-rw-r--r--src/gui/linkhandler.h3
-rw-r--r--src/gui/login.cpp175
-rw-r--r--src/gui/login.h27
-rw-r--r--src/gui/minimap.cpp55
-rw-r--r--src/gui/ministatus.cpp14
-rw-r--r--src/gui/ministatus.h13
-rw-r--r--src/gui/npc_text.cpp23
-rw-r--r--src/gui/npc_text.h5
-rw-r--r--src/gui/npclistdialog.cpp (renamed from src/gui/npc.cpp)12
-rw-r--r--src/gui/npclistdialog.h (renamed from src/gui/npc.h)4
-rw-r--r--src/gui/popupmenu.cpp34
-rw-r--r--src/gui/register.cpp173
-rw-r--r--src/gui/register.h25
-rw-r--r--src/gui/requesttrade.cpp54
-rw-r--r--src/gui/requesttrade.h7
-rw-r--r--src/gui/sell.cpp7
-rw-r--r--src/gui/sell.h4
-rw-r--r--src/gui/setup.cpp17
-rw-r--r--src/gui/skill.cpp24
-rw-r--r--src/gui/skill.h23
-rw-r--r--src/gui/status.cpp125
-rw-r--r--src/gui/status.h4
-rw-r--r--src/gui/trade.cpp19
-rw-r--r--src/gui/trade.h4
-rw-r--r--src/gui/updatewindow.cpp8
-rw-r--r--src/gui/updatewindow.h3
-rw-r--r--src/inventory.cpp44
-rw-r--r--src/inventory.h16
-rw-r--r--src/localplayer.cpp386
-rw-r--r--src/localplayer.h149
-rw-r--r--src/lockedarray.h110
-rw-r--r--src/logindata.h35
-rw-r--r--src/main.cpp149
-rw-r--r--src/main.h3
-rw-r--r--src/map.cpp12
-rw-r--r--src/monster.cpp101
-rw-r--r--src/monster.h (renamed from src/playerinfo.h)34
-rw-r--r--src/net/beinghandler.cpp356
-rw-r--r--src/net/beinghandler.h37
-rw-r--r--src/net/buysellhandler.cpp129
-rw-r--r--src/net/buysellhandler.h37
-rw-r--r--src/net/charserverhandler.cpp210
-rw-r--r--src/net/charserverhandler.h48
-rw-r--r--src/net/chathandler.cpp118
-rw-r--r--src/net/chathandler.h37
-rw-r--r--src/net/equipmenthandler.cpp210
-rw-r--r--src/net/equipmenthandler.h37
-rw-r--r--src/net/inventoryhandler.cpp128
-rw-r--r--src/net/inventoryhandler.h37
-rw-r--r--src/net/itemhandler.cpp67
-rw-r--r--src/net/itemhandler.h37
-rw-r--r--src/net/loginhandler.cpp114
-rw-r--r--src/net/loginhandler.h37
-rw-r--r--src/net/maploginhandler.cpp63
-rw-r--r--src/net/maploginhandler.h37
-rw-r--r--src/net/messagehandler.cpp45
-rw-r--r--src/net/messagehandler.h48
-rw-r--r--src/net/messageout.cpp49
-rw-r--r--src/net/messageout.h26
-rw-r--r--src/net/network.cpp505
-rw-r--r--src/net/network.h86
-rw-r--r--src/net/npchandler.cpp74
-rw-r--r--src/net/npchandler.h37
-rw-r--r--src/net/playerhandler.cpp301
-rw-r--r--src/net/playerhandler.h37
-rw-r--r--src/net/protocol.cpp106
-rw-r--r--src/net/protocol.h23
-rw-r--r--src/net/skillhandler.cpp93
-rw-r--r--src/net/skillhandler.h37
-rw-r--r--src/net/tradehandler.cpp179
-rw-r--r--src/net/tradehandler.h39
-rw-r--r--src/npc.cpp99
-rw-r--r--src/npc.h53
-rw-r--r--src/player.cpp133
-rw-r--r--src/player.h44
-rw-r--r--src/resources/mapreader.h4
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());
}
}
diff --git a/src/game.h b/src/game.h
index 49696300..38633560 100644
--- a/src/game.h
+++ b/src/game.h
@@ -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);
diff --git a/src/main.h b/src/main.h
index a939879b..679d63cf 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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;